diff --git a/.bzrignore b/.bzrignore deleted file mode 100644 index 8cdc9d12d..000000000 --- a/.bzrignore +++ /dev/null @@ -1,65 +0,0 @@ -00_header -10_* -30_os-prober -40_custom -aclocal.m4 -autom4te.cache -build_env.mk -.bzrignore -config.cache -config.guess -config.h -config.h.in -config.log -config.status -config.sub -configure -conf/*.mk -conf/gcry.rmk -*.d -DISTLIST -docs/*.info -docs/stamp-vti -docs/version.texi -*.elf -*.exec -genkernsyms.sh -gensymlist.sh -grub-dumpbios -grub-editenv -grub-emu -grub_emu_init.c -grub_emu_init.h -grub-fstest -grub_fstest_init.c -grub_fstest_init.h -grub-install -grub-mk* -grub-pbkdf2 -grub-pe2elf -grub-probe -grub_probe_init.c -grub_probe_init.h -grub_script.tab.c -grub_script.tab.h -grub-setup -grub_setup_init.c -grub_setup_init.h -*.img -include/grub/cpu -include/grub/machine -install-sh -lib/libgcrypt-grub -*.lst -Makefile -*.mod -mod-*.c -missing -*.pf2 -po/*.mo -po/grub.pot -stamp-h -stamp-h1 -stamp-h.in -symlist.c -update-grub_lib diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..33ffaa404 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +po/exclude.pot binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..524f2e6d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,285 @@ +# +# Ignore patterns in this directory and all subdirectories. +# +*.1 +*.8 +*.a +*.exec +*.exec.exe +*.image +*.image.exe +*.img +*.log +*.lst +*.marker +*.mod +*.o +*.pf2 +*.pp +*.pyc +*.trs +*~ +.deps-core/ +.deps-util/ +.deps/ +.dirstamp +DISTLIST +GPATH +GRTAGS +GSYMS +GTAGS +Makefile +Makefile.in +ascii.bitmaps +genkernsyms.sh +gensymlist.sh +grub-bin2h +grub-emu +grub-emu-lite +grub-emu-lite.exe +grub-emu.exe +grub-macho2img +grub_emu_init.c +grub_emu_init.h +grub_probe_init.c +grub_probe_init.h +grub_script.tab.c +grub_script.tab.h +grub_script.yy.c +grub_script.yy.h +grub_script_check_init.c +grub_script_check_init.h +grub_setup_init.c +grub_setup_init.h +mdate-sh +mod-*.c +update-grub_lib +widthspec.bin + +# +# Ignore patterns relative to this .gitignore file's directory. +# +/00_header +/10_* +/20_linux_xen +/30_os-prober +/30_uefi-firmware +/40_custom +/41_custom +/ABOUT-NLS +/ChangeLog +/INSTALL.grub +/Makefile.util.am +/Makefile.utilgcry.def +/aclocal.m4 +/ahci_test +/ascii.h +/autom4te.cache/ +/btrfs_test +/build-aux/ +/build-grub-gen-asciih +/build-grub-gen-widthspec +/build-grub-mkfont +/cdboot_test +/cmp_test +/compile +/config-util.h +/config-util.h.in +/config.cache +/config.guess +/config.h +/config.log +/config.status +/config.sub +/configure +/contrib +/core_compress_test +/cpio_test +/date_test +/depcomp +/docs/*.info +/docs/*.info-[0-9]* +/docs/stamp-1 +/docs/stamp-vti +/docs/version-dev.texi +/docs/version.texi +/ehci_test +/erofs_test +/example_grub_script_test +/example_scripted_test +/example_unit_test +/exfat_test +/ext234_test +/f2fs_test +/fat_test +/fddboot_test +/file_filter_test +/garbage-gen +/garbage-gen.exe +/gettext_strings_test +/gnulib/ +/grub-2.[0-9]*/ +/grub-2.[0-9]*.tar.gz +/grub-bios-setup +/grub-bios-setup.exe +/grub-core/*.module +/grub-core/*.module.exe +/grub-core/*.pp +/grub-core/Makefile.core.am +/grub-core/Makefile.gcry.def +/grub-core/bootinfo.txt +/grub-core/build-grub-module-verifier +/grub-core/build-grub-pe2elf.exe +/grub-core/contrib +/grub-core/gdb_grub +/grub-core/genmod.sh +/grub-core/gensyminfo.sh +/grub-core/gentrigtables +/grub-core/gentrigtables.exe +/grub-core/gmodule.pl +/grub-core/grub.chrp +/grub-core/kernel.img.bin +/grub-core/lib/gnulib +/grub-core/lib/libgcrypt-grub +/grub-core/lib/libtasn1-grub +/grub-core/modinfo.sh +/grub-core/rs_decoder.h +/grub-core/symlist.c +/grub-core/symlist.h +/grub-core/tests/asn1/tests +/grub-core/trigtables.c +/grub-core/unidata.c +/grub-editenv +/grub-editenv.exe +/grub-file +/grub-file.exe +/grub-fs-tester +/grub-fstest +/grub-fstest.exe +/grub-glue-efi +/grub-glue-efi.exe +/grub-install +/grub-install.exe +/grub-kbdcomp +/grub-macbless +/grub-macbless.exe +/grub-menulst2cfg +/grub-menulst2cfg.exe +/grub-mk* +/grub-mount +/grub-ofpathname +/grub-ofpathname.exe +/grub-probe +/grub-probe.exe +/grub-protect +/grub-protect.exe +/grub-reboot +/grub-render-label +/grub-render-label.exe +/grub-script-check +/grub-script-check.exe +/grub-set-default +/grub-shell +/grub-shell-tester +/grub-sparc64-setup +/grub-sparc64-setup.exe +/grub-syslinux2cfg +/grub-syslinux2cfg.exe +/grub_cmd_date +/grub_cmd_echo +/grub_cmd_regexp +/grub_cmd_set_date +/grub_cmd_sleep +/grub_cmd_test +/grub_cmd_tr +/grub_fstest_init.c +/grub_fstest_init.h +/grub_func_test +/grub_script_blanklines +/grub_script_blockarg +/grub_script_break +/grub_script_comments +/grub_script_continue +/grub_script_dollar +/grub_script_echo1 +/grub_script_echo_keywords +/grub_script_escape_comma +/grub_script_eval +/grub_script_expansion +/grub_script_final_semicolon +/grub_script_for1 +/grub_script_functions +/grub_script_gettext +/grub_script_if +/grub_script_leading_whitespace +/grub_script_no_commands +/grub_script_not +/grub_script_return +/grub_script_setparams +/grub_script_shift +/grub_script_strcmp +/grub_script_test +/grub_script_vars1 +/grub_script_while1 +/gzcompress_test +/hddboot_test +/help_test +/hfs_test +/hfsplus_test +/include/grub/cpu +/include/grub/gcrypt/g10lib.h +/include/grub/gcrypt/gcrypt.h +/include/grub/machine +/install-sh +/iso9660_test +/jfs_test +/lib/libgcrypt-grub +/libgrub_a_init.c +/lzocompress_test +/luks1_test +/luks2_test +/m4/ +/minixfs_test +/missing +/netboot_test +/nilfs2_test +/ntfs_test +/ohci_test +/partmap_test +/pata_test +/po/*.gmo +/po/*.mo +/po/*.po +/po/LINGUAS +/po/Makefile.in.in +/po/Makevars +/po/Makevars.template +/po/POTFILES +/po/POTFILES-shell.in +/po/POTFILES.in +/po/Rules-quot +/po/grub.pot +/po/remove-potcdate.sed +/po/stamp-po +/printf_test +/priority_queue_unit_test +/pseries_test +/reiserfs_test +/romfs_test +/squashfs_test +/stamp-h +/stamp-h.in +/stamp-h1 +/syslinux_test +/tar_test +/test_sha512sum +/test_unset +/tests/syslinux/ubuntu10.04_grub.cfg +/texinfo.tex +/udf_test +/uhci_test +/util/bash-completion.d/grub +/widthspec.h +/xfs_test +/xzcompress_test +/zfs_test diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..4bd05a30a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: GPL-3.0+ +# Originally Copyright Roger Meier +# Adapted for GRUB by Alexander Graf +# +# Build GRUB on Travis CI - https://www.travis-ci.org/ +# + +dist: xenial + +language: c + +addons: + apt: + packages: + - autopoint + - libsdl1.2-dev + - lzop + - ovmf + - python + - qemu-system + - unifont + +env: + global: + # Include all cross toolchain paths, so we can just call them later down. + - PATH=/tmp/qemu-install/bin:/tmp/grub/bin:/usr/bin:/bin:/tmp/cross/gcc-8.1.0-nolibc/aarch64-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/arm-linux-gnueabi/bin:/tmp/cross/gcc-8.1.0-nolibc/ia64-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/mips64-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/powerpc64-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/riscv32-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/riscv64-linux/bin:/tmp/cross/gcc-8.1.0-nolibc/sparc64-linux/bin + +before_script: + # Install necessary toolchains based on $CROSS_TARGETS variable. + - mkdir /tmp/cross + # These give us binaries like /tmp/cross/gcc-8.1.0-nolibc/ia64-linux/bin/ia64-linux-gcc + - for i in $CROSS_TARGETS; do + ( cd /tmp/cross; wget -t 3 -O - https://mirrors.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.1.0/x86_64-gcc-8.1.0-nolibc-$i.tar.xz | tar xJ ); + done + +script: + # Comments must be outside the command strings below, or the Travis parser + # will get confused. + - ./bootstrap + + # Build all selected GRUB targets. + - for target in $GRUB_TARGETS; do + plat=${target#*-}; + arch=${target%-*}; + [ "$arch" = "arm64" ] && arch=aarch64-linux; + [ "$arch" = "arm" ] && arch=arm-linux-gnueabi; + [ "$arch" = "ia64" ] && arch=ia64-linux; + [ "$arch" = "mipsel" ] && arch=mips64-linux; + [ "$arch" = "powerpc" ] && arch=powerpc64-linux; + [ "$arch" = "riscv32" ] && arch=riscv32-linux; + [ "$arch" = "riscv64" ] && arch=riscv64-linux; + [ "$arch" = "sparc64" ] && arch=sparc64-linux; + echo "Building $target"; + mkdir obj-$target; + JOBS=`getconf _NPROCESSORS_ONLN 2> /dev/null || echo 1`; + [ "$JOBS" == 1 ] || JOBS=$(($JOBS + 1)); + ( cd obj-$target && ../configure --target=$arch --with-platform=$plat --prefix=/tmp/grub && make -j$JOBS && make -j$JOBS install ) &> log || ( cat log; false ); + done + + # Our test canary. + - echo -e "insmod echo\\ninsmod reboot\\necho hello world\\nreboot" > grub.cfg + + # Assemble images and possibly run them. + - for target in $GRUB_TARGETS; do grub-mkimage -c grub.cfg -p / -O $target -o grub-$target echo reboot normal; done + + # Run images we know how to run. + - if [[ "$GRUB_TARGETS" == *"x86_64-efi"* ]]; then qemu-system-x86_64 -bios /usr/share/ovmf/OVMF.fd -m 512 -no-reboot -nographic -net nic -net user,tftp=.,bootfile=grub-x86_64-efi | tee grub.log && grep "hello world" grub.log; fi + +matrix: + include: + # Each env setting here is a dedicated build. + - name: "x86_64" + env: + - GRUB_TARGETS="x86_64-efi x86_64-xen" + - name: "i386" + env: + - GRUB_TARGETS="i386-coreboot i386-efi i386-ieee1275 i386-multiboot i386-pc i386-qemu i386-xen i386-xen_pvh" + - name: "powerpc" + env: + - GRUB_TARGETS="powerpc-ieee1275" + - CROSS_TARGETS="powerpc64-linux" + - name: "sparc64" + env: + - GRUB_TARGETS="sparc64-ieee1275" + - CROSS_TARGETS="sparc64-linux" + - name: "ia64" + env: + - GRUB_TARGETS="ia64-efi" + - CROSS_TARGETS="ia64-linux" + - name: "mips" + env: + - GRUB_TARGETS="mips-arc mipsel-arc mipsel-qemu_mips mips-qemu_mips" + - CROSS_TARGETS="mips64-linux" + - name: "arm" + env: + - GRUB_TARGETS="arm-coreboot arm-efi arm-uboot" + - CROSS_TARGETS="arm-linux-gnueabi" + - name: "arm64" + env: + - GRUB_TARGETS="arm64-efi" + - CROSS_TARGETS="aarch64-linux" + - name: "riscv32" + env: + - GRUB_TARGETS="riscv32-efi" + - CROSS_TARGETS="riscv32-linux" + - name: "riscv64" + env: + - GRUB_TARGETS="riscv64-efi" + - CROSS_TARGETS="riscv64-linux" diff --git a/BUGS b/BUGS new file mode 100644 index 000000000..46faa6452 --- /dev/null +++ b/BUGS @@ -0,0 +1,7 @@ +GRUB team is aware of following problems: + - Currently search and assembling multidevice abstractions scans + all the devices which can be slow. + - Cache isn't used correctly for video which results in slowness. + +While these are bugs their solution has a potential of breaking more and more +seriously. So it was decided for 1.99 that they aren't fixed. diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 56f19acde..000000000 --- a/ChangeLog +++ /dev/null @@ -1,25216 +0,0 @@ -2010-03-06 Vladimir Serbinenko - - * NEWS: Put the date of 1.98 release. - -2010-03-06 Vladimir Serbinenko - - * configure.ac: Update CPPFLAGS and not CFLAGS when checking for - ft2build.h. - -2010-03-06 Vladimir Serbinenko - - * normal/cmdline.c (grub_cmdline_get): Fix gabled line after - completition in the middle of string. - -2010-03-06 Vladimir Serbinenko - - * util/grub-mkrescue.in: Use mktemp with explicit template. - -2010-03-06 Vladimir Serbinenko - - * loader/i386/bsd.c (grub_bsd_get_device): Fix a memory leak. - -2010-03-06 Vladimir Serbinenko - - * loader/i386/multiboot_mbi.c (grub_multiboot_set_bootdev): Free the - right pointer. - -2010-03-05 Vladimir Serbinenko - - Fix FreeBSD compilation. - - * Makefile.in (TARGET_CPPFLAGS): Remove -nostdinc -isystem. - * configure.ac: Add -nostdinc -isystem to TARGET_CPPFLAGS if it works. - -2010-03-05 Vladimir Serbinenko - - * util/import_gcry.py: Add autogenerated files to MAINTAINER_CLEANFILES. - -2010-03-04 Vladimir Serbinenko - - * gettext/gettext.c (grub_gettext_init_ext): Fix a memory leak. - -2010-03-04 Vladimir Serbinenko - - * disk/scsi.c (grub_scsi_iterate): Fix a memory leak. - -2010-03-04 Robert Millan - - Support relative image path in theme file. - - * gfxmenu/gui_image.c (grub_gui_image): New member theme_dir. - (image_set_property): Handle theme_dir and relative path. - -2010-03-04 Vladimir Serbinenko - - * configure.ac: Alias amd64 to x86_64. - -2010-03-04 Vladimir Serbinenko - - * NEWS: mention multiboot on EFI. - -2010-03-04 Vladimir Serbinenko - - * kern/main.c (grub_load_modules): Handle errors from init functions of - embeded modules. - -2010-03-04 Vladimir Serbinenko - - * normal/autofs.c (autoload_fs_module): Handle errors. - -2010-03-04 Vladimir Serbinenko - - Disable linux.mod on qemu-mips since it's not functional and leads - to compilation failure. - - * conf/mips.rmk (pkglib_MODULES): Remove linux.mod. - * conf/mips-yeeloong.rmk (pkglib_MODULES): Add linux.mod. - * conf/mips.rmk (linux_mod_SOURCES): Move from here ... - * conf/mips-yeeloong.rmk (linux_mod_SOURCES): ... here - * conf/mips.rmk (linux_mod_CFLAGS): Move from here ... - * conf/mips-yeeloong.rmk (linux_mod_CFLAGS): ... here - * conf/mips.rmk (linux_mod_ASFLAGS): Move from here ... - * conf/mips-yeeloong.rmk (linux_mod_ASFLAGS): ... here - * conf/mips.rmk (linux_mod_LDFLAGS): Move from here ... - * conf/mips-yeeloong.rmk (linux_mod_LDFLAGS): ... here - Reported by: BVK Chaitanya - -2010-03-04 Jordan Uggla - - * INSTALL: Add gettext as a dependency and add qemu to a new section - "Prerequisites for make-check". - -2010-03-04 Christian Franke - - * util/grub-pe2elf.c: Add missing include "progname.h". - -2010-03-04 Vladimir Serbinenko - - * normal/crypto.c (read_crypto_list): Fix a typo. - Reported by: Seth Goldberg. - -2010-03-04 Vladimir Serbinenko - - * Makefile.in (DISTCLEANFILES): Add stamp-h1. - Reported by: Seth Goldberg. - -2010-03-04 Vladimir Serbinenko - - * Makefile.in (CLEANFILES) [FONT_SOURCE && grub_mkfont]: Add - ascii.bitmaps. - -2010-03-04 Vladimir Serbinenko - - * genmk.rb: Remove terminal*.lst in make clean. - Reported by: Seth Goldberg. - -2010-03-04 Vladimir Serbinenko - - * util/i386/efi/grub-install.in: Copy gettext files. - -2010-03-01 Vladimir Serbinenko - - * fs/ext2.c (grub_ext2_read_block): Fix an integer overflow. - -2010-03-01 Vladimir Serbinenko - - Wait for user entry basing on presence of output rather than on errors. - - * include/grub/normal.h (grub_normal_get_line_counter): New proto. - (grub_install_newline_hook): Likewise. - * normal/main.c (GRUB_MOD_INIT): Call grub_install_newline_hook. - * normal/menu.c (show_menu): Check line_counter to determine presence - of output. - * normal/term.c (grub_normal_line_counter): New variable. - (grub_normal_get_line_counter): New function. - (grub_install_newline_hook): Likewise. - -2010-03-01 Vladimir Serbinenko - - * commands/cat.c (grub_cmd_cat): Propagate grub_gzfile_open error. - -2010-03-01 Vladimir Serbinenko - - * configure.ac: Update version to 1.98. - -2010-02-26 Vladimir Serbinenko - - * util/grub.d/10_linux.in (linux_entry): Don't default to - gfxpayload=keep if Linux doesn't support video handover. - -2010-02-25 Vladimir Serbinenko - - Don't compile video modules on yeeloong since video subsystem is part - of kernel. - - * conf/common.rmk (pkglib_MODULES) [yeeloong]: Remove video.mod, - video_fb.mod, bitmap.mod, font.mod, gfxterm.mod and bufio.mod - * conf/mips-yeeloong.rmk (kernel_img_HEADERS): Add bitmap.h, - video.h, gfxterm.h, font.h, bitmap_scale.h and bufio.h. - * conf/mips.rmk (kernel_img_HEADERS): Add values instead of overwriting. - * include/grub/bitmap.h: Add EXPORT_FUNC and EXPORT_VAR. - * include/grub/bitmap_scale.h: Likewise. - * include/grub/bufio.h: Likewise. - * include/grub/font.h: Likewise. - * include/grub/gfxterm.h: Likewise. - * include/grub/video.h: Likewise. - * include/grub/vbe.h: Don't include video_fb.h. - * video/i386/pc/vbe.c: Include video_fb.h. - * commands/i386/pc/vbetest.c: Include video.h. - -2010-02-25 Jordan Uggla - - * util/grub-mkconfig.in (GRUB_SAVEDEFAULT): Export new variable. - * util/grub-mkconfig_lib.in (save_default_entry): Only save a new - default entry if GRUB_SAVEDEFAULT=true. This allows using - GRUB_DEFAULT=saved on its own to let grub-reboot work, without - saving a new default on every boot. - -2010-02-24 Vladimir Serbinenko - - * normal/crypto.c (read_crypto_list): Fix a memory leak. - * normal/term.c (read_terminal_list): Likewise. - * normal/main.c (grub_normal_init_page): Likewise. - (grub_normal_read_line_real): Likewise. - -2010-02-24 Vladimir Serbinenko - - * loader/i386/multiboot_mbi.c (grub_multiboot_set_bootdev): Fix a - memory leak. - Reported by: Seth Goldberg. - -2010-02-24 Joey Korkames - - * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Remove - duplicate declaration of `start'. - -2010-02-20 Vladimir Serbinenko - - * fs/iso9660.c (grub_iso9660_iterate_dir): Strip version from joliet - filename. - Reported by: Georgy Buranov - -2010-02-20 Carles Pina i Estany - - * util/grub-mkrawimage.c (usage): Change string formatting to - improve gettext. - -2010-02-20 Manoel Rebelo Abranches - - * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Add delete and - backspace keys. - -2010-02-20 Vladimir Serbinenko - - * video/fb/video_fb.c (grub_video_fb_scroll): Fix a pixel size bug. - Reported by: Michael Suchanek. - -2010-02-18 Samuel Thibault - - * util/grub-mkconfig.in: Export GRUB_INIT_TUNE. - * util/grub.d/00_header.in: Handle GRUB_INIT_TUNE. - -2010-02-16 Vladimir Serbinenko - - Remove any reference to non-free fonts. - - * commands/videotest.c (grub_cmd_videotest): Use unifont by default. - * docs/gfxmenu-theme-example.txt: Removed. It's both outdated and - uses non-free components. - * font/font.c (grub_font_get_name): Remove example name. - * gfxmenu/gui_label.c (grub_gui_label_new): Use unifont by default. - * gfxmenu/gui_list.c (grub_gui_list_new): Likewise. - * gfxmenu/gui_progress_bar.c (grub_gui_progress_bar_new): Likewise. - * gfxmenu/view.c (grub_gfxmenu_view_new): Likewise. - -2010-02-16 Georgy Buranov - - * disk/efi/efidisk.c (grub_efidisk_get_device_name): Fix a typo. - -2010-02-15 Vladimir Serbinenko - - * term/serial.c (serial_get_divisor) [GRUB_MACHINE_MIPS_YEELOONG]: - Double divisor. - (serial_hw_init) [GRUB_MACHINE_MIPS_YEELOONG]: Don't enable advanced - features. - (GRUB_MOD_INIT) [GRUB_MACHINE_MIPS_YEELOONG]: Default to 115200. - -2010-02-15 Vladimir Serbinenko - - * gensymlist.sh.in: Use TARGET_CC instead of CC. - -2010-02-14 Samuel Thibault - - * commands/i386/pc/play.c (GRUB_MOD_INIT(play)): Fix help. - * docs/grub.texi (Command-line and menu entry commands): Document play - command. - -2010-02-14 Samuel Thibault - - * commands/i386/pc/play.c (grub_cmd_play): If grub_file_open fails, - parse arguments as inline tempo and notes. Move code for playing notes - to... - (play): ... new function. - -2010-02-14 Samuel Thibault - - * commands/i386/pc/play.c (T_REST, T_FINE, struct note, beep_on): Use - grub_uint16_t instead of short. - (grub_cmd_play): Use grub_uint32_t instead of int, convert data from - disk from little endian to cpu endianness. - -2010-02-07 Samuel Thibault - - * commands/i386/pc/play.c (BASE_TEMPO): Set to 60 * - GRUB_TICKS_PER_SECOND instead of 120. - -2010-02-14 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Wait for possible - escape sequence after \e. - -2010-02-14 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_putchar): Don't output - non-ASCII characters. - -2010-02-14 Vladimir Serbinenko - - * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Enclose - set root in single quotes to prevent \, from being unescaped. - -2010-02-14 Vladimir Serbinenko - - Prevent unknown commands from stopping menuentry execution. - - * script/execute.c (grub_script_execute_cmdline): Print error after - unknown command. - -2010-02-14 Vladimir Serbinenko - - * fs/i386/pc/pxe.c (GRUB_MOD_INIT): Fix typo. - Reported by: Pavel Pisa. - -2010-02-13 Vladimir Serbinenko - - * io/gzio.c (grub_gzio_open): Use grub_zalloc. - -2010-02-13 Vladimir Serbinenko - - Merge grub_ieee1275_map_physical into grub_map and rename to - grub_ieee1275_map - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_map): New proto. - * include/grub/sparc64/ieee1275/ieee1275.h (grub_ieee1275_map_physical): - Remove. - * kern/ieee1275/openfw.c (grub_map): Rename to ... - (grub_ieee1275_map): ... this. All users updated. Add phys_lo when - necessary. - * kern/sparc64/ieee1275/ieee1275.c (grub_ieee1275_map_physical): Remove. - -2010-02-13 Vladimir Serbinenko - - * disk/ieee1275/ofdisk.c (grub_ofdisk_open): Check device type before - opening and not after. - -2010-02-13 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Macroify - constants. - -2010-02-13 Vladimir Serbinenko - - * loader/sparc64/ieee1275/linux.c (align_addr): Remove. - (alloc_phys): Use ALIGN_UP instead of align_addr. - -2010-02-13 Vladimir Serbinenko - - * loader/sparc64/ieee1275/linux.c (alloc_phys): Correct bounds checking. - -2010-02-13 Vladimir Serbinenko - - * kern/sparc64/ieee1275/crt0.S (codestart): Move modules backwards. - -2010-02-13 Vladimir Serbinenko - - * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Remove excessively - verbose dprintf. - -2010-02-13 Vladimir Serbinenko - - Fix over-4GiB seek on sparc64. - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_seek): - Replace pos_i and pos_lo with pos. All users updated. - * include/grub/powerpc/ieee1275/ieee1275.h (GRUB_IEEE1275_CELL_SIZEOF): - New constant. - * include/grub/sparc64/ieee1275/ieee1275.h (GRUB_IEEE1275_CELL_SIZEOF): - Likewise. - * kern/ieee1275/ieee1275.c (grub_ieee1275_seek): Split pos into pos_hi - and pos_lo. - -2010-02-13 Vladimir Serbinenko - - * util/grub-mkrawimage.c (main): Call set_program_name. - -2010-02-13 Vladimir Serbinenko - - Properly align 64-bit targets. - - * util/grub-mkrawimage.c (ALIGN_ADDR): New macro. - (generate_image): Use ALIGN_ADDR. - -2010-02-13 Vladimir Serbinenko - - Properly create cross-endian images. - - * include/grub/types.h (grub_host_to_target_addr): New macro - * util/grub-mkrawimage.c (generate_image): Add missing host_to_target. - -2010-02-13 Vladimir Serbinenko - - * util/grub-mkrawimage.c (generate_image): Add forgotten ALIGN_UP. - -2010-02-10 Vladimir Serbinenko - - Pass SIMPLE framebuffer size in bytes and not 64K blocks. - - * loader/i386/efi/linux.c (grub_linux_setup_video): Don't divide by 64K. - * loader/i386/linux.c (grub_linux_setup_video): Likewise. - (grub_linux_boot): Divide by 64K when on VESA. - -2010-02-10 Vladimir Serbinenko - - Support GRUB_GFXPAYLOAD_LINUX. - - * util/grub-mkconfig.in: Export GRUB_GFXPAYLOAD_LINUX. - * util/grub.d/10_linux.in (linux_entry): Handle GRUB_GFXPAYLOAD_LINUX. - -2010-02-10 Vladimir Serbinenko - - * script/execute.c (grub_script_execute_cmdline): Use grub_print_error - to show messages instead of discarding them. - Process errors after executing command and not before. Keep old method - too as precaution. - -2010-02-09 Vladimir Serbinenko - - * configure.ac: Check for ft2build.h. - -2010-02-07 Vladimir Serbinenko - - * kern/ieee1275/openfw.c (grub_halt): Try executing "poweroff". - -2010-02-07 Vladimir Serbinenko - - * genkernsyms.sh.in: Use TARGET_CC. - -2010-02-07 Colin Watson - - * NEWS: Update. - -2010-02-07 Vladimir Serbinenko - - * include/grub/multiboot2.h: Remove leftover file. - * include/grub/normal.h [GRUB_UTIL]: Remove leftover declarations. - * include/grub/partition.h [GRUB_UTIL]: Likewise. - -2010-02-07 Yves Blusseau - - * gnulib/getdelim.c: add missing header (type ssize_t must be defined). - -2010-02-07 Vladimir Serbinenko - - Fix warnings in grub-emu when compiling with maximum warning options. - - * util/grub-emu.c (ENABLE_RELOCATABLE): New definition. - (grub_arch_modules_addr): Return 0 and not NULL. - * util/misc.c (ENABLE_RELOCATABLE): New definition. - (xstrdup): Use newstr instead of dup. - * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Rename one instance - of disk to dsk to avoid shadowing. - (find_free_slot): Fix prototype. - * util/getroot.c (grub_util_is_dmraid): Make static. - * include/grub/time.h (grub_get_rtc) [GRUB_MACHINE_EMU || GRUB_UTIL]: - Add missing prototype. - * util/sdl.c (grub_video_sdl_set_viewport): Remove. - -2010-02-07 Vladimir Serbinenko - - * loader/i386/linux.c (grub_linux_setup_video): Handle error - appropriately. - -2010-02-07 Vladimir Serbinenko - - * fs/reiserfs.c (grub_reiserfs_read): Use #if 0 instead of commenting - code out. - -2010-02-07 Vladimir Serbinenko - - * include/grub/cache.h (grub_arch_sync_caches) [i386 || x86_64]: Inline. - * kern/i386/coreboot/init.c (grub_arch_sync_caches): Remove. - * kern/i386/efi/init.c (grub_arch_sync_caches): Likewise. - * kern/i386/ieee1275/init.c (grub_arch_sync_caches): Likewise. - * kern/i386/pc/init.c (grub_arch_sync_caches): Likewise. - * util/misc.c (grub_arch_sync_caches) [i386 || x86_64]: Likewise. - -2010-02-07 Vladimir Serbinenko - - * include/grub/err.h (grub_err_printf): Don't export. - -2010-02-07 Vladimir Serbinenko - - * include/grub/dl.h (grub_dl_register_symbol): Don't export. - -2010-02-07 Vladimir Serbinenko - - * include/grub/i18n.h (grub_gettext_dummy): Removed. - * kern/misc.c (grub_gettext_dummy): Make static. - -2010-02-06 Vladimir Serbinenko - - * kern/misc.c (grub_utf8_to_ucs4): Don't eat valid characters preceeded - by non-valid ones. - * kern/term.c (grub_putchar): Likewise. - -2010-02-06 Vladimir Serbinenko - - * partmap/sun.c (sun_partition_map_iterate): Restructure flow to fix - buggy hook call and memory leak. - -2010-02-06 Vladimir Serbinenko - - * commands/ls.c (grub_ls_list_files): Free pathname on exit. - -2010-02-06 Vladimir Serbinenko - - * fs/fat.c (grub_fat_iterate_dir): Free unibuf at exit. - -2010-02-06 Vladimir Serbinenko - - * loader/i386/pc/xnu.c (grub_xnu_set_video): Add const qualifier to - modevar. - Return grub_errno on allocation error. - -2010-02-06 Vladimir Serbinenko - - * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Correct error handling. - -2010-02-06 Yves Blusseau - - * conf/common.rmk (grub_script_check_SOURCES): add missing dependencies. - (grub_mkpasswd_pbkdf2_SOURCES): Likewise. - -2010-02-06 Vladimir Serbinenko - - * fs/i386/pc/pxe.c (grub_pxefs_dir): Return with failure on - non-pxe disk. - (grub_pxefs_open): Likewise. - -2010-02-06 Robert Millan - - * util/grub.d/10_hurd.in: Add --class information to menuentries. - * util/grub.d/10_kfreebsd.in: Likewise. - * util/grub.d/10_linux.in: Likewise. - -2010-02-06 Colin D Bennett - - * conf/common.rmk (pkglib_MODULES): Add gfxmenu.mod. - (gfxmenu_mod_SOURCES): New variable. - (gfxmenu_mod_CFLAGS): Likewise. - (gfxmenu_mod_LDFLAGS): Likewise. - * include/grub/term.h (grub_term_set_current_output): Declare - argument as const. - * docs/gfxmenu-theme-example.txt: New file. - * gfxmenu/gfxmenu.c: Likewise. - * gfxmenu/gui_box.c: Likewise. - * gfxmenu/gui_canvas.c: Likewise. - * gfxmenu/gui_circular_progress.c: Likewise. - * gfxmenu/gui_image.c: Likewise. - * gfxmenu/gui_label.c: Likewise. - * gfxmenu/gui_list.c: Likewise. - * gfxmenu/gui_progress_bar.c: Likewise. - * gfxmenu/gui_string_util.c: Likewise. - * gfxmenu/gui_util.c: Likewise. - * gfxmenu/icon_manager.c: Likewise. - * gfxmenu/model.c: Likewise. - * gfxmenu/named_colors.c: Likewise. - * gfxmenu/theme_loader.c: Likewise. - * gfxmenu/view.c: Likewise. - * gfxmenu/widget-box.c: Likewise. - * include/grub/gfxmenu_model.h: Likewise. - * include/grub/gfxmenu_view.h: Likewise. - * include/grub/gfxwidgets.h: Likewise. - * include/grub/gui.h: Likewise. - * include/grub/gui_string_util.h: Likewise. - * include/grub/icon_manager.h: Likewise. - -2010-02-06 Vladimir Serbinenko - - Agglomerate scrolling in gfxterm. - - * term/gfxterm.c (grub_virtual_screen): New member 'total_screen'. - (grub_virtual_screen_setup): Initialise 'total_screen'. - (write_char): Split to ... - (paint_char): ... this ... - (write_char): ... and this. - (paint_char): Handle delayed scrolling. - (draw_cursor): Likewise. - (scroll_up): Split to ... - (real_scroll): ... this ... - (scroll_up): ... and this. - (real_scroll): Handle multi-line scroll and draw below-the-bottom - characters. - (grub_gfxterm_refresh): Call real_scroll. - -2010-02-06 Colin D Bennett - - * include/grub/misc.h (grub_iscntrl): New inline function. - (grub_isalnum): Likewise. - (grub_strtol): Likewise. - -2010-02-06 Colin D Bennett - - * normal/menu_text.c (get_entry_number): Move from here ... - * normal/menu.c (get_entry_number): ... moved here. - * include/grub/menu.h (grub_menu_get_default_entry_index): - New prototype. - * normal/menu.c (grub_menu_get_default_entry_index): New function. - * normal/menu_text.c (run_menu): Use grub_menu_get_default_entry_index. - * include/grub/menu_viewer.h (grub_menu_viewer_init): New prototype. - (grub_menu_viewer_should_return): Likewise. - * normal/main.c (GRUB_MOD_INIT (normal)): Call grub_menu_viewer_init. - * normal/menu_text.c (run_menu): Enable menu switching. - * normal/menu_viewer.c (should_return): New variable. - (menu_viewer_changed): Likewise. - (grub_menu_viewer_show_menu): Handle menu viewer changes. - (grub_menu_viewer_should_return): New function. - (menuviewer_write_hook): Likewise. - (grub_menu_viewer_init): Likewise. - -2010-02-06 Colin D Bennet -2010-02-06 Vladimir Serbinenko - - Support for gfxterm in a window. - - * include/grub/gfxterm.h: New file. - * include/grub/video.h (struct grub_video_rect): New declaration. - (grub_video_rect_t): Likewise. - * term/gfxterm.c (struct grub_gfxterm_window): New type. - (refcount): New variable. - (render_target): Likewise. - (window): Likewise. - (repaint_callback): Likewise. - (grub_virtual_screen_setup): Use 'render_target'. - (init_window): New function. - (grub_gfxterm_init_window): Likewise. - (grub_gfxterm_init): Check reference counter. - Use init_window. - (destroy_window): New function. - (grub_gfxterm_destroy_window): Likewise. - (grub_gfxterm_fini): Check reference counter. - Use destroy_window. - (redraw_screen_rect): Restore viewport. - Use 'render_target' and 'window'. - Call 'repaint_callback'. - (write_char): Use 'render_target'. - (draw_cursor): Likewise. - (scroll_up): Restore viewport. - Use 'render_target' and 'window'. - Call 'repaint_callback'. - (grub_gfxterm_cls): Likewise. - (grub_gfxterm_refresh): Use 'window'. - (grub_gfxterm_set_repaint_callback): New function. - (grub_gfxterm_background_image_cmd): Use 'window'. - (grub_gfxterm_get_term): New function. - (GRUB_MOD_INIT(term_gfxterm)): Set 'refcount' to 0. - -2010-02-06 Colin D Bennett - - Bitmap scaling support. - - * conf/common.rmk (pkglib_MODULES): Add bitmap_scale.mod. - (bitmap_scale_mod_SOURCES): New variable. - (bitmap_scale_mod_CFLAGS): Likewise. - (bitmap_scale_mod_LDFLAGS): Likewise. - * include/grub/bitmap_scale.h: New file. - * term/gfxterm.c (BACKGROUND_CMD_ARGINDEX_MODE): New definiton. - (background_image_cmd_options): New variable. - (grub_gfxterm_background_image_cmd): Support bitmap stretching. - (cmd): Rename and change type to ... - (background_image_cmd_handle): ... this. All users updated. - (GRUB_MOD_INIT(term_gfxterm)): Make background_image extended command. - * video/bitmap_scale.c: New file. - -2010-02-06 Vladimir Serbinenko - - SDL support. - - * Makefile.in (LIBSDL): New variable. - (enable_grub_emu_sdl): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Add video files. - (grub_emu_SOURCES) [enable_grub_emu_sdl]: Add util/sdl.c. - (grub_emu_LDFLAGS) [enable_grub_emu_sdl]: Add $(LIBSDL). - * configure.ac: Detect SDL availability and add --enable-grub-emu-sdl - * util/sdl.c: New file. - -2010-02-06 Colin D Bennett -2010-02-06 Vladimir Serbinenko - - Double buffering support. - - * commands/i386/pc/videotest.c (grub_cmd_videotest): Swap doublebuffers. - * include/grub/video.h: Update comment. - * include/grub/video_fb.h (grub_video_fb_doublebuf_update_screen_t): - New type. - (grub_video_fb_doublebuf_blit_init): New prototype. - * term/gfxterm.c (scroll_up): Support double buffering. - (grub_gfxterm_refresh): Likewise. - * video/fb/video_fb.c (doublebuf_blit_update_screen): New function. - (grub_video_fb_doublebuf_blit_init): Likewise. - * video/i386/pc/vbe.c (framebuffer): Remove 'render_target'. Add - 'front_target', 'back_target', 'offscreen_buffer', 'page_size', - 'displayed_page', 'render_page' and 'update_screen'. - (grub_video_vbe_fini): Free offscreen buffer. - (doublebuf_pageflipping_commit): New function. - (doublebuf_pageflipping_update_screen): Likewise. - (doublebuf_pageflipping_init): Likewise. - (double_buffering_init): Likewise. - (grub_video_vbe_setup): Enable doublebuffering. - (grub_video_vbe_swap_buffers): Implement. - (grub_video_vbe_set_active_render_target): Handle double buffering. - (grub_video_vbe_get_active_render_target): Likewise. - (grub_video_vbe_get_info_and_fini): Likewise. Free offscreen_buffer. - (grub_video_vbe_adapter): Use grub_video_vbe_get_active_render_target. - (grub_video_vbe_enable_double_buffering): Likewise. - (grub_video_vbe_swap_buffers): Use update_screen. - (grub_video_set_mode): Use double buffering. - -2010-02-06 Robert Millan - - * maintainance/gentrigtables.py: Remove. - * lib/trig.c: Likewise. - - * gentrigtables.c: New file. C rewrite of gentrigtables.py. - - * conf/common.rmk (trig_mod_SOURCES): Replace `lib/trig.c' with - `trigtables.c'. - (trigtables.c): New rule. - (gentrigtables): Likewise. - (DISTCLEANFILES): Add `trigtables.c' and `gentrigtables'. - -2010-02-06 Robert Millan - - * maintainance/gentrigtables.py: Avoid duplicate hardcoding of - integer constants. - -2010-02-06 Colin D Bennet - - Trigonometry support. - - * include/grub/trig.h: New file. - * lib/trig.c: Likewise. - * maintainance/gentrigtables.py: Likewise. - * conf/common.rmk (pkglib_MODULES): Add trig.mod. - (trig_mod_SOURCES): New variable. - (trig_mod_CFLAGS): Likewise. - (trig_mod_LDFLAGS): Likewise. - -2010-02-06 Vladimir Serbinenko - - * kern/ieee1275/openfw.c (grub_ieee1275_encode_devname): Support whole - disk devices. - -2010-02-06 Vladimir Serbinenko - - * kern/ieee1275/openfw.c (grub_devalias_iterate): Stop iterating on - error. - -2010-02-03 Vladimir Serbinenko - - * util/hostdisk.c (open_device): Don't use partition device when reading - before the partition. - (grub_util_biosdisk_read): Don't read from partition and before the - partition in single operation. - (grub_util_biosdisk_write): Don't write to partition and before the - partition in single operation. - -2010-02-03 Torsten Landschoff - - * kern/disk.c (grub_disk_read): Fix offset computation when reading - last sectors. - -2010-02-03 Vladimir Serbinenko - - * disk/i386/pc/biosdisk.c (grub_biosdisk_read): Handle non-2048 aligned - CDROM reads. - (grub_biosdisk_write): Refuse to write to CDROM. - -2010-01-31 Vladimir Serbinenko - - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Fix off-by-one error. - -2010-01-31 Vladimir Serbinenko - - * font/font.c (find_glyph): Check that bmp_idx is available before - using it. - (grub_font_get_string_width): Never call grub_font_get_glyph_internal - with (font == NULL). - -2010-01-28 Christian Schmitt - - * util/ieee1275/grub-install.in: Fix nvsetenv arguments. - -2010-01-28 BVK Chaitanya - - * include/grub/script_sh.h (sourcecode): Add const qualifier. - * util/grub-script-check.c (getline): Fix empty lines case. - -2010-01-28 Robert Millan - - * Makefile.in (check): Exit with fail status when one of the tests - fails. - * tests/example_functional_test.c (example_test): Fix reversed assert. - * tests/example_unit_test.c (example_test): Likewise. - -2010-01-28 Colin Watson - - * util/grub.d/10_linux.in: This script does not use any of the - contents of gettext.sh, only the external command `gettext', so stop - sourcing it. (Moreover, gettext.sh isn't necessarily installed in - the same prefix as GRUB.) - * util/grub.d/10_kfreebsd.in: Likewise. - -2010-01-27 Vladimir Serbinenko - - * normal/cmdline.c (grub_cmdline_get): Fix completion in the middle - of the line. - -2010-01-27 Vladimir Serbinenko - - * kern/disk.c (grub_disk_read): Fix offset computation when reading - last sectors. - -2010-01-27 Vladimir Serbinenko - - * commands/hashsum.c (hash_file): Avoid possible stack overflow by - having a 4KiB and not 32KiB buffer size. - -2010-01-27 Robert Millan - - * util/hostfs.c: Include `'. - (grub_hostfs_read): Handle errors from fseeko() and fread(). - -2010-01-27 Robert Millan - - * kern/disk.c (grub_disk_read): Fix bug that would cause infinite - loop when using read hooks on files whose size isn't sector-aligned. - -2010-01-27 Robert Millan - - Remove unused parameter. - - * fs/iso9660.c (struct grub_iso9660_data): Remove `length' parameter. - (grub_iso9660_open): Remove initialization of `data->length'. - -2010-01-27 Robert Millan - - * util/grub-fstest.c (fstest): Rewrite allocation, fixing a few - memleak conditions. - -2010-01-27 Carles Pina i Estany - - * util/lvm.c: New macro LVM_DEV_MAPPER_STRING. - (grub_util_lvm_isvolume): Use LVM_DEV_MAPPER_STRING. - -2010-01-26 Carles Pina i Estany - - * util/bin2h.c (usage): Fix warning (space after backslash). - -2010-01-26 Carles Pina i Estany - - * font/font.c: Include `grub/fontformat.h. - Remove font file format constants. - (grub_font_load): Use the new macros. - * include/grub/fontformat.h: New file. - * util/grub-mkfont.c: Include `grub/fontformat.c'. - (write_font_pf2): Use the new macros. - -2010-01-26 Robert Millan - - * util/bin2h.c (usage): Make --help actually explain what `grub-bin2h' - does. - -2010-01-26 Robert Millan - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_PXE_DL): New macro. - - * boot/i386/pc/pxeboot.S: Include `'. - (_start): Macroify `0x7F'. - - * kern/i386/pc/init.c: Include `'. - (make_install_device): Use "(pxe)" as fallback prefix when booting - via PXE. - -2010-01-26 Vladimir Serbinenko - - * configure.ac: Reset LIBS after check for libgcc symbols. - -2010-01-25 Colin Watson - - * util/hostdisk.c (open_device): Add trailing newline to debug - message. - -2010-01-25 GrĂ©goire Sutre - - * configure.ac: Check for `limits.h'. - * util/misc.c: Include `' (for PATH_MAX). - -2010-01-24 Robert Millan - - * loader/mips/linux.c (grub_cmd_linux, grub_cmd_initrd): Don't - capitalize error strings. - -2010-01-24 Samuel Thibault - - * util/grub.d/10_hurd.in: Add a recovery mode. - -2010-01-23 Vladimir Serbinenko - - * configure.ac: Check for libgcc symbols with -nostdlib. - -2010-01-23 BVK Chaitanya - - * acinclude.m4: Quote underquoted AC_DEFUN parameters. - -2010-01-22 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_setcolorstate): Allocate on - stack since heap may be unavailable at that point. - (grub_ofconsole_gotoxy): Likewise. - -2010-01-22 Vladimir Serbinenko - - * configure.ac: Check for _restgpr_14_x. - * include/grub/powerpc/libgcc.h [HAVE__RESTGPR_14_X]: Add _restgpr_*_x - and _savegpr_* prototypes. - -2010-01-22 Robert Millan - - Use generic grub_reboot() for i386-efi. - - * kern/efi/efi.c [__i386__] (grub_reboot): Remove. - * kern/i386/efi/startup.S: Include `"../realmode.S"'. - * kern/i386/realmode.S: Include `'. - -2010-01-22 Vladimir Serbinenko - - * kern/ieee1275/init.c (grub_machine_set_prefix): Don't check for - presence of "prefix" variable as it breaks when normal.mod is - embedded. - -2010-01-21 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_dimensions): Allocate on - stack since heap is unavailable at that point. - -2010-01-21 Vladimir Serbinenko - - * include/grub/i386/bsd.h (FREEBSD_N_BIOS_GEOM): Removed. - (grub_freebsd_bootinfo): Rewritten. - * loader/i386/bsd.c (grub_freebsd_boot): Use new grub_freebsd_bootinfo. - -2010-01-21 Vladimir Serbinenko - - * util/misc.c (make_system_path_relative_to_its_root): Fix typo. - -2010-01-21 Robert Millan - - * po/POTFILES: Remove mkisofs-related files. They have their own TLP - domain now. - -2010-01-20 Felix Zielcke - - * util/misc.c (make_system_path_relative_to_its_root): Change the work - around for handling "/" to the correct fix. Fix a memory leak. Use - xstrdup instead of strdup. - -2010-01-20 Vladimir Serbinenko - - * conf/mips.rmk (kernel_img_HEADERS): Add env_private.h - -2010-01-20 Vladimir Serbinenko - - Optimise glyph lookup by Basic Multilingual Plane lookup array. - - * font/font.c (struct grub_font): New member 'bmp_idx'. - (font_init): Initialise 'bmp_idx'. - (load_font_index): Fill 'bmp_idx'. - (find_glyph): Make inline. Use bmp_idx for BMP characters. - -2010-01-20 Vladimir Serbinenko - - * video/fb/video_fb.c (grub_video_fb_scroll): Optimise by avoiding - unnecessary calls. - -2010-01-20 Vladimir Serbinenko - - Move context handling out of the kernel. - - * conf/any-emu.rmk (grub_emu_SOURCES): Add normal/context.c. - * conf/common.rmk (normal_mod_SOURCES): Add normal/context.c. - * conf/i386-coreboot.rmk (kernel_img_HEADERS): Add env_private.h. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * include/grub/env.h: Include grub/menu.h. - (grub_env_var_type): Removed. - (grub_env_var): Replaced field 'type' with 'global'. - (grub_env_find): New prototype. - (grub_env_context_open): Remove EXPORT_FUNC. - (grub_env_context_close): Likewise. - (grub_env_export): Likewise. - (grub_env_set_data_slot): Removed. - (grub_env_get_data_slot): Likewise. - (grub_env_unset_data_slot): Likewise. - (grub_env_unset_menu): New prototype. - (grub_env_set_menu): Likewise. - (grub_env_get_menu): Likewise. - * include/grub/env_private.h: New file. - * include/grub/normal.h (grub_context_init): New prototype. - (grub_context_fini): Likewise. - * kern/corecmd.c (grub_core_cmd_export): Moved from here ... - * normal/context.c (grub_cmd_export): ... to here. - * kern/env.c: Include env_private.h. - (HASHSZ): Moved to include/grub/env_private.h. - (grub_env_context): Likewise. - (grub_env_sorted_var): Likewise. - (current_context): Renamed from this ... - (grub_current_context): ...to this. 'static' removed. All users updated. - (grub_env_find): Removed 'static'. - (grub_env_context_open): Moved to normal/context.c. - (grub_env_context_close): Likewise. - (grub_env_export): Likewise. - (mangle_data_slot_name): Removed. - (grub_env_set_data_slot): Likewise. - (grub_env_get_data_slot): Likewise. - (grub_env_unset_data_slot): Likewise. - * kern/main.c (grub_set_root_dev): Don't export root. - It will be done later. - (grub_main): Don't export prefix. - It will be done later. - * normal/context.c: New file. - * normal/main.c (free_menu): Use grub_env_unset_menu. - (grub_normal_add_menu_entry): Use grub_env_get_menu. - (read_config_file): Use grub_env_get_menu and grub_env_set_menu. - (GRUB_MOD_INIT(normal)): Call grub_context_init. - (GRUB_MOD_FINI(normal)): Call grub_context_fini. - -2010-01-20 Vladimir Serbinenko - - setpci support. - - * commands/setpci.c: New file. - * conf/i386.rmk (pkglib_MODULES): Add setpci.mod. - (setpci_mod_SOURCES): New variable. - (setpci_mod_CFLAGS): Likewise. - (setpci_mod_LDFLAGS): Likewise. - -2010-01-20 Vladimir Serbinenko - - Byte-addressable PCI configuration space. - - * bus/pci.c (grub_pci_make_address): Use byte address instead of - dword address. - (grub_pci_iterate): Use macroses GRUB_PCI_REG_PCI_ID and - GRUB_PCI_REG_CACHELINE. - * bus/usb/ohci.c (grub_ohci_pci_iter): Use macroses - GRUB_PCI_REG_CLASS and GRUB_PCI_REG_ADDRESS_REG0. - * bus/usb/uhci.c (grub_ohci_pci_iter): Use macroses - GRUB_PCI_REG_CLASS and GRUB_PCI_REG_ADDRESS_REG4. - * commands/efi/fixvideo.c (scan_card): Use macros GRUB_PCI_REG_CLASS. - * commands/efi/loadbios.c (enable_rom_area): Pass byte-address to - grub_pci_make_address. - (lock_rom_area): Likewise. - * commands/lspci.c (grub_lspci_iter): Use macroses - GRUB_PCI_REG_CLASS and GRUB_PCI_REG_ADDRESSES. Handle byte-addressing - of grub_pci_make_address. - * disk/ata.c (grub_ata_pciinit): Likewise. - * include/grub/pci.h (GRUB_PCI_REG_PCI_ID): New macro. - (GRUB_PCI_REG_VENDOR): Likewise. - (GRUB_PCI_REG_DEVICE): Likewise. - (GRUB_PCI_REG_COMMAND): Likewise. - (GRUB_PCI_REG_STATUS): Likewise. - (GRUB_PCI_REG_REVISION): Likewise. - (GRUB_PCI_REG_CLASS): Likewise. - (GRUB_PCI_REG_CACHELINE): Likewise. - (GRUB_PCI_REG_LAT_TIMER): Likewise. - (GRUB_PCI_REG_HEADER_TYPE): Likewise. - (GRUB_PCI_REG_BIST): Likewise. - (GRUB_PCI_REG_ADDRESSES): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_ADDRESS_REG): Likewise. - (GRUB_PCI_REG_CIS_POINTER): Likewise. - (GRUB_PCI_REG_SUBVENDOR): Likewise. - (GRUB_PCI_REG_SUBSYSTEM): Likewise. - (GRUB_PCI_REG_ROM_ADDRESS): Likewise. - (GRUB_PCI_REG_CAP_POINTER): Likewise. - (GRUB_PCI_REG_IRQ_LINE): Likewise. - (GRUB_PCI_REG_IRQ_PIN): Likewise. - (GRUB_PCI_REG_MIN_GNT): Likewise. - (GRUB_PCI_REG_MAX_LAT): Likewise. - * loader/i386/efi/linux.c (find_framebuf): Use GRUB_PCI_REG_CLASS. - * loader/i386/efi/xnu.c (find_framebuf): Likewise. - * video/efi_uga.c (find_framebuf): Likewise. - * video/sm712.c (grub_video_sm712_setup): Likewise. - * util/pci.c (grub_pci_make_address): Use byte-addressed configuration - space. - -2010-01-20 Robert Millan - - * util/grub.d/10_linux.in (linux_entry): Set gfxpayload=keep when it - can be reliably determined to be supported. - -2010-01-20 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): If `vga=' was used, write down - that VESA is supported. - (grub_linux_boot): Use generic framebuffer unless VESA is known to be - supported. - -2010-01-20 Vladimir Serbinenko - - * conf/common.rmk (font/font.c_DEPENDENCIES): Condition on FONT_SOURCE. - -2010-01-20 Robert Millan - - * util/misc.c (make_system_path_relative_to_its_root): Work around - special-casing of "/", as previous incarnation of this routine did. - -2010-01-20 Vladimir Serbinenko - - Fix any-emu compilation. - - * conf/any-emu.rmk (bin_UTILITIES): Add grub-bin2h. - * grub_bin2h_SOURCES: New variable. - -2010-01-20 Robert Millan - - * util/grub.d/00_header.in: Fix stupid mistake from last commit. - -2010-01-20 Robert Millan - - * util/grub.d/00_header.in: Fix handling of locale_dir. - -2010-01-20 Vladimir Serbinenko - - * configure.ac: Add /usr/share/fonts/unifont/unifont.pcf.gz - as possible unifont location (Gentoo). - Reported by: Alexander BrĂ¼ning - -2010-01-20 Vladimir Serbinenko - - Don't try to generate lists for kernel.img. - - * conf/i386-efi.rmk (pkglib_PROGRAMS): New variable. - (pkglib_MODULES): Remove kernel.img. - (kernel_img_EXPORTS): Removed. - (kernel_img_RELOCATABLE): New variable. - * conf/x86_64-efi.rmk: Likewise. - * genmk.rb: Remove *_EXPORTS support and add *_RELOCATABLE support. - -2010-01-20 Vladimir Serbinenko - - * include/grub/misc.h (grub_sprintf): Removed. All users switched to - grub_xasprintf or grub_snprintf. - (grub_vsprintf): Likewise. - (grub_snprintf): New proto. - (grub_vsnprintf): Likewise. - (grub_xasprintf): Likewise. - (grub_xvasprintf): Likewise. - * kern/misc.c (grub_vprintf): Use grub_vsnprintf_real. - (grub_sprintf): Removed. - (grub_vsnprintf): New function. - (grub_snprintf): Likewise. - (grub_xvasprintf): Likewise. - (grub_xasprintf): Likewise. - (grub_vsprintf): Renamed to ... - (grub_vsnprintf_real): ...this. New argument max_len. - -2010-01-20 BVK Chaitanya - - * include/grub/script_sh.h (sourcecode): Remove const qualifier to - fix grub-script-check warning. - -2010-01-20 Vladimir Serbinenko - - * include/grub/font.h (grub_font_load): Fix prototype. - -2010-01-20 Vladimir Serbinenko - - * conf/mips.rmk (kernel_img_HEADERS) [yeeloong]: Add pci.h. - -2010-01-20 Vladimir Serbinenko - - * include/grub/x86_64/at_keyboard.h: New file. - -2010-01-20 Vladimir Serbinenko - - * loader/mips/linux.c: Include missing grub/i18n.h. - -2009-12-20 Robert Millan - - * normal/menu.c (notify_execution_failure): Clarify error message. - -2009-12-20 Robert Millan - - * commands/loadenv.c (check_blocklists): Use `grub_err_t' as - return value (and revert all return statements). Update users. - -2010-01-20 Dan Merillat - - * kern/device.c (grub_device_iterate): Allocate new part_ent - structure based on sizeof (*p) rather than sizeof (p->next), to - account for structure padding. - - * util/grub-probe.c (probe_raid_level): Return -1 immediately if - disk is NULL, which might happen for LVM physical volumes with no - LVM signature. - -2009-12-20 Robert Millan - - * loader/mips/linux.c (grub_cmd_initrd) - (GRUB_MOD_INIT(linux)): Adjust and gettextize a few strings. - -2009-12-20 Robert Millan - - * kern/mips/yeeloong/init.c (grub_video_sm712_init) - (grub_video_video_init, grub_video_bitmap_init) - (grub_font_manager_init, grub_term_gfxterm_init) - (grub_at_keyboard_init): New extern declarations. - (grub_machine_init): Initialize gfxterm and at_keyboard. - - * kern/main.c (grub_main): Revert grub_printf delay kludge. - - * util/grub-install.in: Revert embed of `at_keyboard.mod' and - `gfxterm.mod' into core image. - - * conf/mips.rmk (pkglib_IMAGES, kernel_img_SOURCES) - (kernel_img_CFLAGS, kernel_img_ASFLAGS, kernel_img_LDFLAGS) - (kernel_img_FORMAT): Copy to ... - - * conf/mips-qemu-mips.rmk (pkglib_IMAGES, kernel_img_SOURCES) - (kernel_img_CFLAGS, kernel_img_ASFLAGS, kernel_img_LDFLAGS) - (kernel_img_FORMAT): ... here, and ... - - * conf/mips-yeeloong.rmk (pkglib_IMAGES, kernel_img_SOURCES) - (kernel_img_CFLAGS, kernel_img_ASFLAGS, kernel_img_LDFLAGS) - (kernel_img_FORMAT): ... here. - - (kernel_img_SOURCES): Add files necessary for output (gfxterm) - and input (at_keyboard) terminals in kernel. - (kernel_img_CFLAGS): Add `-DUSE_ASCII_FAILBACK'. - - (pkglib_MODULES): Remove `pci.mod'. - (pci_mod_SOURCES, pci_mod_CFLAGS, pci_mod_LDFLAGS) - (sm712_mod_SOURCES, sm712_mod_CFLAGS, sm712_mod_LDFLAGS) - (at_keyboard_mod_SOURCES, at_keyboard_mod_CFLAGS) - (at_keyboard_mod_LDFLAGS): Remove variables. - -2010-01-11 Felix Zielcke - - * po/POTFILES: Replace `term/i386/pc/serial.c' with `term/serial.c'. - -2009-12-10 Robert Millan - - * include/grub/mips/libgcc.h: Only export symbols for functions - that libgcc provides. - -2009-12-02 Vladimir Serbinenko - - MIPS support. - - * bus/bonito.c: New file. - * bus/pci.c (grub_pci_iterate): Use GRUB_PCI_NUM_BUS and - GRUB_PCI_NUM_DEVICES. - * term/i386/pc/serial.c: Move to ... - * term/serial.c: ... here. All users updated. - * util/i386/pc/grub-mkimage.c: Move to ... - * util/grub-mkrawimage.c: ... here. All users updated. - * term/i386/pc/at_keyboard.c: Move to ... - * term/at_keyboard.c: ... here. All users updated. - * conf/mips-qemu-mips.rmk: New file. - * conf/mips-yeeloong.rmk: Likewise. - * conf/mips.rmk: Likewise. - * configure.ac: New platforms mipsel-yeeloong, mips-qemu-mips and - mipsel-qemu-mips. - * disk/ata.c (grub_ata_device_initialize): Add GRUB_MACHINE_PCI_IO_BASE - to port addresses. - (grub_ata_pciinit): Support CS5536. - * font/font.c (grub_font_load): Use grub_file_t instead of filename. - * font/font_cmd.c (loadfont_command): Open file before passing it to - grub_font_load. - (pseudo_file_read): New function. - (pseudo_file_close): Likewise. - (pseudo_fs): New structure. - (load_font_module): New function. - (GRUB_MOD_INIT(font_manager)): Load embedded font. - * fs/cpio.c (grub_cpio_open): Handle partial matches correctly. - * genmk.rb: Strip .rel.dyn, .reginfo, .note and .comment. - * genmoddep.awk: Ignore __gnu_local_gp. It's defined by linker. - * include/grub/i386/at_keyboard.h: Split into ... - * include/grub/at_keyboard.h: ... this ... - * include/grub/i386/at_keyboard.h: ... and this. - * include/grub/dl.h (grub_arch_dl_init_linker) [_mips && !GRUB_UTIL]: - New prototype. - * include/grub/elfload.h (grub_elf32_size): New parameter. All users - updated. - (grub_elf64_size): Likewise. - * include/grub/font.h (grub_font_load): Use grub_file_t instead of - filename. - * include/grub/i386/io.h (grub_port_t): New type. All users updated. - * include/grub/i386/coreboot/serial.h: Rewritten. - * include/grub/i386/ieee1275/serial.h: Include - grub/i386/coreboot/serial.h instead of grub/i386/pc/serial.h. - * include/grub/i386/pc/serial.h: Moved from here ... - * include/grub/serial.h: ... to here. All users updated. - * include/grub/i386/pci.h (GRUB_MACHINE_PCI_IO_BASE): New definition. - (GRUB_PCI_NUM_BUS): Likewise. - (GRUB_PCI_NUM_DEVICES): Likewise. - (grub_pci_device_map_range): Add missing volatile keyword. - * include/grub/kernel.h (OBJ_TYPE_FONT): New enum value. - * include/grub/mips/at_keyboard.h: New file. - * include/grub/mips/cache.h: Likewise. - * include/grub/mips/io.h: Likewise. - * include/grub/mips/kernel.h: Likewise. - * include/grub/mips/libgcc.h: Likewise. - * include/grub/mips/pci.h: Likewise. - * include/grub/mips/qemu-mips/boot.h: Likewise. - * include/grub/mips/qemu-mips/kernel.h: Likewise. - * include/grub/mips/qemu-mips/loader.h: Likewise. - * include/grub/mips/qemu-mips/memory.h: Likewise. - * include/grub/mips/qemu-mips/serial.h: Likewise. - * include/grub/mips/qemu-mips/time.h: Likewise. - * include/grub/mips/relocator.h: Likewise. - * include/grub/mips/time.h: Likewise. - * include/grub/mips/types.h: Likewise. - * include/grub/mips/yeeloong/at_keyboard.h: Likewise. - * include/grub/mips/yeeloong/boot.h: Likewise. - * include/grub/mips/yeeloong/kernel.h: Likewise. - * include/grub/mips/yeeloong/loader.h: Likewise. - * include/grub/mips/yeeloong/memory.h: Likewise. - * include/grub/mips/yeeloong/pci.h: Likewise. - * include/grub/mips/yeeloong/serial.h: Likewise. - * include/grub/mips/yeeloong/time.h: Likewise. - * kern/dl.c (grub_dl_resolve_symbols): Handle STT_OBJECT correctly. - * kern/elf.c (grub_elf32_size): New parameter. All users - updated. - (grub_elf64_size): Likewise. - * kern/main.c (grub_main): Call grub_arch_dl_init_linker if necessary. - Load modules before saying "Welcome to GRUB!". - Call grub_refresh after saying "Welcome to GRUB!". - * kern/mips/cache.S: New file. - * kern/mips/cache_flush.S: Likewise. - * kern/mips/dl.c: Likewise. - * kern/mips/init.c: Likewise. - * kern/mips/qemu-mips/init.c: Likewise. - * kern/mips/startup.S: Likewise. - * kern/mips/yeeloong/init.c: Likewise. - * kern/term.c (grub_putcode): Handle NULL terminal. - (grub_getcharwidth): Likewise. - (grub_getkey): Likewise. - (grub_checkkey): Likewise. - (grub_getkeystatus): Likewise. - (grub_getxy): Likewise. - (grub_getwh): Likewise. - (grub_gotoxy): Likewise. - (grub_cls): Likewise. - (grub_setcolorstate): Likewise. - (grub_setcolor): Likewise. - (grub_getcolor): Likewise. - (grub_refresh): Likewise. - * lib/mips/relocator.c (JUMP_SIZEOF): Fix incorrect value. - (write_jump): Add hatch nop. - * lib/mips/relocator_asm.S: Use kern/mips/cache_flush.S. - * lib/mips/setjmp.S: New file. - * loader/mips/linux.c: Likewise. - * term/i386/pc/at_keyboard.c: Move from here ... - * term/at_keyboard.c: ... to here. - * term/i386/pc/serial.c: Moved from here ... - * term/serial.c: ... to here. All users updated. - (TEXT_HEIGHT): Set to 24 to fit linux terminal. - (serial_hw_io_addr): Use GRUB_MACHINE_SERIAL_PORTS. - (serial_translate_key_sequence): Avoid deadlock. - (grub_serial_getkey): Handle backspace. - (grub_serial_putchar): Fix newline handling. - * util/i386/pc/grub-mkimage.c: Move from here ... - * util/grub-mkrawimage.c: ... to here. All users updated. - (generate_image): New parameters 'font_path' and 'format'. - Support embedding font. - Use grub_host_to_target* instead of grub_cpu_to_le*. - (generate_image) [GRUB_MACHINE_MIPS]: Support ELF encapsulation. - (options) [GRUB_PLATFORM_IMAGE_DEFAULT]: New option "--format". - (options): New option "--font". - (usage): Likewise. - (main) [GRUB_PLATFORM_IMAGE_DEFAULT]: Handle "--format". - (main): Handle "--font". - * term/gfxterm.c (grub_virtual_screen): New member bg_color_display. - (grub_virtual_screen_setup): Set bg_color_display. - (redraw_screen_rect): Use bg_color_display instead of incorrect - bg_color. - (grub_gfxterm_cls): Likewise. - * util/elf/grub-mkimage.c (load_modules): New parameter 'config_path'. - Support embedding config file. - (add_segments): Likewise. - (options): New option "--config". - (main): Handle "--config". - * video/sm712.c: New file. - -2010-01-18 Robert Millan - - Fix parallel builds. - - * conf/common.rmk (font/font.c_DEPENDENCIES): New variable (makes - font.c depend on ascii.h). - -2010-01-12 Carles Pina i Estany - - * Makefile.in (DUSE_ASCII_FAILBACK): New macro. - -2010-01-11 Carles Pina i Estany - - * font/font.c (GENERATE_ASCII): Change the name to USE_ASCII_FAILBACK. - By default: disabled. - * Makefile.in (ascii.h): Remove the non-needed grub/bin2h size - parameter. - -2010-01-10 Carles Pina i Estany - - * font/font.c: Update copyright years. - * util/grub-mkfont.c (write_font_ascii_bitmap): Change comment format. - -2010-01-10 Carles Pina i Estany - - * font/font.c: Include `ascii.h'. - (ASCII_BITMAP_SIZE): New macro. - (ascii_font_glyph): Define. - (ascii_glyph_lookup): New function. - (grub_font_get_string_width): Change comment. If glyph not found, use - ascii_glyph_lookup. - (grub_font_get_glyph_with_fallback): If glyph not available returns - ascii_glyph_lookup. - * util/grub-mkfont.c (file_formats): New enum. - (options): Add `ascii-bitmaps' new option. - (usage): Add `asii-bitmaps' new option. - (write_font_ascii_bitmap): New function. - (write_font): Rename to ... - (write_font_p2): ... this. Remove print_glyphs call. - (main): Use file_format. Implement code for ranges if ascii-bitmaps is - used. Call print_glyphs. - * Makefile.in (pkgdata_DATA): Add `ascii.h'. - -2010-01-14 Robert Millan - - * conf/common.rmk (bin_UTILITIES): Add `grub-bin2h'. - (grub_bin2h_SOURCES): New variable. - * util/bin2h.c: New file. - -2010-01-20 Vladimir Serbinenko - - * include/multiboot.h: Resynced with spec. - * include/multiboot2.h: Likewise. - * loader/i386/multiboot_mbi.c (grub_fill_multiboot_mmap): Handle - GRUB_MACHINE_MEMORY_ACPI_RECLAIMABLE and GRUB_MACHINE_MEMORY_NVS. - -2010-01-18 Robert Millan - - * include/grub/term.h (grub_term_register_input, - grub_term_register_output): Check return of terminal init() - routines, and abort if errors are raised. - - * commands/terminal.c: Update copyright year. - -2010-01-18 Robert Millan - - * commands/terminal.c (grub_cmd_terminal_input) - (grub_cmd_terminal_output): Check return of terminal init() - routines, and abort if errors are raised. - -2010-01-18 Vladimir Serbinenko - - * include/grub/i386/bsd.h: Fix include pathes. - -2010-01-18 Vladimir Serbinenko - - Add missing *BSD copyright headers. - - * include/grub/aout.h: Add BSD licence. - * include/grub/i386/bsd.h: Parts under different licences moved to ... - * include/grub/i386/freebsd_linker.h: ... here, - * include/grub/i386/freebsd_reboot.h: ... here, - * include/grub/i386/netbsd_bootinfo.h: ... here, - * include/grub/i386/netbsd_reboot.h: ... here, - * include/grub/i386/openbsd_bootarg.h: ... here, - * include/grub/i386/openbsd_reboot.h: ... and here. Added appropriate - licence to each file. - -2010-01-18 Robert Millan - - * acinclude.m4: Remove `nop' assembly instruction; it's not - implemented by all architectures. - -2010-01-18 Robert Millan - - * loader/i386/efi/linux.c (grub_cmd_linux): Stop pretending we're - ELILO. This is no longer necessary. - -2010-01-18 BVK Chaitanya - - Added new tool, grub-scrit-check to verify grub.cfg syntax. - - * util/grub-script-check.c: grub-script-check tool. - * conf/common.rmk: Make rules for grub-script-check. - -2010-01-18 Robert Millan - - Fix annoying UI bug in rescue mode. Thanks to Tristan Gingold for - spotting it back in 2008. Shame on me for forgetting he did. - - * kern/rescue_reader.c (grub_rescue_run): Skip zero-length lines. - -2010-01-18 Robert Millan - - * include/grub/i386/linux.h (GRUB_VIDEO_TYPE_TEXT): Rename to ... - (GRUB_VIDEO_LINUX_TYPE_TEXT): ... this. Update all users. - (GRUB_VIDEO_TYPE_VLFB): Rename to ... - (GRUB_VIDEO_LINUX_TYPE_VESA): ... this. Update all users. - (GRUB_VIDEO_TYPE_EFI): Rename to ... - (GRUB_VIDEO_LINUX_TYPE_SIMPLE): ... this. Update all users. - -2010-01-17 Robert Millan - - * include/grub/test.h: Add license header. - * tests/example_functional_test.c: Likewise. - * tests/example_unit_test.c: Likewise. - * tests/lib/functional_test.c: Likewise. - * tests/lib/test.c: Likewise. - * tests/lib/unit_test.c: Likewise. - -2010-01-17 Vladimir Serbinenko - - Use flag-based instead of hook-based video mode selection and "auto" - keyword. - - * include/grub/video.h (grub_video_adapter): Changed 'setup' member. - (grub_video_set_mode): Changed prototype. All users updated. - (grub_video_check_mode_flag): New inline function. - * video/video.c (parse_modespec): New function. - (grub_video_set_mode): Parse flags and keywords. - -2010-01-17 Carles Pina i Estany - - * util/misc.c (grub_util_info): Fix the order of the parameters in a - fprintf call. - -2010-01-16 GrĂ©goire Sutre - - * genmk.rb (class SCRIPT): Replace option -i of sed by a pipe. - -2010-01-16 Carles Pina i Estany - - * util/grub-editenv.c (usage): Use `program_name' instead of hardcoded - string. - * util/grub-emu.c (usage): Likewise. - * util/grub-mkpasswd-pbkdf2.c (usage): Likewise. - * util/i386/efi/grub-mkimage.c (usage): Likewise. - * util/i386/pc/grub-mkimage.c (usage): Likewise. - * util/i386/pc/grub-setup.c (usage): Likewise. - -2010-01-16 Carles Pina i Estany - - * util/misc.c (grub_util_warn): Gettextizze, print full stop after - the message. - (grub_util_info): Likewise. - (grub_util_error): Likewise. - * util/elf/grub-mkimage.c: Fix capitalisation, quotes, full stops - and/or new lines in `grub_util_warna', `grub_util_info', - `grub_util_error' calls. - * util/getroot.c: Likewise. - * util/grub-editenv.c: Likewise. - * util/grub-emu.c: Likewise. - * util/grub-fstest.c: Likewise. - * util/grub-mkdevicemap.c: Likewise. - * util/grub-mkfont.c: Likewise. - * util/grub-mkpasswd-pbkdf2.c: Likewise. - * util/grub-mkrelpath.c: Likewise. - * util/grub-pe2elf.c: Likewise. - * util/grub-probe.c: Likewise. - * util/hostdisk.c: Likewise. - * util/i386/efi/grub-mkimage.c: Likewise. - * util/i386/pc/grub-mkimage.c: Likewise. - * util/i386/pc/grub-setup.c: Likewise. - * util/ieee1275/ofpath.c: Likewise. - * util/mkisofs/eltorito.c: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/write.c: Likewise. - * util/raid.c: Likewise. - * util/sparc64/ieee1275/grub-mkimage.c: Likewise. - * util/sparc64/ieee1275/grub-setup.c: Likewise. - -2010-01-15 Vladimir Serbinenko - - Enable multiboot on non-pc. - - * conf/i386-coreboot.rmk, conf/i386-pc.rmk (pkglib_MODULES): Move - multiboot.mod and multiboot2.mod to ... - * conf/i386.rmk (pkglib_MODULES): ... here. - * conf/i386-coreboot.rmk, conf/i386-pc.rmk (multiboot_mod_SOURCES): - Moved to ... - * conf/i386.rmk (multiboot_mod_SOURCES): .. here. - * conf/i386-coreboot.rmk, conf/i386-pc.rmk (multiboot_mod_CFLAGS): - Moved to ... - * conf/i386.rmk (multiboot_mod_CFLAGS): .. here. - * conf/i386-coreboot.rmk, conf/i386-pc.rmk (multiboot_mod_ASFLAGS): - Moved to ... - * conf/i386.rmk (multiboot_mod_ASFLAGS): .. here. - * conf/i386-coreboot.rmk, conf/i386-pc.rmk (multiboot_mod_LDFLAGS): - Moved to ... - * conf/i386.rmk (multiboot_mod_LDFLAGS): .. here. - * conf/x86_64-efi.rmk (pkglib_MODULES): Remove ata.mod and - relocator.mod. - (ata_mod_SOURCES): Removed. - (ata_mod_CFLAGS): Likewise. - (ata_mod_LDFLAGS): Likewise. - (relocator_mod_SOURCES): Removed. - (relocator_mod_CFLAGS): Likewise. - (relocator_mod_ASFLAGS): Likewise. - (relocator_mod_LDFLAGS): Likewise. - Include i386.mk. - * include/grub/x86_64/multiboot.h: New file. - * loader/i386/multiboot.c (grub_multiboot_boot) [GRUB_MACHINE_EFI]: - Terminate EFI. - -2010-01-15 Vladimir Serbinenko - - Video multiboot support. - - * include/grub/multiboot.h (grub_multiboot_set_accepts_video): - New prototype. - * include/multiboot.h: Resynced with multiboot specification. - * include/multiboot2.h: Likewise. - * loader/i386/multiboot.c (UNSUPPORTED_FLAGS): Support video flags. - (grub_multiboot): Parse MULTIBOOT_VIDEO_MODE fields. - * loader/i386/multiboot_mbi.c (DEFAULT_VIDEO_MODE): New constant. - (HAS_VGA_TEXT): Likewise. - (accepts_video): New variable. - (grub_multiboot_set_accepts_video): New function. - (grub_multiboot_get_mbi_size): Account for video structures. - (set_video_mode): New function. - (retrieve_video_parameters): Likewise. - (grub_multiboot_make_mbi): Fill video fields. - -2010-01-15 Vladimir Serbinenko - - Video driver ids. - - * include/grub/video.h (grub_video_driver_id): New type. - (grub_video_adapter): New member 'id'. All users updated. - (grub_video_get_driver_id): New proto. - * video/video.c (grub_video_get_driver_id): New function. - -2010-01-14 Carles Pina i Estany - - * util/grub.d/30_os-prober.in: Use `set var=val' rather than plain - `var=val'. - -2010-01-14 Carles Pina i Estany - - * normal/cmdline.c (print_completion): Gettextizze. - -2001-01-14 Carles Pina i Estany - - * loader/i386/pc/chainloader.c: Include `'. - -2010-01-14 Carles Pina i Estany - - * gettext/gettext.c (grub_gettext_translate): Push and pop - grub_errno. - (grub_gettext_delete_list): Change comment style. - * kern/err.c (grub_error): Gettextizze. - (grub_fatal): Gettextizze. - -2010-01-14 Robert Millan - - * include/grub/i386/loader.h (grub_linux16_boot): Renamed to ... - (grub_linux16_real_boot): ... this. - * kern/i386/loader.S: Likewise. - * loader/i386/pc/linux.c: Include `' and `'. - (grub_linux16_boot): New function. Switches to text mode and calls - grub_linux16_real_boot(). - - * loader/i386/bsd.c: Include `'. - (grub_freebsd_boot, grub_openbsd_boot, grub_netbsd_boot): Switch to - text mode before calling grub_unix_real_boot(). - - * loader/i386/multiboot.c: Include `'. - (grub_multiboot_boot): Switch to text mode before calling - grub_relocator32_boot(). - - * loader/i386/pc/chainloader.c: Include `'. - (grub_chainloader_boot): Switch to text mode before calling - grub_chainloader_real_boot(). - -2010-01-05 Jordan Uggla -2010-01-05 Colin Watson - - * util/grub-reboot.in: Make sure prev_saved_entry always gets a - non-empty value. - -2010-01-05 Jordan Uggla -2010-01-05 Colin Watson - - * util/grub.d/00_header.in: Define a "savedefault" function for use - in menu entries. - * util/grub-mkconfig_lib.in (save_default_entry): Use it. - -2010-01-05 Jordan Uggla -2010-01-05 Colin Watson - - * util/grub-mkconfig_lib.in (save_default_entry): Only set - saved_entry if boot_once is unset. - * util/grub.d/00_header.in: Set boot_once to "true" if there was a - previous saved entry (i.e. grub-reboot). - -2009-12-08 Colin Watson - - * util/grub.d/30_os-prober.in: Call save_default_entry for hurd. - -2009-12-08 Colin Watson - - * util/grub.d/00_header.in: Use `set var=val' rather than plain - `var=val'. - * util/grub-mkconfig_lib.in (save_default_entry): Likewise. - -2009-12-08 Colin Watson - - * util/grub-reboot.in: Fix --version output. - * util/grub-set-default.in: Likewise. - -2009-12-08 Colin Watson - - * util/grub.d/00_header.in: Silently ignore zero-sized environment - blocks. - -2009-12-08 Colin Watson - - * util/grub.d/00_header.in: Quote the value assigned to `default', - in case it contains spaces. - -2009-12-08 Colin Watson - - * util/grub.d/30_os-prober.in: Fix merge error that moved a - `save_default_entry' call from the macosx case to the linux case. - -2009-10-25 Vladimir Serbinenko -2009-10-25 Colin Watson - - * normal/menu.c (grub_menu_execute_entry): Save selected entry title - in `chosen' environment variable. - * normal/menu_text.c (get_entry_number): Check if the variable - matches the title of a menu entry. - (run_menu): Pass menu to get_entry_number. - - * util/grub-reboot.in: New file. - * util/grub-set-default.in: New file. - * conf/common.rmk (grub-reboot): New utility. - (grub-set-default): New utility. - - * util/grub-mkconfig_lib.in (save_default_entry): New function. - * util/grub.d/00_header.in: If GRUB_DEFAULT is `saved', set - default to `${saved_entry}'. If `${prev_saved_entry}' is non-empty, - move it to `saved_entry' for the next boot. Load environment on - initialisation. - * util/grub.d/10_kfreebsd.in: Call save_default_entry. - * util/grub.d/10_hurd.in: Likewise. - * util/grub.d/10_linux.in (linux_entry): Likewise. - * util/grub.d/10_windows.in: Likewise. - * util/grub.d/30_os-prober.in: Likewise. - - * util/grub-install.in: Create environment block. - * util/i386/efi/grub-install.in: Likewise. - * util/ieee1275/grub-install.in: Likewise. - * util/sparc64/ieee1275/grub-install.in: Likewise. - -2010-01-14 BVK Chaitanya - - Unit testing framework for GRUB. - - * Makefile.in: Test framework build rules for 'make check'. - * conf/tests.rmk: Build rules for individual tests and framework. - - * include/grub/test.h: Header file for whitebox tests. - * tests/lib/functional_test.c: Framework support for whitebox - functional tests. - * tests/lib/test.c: Common whitebox testing code for unit and - functional tests. - * tests/lib/unit_test.c: Framework support for whitebox unit - tests. - - * tests/util/grub-shell-tester.in: Support utility for grub-script - tests. - * tests/util/grub-shell.in: Utility to execute grub-script - commands in a Qemu instance. - - * tests/example_functional_test.c: Example whitebox functional - test. - * tests/example_grub_script_test.in: Example grub-script test. - * tests/example_scripted_test.in: Example scripted test. - * tests/example_unit_test.c: Example whitebox unit test. - -2010-01-14 Vladimir Serbinenko - - * conf/i386-coreboot.rmk (multiboot_mod_SOURCES): - Add loader/i386/multiboot_mbi.c. - (multiboot2_mod_SOURCES): Likewise. - * conf/i386-pc.rmk (multiboot_mod_SOURCES): Likewise. - (multiboot2_mod_SOURCES): Likewise. - * include/grub/multiboot.h (grub_multiboot_get_mbi_size): New proto. - (grub_multiboot_make_mbi): Likewise. - (grub_multiboot_free_mbi): Likewise. - (grub_multiboot_init_mbi): Likewise. - (grub_multiboot_add_module): Likewise. - (grub_multiboot_set_bootdev): Likewise. - * loader/i386/multiboot.c (mbi): Removed. - (mbi_dest): Likewise. - (alloc_mbi): New variable. - (grub_multiboot_payload_size): Removed. All users updated. - (grub_multiboot_pure_size): New variable. - (grub_multiboot_boot): Use grub_multiboot_make_mbi. - (grub_multiboot_unload): Use grub_multiboot_free_mbi. - (grub_get_multiboot_mmap_len): Moved to loader/i386/multiboot_mbi.c. - (grub_fill_multiboot_mmap): Likewise. - (grub_multiboot_get_bootdev): Likewise. - (grub_multiboot): Use multiboot_mbi functions. - * loader/i386/multiboot_mbi.c: New file. - -2010-01-13 Vladimir Serbinenko - - * kern/efi/init.c (grub_efi_fini): Don't call grub_efi_mm_fini as - it would result in module crash. - -2010-01-13 Vladimir Serbinenko - - * term/ieee1275/ofconsole.c (grub_ofconsole_putchar): Handle '\r'. - (grub_ofconsole_getwh): Split to ... - (grub_ofconsole_getwh): ... this. - (grub_ofconsole_dimensions): ...and this. - (grub_ofconsole_init_output): Call grub_ofconsole_dimensions. - -2010-01-13 Robert Millan - - * util/mkisofs/rock.c (generate_rock_ridge_attributes): Fix a typo. - -2010-01-12 Vladimir Serbinenko - - * loader/i386/pc/multiboot2.c: Removed stalled file. - -2010-01-12 Vladimir Serbinenko - - * util/grub-mkpasswd-pbkdf2.c (main): Use grub_util_init_nls. - Reported by: GrĂ©goire Sutre - -2010-01-11 Robert Millan - - * util/misc.c (canonicalize_file_name): New function. - (make_system_path_relative_to_its_root): Use canonicalize_file_name() - instead of realpath(). - -2010-01-11 Colin Watson - - * util/grub-install.in (usage): Clarify meaning of --root-directory, - and make it clearer that it's optional. Based on confusion - witnessed on IRC. - -2010-01-10 Vladimir Serbinenko - - * term/i386/pc/vga_text.c (inc_y): Fix off-by-one error which resulted - in premature implicit newline. - -2010-01-10 Vladimir Serbinenko - - * normal/cmdline.c (grub_cmdline_get): Fix off-by-one error - which resulted in garbled command line at the end of screen. - -2010-01-10 Robert Millan - - * loader/i386/ieee1275/linux.c (grub_linux_boot): Rework video position - initialization with similar approach as with other Linux loaders. - -2010-01-10 Robert Millan - - Fix i386-ieee1275 build. - - * loader/i386/ieee1275/linux.c (grub_linux_boot): Use grub_term_width() - and grub_term_height() for video_{width,height} initialization. - -2010-01-10 Robert Millan - - Fix grub-emu build. - - * conf/any-emu.rmk (grub_emu_SOURCES): Remove `kern/reader.c'. - -2010-01-07 Vladimir Serbinenko -2010-01-09 Robert Millan - - Support for multiple terminals. - - * Makefile.in (pkglib_DATA): terminal.lst. - (terminal.lst): New target. - * commands/handler.c (grub_cmd_handler): Don't handle terminals. - (GRUB_MOD_INIT(handler)): Likewise. - (GRUB_MOD_FINI(handler)): Likewise. - * commands/help.c (grub_cmd_help): Handle multiple terminals. - * commands/keystatus.c (grub_cmd_keystatus): Likewise. - * commands/sleep.c (do_print): Use grub_term_restore_pos. - (grub_cmd_sleep): Use grub_term_save_pos. - * commands/terminal.c: New file. - * conf/any-emu.rmk (grub_emu_SOURCES): Add normal/term.c - commands/terminal.c and lib/charset.c. - * conf/common.rmk (normal_mod_SOURCES): Add normal/term.c. - (pkglib_MODULES): Add terminal.mod. - (terminal_mod_SOURCES): New variable. - (terminal_mod_CFLAGS): Likewise. - (terminal_mod_LDFLAGS): Likewise. - * genhandlerlist.sh: Don't handle terminals. - * genmk.rb: Generate terminal-*.lst. - * genterminallist.sh: New file. - * include/grub/charset.h (grub_ucs4_to_utf8_alloc): New proto. - (grub_is_valid_utf8): Likewise. - (grub_utf8_to_ucs4_alloc): Likewise. - * include/grub/menu_viewer.h (grub_menu_viewer): Rewritten. - (grub_menu_register_viewer): Changed argument. - (grub_menu_try_text): New proto. - (grub_gfxmenu_try_hook): New declaration. - * include/grub/normal.h (grub_normal_exit_level): New declaration. - (grub_menu_init_page): Additional argument term. - (grub_normal_init_page): Likewise. - (grub_cmdline_get): Arguments simplified. - (grub_utf8_to_ucs4_alloc): Removed. - (grub_print_ucs4): Additional argument term. - (grub_getstringwidth): Likewise. - (grub_print_message_indented): Likewise. - (grub_menu_text_register_instances): New proto. - (grub_show_menu): Likewise. - (read_terminal_list): Likewise. - (grub_set_more): Likewise. - * include/grub/parser.h: Include handler.h. - * include/grub/reader.h: Rewritten. - * include/grub/term.h (GRUB_TERM_NEED_INIT): Removed. - (GRUB_TERM_WIDTH): Changed to function. - (GRUB_TERM_HEIGHT): Likewise. - (GRUB_TERM_BORDER_WIDTH): Likewise. - (GRUB_TERM_BORDER_HEIGHT): Likewise. - (GRUB_TERM_NUM_ENTRIES): Likewise. - (GRUB_TERM_ENTRY_WIDTH): Likewise. - (GRUB_TERM_CURSOR_X): Likewise. - (grub_term_input_class): Likewise. - (grub_term_output_class): Likewise. - (grub_term_outputs_disabled): New declaration. - (grub_term_inputs_disabled): Likewise. - (grub_term_outputs): Likewise. - (grub_term_inputs): Likewise. - (grub_term_register_input): Rewritten. - (grub_term_register_output): Likewise. - (grub_term_unregister_input): Likewise. - (grub_term_unregister_output): Likewise. - (FOR_ACTIVE_TERM_INPUTS): New macro. - (FOR_DISABLED_TERM_INPUTS): Likewise. - (FOR_ACTIVE_TERM_OUTPUTS): Likewise. - (FOR_DISABLED_TERM_OUTPUTS): Likewise. - * include/grub/terminfo.h: Add oterm argument to all protypes. - * kern/main.c (grub_main): Don't call grub_register_rescue_reader. - Use grub_rescue_run. - * kern/misc.c (grub_utf8_to_ucs4): Put '?' for invalid characters. - All users updated. - * kern/reader.c: Removed. All users updated. - * kern/rescue_reader.c (grub_rescue_init): Removed. - (grub_rescue_reader): Likewise. - (grub_register_rescue_reader): Likewise. - (grub_rescue_run): New function based on kern/reader.c. - * kern/term.c: Adapted for multiterm. - * lib/charset.c (grub_ucs4_to_utf8_alloc): New function. - (grub_is_valid_utf8): Likewise. - (grub_utf8_to_ucs4_alloc): Moved from normal/menu_text.c. - * loader/i386/efi/linux.c (grub_cmd_linux): Retrieve parameters of - right terminal. - * loader/i386/linux.c (grub_linux_boot): Likewise. - * normal/auth.c (grub_username_get): New function. - (grub_auth_check_authentication): Use grub_username_get. - * normal/cmdline.c: Changed to UCS4. Adapted for multiterm. - * normal/color.c: Adapt for multiterm. - * normal/main.c (read_config_file): Don't use grub_reader_loop. - (grub_normal_init_page): Additional argument term. - (read_lists): Call read_terminal_lists. - (grub_enter_normal_mode): Call grub_cmdline_run. - Handle grub_normal_exit_level. - (grub_cmd_normal): Make reentrant. - (grub_cmd_normal_exit): New function. - (grub_normal_reader_init): Additional argument nested. Handle multiterm. - * normal/menu.c: Adapt for multiterm. - * normal/menu_entry.c: Likewise. - * normal/menu_text.c: Likewise. - * normal/menu_viewer.c: Removed. All users updated. - * normal/term.c: New file. - * util/console.c: Change order of includes to workaround a bug in - ncurses headers. - * term/terminfo.c: New argument oterm on all exported functions. - All users updated. - * util/grub-editenv.c (grub_term_input_class): Removed. - (grub_term_output_class): Likewise. - -2010-01-09 Robert Millan - - Make loader output a bit more user-friendly. - - * util/grub.d/10_hurd.in: Print message indicating that GNU Mach - is being loaded. Likewise for the Hurd. - - * util/grub.d/10_kfreebsd.in (kfreebsd_entry): Print message indicating - that kernel of FreeBSD ${version} is being loaded. - - * loader/i386/linux.c (grub_cmd_linux): Move debug info to - grub_dprintf(). - (grub_cmd_initrd): Likewise. - * util/grub.d/10_linux.in (linux_entry): Print message indicating - that Linux ${version} is being loaded. Likewise for initrd. - -2010-01-09 Carles Pina i Estany - - * gettext/gettext.c (GRUB_MOD_INIT): Gettextizze. - -2010-01-08 Carles Pina i Estany - - * loader/efi/appleloader.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * loader/efi/chainloader.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/efi/linux.c: Include `'. - (grub_cmd_linux): Capitalise Linux. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/ieee1275/linux.c: Include `'. - (grub_cmd_linux): Capitalise Linux. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/linux.c: Include `'. - (grub_cmd_linux): Capitalise Linux. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/pc/chainloader.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/pc/linux.c: Include `'. - (grub_cmd_linux): Capitalise Linux. - (GRUB_MOD_INIT): Gettextizze. - * loader/i386/xnu.c: Include `'. - (grub_cpu_xnu_init): Gettextizze. - * loader/multiboot_loader.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * loader/powerpc/ieee1275/linux.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * loader/sparc64/ieee1275/linux.c: Include `'. - (grub_linux_load64): Capitalise Linux. - (GRUB_MOD_INIT): Gettextizze. - * loader/xnu.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * po/POTFILES: Add `loader/efi/appleloader.c', - `loader/efi/chainloader.c', `loader/i386/efi/linux.c', - `loader/i386/ieee1275/linux.c', `loader/i386/linux.c', - `loader/i386/pc/chainloader.c', `loader/i386/pc/linux.c', - `loader/i386/xnu.c', `loader/multiboot_loader.c', - `loader/powerpc/ieee1275/linux.c', `loader/sparc64/ieee1275/linux.c' - and `loader/xnu.c'. - -2010-01-08 Robert Millan - - * src/mkisofs.c: Remove `ifdef linux' portability kludge. - -2010-01-08 Robert Millan - - * util/mkisofs/defaults.h (APPID_DEFAULT): Redefine using PACKAGE_NAME. - (SYSTEM_ID_DEFAULT): Set to "GNU" unconditionally. - * util/mkisofs/mkisofs.c (main): Readjust --version output. - -2010-01-07 Robert Millan - - Reset Multiboot 2 support. New loader implements the draft in - /branches/multiboot2 and shares as much code as possible with the - production Multiboot 1 implementation. - - * loader/ieee1275/multiboot2.c: Remove file. Update all users. - * loader/multiboot2.c: Likewise. - * loader/i386/multiboot_helper.S: Likewise. - * include/multiboot2.h: Replace with latest version from the draft - in /branches/multiboot2. - - * conf/i386-coreboot.rmk (multiboot_mod_SOURCES): Remove - `loader/i386/multiboot_helper.S', `loader/i386/pc/multiboot2.c' - and `loader/multiboot2.c'. - (pkglib_MODULES): Add `multiboot2.mod'. - (multiboot2_mod_SOURCES): New variable. - (multiboot2_mod_LDFLAGS): Likewise. - (multiboot2_mod_CFLAGS): Likewise. Define `GRUB_USE_MULTIBOOT2'. - - * conf/i386-pc.rmk: Likewise. - - * conf/powerpc-ieee1275.rmk (pkglib_MODULES): Remove `multiboot.mod'. - (multiboot_mod_SOURCES): Remove variable. - (multiboot_mod_LDFLAGS): Likewise. - (multiboot_mod_CFLAGS): Likewise. - - * include/grub/multiboot.h [GRUB_USE_MULTIBOOT2]: Include - `' instead of `'. - [GRUB_USE_MULTIBOOT2] (MULTIBOOT_BOOTLOADER_MAGIC) - (MULTIBOOT_HEADER_MAGIC): New macros. - - * loader/multiboot_loader.c (module_version_status): Remove variable. - (find_multi_boot2_header): Remove function. - (grub_cmd_multiboot_loader): Remove Multiboot 2 / Multiboot 1 selection - logic. Always check for the Multiboot version we're compiling for. - (grub_cmd_module_loader): Likewise. - [GRUB_USE_MULTIBOOT2] (GRUB_MOD_INIT(multiboot)): Register `multiboot2' - command instead of `multiboot'. - -2010-01-07 Robert Millan - - * include/multiboot.h (MULTIBOOT_UNSUPPORTED): Moved from here ... - * loader/i386/multiboot.c (UNSUPPORTED_FLAGS): ... to here. Update - all users. - -2010-01-07 Robert Millan -2010-01-07 Vladimir Serbinenko - - Fix breakage introduced with previous commit. - - * normal/dyncmd.c (read_command_list): Avoid unregistering kernel - commands. - * normal/handler.c (read_handler_list): Revert part of previous commit - affecting this file. - * normal/main.c (read_lists): Move read_handler_list() call back to ... - (grub_normal_execute): ... here. - -2010-01-07 Robert Millan - - Merge prefix-redefinition-fix branch. - - * normal/autofs.c (read_fs_list): Make function capable of being - run multiple times, gracefuly replacing the previous data - structures. - * normal/dyncmd.c (read_command_list): Likewise. - * normal/handler.c (read_handler_list): Likewise. - * normal/main.c (read_lists): New function. Calls all the - list reading functions. - (grub_normal_execute): Use read_lists() instead of calling all - list reading functions explicitly. Register read_lists() as a - variable hook attached to ${prefix}. - -2010-01-07 Vladimir Serbinenko - - Merge crypto branch. - - * Makefile.in (pkglib_DATA): Add crypto.lst. - (crypto.lst): New target. - * commands/hashsum.c: New file. - * commands/password.c (check_password): Use grub_crypto_memcmp. - * commands/password_pbkdf2.c: New file. - * commands/xnu_uuid.c: Remove MD5. Use GRUB_MD_MD5. - * conf/any-emu.rmk (grub_emu_SOURCES): Add lib/crypto.c, - normal/crypto.c and lib/libgcrypt-grub/cipher/md5.c. - (grub_emu_CFLAGS): Add -Wno-missing-field-initializers -Wno-error - -I$(srcdir)/lib/libgcrypt_wrap. - * conf/common.rmk (normal_mod_SOURCES): Add normal/crypto.c. - (pkglib_MODULES): Add crypto.mod, hashsum.mod, pbkdf2.mod and - password_pbkdf2.mod. - (crypto_mod_SOURCES): New variable. - (crypto_mod_CFLAGS): Likewise. - (crypto_mod_LDFLAGS): Likewise. - (hashsum_mod_SOURCES): New variable. - (hashsum_mod_CFLAGS): Likewise. - (hashsum_mod_LDFLAGS): Likewise. - (pbkdf2_mod_SOURCES): New variable. - (pbkdf2_mod_CFLAGS): Likewise. - (pbkdf2_mod_LDFLAGS): Likewise. - (password_pbkdf2_mod_SOURCES): New variable. - (password_pbkdf2_mod_CFLAGS): Likewise. - (password_pbkdf2_mod_LDFLAGS): Likewise. - (bin_UTILITIES): Add grub-mkpasswd-pbkdf2. - (grub_mkpasswd_pbkdf2_SOURCES): New variable. - (grub_mkpasswd_pbkdf2_CFLAGS): Likewise. - Include conf/gcry.rmk. - * include/grub/auth.h: Rewritten. - * include/grub/crypto.h: New file. - * include/grub/disk.h (grub_disk_dev_id): Add GRUB_DISK_DEVICE_LUKS_ID. - * include/grub/normal.h (read_crypto_list): New prototype. - * lib/crypto.c: New file. - * lib/libgcrypt_wrap/cipher_wrap.h: Likewise. - * lib/pbkdf2.c: Likewise. - * normal/auth.c (grub_auth_strcmp): Removed. - (grub_iswordseparator): Likewise. - (grub_auth_strword): Likewise. - (is_authenticated): Use grub_strword. - (grub_auth_check_authentication): Use grub_strcmp, grub_password_get - and grub_strword. Pass entered password to authentication callback. - * normal/crypto.c: New file. - * normal/main.c: Call read_crypto_list. - * util/grub-mkpasswd-pbkdf2.c: New file. - * util/import_gcry.py: Generate crypto.lst. Add hash blocklen. - -2010-01-06 Vladimir Serbinenko - - Fix descent and ascent calculation. - - * util/grub-mkfont.c (grub_font_info): New fields 'asce' and 'max_y'. - (options): New option "asce". - (usage): Likewise. - (add_char): Ignore invalid glyphs for descent calculation. - Calculate ascent from actual content. - (print_glyphs): Use 'asce'. - (write_font): Likewise. Allow ascent override. - (main): Handle "asce" option. - -2010-01-06 Carles Pina i Estany - - * kern/err.c: Include `'. - (grub_print_error): Add full stop. Gettextizze. - * loader/i386/bsd.c (grub_netbsd_boot): Change grub_error description. - (grub_bsd_load_elf): Capitalise ELF. - (grub_cmd_freebsd_loadenv): Add `s' in error string. - (grub_cmd_freebsd_module): Likewise. - (grub_cmd_freebsd_module_elf): Likewise. - * loader/i386/bsdXX.c (SUFFIX): Capitalise ELF. - -2010-01-06 Carles Pina i Estany - - * commands/search.c (GRUB_MOD_INIT): Use HELP_MESSAGE. - * commands/search_file.c (HELP_MESSAGE): New macro. - * commands/search_label.c (HELP_MESSAGE): Likewise. - * commands/search_uuid.c (HELP_MESSAGE): Likewise. - * po/POTFILES: Add `commands/search_file.c', - `commands/search_label.c', `commands_uuid.c'. Remove duplicate - `commands/search.c'. - -2010-01-05 Robert Millan - - * config.rpath: Update from Gnulib. - -2010-01-05 Yves Blusseau - - * commands/acpi.c (grub_acpi_create_ebda): fix incorrect message. - -2010-01-05 Yves Blusseau - - * util/sparc64/ieee1275/grub-mkimage.c (main): Typo fix. - -2010-01-05 Colin Watson - - * util/mkisofs/write.c (padblock_write): Switch size and nmemb - arguments to fread so that we get a return value in bytes, rather - than something that will normally be rounded down to 0. - Adjust error handling to avoid producing garbage when size_t is not - the same size as long long. - -2010-01-05 Colin Watson - - * util/mkisofs/write.c (padblock_write): Check return value of - fread. - -2010-01-05 Robert Millan - - Remove grub-mkfloppy. Images produced by grub-mkrescue are valid - floppy images now. - - * util/i386/pc/grub-mkfloppy.in: Remove. Update all users. - -2010-01-04 Robert Millan - - * disk/i386/pc/biosdisk.c (grub_biosdisk_rw): Use ALIGN_UP macro - instead of manual alignment. - * kern/disk.c (grub_disk_read): Remove grub_dprintf call (excessively - verbose). Avoid attempts to read past end of the device - (grub_disk_adjust_range() guarantees that we can read `size' bytes, - but GRUB_DISK_CACHE_SIZE may exceed that). - -2010-01-04 Robert Millan - - * commands/crc.c (grub_cmd_crc): Abort on read errors. - * fs/iso9660.c (grub_iso9660_read): Check for read error and pass - it to upper layer. - -2010-01-04 Vladimir Serbinenko - - * include/grub/efi/api.h (GRUB_EFI_PIWG_DEVICE_PATH_SUBTYPE): - New constant. - (grub_efi_piwg_device_path): New structure - (grub_efi_piwg_device_path_t): New type. - * loader/efi/appleloader.c (piwg_full_device_path): New structure. - (devpath_1): Transform to a structure. All users updated. - (devpath_2): Likewise. - (devpath_3): Likewise. - (devpath_4): Likewise. - (devpath_5): Likewise. - -2010-01-04 Vladimir Serbinenko - - * loader/efi/appleloader.c: Restored. Update all users. - -2010-01-03 Robert Millan - - * boot/i386/pc/diskboot.S: Fix inaccurate comment. - - * util/i386/pc/grub-setup.c: Include `'. - (struct boot_blocklist): Move from here ... - * include/grub/i386/pc/boot.h [ASM_FILE] - (struct grub_boot_blocklist): ... to here. Update all users. - (setup): Only initialize `start' member of `first_block' - structure. Add assert() calls to verify the other members. - - * util/i386/pc/grub-mkimage.c: Include `'. - (generate_image): Fix broken blocklist length initialization. - Add assert() call to verify blocklist `segment' field. - -2010-01-03 Robert Millan - - * loader/efi/appleloader.c: Remove. Update all users. - -2010-01-03 Robert Millan - - * boot/i386/pc/boot.S: Update copyright year. - * boot/i386/pc/cdboot.S: Likewise. - * boot/i386/pc/diskboot.S: Likewise. - * boot/i386/pc/lnxboot.S: Likewise. - * boot/i386/pc/pxeboot.S: Likewise. - * bus/pci.c: Likewise. - * commands/cmp.c: Likewise. - * commands/help.c: Likewise. - * commands/hexdump.c: Likewise. - * commands/i386/pc/halt.c: Likewise. - * commands/i386/pc/play.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/ls.c: Likewise. - * commands/test.c: Likewise. - * disk/dmraid_nvidia.c: Likewise. - * disk/i386/pc/biosdisk.c: Likewise. - * disk/ieee1275/nand.c: Likewise. - * disk/ieee1275/ofdisk.c: Likewise. - * disk/lvm.c: Likewise. - * disk/raid.c: Likewise. - * disk/raid6_recover.c: Likewise. - * disk/scsi.c: Likewise. - * fs/affs.c: Likewise. - * fs/cpio.c: Likewise. - * fs/ext2.c: Likewise. - * fs/hfs.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/ntfs.c: Likewise. - * fs/sfs.c: Likewise. - * fs/udf.c: Likewise. - * fs/ufs.c: Likewise. - * fs/xfs.c: Likewise. - * gencmdlist.sh: Likewise. - * genmk.rb: Likewise. - * include/grub/disk.h: Likewise. - * include/grub/efi/api.h: Likewise. - * include/grub/efi/efi.h: Likewise. - * include/grub/efi/pe32.h: Likewise. - * include/grub/elf.h: Likewise. - * include/grub/fs.h: Likewise. - * include/grub/i386/at_keyboard.h: Likewise. - * include/grub/i386/pc/memory.h: Likewise. - * include/grub/i386/pc/vbe.h: Likewise. - * include/grub/i386/pci.h: Likewise. - * include/grub/i386/tsc.h: Likewise. - * include/grub/ieee1275/ieee1275.h: Likewise. - * include/grub/ntfs.h: Likewise. - * include/grub/sparc64/ieee1275/ieee1275.h: Likewise. - * include/grub/sparc64/libgcc.h: Likewise. - * include/grub/symbol.h: Likewise. - * include/grub/types.h: Likewise. - * include/multiboot2.h: Likewise. - * io/gzio.c: Likewise. - * kern/device.c: Likewise. - * kern/disk.c: Likewise. - * kern/efi/efi.c: Likewise. - * kern/efi/mm.c: Likewise. - * kern/elf.c: Likewise. - * kern/file.c: Likewise. - * kern/i386/dl.c: Likewise. - * kern/i386/pc/init.c: Likewise. - * kern/i386/pc/startup.S: Likewise. - * kern/ieee1275/ieee1275.c: Likewise. - * kern/ieee1275/init.c: Likewise. - * kern/main.c: Likewise. - * kern/mm.c: Likewise. - * kern/powerpc/dl.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/x86_64/dl.c: Likewise. - * lib/hexdump.c: Likewise. - * loader/efi/appleloader.c: Likewise. - * loader/i386/ieee1275/linux.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/i386/pc/multiboot2.c: Likewise. - * loader/ieee1275/multiboot2.c: Likewise. - * loader/multiboot2.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * normal/completion.c: Likewise. - * normal/menu_entry.c: Likewise. - * partmap/apple.c: Likewise. - * util/grub.d/10_hurd.in: Likewise. - * util/hostfs.c: Likewise. - * video/readers/png.c: Likewise. - -2010-01-03 Colin Watson - - * include/grub/misc.h (GNUC_PREREQ): New macro. - (ATTRIBUTE_ERROR): New macro. - * include/grub/list.h (grub_bad_type_cast_real): Use - ATTRIBUTE_ERROR. - -2010-01-03 Carles Pina i Estany - - * normal/menu_text.c (print_message): Change messages. - -2010-01-03 Carles Pina i Estany - - * normal/menu_entry.c (store_completion): Gettextizze. - -2010-01-03 Carles Pina i Estany - - * kern/env.c (grub_env_unset): Set the variable to "" if has hooks. - -2010-01-03 Carles Pina i Estany - - * po/POTFILES: Sort correctly. - -2010-01-03 Carles Pina i Estany - - * commands/acpi.c (GRUB_MOD_INIT): Capitalise some words from help. - * commands/efi/loadbios.c (GRUB_MOD_INIT): Capitalise BIOS. - * commands/i386/pc/drivemap.c (GRUB_MOD_INIT): Remove space. Add - full stop. - * commands/loadenv.c (GRUB_MOD_INIT): Remove command name from - summary. Gettextizze the strings. - * commands/probe.c (grub_cmd_probe): Capitalise UUID and FS. - * commands/xnu_uuid.c (GRUB_MOD_INIT): Capitalise XNU. - * disk/loopback.c (grub_arg_options): Capitalise first letter. Add - full stop. - (GRUB_MOD_INIT): Remove command name from summary. - * hello/hello.c (GRUD_MOT_INIT): Add missing full stop. Improve the - summary. - * loader/i386/bsd.c (grub_arg_option): Capitalise CDROM. - * term/i386/pc/serial.c (options): Add full stops. - (GRUB_MOD_INIT): Remove command name from the summary. - -2010-01-03 Carles Pina i Estany - - * commands/acpi.c: Gettextizze help strings and/or options. Include - `grub/i18n.h' if needed. - * commands/blocklist.c: Likewise. - * commands/boot.c: Likewise. - * commands/cat.c: Likewise. - * commands/cmp.c: Likewise. - * commands/configfile.c: Likewise. - * commands/crc.c: Likewise. - * commands/date.c: Likewise. - * commands/echo.c: Likewise. - * commands/efi/fixvideo.c: Likewise. - * commands/efi/loadbios.c: Likewise. - * commands/gptsync.c: Likewise. - * commands/halt.c: Likewise. - * commands/handler.c: Likewise. - * commands/hdparm.c: Likewise. - * commands/hexdump.c: Likewise. - * commands/i386/cpuid.c: Likewise. - * commands/i386/pc/drivemap.c: Likewise. - * commands/i386/pc/halt.c: Likewise. - * commands/i386/pc/pxecmd.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/i386/pc/vbetest.c: Likewise. - * commands/ieee1275/suspend.c: Likewise. - * commands/keystatus.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/ls.c: Likewise. - * commands/lsmmap.c: Likewise. - * commands/lspci.c: Likewise. - * commands/memrw.c: Likewise. - * commands/minicmd.c: Likewise. - * commands/parttool.c: Likewise. - * commands/password.c: Likewise. - * commands/probe.c: Likewise. - * commands/read.c: Likewise. - * commands/reboot.c: Likewise. - * commands/search.c: Likewise. - * commands/sleep.c: Likewise. - * commands/test.c: Likewise. - * commands/true.c: Likewise. - * commands/usbtest.c: Likewise. - * commands/videotest.c: Likewise. - * commands/xnu_uuid.c: Likewise. - * disk/loopback.c: Likewise. - * hello/hello.c: Likewise. - * loader/i386/bsd.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * po/POTFILES: Add new files. - -2010-01-02 Colin Watson - - * term/i386/pc/at_keyboard.c - (keyboard_controller_wait_untill_ready): Rename to ... - (keyboard_controller_wait_until_ready): ... this. Update all users. - -2010-01-01 Carles Pina i Estany - - * commands/help.c: Include `grub/mm.h' and `grub/normal.h'. - (grub_cmd_help): Print the cmd->name before the cmd->summary. Cut the - string using string width. - * normal/menu_text.c (grub_print_message_indented): Use - grub_print_spaces and not print_spaces. - (print_timeout): Likewise. - (print_spaces): Move to... - * include/grub/term.h: ... here. Change the name to grub_print_spaces. - -2010-01-01 Robert Millan - - Import from Gnulib. - - * gnulib/getdelim.c: New file. - * gnulib/getline.c: Likewise. - -2009-12-31 BVK Chaitanya - - * include/grub/list.h (grub_assert_fail): Removed. - (grub_bad_type_cast_real): New function. - (grub_bad_type_cast): New macro. - (GRUB_AS_LIST): Use grub_bad_type_cast. - (GRUB_AS_LIST_P): Likewise. - (GRUB_AS_NAMED_LIST): Likewise. - (GRUB_AS_NAMED_LIST_P): Likewise. - (GRUB_AS_PRIO_LIST): Likewise. - (GRUB_AS_PRIO_LIST_P): Likewise. - * include/grub/handler.h (GRUB_AS_HANDLER): Likewise. - -2009-12-29 Vladimir Serbinenko - - * loader/sparc64/ieee1275/linux.c (GRUB_MOD_INIT (linux)): - Fix syntax error. - -2009-12-29 Robert Millan - - * configure.ac: Check for TARGET_CFLAGS initialization before we - initialize it ourselves (sigh). - Move a few modifications to TARGET_CFLAGS to be unconditional - (extra warning flags, loop alignment, i386 CPU extensions, GCC 4.4 - eh_frame) - - * gettext/gettext.c (grub_gettext_delete_list): Add `void' argument. - * term/i386/pc/at_keyboard.c - (keyboard_controller_wait_untill_ready): Likewise. - (keyboard_controller_led): Rename `led_status' paramter to avoid - name conflict. - -2009-12-28 Carles Pina i Estany - - * normal/misc.c (grub_normal_print_device_info): Add spaces and double - quotes. - -2009-12-27 Vladimir Serbinenko - - * kern/parser.c (grub_parser_split_cmdline): Don't dereference NULL. - -2009-12-27 Vladimir Serbinenko - - * normal/menu_text.c (grub_print_message_indented): Prevent - past-the-end-of-array dereference. - -2009-12-27 Vladimir Serbinenko - - * video/readers/jpeg.c (GRUB_MOD_FINI (grub_cmd_jpegtest)): Rename to .. - (GRUB_MOD_FINI (video_reader_jpeg)): ...this - -2009-12-27 Carles Pina i Estany - - * normal/cmdline.c (grub_cmdline_get): Print a space after prompt. - * normal/main.c (grub_normal_read_line): Remove a space from the - default prompt. - -2009-12-27 Carles Pina i Estany - - * loader/i386/efi/linux.c (GRUB_MOD_INIT): Improve command summary. - * loader/i386/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/i386/linux.c (GRUB_MOD_INIT): Likewise. - * loader/i386/pc/linux.c (GRUB_MOD_INIT): Likewise. - * loader/powerpc/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/sparc64/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/xnu.c (GRUB_MOD_INIT): Likewise. - -2009-12-26 Carles Pina i Estany - - * video/readers/jpeg.c (cmd): Declare. - (grub_cmd_jpegtest): Use `grub_command_t' type. - (GRUB_MOD_INIT): Fix arguments passed to `grub_register_command'. - Assign to `cmd'. - (GRUB_MOD_FINI): Use `cmd' to unregister. - * video/readers/png.c (cmd): Declare. - (grub_cmd_pngtest): Use `grub_command_t' type. - (GRUB_MOD_INIT): Fix arguments passed to `grub_register_command'. - Assign to `cmd'. - (GRUB_MOD_FINI): Use `cmd' to unregister. - * video/readers/tga.c (cmd): Declare. - (grub_cmd_tgatest): Use `grub_command_t' type. - (GRUB_MOD_INIT): Fix arguments passed to `grub_register_command'. - Assign to `cmd'. - (GRUB_MOD_FINI): Use `cmd' to unregister. - -2009-12-26 Carles Pina i Estany - - * efiemu/main.c (GRUB_MOD_INIT): Fix capitalizations and/or full - stops. - * kern/corecmd.c (grub_register_core_commands): Likewise. - * loader/efi/chainloader.c (GRUB_MOD_INIT): Likewise. - * loader/i386/bsd.c (GRUB_MOD_INIT): Likewise. - * loader/i386/efi/linux.c (GRUB_MOD_INIT): Likewise. - * loader/i386/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/i386/linux.c (GRUB_MOD_INIT): Likewise. - * loader/i386/pc/chainloader.c (GRUB_MOD_INIT): Likewise. - * loader/i386/pc/linux.c (GRUB_MOD_INIT): Likewise. - * loader/multiboot_loader.c (GRUB_MOD_INIT): Likewise. - * loader/powerpc/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/sparc64/ieee1275/linux.c (GRUB_MOD_INIT): Likewise. - * loader/xnu.c (GRUB_MOD_INIT): Likewise. - * mmap/mmap.c (GRUB_MOD_INIT): Likewise. - * normal/handler.c (insert_handler): Likewise. - * normal/main.c (GRUB_MOD_INIT): Likewise. - * term/gfxterm.c (GRUB_MOD_INIT): Likewise. - -2009-12-26 Carles Pina i Estany - - * commands/help.c (grub_cmd_help): Print the command name before the - summary. - (GRUB_MOD_INIT): Remove command name from the summary. - * kern/command.c (GRUB_MOD_INIT): If summary is null assign an empty - string as summary. - * lib/arg.c (find_long): Print the command name before the summary. - * commands/acpi.c (GRUB_MOD_INIT): Remove command name from the - summary. - * commands/blocklist.c (GRUB_MOD_INIT): Likewise. - * commands/cat.c (GRUB_MOD_INIT): Likewise. - * commands/cmp.c (GRUB_MOD_INIT): Likewise. - * commands/configfile.c (GRUB_MOD_INIT): Likewise. - * commands/crc.c (GRUB_MOD_INIT): Likewise. - * commands/date.c (GRUB_MOD_INIT): Likewise. - * commands/echo.c (GRUB_MOD_INIT): Likewise. - * commands/efi/loadbios.c (GRUB_MOD_INIT): Likewise. - * commands/gptsync.c (GRUB_MOD_INIT): Likewise. - * commands/handler.c (GRUB_MOD_INIT): Likewise. - * commands/hdparm.c (GRUB_MOD_INIT): Likewise. - * commands/hexdump.c (GRUB_MOD_INIT): Likewise. - * commands/i386/cpuid.c (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/halt.c (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/play.c (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/pxecmd.c (GRUB_MOD_INIT): Likewise. - * commands/keystatus.c (GRUB_MOD_INIT): Likewise. - * commands/loadenv.c (GRUB_MOD_INIT): Likewise. - * commands/ls.c (GRUB_MOD_INIT): Likewise. - * commands/lspci.c (GRUB_MOD_INIT): Likewise. - * commands/memrw.c (GRUB_MOD_INIT): Likewise. - * commands/minicmd.c (GRUB_MOD_INIT): Likewise. - * commands/parttool.c (GRUB_MOD_INIT): Likewise. - * commands/password.c (GRUB_MOD_INIT): Likewise. - * commands/probe.c (GRUB_MOD_INIT): Likewise. - * commands/read.c (GRUB_MOD_INIT): Likewise. - * commands/search.c (GRUB_MOD_INIT): Likewise. - * commands/sleep.c (GRUB_MOD_INIT): Likewise. - * commands/test.c (GRUB_MOD_INIT): Likewise. - * commands/xnu_uuid.c (GRUB_MOD_INIT): Likewise. - * efiemu/main.c (GRUB_MOD_INIT): Likewise. - * font/font_cmd.c (GRUB_MOD_INIT): Likewise. - * gettext/gettext.c (GRUB_MOD_INIT): Likewise. - * kern/corecmd.c (GRUB_MOD_INIT): Likewise. - * lib/arg.c (GRUB_MOD_INIT): Likewise. - * loader/efi/appleloader.c (GRUB_MOD_INIT): Likewise. - * loader/i386/bsd.c (GRUB_MOD_INIT): Likewise. - * loader/xnu.c (GRUB_MOD_INIT): Likewise. - * mmap/mmap.c (GRUB_MOD_INIT): Likewise. - * term/terminfo.c (GRUB_MOD_INIT): Likewise. - * video/readers/jpeg.c (GRUB_MOD_INIT): Likewise. - * video/readers/png.c (GRUB_MOD_INIT): Likewise. - * video/readers/tga.c (GRUB_MOD_INIT): Likewise. - -2009-12-25 Vladimir Serbinenko - - Use search command for preliminar UUID search. - - * commands/search.c: Split into ... - * commands/search_wrap.c: ...this - * commands/search.c: ...and this. - * commands/search_file.c: New file. - * commands/search_label.c: New file. - * commands/search_uuid.c: New file. - * conf/any-emu.rmk (grub_emu_SOURCES): Remove commands/search.c. - Add commands/search_wrap.c, commands/search_file.c, - commands/search_label.c and commands/search_uuid.c. - * conf/common.rmk (pkglib_MODULES): Remove fs_uuid.mod and fs_file.mod. - (search_mod_SOURCES): Set to commands/search_wrap.c. - (pkglib_MODULES): Add search_fs_file.mod, search_fs_uuid.mod and - search_label.mod. - (search_fs_file_mod_SOURCES): New variable. - (search_fs_file_mod_CFLAGS): Likewise. - (search_fs_file_mod_LDFLAGS): Likewise. - (search_label_mod_SOURCES): Likewise. - (search_label_mod_CFLAGS): Likewise. - (search_label_mod_LDFLAGS): Likewise. - (search_fs_uuid_mod_SOURCES): New variable. - (search_fs_uuid_mod_CFLAGS): Likewise. - (search_fs_uuid_mod_LDFLAGS): Likewise. - (fs_file_mod_SOURCES): Removed. - (fs_file_mod_CFLAGS): Likewise. - (fs_file_mod_LDFLAGS): Likewise. - (fs_uuid_mod_SOURCES): Removed. - (fs_uuid_mod_CFLAGS): Likewise. - (fs_uuid_mod_LDFLAGS): Likewise. - * conf/sparc64-ieee1275.rmk (grub_install_SOURCES): - Set to util/grub-install.in. - * disk/fs_file.c: Removed. - * disk/fs_uuid.c: Likewise. - * include/grub/search.h: New file. - * util/grub-install.in: Handle sparc64. - Create and use load.cfg. - * util/sparc64/ieee1275/grub-install.in: Removed. - -2009-12-25 Vladimir Serbinenko - - * kern/i386/pc/startup.S (grub_biosdisk_get_diskinfo_int13_extensions): - Ignore return status if CF is cleared. - (grub_biosdisk_get_diskinfo_standard): Likewise. - -2009-12-25 Robert Millan - - * term/i386/pc/at_keyboard.c - (keyboard_controller_wait_untill_ready): New function. - (grub_keyboard_controller_write, grub_keyboard_controller_read) - (keyboard_controller_led): Use keyboard_controller_wait_untill_ready() - for keyboard polling, rather than duplicate the same loop. This - saves a few bytes in code size. - -2009-12-25 Vladimir Serbinenko - - Support for (pxe[:server[:gateway]]) syntax and - use environment variable for PXE. - - * commands/i386/pc/pxecmd.c (options): Removed. - (print_ip): Removed. - (grub_cmd_pxe): Removed - (grub_cmd_pxe_unload): New function. - * fs/i386/pc/pxe.c (grub_pxe_disk_data): New structure. - (grub_pxe_your_ip): Made static. - (grub_pxe_default_server_ip): Likewise. - (grub_pxe_default_gateway_ip): Likewise. - (grub_pxe_blksize): Likewise. - (parse_ip): New function. - (grub_pxe_open): Support server and gateway specification. - (grub_pxe_close): Free disk->data. - (grub_pxefs_open): Use disk->data. - (grub_pxefs_read): Likewise. - (grub_env_write_readonly): New function. - (set_mac_env): Likewise. - (set_env_limn_ro): Likewise. - (parse_dhcp_vendor): Likewise. - (grub_pxe_detect): Set the environment variables. - (set_ip_env): New function. - (write_ip_env): Likewise. - (grub_env_write_pxe_default_server): Likewise. - (grub_env_write_pxe_default_gateway): Likewise. - (grub_env_write_pxe_blocksize): Likewise. - (GRUB_MOD_INIT(pxe)): Set environment variables. - * include/grub/i386/pc/pxe.h (grub_pxe_mac_addr): Rename to ... - (grub_pxe_mac_addr_t): ... this. All users updated. - (grub_pxe_your_ip): Removed. - (grub_pxe_server_ip): Likewise. - (grub_pxe_gateway_ip): Likewise. - (grub_pxe_blksize): Likewise. - -2009-12-25 Carles Pina i Estany - - * commands/help.c: Include `'. - (grub_cmd_help): Gettextizze. - (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/play.c: Include `'. - (GRUB_MOD_INIT): Gettextizze. - * commands/search.c: Include `'. - (options): Gettextizze. - (GRUB_MOD_INIT): Gettextizze. - * lib/arg.c: Include `'. - (help_options): Gettextizze. - (find_long): Likewise. - (grub_arg_show_help): Likewise. - * normal/dyncmd.c: Include `'. - (read_command_list): Gettextizze. - * po/POTFILES: Add `commands/i386/pc/play.c', `commands/search.c', - `commands/help.c', `lib/arg.c' and `normal/dyncmd.c'. - -2009-12-25 Robert Millan - - * include/grub/i386/at_keyboard.h (NUM_LOCK, SCROLL_LOCK): New macros. - * term/i386/pc/at_keyboard.c (KEYBOARD_STATUS_NUM_LOCK) - (KEYBOARD_LED_SCROLL, KEYBOARD_LED_NUM, KEYBOARD_LED_CAPS): New macros. - (led_status): New variable. - (keyboard_controller_led): New function. - (grub_at_keyboard_getkey_noblock): Handle num lock and scroll lock, - update led status for caps lock, num lock and scroll lock. - -2009-12-25 Felix Zielcke - - * util/hostdisk.c (open_device): Fix a comment. - -2009-12-24 Robert Millan - - * util/grub-install.in (host_os): New variable. - * util/i386/efi/grub-install.in (host_os): Likewise. - -2009-12-24 Robert Millan - - * util/mkisofs/write.c (padblock_write): Abort when given an - excedingly large embed image, instead of silently truncating it. - -2009-12-24 Robert Millan - - * include/multiboot.h: Indentation fixes. - -2009-12-24 Robert Millan - - * include/multiboot.h (struct multiboot_aout_symbol_table) - (struct multiboot_elf_section_header_table): New structure - declarations (stolen from GRUB Legacy). - (struct multiboot_info): Replace opaque `syms' with a.out and ELF - table information. - - (multiboot_aout_symbol_table_t, multiboot_elf_section_header_table_t) - (multiboot_info_t, multiboot_memory_map_t, multiboot_module_t): New - type aliases. - -2009-12-24 Robert Millan - - * include/multiboot.h: Make comments src2texi-friendly. - -2009-12-24 Robert Millan - - For consistency with [multiboot]/docs/boot.S. - - * include/multiboot.h (MULTIBOOT_MAGIC): Rename from this ... - (MULTIBOOT_HEADER_MAGIC): ... to this. Update all users. - (MULTIBOOT_MAGIC2): Rename from this ... - (MULTIBOOT_BOOTLOADER_MAGIC): ... to this. Update all users. - -2009-12-24 Robert Millan - - * include/multiboot.h: Remove `'. - (multiboot_uint16_t, multiboot_uint32_t, multiboot_uint64_t): New - types. Update all users. - -2009-12-25 Carles Pina i Estany - - * commands/efi/loadbios.c: Capitalize acronyms, replace `could not' by - `couldn't' and `can not' by `cannot'. - * commands/i386/pc/drivemap.c: Likewise. - * disk/ata.c: Likewise. - * disk/ieee1275/nand.c: Likewise. - * fs/affs.c: Likewise. - * fs/fat.c: Likewise. - * fs/hfs.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/jfs.c: Likewise. - * fs/minix.c: Likewise. - * fs/reiserfs.c: Likewise. - * fs/sfs.c: Likewise. - * fs/udf.c: Likewise. - * fs/ufs.c: Likewise. - * fs/xfs.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * loader/sparc64/ieee1275/linux.c: Likewise. - * util/grub-probe.c: Likewise. - * util/misc.c: Likewise. - -2009-12-24 Carles Pina i Estany - - * bus/usb/usbhub.c: Fix capitalization, fullstop and newlines in - grub_errno calls. - * commands/acpi.c: Likewise. - * commands/blocklist.c: Likewise. - * commands/efi/loadbios.c: Likewise. - * commands/i386/pc/drivemap.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/memrw.c: Likewise. - * commands/password.c: Likewise. - * commands/videotest.c: Likewise. - * disk/ata.c: Likewise. - * disk/ata_pthru.c: Likewise. - * disk/dmraid_nvidia.c: Likewise. - * disk/ieee1275/nand.c: Likewise. - * disk/ieee1275/ofdisk.c: Likewise. - * disk/loopback.c: Likewise. - * disk/lvm.c: Likewise. - * disk/mdraid_linux.c: Likewise. - * disk/raid.c: Likewise. - * disk/raid6_recover.c: Likewise. - * disk/scsi.c: Likewise. - * efiemu/main.c: Likewise. - * efiemu/mm.c: Likewise. - * efiemu/pnvram.c: Likewise. - * efiemu/symbols.c: Likewise. - * font/font.c: Likewise. - * fs/cpio.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/jfs.c: Likewise. - * fs/minix.c: Likewise. - * fs/ntfs.c: Likewise. - * fs/ntfscomp.c: Likewise. - * fs/reiserfs.c: Likewise. - * fs/ufs.c: Likewise. - * fs/xfs.c: Likewise. - * gettext/gettext.c: Likewise. - * include/grub/auth.h: Likewise. - * kern/elf.c: Likewise. - * kern/file.c: Likewise. - * kern/ieee1275/init.c: Likewise. - * kern/ieee1275/mmap.c: Likewise. - * kern/ieee1275/openfw.c: Likewise. - * kern/powerpc/dl.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * lib/arg.c: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/i386/bsdXX.c: Likewise. - * loader/i386/efi/linux.c: Likewise. - * loader/i386/efi/xnu.c: Likewise. - * loader/i386/ieee1275/linux.c: Likewise. - * loader/i386/linux.c: Likewise. - * loader/i386/multiboot.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/i386/pc/multiboot2.c: Likewise. - * loader/i386/xnu.c: Likewise. - * loader/ieee1275/multiboot2.c: Likewise. - * loader/macho.c: Likewise. - * loader/machoXX.c: Likewise. - * loader/multiboot2.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * loader/sparc64/ieee1275/linux.c: Likewise. - * loader/xnu.c: Likewise. - * loader/xnu_resume.c: Likewise. - * mmap/i386/pc/mmap.c: Likewise. - * normal/menu_viewer.c: Likewise. - * partmap/acorn.c: Likewise. - * partmap/amiga.c: Likewise. - * partmap/apple.c: Likewise. - * script/lexer.c: Likewise. - * term/gfxterm.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/i386/pc/vga.c: Likewise. - * term/ieee1275/ofconsole.c: Likewise. - * term/terminfo.c: Likewise. - * video/bitmap.c: Likewise. - * video/efi_gop.c: Likewise. - * video/efi_uga.c: Likewise. - * video/fb/video_fb.c: Likewise. - * video/i386/pc/vbe.c: Likewise. - * video/readers/tga.c: Likewise. - * video/video.c: Likewise. - -2009-12-23 Felix Zielcke - - * commands/i386/pc/drivemap.c: Remove all trailing whitespace. - * commands/lspci.c: Likewise. - * commands/probe.c: Likewise. - * commands/xnu_uuid.c: Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * fs/i386/pc/pxe.c: Likewise. - * gettext/gettext.c: Likewise. - * include/grub/efi/graphics_output.h: Likewise. - * include/grub/i386/pc/memory.h: Likewise. - * kern/env.c: Likewise. - * kern/i386/qemu/startup.S: Likewise. - * lib/i386/pc/biosnum.c: Likewise. - * lib/i386/relocator.c: Likewise. - * lib/i386/relocator_asm.S: Likewise. - * lib/relocator.c: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/i386/multiboot.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/xnu.c: Likewise. - * loader/xnu.c: Likewise. - * normal/main.c: Likewise. - * normal/menu_text.c: Likewise. - * util/getroot.c: Likewise. - * util/grub-mkconfig_lib.in: Likewise. - * util/grub.d/00_header.in: Likewise. - * util/i386/pc/grub-mkimage.c: Likewise. - * util/mkisofs/eltorito.c: Likewise. - * util/mkisofs/exclude.h: Likewise. - * util/mkisofs/hash.c: Likewise. - * util/mkisofs/iso9660.h: Likewise. - * util/mkisofs/joliet.c: Likewise. - * util/mkisofs/mkisofs.c: Likewise. - * util/mkisofs/mkisofs.h: Likewise. - * util/mkisofs/multi.c: Likewise. - * util/mkisofs/name.c: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/tree.c: Likewise. - * util/mkisofs/write.c: Likewise. - * video/efi_gop.c: Likewise. - -2009-12-23 Vladimir Serbinenko - - * video/efi_gop.c (grub_video_gop_get_bitmask): Fix off-by-one in mask - size counting. - -2009-12-22 Felix Zielcke - - * util/grub-mkrescue.in (pkglib_DATA): Set to @pkglib_DATA@. - * genmk.rb (class SCRIPT): Modify the target file instead of source. - -2009-12-22 Vladimir Serbinenko - - * commands/memrw.c (grub_cmd_write): Support for mask parameter. - (GRUB_MOD_INIT(memrw)): Update help line. - -2009-12-22 Vladimir Serbinenko - - * commands/memrw.c (cmd_read_byte, cmd_read_word, cmd_read_dword): - Use grub_extcmd_t. All users updated. - (options): New variable. - (grub_cmd_read): Restructure for readability. Support "-v" option. - (grub_cmd_write): Restructure for readability. - -2009-12-22 Felix Zielcke - - * genmk.rb (class SCRIPT): Prepend #{src} path with $(srcdir). - -2009-12-22 Felix Zielcke - - * genmk.rb (class SCRIPT): Use sed to substitute @pkglib_DATA@ - with the actual contents of the correspondending make variable. - * util/grub-mkrescue.in (pkglib_DATA): New variable. - (process_input_dir): Copy all $pkglib_DATA files instead of explicitly - specifying `*.lst' and `efiemu??.o' - -2009-12-22 Felix Zielcke - - * util/grub.d/30_os-prober.in (osx_entry): Add round brackets - after function name. - Noticed by Rene Engelhard . - -2009-12-22 Vladimir Serbinenko - - * commands/lspci.c (grub_pci_classes): Add "USB Controller". - (options): New variable. - (iospace): Likewise. - (grub_lspci_iter): List IO spaces if "-i" was given. - (grub_cmd_lspci): Parse options. - (GRUB_MOD_INIT(lspci)): Use extcmd. - (GRUB_MOD_FINI(lspci)): Likewise. - -2009-12-22 Felix Zielcke - - * util/grub.d/30_os-prober.in (osx_entry): Remove non POSIX compliant - `function' keyword. - Patch by Tony Mancill . - -2009-12-22 Vladimir Serbinenko - - * bus/usb/uhci.c (grub_uhci_transfer): Set a limit transaction time. - (grub_uhci_portstatus): Likewise. - (grub_uhci_portstatus): Add necessary delay. - * bus/usb/usbhub.c (grub_usb_hub_add_dev): Fix loop-break condition. - -2009-12-21 Carles Pina i Estany - - * commands/acpi.c (options): Fix capitalizations and/or full stops. - (GRUB_MOD_INIT): Likewise. - * commands/boot.c (GRUB_MOD_INIT): Likewise. - * commands/cmp.c (grub_cmd_cmp): Improve the help message. - * commands/echo.c (options): Fix capitalizations and/or full stops. - * commands/efi/loadbios.c (enable_rom_area): Likewise. - (enable_rom_area): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/gptsync.c (GRUB_MOD_INIT): Likewise. - * commands/halt.c (GRUB_MOD_INIT): Improve the help message. - * commands/handler.c (GRUB_MOD_INIT): Likewise. - * commands/hdparm.c (options): Fix capitalizations and/or full stops. - * commands/hexdump.c (options): Likewise. - * commands/i386/cpuid.c (options): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/drivemap.c (options): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/halt (options): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/play.c (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/pxecmd.c (options): Likewise. - * commands/i386/pc/vbetest.c (GRUB_MOD_INIT): Likewise. - * commands/ieee1275/suspend.c (GRUB_MOD_INIT): Likewise. - * commands/keystatus.c (options): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/loadenv.c (options): Likewise. - * commands/ls.c (options): Likewise. - * commands/lspci.c (GRUB_MOD_INIT): Likewise. - * commands/memrw.c (GRUB_MOD_INIT): Likewise. - * commands/minicmd.c (GRUB_MOD_INIT): Likewise. - * commands/parttool.c (helpmsg): Likewise. - * commands/probe.c (options): Likewise. - * commands/read.c (GRUB_MOD_INIT): Likewise. - * commands/reboot.c (GRUB_MOD_INIT): Likewise. - * commands/search.c (options): Likewise. - * commands/sleep.c (options): Likewise. - * commands/test.c (GRUB_MOD_INIT): Likewise. - * commands/true.c (GRUB_MOD_INIT): Likewise. - * commands/usbtest.c (GRUB_MOD_INIT): Likewise. - * commands/videotest.c (GRUB_MOD_INIT): Likewise. - * lib/arg.c (help_options): Likewise. - * Makefile.in ($(srcdir)/po/$(PACKAGE).pot): Pass -ctranslate to - `$(XGETTEXT)'. - * po/POTFILES: Add `commands/loadenv.c'. - -2009-12-21 Felix Zielcke - - * util/grub-mkrescue.in (process_input_dir): Copy `*.lst' files - instead of specifying them explicit. - -2009-12-21 Robert Millan - - * NEWS: Add grub-probe support for GNU/Hurd. - -2009-12-21 Robert Millan - - * NEWS: gettext was added after 1.97. - -2009-12-21 Robert Millan - - * util/mkisofs/msdos_partition.h: New file (based on - include/grub/msdos_partition.h). - * util/mkisofs/mkisofs.c (use_protective_msdos_label): New variable. - (OPTION_PROTECTIVE_MSDOS_LABEL): New macro. - (ld_options, main): Recognize --protective-msdos-label. - * util/mkisofs/mkisofs.h (use_protective_msdos_label): New declaration. - * util/mkisofs/write.c: Include `"msdos_partition.h"'. - (padblock_write): If `use_protective_msdos_label' is set, patch a - protective DOS-style label in the output image. - - * util/grub-mkrescue.in: Use --protective-msdos-label. - -2009-12-21 Robert Millan - - * util/grub-mkrescue.in: Do not zero-pad image for BIOS-based disk - boot. - -2009-12-21 Robert Millan - - * util/mkisofs/mkisofs.c (use_embedded_boot, boot_image_embed): New - variables. - (ld_options, main): Recognize `--embedded-boot'. - * util/mkisofs/mkisofs.h (use_embedded_boot, boot_image_embed): New - declarations. - * util/mkisofs/write.c (PADBLOCK_SIZE): New variable. - (padblock_size): Use `PADBLOCK_SIZE' instead of hardcoding 16. - (padblock_write): Likewise. Rewrite to support embedded boot image. - - * util/grub-mkrescue.in: When building i386-pc images, embed core.img - for BIOS-based disk boot instead of only ElTorito. - -2009-12-21 Robert Millan - - * util/grub-mkrescue.in: Remove `configfile' and `sh' from i386-pc - build (not needed for bootstrap). - -2009-12-21 Robert Millan - - * util/grub-mkrescue.in: Remove `memdisk', `tar' and `search' modules - from i386-pc build (not needed for bootstrap). - Rewrite a pair of strings. - -2009-12-21 Robert Millan - - * normal/main.c (grub_normal_reader_init): Set left margin back to 3. - -2009-12-21 Vladimir Serbinenko - - * video/i386/pc/vbe.c (grub_video_vbe_fini): Set 'last_set_mode'. - -2009-12-21 Andreas Born - - * kern/env.c (grub_env_context_open): Mark exported variable for - reexport. - -2009-12-21 Andreas Born - - * kern/env.c (grub_env_export): Create nonexistent variables before - exporting. - -2009-12-20 Carles Pina i Estany - - * include/grub/auth.h: Include `'. - (GRUB_GET_PASSWORD): Gettextizze string. - * include/grub/normal.h (STANDARD_MARGIN): New macro, moved from - menu_text.c. - (grub_utf8_to_ucs4_alloc): Fix indentation. - (grub_print_ucs4): Likewise. - (grub_getstringwidth): Likewise. - (print_message_indented): New declaration. - * normal/auth.c: Include `'. - (grub_auth_check_authentication): Gettexttize string. - * normal/cmdline.c: Include `'. - (grub_cmdline_get): Gettextizze. - * normal/color.c: Include `'. - (grub_parse_color_name_pair): Gettexttize strings. - * normal/main.c (grub_normal_reader_init): Cleanup gettexttized - string (use `print_message_indented'). - * normal/menu_text.c (STANDARD_MARGIN): Moved from here to - `include/grub/normal.h'. - (print_message_indented): Renamed to ... - (grub_print_message_indented): ... this. Remove `static' qualifer (now - used in normal/main.c). - (print_message): Use `grub_print_message_indented' instead of - `print_message_indented'. - (print_timeout): Likewise. - * normal/misc.c: Include `' and `'. - (grub_normal_print_device_info): Gettexttize strings. - * po/POTFILES: Add `auth.c', `color.c' and `misc.c'. - -2009-12-20 Vladimir Serbinenko - - * kern/parser.c (grub_parser_split_cmdline): Fix incorrect counting - of arguments. Return number of tokens and not arguments. All users - updated. - -2009-12-20 Vladimir Serbinenko - - * util/i386/pc/grub-setup.c (setup): Don't install on non-GPT, - non-MSDOS paritions. - -2009-12-19 Vladimir Serbinenko - - * include/grub/types.h (UNUSED): Removed since it conflicts with - NetBSD headers. All users changed to direct __attribute__ ((unused)). - Reported by GrĂ©goire Sutre. - -2009-12-19 Carles Pina i Estany - - * include/grub/normal.h (grub_utf8_to_ucs4): New declaration. - (grub_print_ucs4_alloc): Likewise. - (grub_getstringwidth): Likewise. - * normal/main.c (grub_normal_init_page): Gettextize version string. - * normal/menu_text.c (grub_utf8_to_ucs4_alloc): New definition. - (getstringwidth): Renamed to ... - (grub_getstringwidth): ... this. Remove `static' qualifier (now used - in normal/main.c). Use `grub_utf8_to_ucs4_alloc'. - (grub_print_ucs4): Remove `static' qualifer (now used in - normal/main.c). - * po/POTFILES: Add normal/main.c. - -2009-12-19 Carles Pina i Estany - - * normal/menu_text.c (STANDARD_MARGIN): New macro. - (print_message_indented): Add `margin_left' and `margin_right' - parameters. - (print_message): Update `print_message_indented' calls. Adds '\n' to the - strings. - (print_timeout): Use `print_message_indented' to print the message. - Deletes `second_stage' parameter. - (run_menu): Update `print_timeout' calls. - -2009-12-18 Vladimir Serbinenko - - Fix console palette on OpenFirmware. - - * term/ieee1275/ofconsole.c (MAX): Removed. - (colors): Redone based on VGA palette. - (grub_ofconsole_setcolor): Discard brightness bit since only 8 - colors are supported. - (grub_ofconsole_init_output): Use ARRAY_SIZE instead of hardcoded size. - -2009-12-18 Vladimir Serbinenko - - Fix potential EfiEmu double prepare. - - * efiemu/main.c (prepared): New variable - (grub_efiemu_unload): Set prepare to '0'. - (grub_efiemu_prepare): Return if already prepared. Set prepared. - - set_virtual_address_map support. - - * include/grub/efi/efi.h (grub_efi_set_virtual_address_map): New - prototype. - * include/grub/efiemu/efiemu.h (grub_efiemu_write_sym_markers): New - prototype. - (grub_efiemu_crc32): Likewise. - (grub_efiemu_crc64): Likewise. - (grub_efiemu_set_virtual_address_map): Likewise. - * include/grub/autoefi.h (grub_autoefi_exit_boot_services): - New definition. - (grub_autoefi_set_virtual_address_map): Likewise. - * kern/efi/efi.c (grub_efi_set_virtual_address_map): New function. - * loader/i386/xnu.c (grub_xnu_boot): Call set_virtual_address_map. - Restructure flow to accomodate it. - * efiemu/prepare.c (grub_efiemu_prepare): Support set_virtual_address_map. - (grub_efiemu_crc): Recompute CRC32. - * efiemu/runtime/efiemu.c (ptv_relocated): Renamed to ... - (efiemu_ptv_relocated): ... this. Made global. All users updated. - * efiemu/symbols.c (relocated_handle): New variable. - (grub_efiemu_free_syms): Free relocated_handle. - (grub_efiemu_alloc_syms): Allocate relocated_handle. - (grub_efiemu_write_sym_markers): New function. - (grub_efiemu_set_virtual_address_map): Likewise. - - Newer XNU parameters. - - * include/grub/i386/xnu.h (GRUB_XNU_BOOTARGS_VERMINOR): Change to 5. - * include/grub/xnu.h (grub_xnu_extheader): Add nameaddr and namesize. - (grub_xnu_fill_devicetree): New prototype. - (grub_xnu_heap_real_start): New variable. - * loader/xnu.c (get_name_ptr): New function. - (grub_xnu_load_driver): Fill namelen and name. - - 64-bit xnu support. - - * conf/i386-efi.rmk (xnu_mod_SOURCES): Add 'loader/macho32.c' - and 'loader/macho64.c'. - * conf/i386-pc.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * include/grub/i386/macho.h (grub_macho_thread64): New structure. - * include/grub/xnu.h (grub_xnu_is_64bit): New variable. - * include/grub/macho.h (grub_macho_segment64): New structure. - * include/grub/machoload.h (grub_macho32_size): Renamed from ... - (grub_macho_size32): ... to this. - (grub_macho32_get_entry_point): Renamed from ... - (grub_macho_get_entry_point32): ... to this. - (grub_macho_contains_macho64): New prototype. - (grub_macho_size64): Likewise. - (grub_macho_get_entry_point64): Likewise. - (grub_macho32_load): Renamed from ... - (grub_macho_load32): ... to this. - (grub_macho32_filesize): Renamed from ... - (grub_macho_filesize32): ... to this. - (grub_macho32_readfile): Renamed from ... - (grub_macho_readfile32): ... to this. - (grub_macho_filesize64): New prototype. - (grub_macho_readfile64): Likewise. - (grub_macho_parse32): Likewise. - (grub_macho_parse64): Likewise. - * loader/macho.c: Split into ... - * loader/machoXX.c: ... and this. Replace 32 with XX. - * loader/macho32.c: New file. - * loader/macho64.c: Likewise. - * loader/xnu.c (grub_xnu_is_64bit): New variable. - (grub_cmd_xnu_kernel): Make 32-bit only. - (grub_cmd_xnu_kernel64): New function. - (grub_xnu_load_driver): Support Mach-O 64. - (grub_cmd_xnu_mkext): Likewise. - * util/grub.d/30_os-prober.in (osx_entry): New function. - Generate entries for 64-bit boot too. - - Eliminate ad-hoc tree format in XNU and EfiEmu. - - * efiemu/main.c (grub_efiemu_prepare): Update comment. - * efiemu/pnvram.c: Rewritten to use environment variables. - All users updated. - - Inline utf16_to_utf8. - - * kern/misc.c (grub_utf16_to_utf8): Move from here ... - * include/grub/charset.h (grub_utf16_to_utf8): ... to here. Inlined. - All users updated. - * include/grub/misc.h (grub_utf16_to_utf8): Removed. - - * bus/usb/usb.c (grub_usb_get_string): Move from here ... - * commands/usbtest.c (grub_usb_get_string): ... move here. - (usb_print_str): Fix error handling. - * include/grub/usb.h (grub_usb_get_string): Remove. - - UTF-8 to UTF-16 transformation. - - * conf/common.rmk (pkglib_MODULES): Add charset.mod - (charset_mod_SOURCES): New variable. - (charset_mod_CFLAGS): Likewise. - (charset_mod_LDFLAGS): Likewise. - * include/grub/utf.h: New file. - * lib/utf.c: New file. (Based on grub_utf8_to_ucs4 from kern/misc.c) - - Support for device properties. - - * include/grub/i386/xnu.h (grub_xnu_devprop_header): New structure. - (grub_xnu_devprop_device_header): Likewise. - (grub_xnu_devprop_device_descriptor): Likewise. - (grub_xnu_devprop_add_device): New prototype. - (grub_xnu_devprop_remove_device): Likewise. - (grub_xnu_devprop_remove_property): Likewise. - (grub_xnu_devprop_add_property_utf8): Likewise. - (grub_xnu_devprop_add_property_utf16): Likewise. - (grub_cpu_xnu_init): Likewise. - (grub_cpu_xnu_fini): Likewise. - (grub_cpu_xnu_unload): Likewise. - * loader/i386/xnu.c (grub_xnu_devprop_device_descriptor): New structure. - (property_descriptor): Likewise. - (devices): New variable. - (grub_xnu_devprop_remove_property): New function. - (grub_xnu_devprop_add_device): Likewise. - (grub_xnu_devprop_remove_device): Likewise. - (grub_xnu_devprop_add_property): Likewise. - (grub_xnu_devprop_add_property_utf8): Likewise. - (grub_xnu_devprop_add_property_utf16): Likewise. - (hextoval): Likewise. - (grub_cpu_xnu_fill_devprop): Likewise. - (grub_cmd_devprop_load): Likewise. - (grub_xnu_boot): Call grub_cpu_xnu_fill_devprop, - grub_xnu_fill_devicetree, grub_xnu_fill_devicetree - (cmd_devprop_load): New variable. - (grub_cpu_xnu_init): New function. - (grub_cpu_xnu_fini): Likewise. - * loader/i386/xnu.c (grub_xnu_unload): Call grub_cpu_xnu_unload. - * loader/xnu.c (grub_xnu_parse_devtree): Remove. - (grub_cmd_xnu_devtree): Likewise. - (hextoval): New function. - (unescape): Likewise. - (grub_xnu_fill_devicetree): Likewise. - - * util/grub.d/30_os-prober.in: Load devprop.bin. Don't load devtree.txt. - * util/i386/efi/grub-dumpdevtree: Generate devprop.bin. - -2009-12-18 Vladimir Serbinenko - - Workaround for broken ATI VBE. - - * video/i386/pc/vbe.c (last_set_mode): New variable. - (grub_vbe_set_video_mode): Set 'last_set_mode'. - (grub_vbe_get_video_mode): Use 'last_set_mode' if get_mode fails. - (grub_video_vbe_setup): Don't check for reserved flag. - -2009-12-17 Felix Zielcke - - * gendistlist.sh: Use POSIX compliant `!' instead of `-not' in - the `find' command. - -2009-12-16 Vladimir Serbinenko - - UUID support for HFS. - - * fs/hfs.c (grub_hfs_uuid): New function. - (grub_hfs_fs): New value .uuid. - * include/grub/hfs.h (grub_hfs_sblock): New field 'num_serial'. - -2009-12-14 Felix Zielcke - - Fix a segfault with parsing unknown long options. - - * util/grub-mkrelpath.c (options): Zero terminate it. - -2009-12-13 Carles Pina i Estany - - * include/grub/misc.h (grub_puts): New declaration. - (grub_puts_): Likewise. - * kern/misc.c (grub_puts): New definition. - (grub_puts_): Likewise. - -2009-12-13 Robert Millan - - * util/grub-probe.c (probe): Improve error message. - -2009-12-13 Robert Millan - - * loader/i386/multiboot_elfxx.c - (CONCAT(grub_multiboot_load_elf, XX)): Fix `grub_multiboot_payload_eip' - initialization. - -2009-12-13 Vladimir Serbinenko - - Relocator framework - - * loader/i386/xnu_helper.S: Removed. All users updated. - * conf/i386.rmk (pkglib_MODULES): Add relocator.mod. - (relocator_mod_SOURCES): New variable. - (relocator_mod_CFLAGS): Likewise. - (relocator_mod_LDFLAGS): Likewise. - (relocator_mod_ASFLAGS): Likewise. - * conf/x86_64.rmk: Likewise. - * include/grub/i386/multiboot.h (grub_multiboot_payload_orig): Removed. - (grub_multiboot_payload_entry_offset): Likewise. - (grub_multiboot_forward_relocator): Likewise. - (grub_multiboot_forward_relocator_end): Likewise. - (grub_multiboot_backward_relocator): Likewise. - (grub_multiboot_backward_relocator_end): Likewise. - (grub_multiboot_payload_eip): New variable. - (grub_multiboot_payload_orig): Likewise. - * include/grub/i386/pc/memory.h: Include grub/i386/memory.h. - (GRUB_MEMORY_MACHINE_CR0_PE_ON): Move from here ... - * include/grub/i386/memory.h - (GRUB_MEMORY_CPU_CR0_PE_ON): ... to here - (GRUB_MEMORY_CPU_CR4_PAE_ON): New definition. - (GRUB_MEMORY_CPU_CR0_PAGING_ON): Likewise. - (GRUB_MEMORY_CPU_AMD64_MSR): Likewise. - (GRUB_MEMORY_CPU_AMD64_MSR_ON): Likewise. - * include/grub/i386/relocator.h: New file. - * include/grub/x86_64/relocator.h: Likewise. - * include/grub/i386/xnu.h: Include grub/cpu/relocator.h. - (XNU_RELOCATOR): New macro. - (grub_xnu_launcher_start): Remove. - (grub_xnu_launcher_end): Likewise. - * include/grub/xnu.h (grub_xnu_boot_resume): New prototype. - (grub_xnu_heap_real_start): Remove. - (grub_xnu_heap_start): Change to void *. All users updated. - * kern/i386/realmode.S (real_to_prot): Use GRUB_MEMORY_CPU_CR0_PE_ON. - * lib/i386/relocator.c: New file. - * lib/i386/relocator_asm.S: Likewise. - * lib/i386/relocator_backward.S: Likewise. - * lib/mips/relocator.c: Likewise. - * lib/mips/relocator_asm.S: Likewise. - * lib/relocator.c: Likewise. - * loader/i386/multiboot.c: Include grub/i386/relocator.h. - (entry): Removed. - (playground): Likewise. - (grub_multiboot_payload_orig): New variable. - (grub_multiboot_payload_dest): Likewise. - (grub_multiboot_payload_size): Likewise. - (grub_multiboot_payload_eip): Likewise. - (grub_multiboot_payload_esp): Likewise. - (grub_multiboot_boot): Use grub_relocator32_boot. - (grub_multiboot_unload): Free relocators. - (grub_multiboot): Setup stack. Use relocators. - * loader/i386/multiboot_elfxx.c: Include grub/i386/relocator.h. - (grub_multiboot_load_elfXX): Use relocators. - * loader/i386/multiboot_helper.S (grub_multiboot_payload_orig): Removed. - (grub_multiboot_payload_size): Likewise. - (grub_multiboot_payload_dest): Likewise. - (grub_multiboot_payload_entry_offset): Likewise. - (grub_multiboot_forward_relocator): Likewise. - (grub_multiboot_backward_relocator): Likewise. - (grub_multiboot_real_boot): Likewise. - * loader/i386/xnu.c (grub_xnu_heap_will_be_at): New variable. - (grub_xnu_entry_point): Likewise. - (grub_xnu_arg1): Likewise. - (grub_xnu_stack): Likewise. - (grub_xnu_launch): Removed. - (grub_xnu_boot_resume): New function. - (grub_xnu_boot): Use relocators. - * loader/i386/xnu_helper.S: Removed. - * loader/xnu.c (grub_xnu_heap_start): New variable. - (grub_xnu_heap_size): Likewise. - (grub_xnu_heap_malloc): Use relocators. - * loader/xnu_resume.c (grub_xnu_resume): Use relocators. - -2009-12-13 Vladimir Serbinenko - - * kern/i386/pc/startup.S (multiboot_entry): Setup stack before calling - anything. - -2009-12-13 Carles Pina i Estany - - * script/execute.c (grub_script_execute_cmdline): Set grub_errno to - GRUB_ERR_NONE before calling grub_env_set. - -2009-12-12 Robert Millan - - * gendistlist.sh (EXTRA_DISTFILES): Add `genvideolist.sh'. - * genmk.rb (video): New variable. - (CLEANFILES, VIDEOFILES): Add #{video}. - (#{video}): New target rule. - * genvideolist.sh: New file. - * Makefile.in (pkglib_DATA): Add video.lst. - (video.lst): New target rule. - * util/grub-mkconfig.in: Initialize ${GRUB_VIDEO_BACKEND} using - `video.lst'. - * util/grub.d/30_os-prober.in: Replace `vbe' with - ${GRUB_VIDEO_BACKEND}. - -2009-12-11 Robert Millan - - * THANKS: Add David Miller. - -2009-12-11 Vladimir Serbinenko - - libpciaccess support. - - * Makefile.in (LIBPCIACCESS): New variable. - (enable_grub_emu_pci): Likewise. - * conf/any-emu.rmk (grub_emu_SOURCES) [enable_grub_emu_pci]: Add - util/pci.c and commands/lspci.c. - (grub_emu_LDFLAGS) [enable_grub_emu_pci]: Add $(LIBPCIACCESS). - * configure.ac (grub-emu-pci): New option. - * include/grub/i386/pci.h (grub_pci_device_map_range): New function. - (grub_pci_device_unmap_range): Likewise. - * include/grub/pci.h [GRUB_UTIL]: Include grub/pciutils.h. - (grub_pci_device) [!GRUB_UTIL]: New structure. All users updated. - (grub_pci_address_t) [!GRUB_UTIL]: New type. - (grub_pci_device_t) [!GRUB_UTIL]: Likewise. - (grub_pci_get_bus) [!GRUB_UTIL]: New function. - (grub_pci_get_device) [!GRUB_UTIL]: Likewise. - (grub_pci_get_function) [!GRUB_UTIL]: Likewise. - * include/grub/pciutils.h: New file. - * util/pci.c: Likewise. - -2009-12-11 Felix Zielcke - - * util/misc.c: Don't include twice. - -2009-12-10 Felix Zielcke - - * disk/i386/pc/biosdisk.c (grub_biosdisk_open): Show the disk - name in an error message. - (grub_biosdisk_rw): Likewise. - -2009-12-10 Vladimir Serbinenko - - Eliminate NTFS 4Gib barrier. - - * fs/ntfs.c (read_attr): Use grub_disk_addr_t and grub_size_t. - (read_run_data): Likewise. - (grub_ntfs_read_run_list): Likewise. - (grub_ntfs_read_block): Likewise. - (grub_ntfs_iterate_dir): Likewise. - (read_mft): Likewise. - (read_data): Likewise. - Use COM_LOG_LEN. - * fs/ntfscomp.c (read_block): Cast ctx->target_vcn & 0xF to unsigned - to avoid 64-bit division - * include/grub/ntfs.h (COM_LOG_LEN): New definition. - (grub_ntfs_rlst): Use grub_disk_addr_t. - -2009-12-10 Vladimir Serbinenko - - Eliminate grub-fstest 4Gib barrier. - - * util/grub-fstest.c (skip, leng): Use grub_disk_addr_t. - (read_file): Fix error reporting. - -2009-12-10 Vladimir Serbinenko - - Eliminate hexdump 4Gib barrier. - - * commands/hexdump.c (grub_cmd_hexdump): Use grub_disk_addr_t. - * lib/arg.c (grub_arg_parse): Use grub_strtoull. - -2009-12-10 Vladimir Serbinenko - - * kern/device.c (grub_device_iterate): Ignore errors during first scan. - Fixes amarsh bug. - -2009-12-09 Bruce Dubbs - - Remove miscellaneous files in distclean target. - - * Makefile.in: Remove docs/{grub.info,version.texi,stamp-vti} - -2009-12-09 Colin Watson - - * util/grub-mkconfig_lib.in: Don't set grub_probe or grub_mkrelpath - if they're already set. This resolves the conflict between my - grub-install change on 2009-10-06 and Felix' change on 2009-11-11, - fixing the --grub-probe option again. - * util/sparc64/ieee1275/grub-install.in: Revert the last piece of my - change on 2009-10-06, so that we now once again source - `${libdir}/grub/grub-mkconfig_lib' after options have been parsed. - -2009-12-08 Robert Millan - - * conf/common.rmk [sparc64-ieee1275] (grub_mkdevicemap_SOURCES): Use - `util/ieee1275/ofpath.c' and `util/ieee1275/devicemap.c' instead of - `util/devicemap.c'. - -2009-12-08 Carles Pina i Estany - - * include/grub/misc.h (grub_printf_): New declaration. - * kern/misc.c (grub_printf_): New definition. - * normal/main.c (grub_normal_reader_init): Use `grub_printf_' and `N_' - instead of `grub_printf' and `_'. - * normal/menu_entry.c (store_completion): Likewise. - (run): Likewise. - (grub_menu_entry_run): Likewise. - * normal/menu_text.c (grub_wait_after_message): Likewise. - (notify_booting): Likewise. - (notify_fallback): Likewise. - (notify_execution_failure): Likewise. - -2009-12-07 Colin Watson - - * configure.ac: Check for vasprintf. - * util/misc.c (asprintf): Move allocation from here ... - (vasprintf): ... to here. New function. - (xasprintf): New function. - * include/grub/util/misc.h (vasprintf, xasprintf): Add - prototypes. - * util/getroot.c (grub_util_get_grub_dev): Use xasprintf. - * util/grub-mkfont.c (write_font): Likewise. - * util/grub-probe.c (probe): Likewise. - * util/hostdisk.c (make_device_name): Likewise. - -2009-12-06 David S. Miller - - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Recognize - anything even prefixed with 'cdrom' as a cdrom. - -2009-12-06 Felix Zielcke - - * util/misc.c (make_system_path_relative_to_its_root): Correctly cope with - mount points. - -2009-12-05 Carles Pina i Estany - - * gettext/gettext.c: Include `'. Define grub_gettext_msg, - grub_gettext_msg_list. - (grub_gettext_gettranslation_from_position): Return const char * - and not char *. - (grub_gettext_translate): Add the translated strings into a list, - returns from the list if existing there. - (grub_gettext_init_ext): Add \n at the end of grub_dprintf string. - (grub_gettext_delete_list): Delete the list. - (grub_gettext_env_write_lang): Call grub_gettext_delete_list when - lang environment variable is changed. - (GRUB_MOD_FINI): Call grub_gettext_delete_list. - -2009-12-05 Vladimir Serbinenko - - Rename kernel.mod to kernel.img. - - * conf/i386-efi.rmk (pkglib_MODULES): Change kernel.mod to kernel.img. - (kernel_mod_EXPORTS): Rename to ... - (kernel_img_EXPORTS): ... this. - (kernel_mod_SOURCES): Rename to ... - (kernel_img_SOURCES): ... this. - (kernel_mod_HEADERS): Rename to ... - (kernel_img_HEADERS): ... this. All users updated. - (kernel_mod_CFLAGS): Rename to ... - (kernel_img_CFLAGS): ... this. - (kernel_mod_ASFLAGS): Rename to ... - (kernel_img_ASFLAGS): ... this. - (kernel_mod_LDFLAGS): Rename to ... - (kernel_img_LDFLAGS): ... this. - * conf/x86_64-efi.rmk: Likewise. - * util/i386/efi/grub-mkimage.c (read_kernel_module): Rename to ... - (read_kernel_image): ... this. All users updated. - (read_kernel_image): Read "kernel.img" instead of "kernel.mod". - -2009-12-05 Carles Pina i Estany - - * normal/menu_text.c (grub_color_menu_high): Gettexttize string. - (print_spaces): New function. - (grub_print_ucs4): New function. - (getstringwidth): New function. - (print_message_indented): New function. - (print_message): Gettexttize strings using print_message_indented. - (run_menu): Replaces grub_printf by print_spaces and dynamic terminal - width. - (get_entry_number): Gettextize and uses dynamic terminal width. - (notify_booting, notify_fallback, notify_execution_failure): - Gettextize. - * normal/menu_entry.c (store_completion): Cleanup the gettextized - string. - (run): Likewise. - (grub_menu_entry_run): Likewise. - * PO/POTFILES: Add normal/menu_entry.c. - -2009-12-05 Vladimir Serbinenko - - * configure.ac (TARGET_ASFLAGS): Add "-D". - -2009-12-05 Carles Pina i Estany - - * util/grub-install.in: Install gettext .mo files. - * util/grub-mkrescue.in (process_input_dir): Copy gettext .mo files. - -2009-12-05 Carles Pina i Estany - - * gettext/gettext.c (grub_gettext_init_ext): Replace grub_printf with - grub_dprintf. - -2009-12-05 Robert Millan - - * kern/ieee1275/openfw.c (grub_reboot): Disable for i386. The - non-firmware-dependant one in realmode.S takes precedence. - -2009-12-04 Robert Millan - - * commands/halt.c: Replace misc arch-specific headers with - `'. - * commands/reboot.c: Likewise. - * commands/i386/pc/halt.c: Replace `' with - `'. - * conf/i386-coreboot.rmk (kernel_img_HEADERS): Remove `cpu/reboot.h'. - (halt_mod_SOURCES): Move `kern/i386/halt.c' from here ... - (kernel_img_SOURCES): ... to here. - - * include/grub/efi/efi.h (grub_reboot, grub_halt): Remove prototypes. - * include/grub/i386/pc/init.h: Likewise. - * include/grub/powerpc/ieee1275/kernel.h: Likewise. - * include/grub/sparc64/ieee1275/kernel.h: Likewise. - - * include/grub/misc.h (grub_reboot, grub_halt): New prototypes. - - * include/grub/i386/halt.h: Remove. - * include/grub/i386/reboot.h: Likewise. - - * kern/i386/halt.c: Remove `'. - -2009-12-03 David S. Miller - - * conf/sparc64-ieee1275.rmk (grub_mkimage_SOURCES, - grub_setup_SOURCES, grub_ofpathname_SOURCES): Add gnulib/progname.c - * util/sparc64/ieee1275/grub-mkimage.c: Include and - "progname.h" - * util/sparc64/ieee1275/grub-ofpathname.c: Likewise. - * util/sparc64/ieee1275/grub-setup.c: Likewise. - (usage): Add missing comma in printf. - -2009-12-02 Robert Millan - - Use the same reboot approach on i386 coreboot and qemu as we do on - BIOS. - - * conf/i386-coreboot.rmk (kernel_img_HEADERS): Add `cpu/reboot.h'. - (reboot_mod_SOURCES): Remove `kern/i386/reboot.c'. - * kern/i386/reboot.c: Remove. - * include/grub/i386/reboot.h (grub_reboot): Export function. - * kern/i386/pc/startup.S (grub_reboot): Move from here ... - * kern/i386/realmode.S (grub_reboot): ... to here. Jump to - 0xf000:0xfff0 instead of 0xffff:0x0000. - [!GRUB_MACHINE_PCBIOS] (prot_to_real): Do not restore interrupts. - * kern/i386/qemu/startup.S: Include `"../realmode.S"'. - -2009-11-30 Robert Millan - - Fix $srcdir != $objdir build. - - * Makefile.in (po/%.po): Rewrite as ... - ($(foreach lang, $(LINGUAS), $(srcdir)/po/$(lang).po)): ... this. - -2009-11-29 Samuel Thibault - - Fix GNU/Hurd grub-install crash. - * util/grub-probe.c (probe): Try to access `path' only when it is not - NULL. - -2009-11-28 Vladimir Serbinenko - - Correct module naming. - - * video/efi_uga.c (GRUB_MOD_INIT(efi_fb)): Renamed from this ... - (GRUB_MOD_INIT(efi_uga)): ... to this - (GRUB_MOD_FINI(efi_fb)): Renamed from this ... - (GRUB_MOD_FINI(efi_uga)): ... to this - * video/efi_gop.c (GRUB_MOD_INIT(efi_fb)): Renamed from this ... - (GRUB_MOD_INIT(efi_gop)): ... to this - (GRUB_MOD_FINI(efi_fb)): Renamed from this ... - (GRUB_MOD_FINI(efi_gop)): ... to this - -2009-11-28 Robert Millan - - * util/mkisofs/mkisofs.c (ld_options): Mark all `arg' strings as - translatable. - (usage): Translate `arg' strings using gettext(). - Thanks to Jordi Mallach for the suggestion. - -2009-11-28 Vladimir Serbinenko - - GOP support. Based on patch from Bean - (http://lists.gnu.org/archive/html/grub-devel/2009-08/msg00384.html) - - * video/efi_gop.c: New file. - * include/grub/efi/graphics_output.h: Likewise. - * conf/i386-efi.rmk (pkglib_MODULES): Add `efi_gop.mod'. - (efi_fb_mod_SOURCES, efi_fb_mod_CFLAGS, efi_fb_mod_LDFLAGS): New - variables. - * conf/x86_64-efi.rmk: Likewise. - -2009-11-28 Vladimir Serbinenko - - Rename efi_fb to efi_uga. - - * conf/i386-efi.rmk (pkglib_MODULES): Rename 'efi_fb.mod' to - 'efi_uga.mod'. - (efi_fb_mod_SOURCES): Rename this ... - (efi_uga_mod_SOURCES): ... to this. - (efi_fb_mod_CFLAGS): Rename this ... - (efi_uga_mod_CFLAGS): ... to this. - (efi_fb_mod_LDFLAGS): Rename this ... - (efi_uga_mod_LDFLAGS): ... to this. - * conf/x86_64-efi.rmk (pkglib_MODULES): Rename 'efi_fb.mod' to - 'efi_uga.mod'. - (efi_fb_mod_SOURCES): Rename this ... - (efi_uga_mod_SOURCES): ... to this. - (efi_fb_mod_CFLAGS): Rename this ... - (efi_uga_mod_CFLAGS): ... to this. - (efi_fb_mod_LDFLAGS): Rename this ... - (efi_uga_mod_LDFLAGS): ... to this. - * video/efi_fb.c: Move this ... - * video/efi_uga.c: ... to this. Change prefix to 'grub_video_uga_'. - -2009-11-27 Robert Millan - - * po/README: New file. Explain our PO file workflow. - -2009-11-27 Robert Millan - - * po/ChangeLog: Remove. Move relevant entries back to ... - * ChangeLog: ... here. - * po/ca.po: Remove (now handled by TLP). - * po/id.po: Likewise. - * po/zh_CN.po: Likewise. - * Makefile.in (LINGUAS): Initialize in a way that supports - empty set. - -2009-11-27 Robert Millan - - * Makefile.in (LINGUAS): Rewrite by scanning po/ directory instead of - reliing on po/LINGUAS. - ($(foreach lang, $(LINGUAS), $(srcdir)/po/$(lang).po)): Rewrite as ... - (po/%.po): ... this. - -2009-11-26 Felix Zielcke - - * util/i386/efi/grub-mkimage.c: Include "progname.h". - (main): Use `program_name' instead of nonexistent `progname'. - -2009-11-26 Felix Zielcke - - * conf/i386-efi.rmk (grub_mkimage_SOURCES): Add `gnulib/progname.c'. - * conf/x86_64-efi.rmk (grub_mkimage_SOURCES): Likewise. - -2009-11-26 Robert Millan - - * conf/i386-coreboot.rmk: Cleanup stale filenames from my previous - commit. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - -2009-11-26 Felix Zielcke - - * conf/any-emu.rmk (grub_emu_SOURCES): Add `gnulib/progname.c'. - -2009-11-26 Felix Zielcke - - * conf/any-emu.rmk (grub_mkfont_SOURCES): Add `gnulib/progname.c'. - -2009-11-26 Robert Millan - - * conf/common.rmk (sbin_UTILITIES): Add `grub-mkdevicemap'. - (grub_mkdevicemap_SOURCES): New variable. - (grub_probe_SOURCES, grub_fstest_SOURCES, grub_mkfont_SOURCES) - (grub_mkrelpath_SOURCES, grub_editenv_SOURCES) - (grub_pe2elf_SOURCES): Add `gnulib/progname.c'. - * conf/i386-coreboot.rmk (sbin_UTILITIES): Remove `grub-mkdevicemap'. - (grub_mkdevicemap_SOURCES): Remove. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * util/elf/grub-mkimage.c: Include `' and `"progname.h"'. - (usage): Fix strings to use `program_name'. - (main): Initialize gettext. - * util/grub-editenv.c: Likewise. - * util/grub-emu.c: Likewise. - * util/grub-fstest.c: Likewise. - * util/grub-mkdevicemap.c: Likewise. - * util/grub-mkfont.c: Likewise. - * util/grub-mkrelpath.c: Likewise. - * util/grub-pe2elf.c: Likewise. - * util/grub-probe.c: Likewise. - * util/sparc64/ieee1275/grub-mkimage.c: Likewise. - * util/sparc64/ieee1275/grub-ofpathname.c: Likewise. - * util/sparc64/ieee1275/grub-setup.c: Likewise. - - * util/misc.c: Include `"progname.h"'. - (progname): Remove variable. - (grub_util_warn, grub_util_info, grub_util_error): Use `program_name'. - -2009-11-25 Felix Zielcke - - * util/grub.d/10_linux.in (linux_entry): Quote the arguments to - printf and print a newline after the menuentry header line. - * util/grub.d/10_kfreebsd.in (kfreebsd_entry): Likewise. - -2009-11-25 Felix Zielcke - - autoconf >= 2.60 support $(localedir). - - * INSTALL: Note that autoconf 2.60 is required. - * configure.ac (AC_PREREQ): Bump to 2.60. - * util/grub.d/10_kfreebsd.in (TEXTDOMAINDIR): Set to lowercased @localedir@. - * util/grub.d/10_linux.in (TEXTDOMAINDIR): Likewise. - -2009-11-25 Yves Blusseau - - * configure.ac: move the call to AM_GNU_GETTEXT to avoid warnings when - aclocal is run. - -2009-11-25 Robert Millan - - * normal/main.c (grub_normal_read_line): Fix off-by-one - buffer overflow. - -2009-11-25 Robert Millan - - * normal/main.c (grub_normal_execute): Replace "parser.sh" with - "parser.grub" in grub_command_execute() call. - -2009-11-24 Carles Pina i Estany - - * conf/i386-coreboot.rmk (kernel_img_HEADERS): Add i18n.h. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * gettext/gettex.c: Include . - * include/grub/misc.h (grub_gettext_dummy, grub_gettext): Move from - here ... - * include/grub/i18n.h: ... to here - * include/grub/i18n.h: ... to here. - * kern/misc.c: Include - (grub_gettext_dummy): Move above user. - -2009-11-24 Felix Zielcke - - * util/Makefile.in (install-local): Convert a `for' into a normal - shell expansion. - -2009-11-24 Robert Millan - - * autogen.sh: Add automake call. - * config.guess: Remove. - * config.sub: Likewise. - * install-sh: Likewise. - -2009-11-24 Felix Zielcke - - * util/Makefile.in (install-local): Fix the use of $lang shell variable. - -2009-11-24 Felix Zielcke - - * util/Makefile.in (install-local): Convert a make `$(foreach)' - function to a normal shell `for'. - -2009-11-24 Felix Zielcke - - * conf/i386-coreboot.rmk (grub_mkimage_SOURCES): Add `gnulib/progname.c'. - -2009-11-24 Felix Zielcke - - * util/grub-mkrelpath.c: New file. - * conf/common.rmk (bin_UTILITIES): Add grub-mkrelpath. - (grub_mkrelpath_SOURCES): New variable. - * include/grub/util/misc.h: New function prototype. - * util/misc.c (make_system_path_relative_to_its_root): New function. - - * util/grub-mkconfig_lib.in (bindir): New variable. - (grub_mkrelpath): Likewise. - (make_system_path_relative_to_its_root): Use grub-mkrelpath. - - * util/probe.c (probe): Make the file path relative to its root. - Change a info message to use the GRUB path. Enable again the - check if we can read the file with GRUB facilities. - - * util/i386/pc/grub-setup.c (setup): Make core.img path relative - to its root. - -2009-11-24 Felix Zielcke - - * Makefile.in: Don't include GRUB_CONTRIB makefiles with emu - platform. - -2009-11-24 Felix Zielcke - - * util/getroot.c (grub_util_get_dev_abstraction): Properly use - strncmp(). - -2009-11-24 Felix Zielcke - - * util/getroot.c (grub_util_is_dmraid): New function. - (grub_util_get_dev_abstraction): Treat dmraid and multipath - devices as normal ones, not as LVM. - -2009-11-23 Carles Pina i Estany - - * conf/common.rmk: Add grub-gettext_lib target and updates - lib_DATA and CLEANFILES. Adds gettext.mod SOURCES, CFLAGS, - LDFLAGS. - * gettext/gettext.c: New file. (Reads mo files). - * include/grub/file.h (grub_file_pread): New prototype. - * include/grub/i18n.h (_): New prototype. - * include/grub/misc.h (grub_gettext_dummy, grub_gettext): New - prototypes. - * kern/misc.c (grub_gettext_dummy): New function. - * normal/menu_text.c: Include . - * normal/menu_text.c (print_timeout): Gettexttize string. - * normal/menu_text.c (print_message): Gettexttize string. - * po/POTFILES: Add `normal/menu_text.c'. - * po/ca.po: Add new translations. - * util/grub.d/00_header.in: Define locale_dir and lang. insmod - gettext module and defines locale_dir and lang in grub.cfg. - * NEWS: Add gettext support. - -2009-11-23 Robert Millan - - * util/hostdisk.c: Include `'. - (find_grub_drive): Use ARRAY_SIZE for map size calculation. - (make_device_name): Rewrite using asprintf. - (convert_system_partition_to_system_disk): Replace 0 with NULL. - (find_system_device): If a device is not found, generate one just - by reusing the OS path name. - (read_device_map): Make it permissible for device.map not to exist. - -2009-11-23 Robert Millan - - * script/sh/execute.c: Move from here ... - * script/execute.c: ... to here. Update all users. - * script/sh/function.c: Move from here ... - * script/function.c: ... to here. Update all users. - * script/sh/lexer.c: Move from here ... - * script/lexer.c: ... to here. Update all users. - * script/sh/main.c: Move from here ... - * script/main.c: ... to here. Update all users. - * script/sh/parser.y: Move from here ... - * script/parser.y: ... to here. Update all users. - * script/sh/script.c: Move from here ... - * script/script.c: ... to here. Update all users. - -2009-11-23 Robert Millan - - * configure.ac: Detect all `emu' platforms. Define - GRUB_MACHINE_* macros in TARGET_CFLAGS. Remove - --enable-grub-emu logic. Disable include/grub/machine - symlink on `emu' platforms. - - * genkernsyms.sh.in: Use @TARGET_CFLAGS@ during symbol generation. - * gensymlist.sh.in: Likewise. - - * include/grub/i386/coreboot/machine.h: Remove file. - * include/grub/i386/efi/machine.h: Likewise. - * include/grub/i386/ieee1275/machine.h: Likewise. - * include/grub/i386/pc/machine.h: Likewise. - * include/grub/i386/qemu/machine.h: Likewise. - * include/grub/powerpc/ieee1275/machine.h: Likewise. - * include/grub/sparc64/ieee1275/machine.h: Likewise. - * include/grub/x86_64/efi/machine.h: Likewise. - - * commands/acpi.c: Remove `'. - * commands/halt.c: Likewise. - * commands/reboot.c: Likewise. - * include/grub/autoefi.h: Likewise. - * include/grub/i386/at_keyboard.h: Likewise. - * include/grub/i386/kernel.h: Likewise. - * include/grub/i386/loader.h: Likewise. - * include/grub/i386/pc/memory.h: Likewise. - * kern/dl.c: Likewise. - * kern/i386/coreboot/init.c: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/i386/linux.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/usb_keyboard.c: Likewise. - - * include/grub/time.h [!GRUB_MACHINE_EMU]: Remove - `' - [!GRUB_MACHINE_EMU] (GRUB_TICKS_PER_SECOND): New macro. - * util/misc.c: Remove `' and - `'. - - * Makefile.in (enable_grub_emu): Remove variable. - Include $(srcdir)/conf/any-emu.mk for the `emu' platform. - - * conf/any-emu.rmk: New file. - * conf/common.rmk (grub_emu_init.lst, grub_emu_init.h) - (grub_emu_init.c): Move from here ... - * conf/any-emu.rmk: ... to here. - - * conf/i386-coreboot.rmk (sbin_UTILITIES): Remove `grub-emu'. - (grub_emu_SOURCES, grub_emu_LDFLAGS): Move from here ... - * conf/any-emu.rmk: ... to here. - -2009-11-23 Robert Millan - - * include/grub/parser.h (grub_parser_register): Document need - of `name' parameter. - * normal/main.c (grub_normal_read_line): Simplify prompt string. - * script/sh/main.c (grub_sh_parser, GRUB_MOD_INIT(sh)): Rename - "sh" to "grub". - -2009-11-23 Robert Millan - - * Makefile.in ($(srcdir)/po/$(PACKAGE).pot): Pass --keyword=N_ to - `$(XGETTEXT)'. - * include/grub/i18n.h (N_): New macro. - * util/mkisofs/mkisofs.h: Likewise. - * util/mkisofs/mkisofs.c (ld_options): Wrap all translatable strings - around N_(). - (usage): Use gettext() to translate help strings when printing them. - -2009-11-23 Robert Millan - - Based on patch from Bean - (http://lists.gnu.org/archive/html/grub-devel/2009-08/msg00384.html) - - * video/efi_fb.c: New file. - * conf/i386-efi.rmk (pkglib_MODULES): Add `efi_fb.mod'. - (efi_fb_mod_SOURCES, efi_fb_mod_CFLAGS, efi_fb_mod_LDFLAGS): New - variables. - * conf/x86_64-efi.rmk: Likewise. - -2009-11-22 Robert Millan - - * util/i386/pc/grub-mkimage.c: Ungettextize grub_util_info() strings. - * util/i386/pc/grub-setup.c: Likewise. - -2009-11-21 Samuel Thibault - - * util/getroot.c [__GNU__]: Include , , and - - [__GNU__] (grub_guess_root_device): Call file_name_lookup and - file_get_storage_info to implement grub_guess_root_device. - -2009-11-21 Felix Zielcke - - * Makefile.in (target): Use make's builtin $(shell) function - instead of calling directly $(SHELL) to create the locale directories, - inside the $(foreach) function. - -2009-11-21 Felix Zielcke - - * util/grub-mkrescue.in: Print an error and usage if output option - has not been given. - -2009-11-21 Felix Zielcke - - Patch from LoĂ¯c Minier . - * util/grub.d/30_os-prober.in: Cope with Linux entries where - root and /boot are on different devices. - -2009-11-21 Robert Millan - - Fix build for srcdir != objdir. - - * Makefile.in (po/$(PACKAGE).pot): Rename to ... - ($(srcdir)/po/$(PACKAGE).pot): ... this. Run $(XGETTEXT) from - $(srcdir). - ($(foreach lang, $(LINGUAS), po/$(lang).po)): Rename to ... - ($(foreach lang, $(LINGUAS), $(srcdir)/po/$(lang).po): ... this. Use $^ - reference for input. - -2009-11-21 Robert Millan - - * util/grub-mkrescue.in: Use source directory direcly (without copiing - or hardlinking it). Remove -J option, Joliet is not compatible with - multiple source directories. - -2009-11-21 Carles Pina i Estany -2009-11-21 Robert Millan - - * util/grub-mkrescue.in: Recognize `--override-directory' option. - (process_input_dir): New function. Process an arbitrary input - directory. - Misc adjustments to support both "override mode" and system-wide mode. - -2009-11-20 Felix Zielcke - - * configure.ac (UNIFONT_BDF): Rename to ... - (FONT_SOURCE): ... this. Update all users. - -2009-11-20 Felix Zielcke - - * configure.ac: Add `/usr/share/fonts/X11/misc/unifont.pcf.gz' - to the list of unifont files to look for. - -2009-11-19 Robert Millan - - Patch from Joe Auricchio - * commands/minicmd.c (grub_mini_cmd_clear): New function. - (GRUB_MOD_INIT(minicmd)): Register grub_mini_cmd_clear(). - (GRUB_MOD_FINI(minicmd)): Unregister grub_mini_cmd_clear(). - -2009-11-19 Felix Zielcke - - * Makefile.in (install-local): Add a missing backslash. - -2009-11-19 Felix Zielcke - - * include/grub/x86_64/io.h: New file. - -2009-11-19 Robert Millan - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `gnulib/progname.c'. - * util/i386/pc/grub-setup.c: Include `'. - Include `"progname.h"'. - (main): Initialize gettext. - * util/i386/pc/grub-setup.c: Gettexttize. - * util/i386/pc/grub-mkimage.c: Likewise. - - * Makefile.in (po/*.po): Redefine as ... - ($(foreach lang, $(LINGUAS), po/$(lang).po)): ... this. - - * po/POTFILES: Add `util/i386/pc/grub-setup.c'. - -2009-11-19 Robert Millan - - * conf/common.rmk (grub_mkisofs_SOURCES): Add `gnulib/progname.c'. - * util/mkisofs/mkisofs.c: Include `"progname.h"'. - (program_name): Remove. - (main): Initialize gettext support. - * util/mkisofs/mkisofs.h: Include `'. - Include `'. - (_): New macro. - - * util/mkisofs/eltorito.c: Gettexttize. - * util/mkisofs/joliet.c: Likewise. - * util/mkisofs/mkisofs.c: Likewise. - * util/mkisofs/multi.c: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/tree.c: Likewise. - * util/mkisofs/write.c: Likewise. - - * po/POTFILES: Update with new files. - -2009-11-18 Robert Millan - - * util/mkisofs/eltorito.c: Fix minor mistake in license text. - * util/mkisofs/iso9660.h: Likewise. - * util/mkisofs/joliet.c: Likewise. - * util/mkisofs/mkisofs.c: Likewise. - * util/mkisofs/mkisofs.h: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/tree.c: Likewise. - * util/mkisofs/write.c: Likewise. - - * util/mkisofs/eltorito.c (rcsid): Remove. - * util/mkisofs/hash.c: Likewise. - * util/mkisofs/joliet.c: Likewise. - * util/mkisofs/name.c: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/tree.c: Likewise. - * util/mkisofs/write.c: Likewise. - -2009-11-18 Robert Millan - - * util/mkisofs/match.c: Rewrite from scratch, using a linked list - instead of static allocation. - * util/mkisofs/match.h: Likewise. - -2009-11-18 Robert Millan - - * po/POTFILES-shell: New file. List `util/grub.d/10_kfreebsd.in' - and `util/grub.d/10_linux.in'. - * Makefile.in (po/$(PACKAGE).pot): Process `po/POTFILES-shell' for - translatable Shell files. - -2009-11-18 Robert Millan - - * Makefile.in ($(srcdir)/aclocal.m4): New target. - -2009-11-17 Robert Millan - - * INSTALL: Document Automake is needed for bootstrap. - * po/ca.po: Fix PO-Revision-Date and Language-Team fields. - * util/grub.d/10_kfreebsd.in (bindir): New variable. - Add gettext initialization. - (kfreebsd_entry): Make menuentry output translatable. - -2009-11-17 Robert Millan - - * Makefile.in (XGETTEXT, MSGMERGE, MSGFMT): New variables. - (po/$(PACKAGE).pot): Replace `xgettext' with `$(XGETTEXT)'. - (po/*.po): Replace `msgmerge' with `$(MSGMERGE)'. - (po/%.mo): Replace `msgfmt' with `$(MSGFMT)'. - (LINGUAS): Auto-generate using `po/LINGUAS'. - * po/LINGUAS: New file. - -2009-11-17 Robert Millan - - * configure.ac: Call AM_GNU_GETTEXT() (defines localedir, among - other things). - * Makefile.in (CPPFLAGS): Add `-DLOCALEDIR=\"$(localedir)\"'. - * util/i386/pc/grub-mkimage.c (main): Issue setlocale() and - bindtextdomain() calls for gettext initialization. - -2009-11-17 Robert Millan - - * gnulib/progname.c: New file (imported from Gnulib). - * gnulib/progname.h: Likewise. - * conf/i386-pc.rmk (grub_mkimage_SOURCES): Add `gnulib/progname.c'. - * util/i386/pc/grub-mkimage.c: Include `"progname.h"'. - (usage): Replace `progname' with `program_name'. - (main): Use set_program_name() for program name initialization. - -2009-11-17 Robert Millan - - * conf/common.rmk (grub_mkisofs_CFLAGS): Move `-I$(srcdir)/gnulib' - from here ... - * Makefile.in (CPPFLAGS): ... to here. - -2009-11-16 Robert Millan - - * aclocal.m4: Move from here ... - * acinclude.m4: ... to here. - * autogen.sh: Add call to `aclocal'. - * configure.ac: Add AM_INIT_AUTOMAKE() after AC_INIT() call. - -2009-11-16 Robert Millan - - * Makefile.in (CLEANFILES): Add `po/*.mo'. - (LINGUAS): New variable. - (all-local): Add `$(foreach lang, $(LINGUAS), po/$(lang).mo)'. - (install-local): Install MO files. - (po/$(PACKAGE).pot, po/*.po, po/%.mo): New rules. - * include/grub/i18n.h: New file. - * po/POTFILES: New file. - * po/ca.po: New file. - * util/grub.d/10_linux.in (bindir): New variable. - Add gettext initialization. - (linux_entry): Make menuentry output translatable. - * util/i386/pc/grub-mkimage.c: Include `'. - (usage): Make --help output translatable. - (main): Initialize gettext. - -2009-11-17 Robert Millan - - * import_gcry.py: New file (written by Vladimir with minor - adjustments). - * autogen.sh: Use import_gcry.py to auto-generate GRUB-ified - ciphers. - * INSTALL: Document that Python is required for bootstrap. - -2009-11-17 Robert Millan - - Import ciphers from libgcrypt 1.4.4. - - * lib/libgcrypt/cipher/ChangeLog - * lib/libgcrypt/cipher/ac.c - * lib/libgcrypt/cipher/arcfour.c - * lib/libgcrypt/cipher/bithelp.h - * lib/libgcrypt/cipher/blowfish.c - * lib/libgcrypt/cipher/camellia-glue.c - * lib/libgcrypt/cipher/camellia.c - * lib/libgcrypt/cipher/camellia.h - * lib/libgcrypt/cipher/cast5.c - * lib/libgcrypt/cipher/cipher.c - * lib/libgcrypt/cipher/crc.c - * lib/libgcrypt/cipher/des.c - * lib/libgcrypt/cipher/dsa.c - * lib/libgcrypt/cipher/ecc.c - * lib/libgcrypt/cipher/elgamal.c - * lib/libgcrypt/cipher/hash-common.c - * lib/libgcrypt/cipher/hash-common.h - * lib/libgcrypt/cipher/hmac-tests.c - * lib/libgcrypt/cipher/md.c - * lib/libgcrypt/cipher/md4.c - * lib/libgcrypt/cipher/md5.c - * lib/libgcrypt/cipher/primegen.c - * lib/libgcrypt/cipher/pubkey.c - * lib/libgcrypt/cipher/rfc2268.c - * lib/libgcrypt/cipher/rijndael-tables.h - * lib/libgcrypt/cipher/rijndael.c - * lib/libgcrypt/cipher/rmd.h - * lib/libgcrypt/cipher/rmd160.c - * lib/libgcrypt/cipher/rsa.c - * lib/libgcrypt/cipher/seed.c - * lib/libgcrypt/cipher/serpent.c - * lib/libgcrypt/cipher/sha1.c - * lib/libgcrypt/cipher/sha256.c - * lib/libgcrypt/cipher/sha512.c - * lib/libgcrypt/cipher/tiger.c - * lib/libgcrypt/cipher/twofish.c - * lib/libgcrypt/cipher/whirlpool.c - -2009-11-16 Robert Millan - - Fix build for systems without error(). - - * gnulib/error.c: New file (imported from Gnulib). - * gnulib/error.h: Likewise. - * conf/common.rmk (grub_mkisofs_SOURCES): Add `gnulib/error.c'. - * util/mkisofs/mkisofs.c (program_name): Remove `static' qualifier - (this variable is now used by error()). - -2009-11-16 Felix Zielcke - - * util/mkisofs/name.c (iso9660_file_length): Use isascii macro - instead of relying that char is signed. - -2009-11-16 Vladimir Serbinenko - - * fs/i386/pc/pxe.c (grub_pxefs_open): Correctly handle PXE choosing - blocksize different from specified. - (grub_pxefs_read): Likewise. - -2009-11-16 Felix Zielcke - - Enable ata.mod on x86_64-efi, i386-efi and i386-ieee1275. - - * disk/ata.c (grub_ata_dumpinfo): Add a cast. - (grub_ata_readwrite): Likewise. Update 2 format strings. - (grub_atapi_read): Likewise. - - * conf/i386-coreboot.rmk (pkglib_MODULES): Move `ata.mod' from here ... - * conf/i386.rmk (pkglib_MODULES): ... to here ... - * conf/x86_64-efi.rmk (pkglib_MODULES): ... and here. - * conf/i386-coreboot.rmk (ata_mod_SOURCES, ata_mod_CFLAGS) - (ata_mod_LDFLAGS): Move from here ... - * conf/i386.rmk: ... to here ... - * conf/x86_64-efi.rmk: ... and here. - * conf/i386-pc.rmk (pkglib_MODULES): Remove `ata.mod' - (ata_mod_SOURCES, ata_mod_CFLAGS, ata_mod_LDFLAGS): Remove. - -2009-11-16 Robert Millan - - Relicense multiboot.h, with RMS' blessing. - - * include/multiboot.h: Change to X11 license. - -2009-11-15 Robert Millan - - Support --version in grub-mkisofs. - - * util/mkisofs/mkisofs.c (rcsid): Remove variable. - (OPTION_VERSION): New macro. - (ld_options): Recognize --version. - (usage): Move `program_name' from here ... - (program_name): ... to here. Add `static' qualifier. - (main): Recognize `OPTION_VERSION'. - -2009-11-15 Felix Zielcke - - * Makefile.in (TARGET_CPPFLAGS): Replace `-isystem=$(srcdir)/include' - with `-nostdinc -isystem $(shell $(TARGET_CC) -print-file-name=include)'. - -2009-11-14 Robert Millan - - Fix help2man generation for mkisofs. - - * util/mkisofs/mkisofs.c (ld_options): Recognize --help. - (usage): Send output to stdout (rather than stderr). - -2009-11-14 Robert Millan - - * conf/i386-coreboot.rmk (grub_mkrescue_SOURCES): Replace - `util/i386/coreboot/grub-mkrescue.in' with `util/grub-mkrescue.in'. - * conf/i386-pc.rmk (grub_mkrescue_SOURCES): Replace - `util/i386/pc/grub-mkrescue.in' with `util/grub-mkrescue.in'. - (bin_SCRIPTS): Add `grub-mkfloppy'. - (grub_mkfloppy_SOURCES): New variable. - - * util/grub-mkrescue.in: New file. - * util/i386/pc/grub-mkfloppy.in: New file. - - * util/i386/coreboot/grub-mkrescue.in: Remove. - * util/i386/pc/grub-mkrescue.in: Remove. - -2009-11-13 Robert Millan - - * include/grub/multiboot.h (struct grub_multiboot_header): Move - from here ... - * include/multiboot.h (struct multiboot_header): ... to here. Update - all users. - * include/grub/multiboot.h (struct grub_multiboot_info): Move - from here ... - * include/multiboot.h (struct multiboot_info): ... to here. Update - all users. - * include/grub/multiboot.h (struct grub_multiboot_mmap_entry): Move - from here ... - * include/multiboot.h (struct multiboot_mmap_entry): ... to here. - Update all users. - * include/grub/multiboot.h (struct grub_mod_list): Move - from here ... - * include/multiboot.h (struct multiboot_mod_list): ... to here. - Update all users. - -2009-11-13 Robert Millan - - * include/multiboot2.h (multiboot_word): Rename from this ... - (multiboot2_word): ... to this. Update all users. - (multiboot_header): Rename from this ... - (multiboot2_header): ... to this. Update all users. - (multiboot_tag_header): Rename from this ... - (multiboot2_tag_header): ... to this. Update all users. - (multiboot_tag_start): Rename from this ... - (multiboot2_tag_start): ... to this. Update all users. - (multiboot_tag_name): Rename from this ... - (multiboot2_tag_name): ... to this. Update all users. - (multiboot_tag_module): Rename from this ... - (multiboot2_tag_module): ... to this. Update all users. - (multiboot_tag_memory): Rename from this ... - (multiboot2_tag_memory): ... to this. Update all users. - (multiboot_tag_unused): Rename from this ... - (multiboot2_tag_unused): ... to this. Update all users. - (multiboot_tag_end): Rename from this ... - (multiboot2_tag_end): ... to this. Update all users. - -2009-11-13 Robert Millan - - Disable Multiboot2 in i386-ieee1275. It didn't actually work, and on - this platform we should support Multiboot1 first. - - * conf/i386-ieee1275.rmk (pkglib_MODULES): Remove `multiboot.mod'. - (multiboot_mod_SOURCES, multiboot_mod_CFLAGS) - (multiboot_mod_LDFLAGS, multiboot_mod_ASFLAGS): Remove. - -2009-11-12 Robert Millan - - * util/mkisofs/eltorito.c (init_boot_catalog): Handle return code - of write calls (converting them to fwrite() if they aren't already). - (get_torito_desc): Likewise. - * util/mkisofs/rock.c (generate_rock_ridge_attributes): Likewise. - -2009-11-12 Robert Millan - - * util/i386/pc/grub-install.in: Move from here ... - * util/grub-install.in: ... to here. Update all users. - -2009-11-11 Colin Watson - - * util/powerpc/ieee1275/grub-mkrescue.in: Fix --version output. - -2009-11-11 Robert Millan - - Support for El Torito without floppy emulation. - - * util/mkisofs/eltorito.c: Include `'. - (init_boot_catalog): Improve error handling. - (get_torito_desc): Don't use floppy emulation unless requested by - user. Patch boot information table when requested via - `-boot-info-table'. - * util/mkisofs/iso9660.h (struct eltorito_boot_info): New struct. - * util/mkisofs/mkisofs.c (use_eltorito_emul_floppy) - (use_boot_info_table): New variables. - (OPTION_BOOT_INFO_TABLE, OPTION_NO_EMUL_BOOT) - (OPTION_ELTORITO_EMUL_FLOPPY): New macros. - (ld_options): Handle `-boot-info-table', `-no-emul-boot' and - `--eltorito-emul-floppy'. - (main): Handle `OPTION_BOOT_INFO_TABLE', `OPTION_NO_EMUL_BOOT' - and `OPTION_ELTORITO_EMUL_FLOPPY'. - * util/mkisofs/mkisofs.h (use_eltorito_emul_floppy) - (use_boot_info_table, get_731): New prototypes. - * util/mkisofs/write.c (get_731): New function. - -2009-11-11 Felix Zielcke - - Fix the generation of the man page. - - * util/pc/i386/grub-install.in: Source - `${libdir}/grub/grub-mkconfig_lib' after options have been parsed. - -2009-11-11 Robert Millan - - Large file support for grub-mkisofs. - - * conf/common.rmk (grub_mkisofs_CFLAGS): Add `-D_FILE_OFFSET_BITS=64'. - * util/mkisofs/mkisofs.c (next_extent, last_extent) - (session_start): Upgrade type to `uint64_t'. Update all users. - * util/mkisofs/mkisofs.h: Include `'. - (struct directory_entry): Upgrade type of `starting_block' and - `size' to `uint64_t'. Update all users. - (struct deferred): Remove unused structure. - (xfwrite): Upgrade type of `count' and `size' to `uint64_t'. - Update all users. - * util/mkisofs/tree.c (stat_filter, lstat_filter): Return -1 when - file is larger than `UINT32_MAX'. - * util/mkisofs/write.c (xfwrite): Upgrade type of `count' and - `size' to `uint64_t'. Update all users. Fix handling of fwrite() - return value. - (struct deferred_write): Upgrade type of `extent' and `size' to - `uint64_t'. Update all users. - (last_extent_written): Upgrade type to `uint64_t'. Update all - users. - (write_one_file): Upgrade type of `count' and `size' to `uint64_t'. - Update all users. Upgrade type of `remain' to `int64_t' and - `use' to `size_t'. Use error() to handle fread() errors. - (write_files): Rely on write_one_file() rather than calling - xfwrite() directly. - -2009-11-09 Felix Zielcke - - * util/mkisofs/mkisofs.c (ld_options): Fix a spelling mistake. - -2009-11-09 Robert Millan - - * util/mkisofs/fnmatch.c: Remove. - * util/mkisofs/getopt1.c: Likewise. - * util/mkisofs/getopt.c: Likewise. - * conf/common.rmk (grub_mkisofs_SOURCES): Replace - `util/mkisofs/fnmatch.c', `util/mkisofs/getopt1.c' and - `util/mkisofs/getopt.c' with `gnulib/fnmatch.c', - `gnulib/getopt1.c' and `gnulib/getopt.c'. - (grub_mkisofs_CFLAGS): Add `-I$(srcdir)/gnulib'. - - * configure.ac: Detect `mingw32msvc' host_os. - Check for lstat(), getuid() and getgid(). - - * util/mkisofs/joliet.c: Include `'. Replace all - instances of `u_char' with `uint8_t'. - - * util/mkisofs/mkisofs.h: Include `'. - [!HAVE_GETUID] (getuid): New function (stub). - [!HAVE_GETGID] (getgid): Likewise. - [!HAVE_LSTAT] (lstat): Likewise. - [!S_IROTH] (S_IROTH): New macro (dummy). - [!S_IRGRP] (S_IRGRP): Likewise. - -2009-11-09 Robert Millan - - * gnulib/fnmatch_loop.c (EXT): Fix warning (signed and unsigned type in - conditional expression). - -2009-11-09 Robert Millan - - Import from Gnulib. - - * gnulib/fnmatch.c: New file. - * gnulib/fnmatch.h: Likewise. - * gnulib/fnmatch_loop.c: Likewise. - * gnulib/getopt.c: Likewise. - * gnulib/getopt.h: Likewise. - * gnulib/getopt1.c: Likewise. - * gnulib/getopt_int.h: Likewise. - * gnulib/gettext.h: Likewise. - -2009-11-09 Robert Millan - - * normal/dyncmd.c (read_command_list): Replace `0' with `NULL'. - * normal/handler.c (read_handler_list): Likewise. - -2009-11-09 Robert Millan - - Misc cleanup. - - * kern/command.c (grub_register_command_prio): Use - grub_zalloc() instead of explicitly zeroing data. - * kern/list.c: Include `'. - (grub_named_list_find): Replace `0' with `NULL'. - * normal/autofs.c (struct grub_fs_module_list): Remove ad-hoc type. - (fs_module_list): Change type to `grub_named_list_t'. Update all - users. - * normal/dyncmd.c (read_command_list): Add space between function - call and parenthesis. - * normal/handler.c (read_handler_list): Likewise. - -2009-11-09 Robert Millan - - * normal/auth.c (punishment_delay): Moved from here ... - (grub_auth_strcmp): ... to here (inside function). - -2009-11-09 Robert Millan - - * include/grub/list.h (struct grub_named_list): Remove `const' - qualifier from `name'. - (struct grub_prio_list): Likewise. - -2009-11-09 Robert Millan - - * normal/auth.c: Include `'. - (grub_auth_strcmp): Replace `strcmp' with `grub_strcmp'. - -2009-11-09 Robert Millan - - * normal/auth.c (punishment_delay): New variable. - (grub_auth_strcmp): Rewrite using grub_get_time_ms (). - (grub_auth_check_authentication): Punish failed login attempts with - an incremental (2^N) delay. - -2009-11-09 Robert Millan - - * conf/common.rmk (grub_mkisofs_CFLAGS): Prefix include - path with $(srcdir). - -2009-11-09 Vladimir Serbinenko - - * normal/auth.c (grub_auth_strcmp): Fixed incorrect variable usage. - -2009-11-09 Robert Millan - - * util/i386/coreboot/grub-mkrescue.in: New file. - * conf/i386-coreboot.rmk (bin_SCRIPTS, grub_mkrescue_SOURCES): New - variables. - - * conf/common.rmk (bin_UTILITIES): Add `grub-mkisofs'. - (grub_mkisofs_SOURCES, grub_mkisofs_CFLAGS): New variables. - * configure.ac: Add header and function checks to satisfy grub-mkisofs - requirements. - * util/mkisofs/defaults.h: New file. - * util/mkisofs/eltorito.c: Likewise. - * util/mkisofs/exclude.h: Likewise. - * util/mkisofs/fnmatch.c: Likewise. - * util/mkisofs/getopt.c: Likewise. - * util/mkisofs/getopt1.c: Likewise. - * util/mkisofs/hash.c: Likewise. - * util/mkisofs/include/fctldefs.h: Likewise. - * util/mkisofs/include/mconfig.h: Likewise. - * util/mkisofs/include/prototyp.h: Likewise. - * util/mkisofs/include/statdefs.h: Likewise. - * util/mkisofs/iso9660.h: Likewise. - * util/mkisofs/joliet.c: Likewise. - * util/mkisofs/match.c: Likewise. - * util/mkisofs/match.h: Likewise. - * util/mkisofs/mkisofs.c: Likewise. - * util/mkisofs/mkisofs.h: Likewise. - * util/mkisofs/multi.c: Likewise. - * util/mkisofs/name.c: Likewise. - * util/mkisofs/rock.c: Likewise. - * util/mkisofs/tree.c: Likewise. - * util/mkisofs/write.c: Likewise. - -2009-11-09 Vladimir Serbinenko - - * normal/auth.c (grub_auth_strcmp): Fix bug which resulted in function - being insecure. - -2009-11-08 Robert Millan - - * util/i386/pc/grub-mkrescue.in: Fix miss-identification as - `grub-mkimage' (and use $0 when possible). - -2009-11-08 Robert Millan - - * kern/i386/multiboot_mmap.c (grub_machine_mmap_init): Improve - error message for excessively large memory map. - -2009-11-08 Robert Millan - - * autogen.sh: Use `sh gendistlist.sh' to avoid reliing on - executable bit. - -2009-11-08 Robert Millan - - * kern/i386/multiboot_mmap.c (grub_machine_mmap_init): Improve error - message for coreboot users. - -2009-11-07 Robert Millan - - Fix build with GNU gold. - - * conf/i386-pc.rmk (boot_img_LDFLAGS, pxeboot_img_LDFLAGS) - (diskboot_img_LDFLAGS, lnxboot_img_LDFLAGS) - (cdboot_img_LDFLAGS): Prepend `0x' qualifier to hexadecimal - link addresses. - * aclocal.m4: Likewise. - -2009-11-04 Felix Zielcke - - * configure.ac (AC_PREREQ): Bump to 2.59d. - * INSTALL: Make it more clear when Autoconf and Ruby are - needed and when to run `./autogen.sh'. - -2009-11-03 Samuel Thibault - - * util/grub.d/30_os-prober.in: Restore default behavior for unsupported - OSes. - -2009-11-02 Samuel Thibault - - * util/grub.d/30_os-prober.in: Add GNU/Hurd support - -2009-11-02 Samuel Thibault - - * util/grub.d/10_hurd.in: Drop /dev/ prefix from root device path before - giving it to GNU Mach. - -2009-11-02 Samuel Thibault - - * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Subtract 1 from - GNU partition number to get internal GRUB partition number. - -2009-11-02 Samuel Thibault - - * util/grub.d/10_hurd.in: Call prepare_grub_to_access_device - ${GRUB_DEVICE_BOOT} before loading /boot kernel. - -2009-11-01 Robert Millan - - Based on patch from BVK Chaitanya - * kern/misc.c (grub_strchr, grub_strrchr): Fix to handle c == '\0' - case. - -2009-11-01 Felix Zielcke - - * Makefile.in (TARGET_CPPFLAGS): Add `-I$(srcdir)/include'. - -2009-10-30 Robert Millan - - Fix build problem. - - * Makefile.in (TARGET_CPPFLAGS): Replace `-nostdinc' with - `-isystem=$(srcdir)/include'. - -2009-10-30 Robert Millan - - * util/i386/pc/grub-install.in: Remove hint that device.map should be - checked (grub-install doesn't currently rely on it). - -2009-10-29 Robert Millan - - Revert SVN r2660. - - * conf/common.rmk (script/sh/lexer.c_DEPENDENCIES): Moved from here ... - * conf/i386-coreboot.rmk (script/sh/lexer.c_DEPENDENCIES): ... to here. - * conf/i386-efi.rmk (script/sh/lexer.c_DEPENDENCIES): ... and here. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - -2009-10-28 Robert Millan - - * Makefile.in (TARGET_CPPFLAGS): Add `-nostdinc'. - -2009-10-28 Robert Millan - - * include/grub/misc.h: Stop checking for APPLE_CC. - -2009-10-28 Robert Millan - - * kern/i386/coreboot/init.c (grub_exit): Reimplement in a way that - doesn't cause an infinite call loop. - -2009-10-28 Felix Zielcke - - * commands/acpi.c (grub_cmd_acpi): Fix the out of memory error - strings. - -2009-10-26 Robert Millan - - * autogen.sh: Support addition of external modules via `GRUB_CONTRIB' - variable. - * Makefile.in: Likewise. - -2009-10-26 Robert Millan - - * gendistlist.sh: Simplify .svn check. Skip .bzr as well. - -2009-10-26 Robert Millan - - * Makefile.in (RMKFILES): Rewrite using $(wildcard). - -2009-10-26 Robert Millan - - * disk/scsi.c: Remove `' (not needed). - -2009-10-26 Robert Millan - - * gensymlist.sh.in (COMPILE_TIME_ASSERT): Copy macro declaration - from here ... - * include/grub/misc.h (COMPILE_TIME_ASSERT): ... to here. - -2009-10-26 Robert Millan - - * Makefile.in (docs/grub.info): Use make syntax to ignore errors - in $(MAKEINFO) invocation. This makes it clear in output that - errors are being ignored. - -2009-10-26 Robert Millan - - * conf/i386-coreboot.rmk (script/sh/lexer.c_DEPENDENCIES): Moved - from here ... - * conf/common.rmk (script/sh/lexer.c_DEPENDENCIES): ... to here. - * conf/i386-efi.rmk (script/sh/lexer.c_DEPENDENCIES): Remove. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - -2009-10-26 Colin Watson - - * util/grub-editenv.c (main): If only a command is given, use - DEFAULT_DIRECTORY "/" GRUB_ENVBLK_DEFCFG as a default file name. - (usage): FILENAME is now optional and has a default. - -2009-10-26 Colin Watson - - Improve grub-mkconfig performance when there are several menu - entries on a single filesystem. - - * util/grub.d/10_linux.in (linux_entry): Cache the output of - prepare_grub_to_access_device. - * util/grub.d/10_kfreebsd.in (kfreebsd_entry): Likewise. - * util/grub.d/30_os-prober.in: Likewise. - -2009-10-26 Robert Millan - - * util/grub.d/10_freebsd.in: Remove. - * util/grub.d/10_kfreebsd.in: New file (based on 10_linux.in). - * configure.ac: Set host_kernel=kfreebsd for FreeBSD and GNU/kFreeBSD. - -2009-10-26 Robert Millan - - * docs/grub.cfg: Fix example usage of *BSD loaders. - -2009-10-25 Robert Millan - - * util/i386/pc/grub-setup.c (setup): Add missing parameter to - grub_util_error() call. - -2009-10-25 Robert Millan - - * include/grub/fs.h [GRUB_UTIL] (struct grub_fs): Add - `reserved_first_sector' member. - * fs/ext2.c [GRUB_UTIL] (grub_ext2_fs): Initialize - `reserved_first_sector' to 1. - * fs/fat.c [GRUB_UTIL] (grub_fat_fs): Likewise. - * fs/ntfs.c [GRUB_UTIL] (grub_ntfs_fs): Likewise. - * fs/hfsplus.c [GRUB_UTIL] (grub_hfsplus_fs): Likewise. - * util/i386/pc/grub-setup.c (setup): Add safety check that probes for - filesystems which begin at first sector. - (options): New option --skip-fs-probe. - (main): Handle --skip-fs-probe and pass it to setup(). - -2009-10-25 Robert Millan - - * include/grub/misc.h: Fix wrong evaluation of APPLE_CC. - (memset): Fix function prototype. - -2009-10-25 Robert Millan -2009-10-25 Vasily Averin - - * fs/ext2.c (grub_ext2_iterate_dir): Avoid infinite loop when - `dirent.direntlen == 0'. - -2009-10-25 Robert Millan - - * fs/cpio.c [MODE_USTAR]: Initialize `tar' module instead of - `cpio'. - [! MODE_USTAR]: Initialize `cpio' module instead of `tar'. - -2009-10-25 Robert Millan - - * configure.ac: Check for `__ashldi3', `__ashrdi3', `__lshrdi3', - `__trampoline_setup' and `__ucmpdi2'. - * include/grub/powerpc/libgcc.h: Only export symbols for functions - that libgcc provides. - -2009-10-25 Robert Millan - - * include/grub/powerpc/libgcc.h (memset): Remove function prototype. - * include/grub/sparc64/libgcc.h (memset): Likewise. - * include/grub/misc.h (memset, memcmp): New function prototypes. - -2009-10-25 Robert Millan - - * fs/cpio.c [MODE_USTAR]: Finish `tar' module instead of - `cpio'. - [! MODE_USTAR]: Finish `cpio' module instead of `tar'. - -2009-10-25 Robert Millan - - Patch from Samuel Thibault - * docs/grub.cfg: Compensate for recent change in multiboot - loader (since 2009-08-14 it won't pass filename to payload). - * util/grub.d/10_hurd.in: Likewise. - -2009-10-21 Felix Zielcke - - * config.guess: Update to latest version from config git - repository. - * config.sub: Likewise. - -2009-10-20 Robert Millan - - Fix build on sparc64. - - * configure.ac: Perform checks for libgcc symbols before - adding `-nostdlib' to LDFLAGS. - -2009-10-16 Vladimir Serbinenko - - Let user specify OpenBSD root device. - - * loader/i386/bsd.c (openbsd_root): New variable. - (openbsd_opts): New option 'root'. - (OPENBSD_ROOT_ARG): New macro. - (grub_openbsd_boot): Use 'openbsd_root'. - (grub_cmd_openbsd): Fill 'openbsd_root'. - -2009-10-16 Robert Millan - - * NEWS: Misc adjustments. - -2009-10-16 Vladimir Serbinenko - - * NEWS: Mentioned XNU, ACPI, gptsync, password and parttool. - -2009-10-16 Robert Millan - - * configure.ac: Bump version to 1.97. - -2009-10-16 Colin Watson - - * configure.ac (TARGET_CFLAGS): Add -mno-mmx -mno-sse -mno-sse2 - -mno-3dnow on x86 architectures. Some toolchains enable these - features by default, but they rely on registers that aren't enabled - in GRUB. Thanks to Vladimir Serbinenko for the suggestion. - -2009-10-15 Robert Millan - - Make entry text a bit more readable. - - * util/grub.d/10_linux.in: Add `with' before `Linux'. - -2009-10-15 Vladimir Serbinenko - - * loader/i386/pc/xnu.c (grub_xnu_set_video): Fix loading splash image. - -2009-10-15 Vladimir Serbinenko - - * commands/xnu_uuid.c (grub_cmd_xnu_uuid): Remove duplicated bitwise - operations. - -2009-10-15 Vladimir Serbinenko - - * configure.ac: Add missing dollar. - -2009-10-15 Vladimir Serbinenko - - Revert 2009-06-10 Pavel Roskin - - * configure.ac: Put checks for __bswapsi2 and __bswapdi2. - * include/grub/powerpc/libgcc.h: Don't use weak attribute for all - exports. - * include/grub/sparc64/libgcc.h: Likewise. Use - preprocessor conditionals. - -2009-10-14 Robert Millan - - * conf/common.rmk (grub-dumpbios): Remove rule. - (sbin_SCRIPTS, CLEANFILES): Remove `grub-dumpbios'. - * util/grub-dumpbios.in: Remove file. - -2009-10-14 Robert Millan - - Refer to kernel of FreeBSD "kFreeBSD" to avoid confusion between - the Operating System (FreeBSD) and its kernel (kernel of FreeBSD). - - * loader/i386/bsd.c (grub_freebsd_boot): Read kernel environment - from "kFreeBSD" namespace (rather than "FreeBSD"). Update all - users. - - (GRUB_MOD_INIT (bsd)): Rename "freebsd" command to "kfreebsd", - "openbsd" to "kopenbsd", "netbsd" to "knetbsd", "freebsd_loadenv" - to "kfreebsd_loadenv", "freebsd_module" to "kfreebsd_module", - and "freebsd_module_elf" to "kfreebsd_module_elf". Update all - users. - -2009-10-12 Robert Millan - - * term/tparm.c: Switch to GPLv3. - -2009-10-09 Robert Millan - - * include/grub/i386/cpuid.h: Add header protection. - -2009-10-09 Robert Millan - - Fail gracefuly when attempting to load 64-bit kFreeBSD on IA32 CPU. - - * include/grub/i386/cpuid.h: New file. - * commands/i386/cpuid.c: Include `'. - (has_longmode): Rename to ... - (grub_cpuid_has_longmode): ... this. Update all users. Remove - `static' attribute. - * loader/i386/bsd.c: Include `'. - (grub_bsd_load_elf): Fail if load of 64-bit kernel was requested - on a CPU that doesn't implement AMD64 instruction set. - -2009-10-06 Colin Watson - - * Makefile.in (docs/stamp-vti): Depend on configure.ac as well, so - that version.texi is rebuilt on version number changes. - -2009-10-06 Colin Watson - - * Makefile.in: Don't set info_INFOS unless makeinfo was found. - Fixes bug #27602. - -2009-10-06 Colin Watson - - * util/i386/pc/grub-install.in: Source - ${libdir}/grub/grub-mkconfig_lib before option processing, in order - that the --grub-probe option will work. - * util/sparc64/ieee1275/grub-install.in: Likewise. - -2009-10-05 Robert Millan - - * configure.ac: Bump version to 1.97~beta4. - -2009-10-03 Robert Millan - - Resync grub-mkdevicemap in x86_64-efi. - - * conf/x86_64-efi.rmk (sbin_UTILITIES): Enable `grub-mkdevicemap'. - (grub_mkdevicemap_SOURCES): Add missing `util/deviceiter.c' and - `util/devicemap.c'. - -2009-10-01 Colin Watson - - * util/grub-editenv.c (create_envblk_file): Write new block with a - .new suffix and then rename it into place, to ensure atomic - creation. - -2009-09-28 Robert Millan - - Do not automatically install headers. - - * Makefile.in (include_DATA): Remove. Update all users. - -2009-09-26 Robert Millan - - * conf/common.rmk (pkglib_MODULES): Remove `lua.mod'. - (lua_mod_SOURCES, lua_mod_CFLAGS, lua_mod_LDFLAGS): Remove. - - * util/osdetect.lua: Remove. - * script/lua/lauxlib.c: Likewise. - * script/lua/ldebug.c: Likewise. - * script/lua/grub_main.c: Likewise. - * script/lua/lauxlib.h: Likewise. - * script/lua/ldebug.h: Likewise. - * script/lua/ltablib.c: Likewise. - * script/lua/liolib.c: Likewise. - * script/lua/lstrlib.c: Likewise. - * script/lua/lualib.h: Likewise. - * script/lua/ldo.c: Likewise. - * script/lua/ldump.c: Likewise. - * script/lua/ldo.h: Likewise. - * script/lua/loslib.c: Likewise. - * script/lua/lundump.c: Likewise. - * script/lua/grub_lib.c: Likewise. - * script/lua/ldblib.c: Likewise. - * script/lua/lundump.h: Likewise. - * script/lua/lmem.c: Likewise. - * script/lua/grub_lib.h: Likewise. - * script/lua/lmathlib.c: Likewise. - * script/lua/lstate.c: Likewise. - * script/lua/ltm.c: Likewise. - * script/lua/lvm.c: Likewise. - * script/lua/lmem.h: Likewise. - * script/lua/lstate.h: Likewise. - * script/lua/ltm.h: Likewise. - * script/lua/ltable.c: Likewise. - * script/lua/lvm.h: Likewise. - * script/lua/llex.c: Likewise. - * script/lua/lgc.c: Likewise. - * script/lua/grub_lua.h: Likewise. - * script/lua/loadlib.c: Likewise. - * script/lua/lfunc.c: Likewise. - * script/lua/lopcodes.c: Likewise. - * script/lua/lparser.c: Likewise. - * script/lua/ltable.h: Likewise. - * script/lua/llex.h: Likewise. - * script/lua/lgc.h: Likewise. - * script/lua/lfunc.h: Likewise. - * script/lua/lbaselib.c: Likewise. - * script/lua/lopcodes.h: Likewise. - * script/lua/lparser.h: Likewise. - * script/lua/lzio.c: Likewise. - * script/lua/linit.c: Likewise. - * script/lua/lobject.c: Likewise. - * script/lua/llimits.h: Likewise. - * script/lua/lstring.c: Likewise. - * script/lua/lzio.h: Likewise. - * script/lua/lapi.c: Likewise. - * script/lua/lcode.c: Likewise. - * script/lua/lua.h: Likewise. - * script/lua/lobject.h: Likewise. - * script/lua/lstring.h: Likewise. - * script/lua/lapi.h: Likewise. - * script/lua/lcode.h: Likewise. - * script/lua/luaconf.h: Likewise. - -2009-09-26 Colin Watson - - * docs/grub.texi (Command-line and menu entry commands): Document - date and echo commands. - -2009-09-24 Pavel Roskin - - * include/grub/kernel.h (struct grub_module_header): Remove - `grub_module_header_types'. Make `type' unsigned. Make `size' - 32-bit on all platforms. - * util/elf/grub-mkimage.c (load_modules): Treat `type' as an - 8-bit field. Use grub_host_to_target32() for `size'. - * util/i386/efi/grub-mkimage.c (make_mods_section): Likewise. - * util/i386/pc/grub-mkimage.c (generate_image): Likewise. - * util/sparc64/ieee1275/grub-mkimage.c (generate_image): Likewise. - -2009-09-24 Robert Millan - - Fix "lost keypress" bug in at_keyboard. - - * term/i386/pc/at_keyboard.c (grub_at_keyboard_checkkey): New function. - Checks for readyness of input buffer (without flushing it). - (grub_at_keyboard_term): Use grub_at_keyboard_checkkey() rather - than grub_at_keyboard_getkey_noblock() for `checkkey' struct member. - -2009-09-24 Robert Millan - - * util/i386/pc/grub-mkimage.c (generate_image): Enclose BIOS-specific - size check within GRUB_MACHINE_PCBIOS section. - -2009-09-24 Robert Millan - - * include/grub/i386/at_keyboard.h (KEYBOARD_ISREADY): Negate - return value. - * term/i386/pc/at_keyboard.c (grub_keyboard_getkey): Negate - KEYBOARD_ISREADY check. - (grub_at_keyboard_checkkey): Rename to ... - (grub_at_keyboard_getkey_noblock): ... this. Update all users. - Remove gratuitous cast. - -2009-09-23 Colin Watson - - * configure.ac: Call AC_PROG_MKDIR_P. - * Makefile.in (docs/stamp-vti): Create docs directory. Create - version.texi in $(builddir) rather than $(srcdir). - (docs/grub.info): Create docs directory. Prepend $(builddir)/docs - to makeinfo's @include search path. - -2009-09-23 Felix Zielcke - - * util/grub-mkconfig_lib.in (grub_file_is_not_garbage): Cope with `*.dpkg-*' - -2009-09-23 Felix Zielcke - - * util/grub-mkconfig_lib.in (grub_file_is_not_garbage): Add support - for `*.dpkg-new'. - -2009-09-21 Colin Watson - - Build info documentation. Some code borrowed from Automake. - - * configure.ac: Check for makeinfo. - * Makefile.in (MAKEINFO, INFOS, info_INFOS): New variables. - (MAINTAINER_CLEANFILES): Add $(INFOS), docs/stamp-vti, and - docs/version.texi. - (MOSTLYCLEANFILES): Add vti.tmp. - (docs/version.texi, docs/stamp-vti): Update automatically. - (docs/grub.info): Build info documentation. Use --force and ignore - errors for now. - (all-local): Add $(INFOS). - (install-local): Install info files. - (uninstall): Uninstall info files. - * docs/version.texi: Remove from revision control. This file is - automatically generated on build now. - * gendistlist.sh: Add `*.info'. - -2009-09-21 Felix Zielcke - - * kern/term.c: Fix indentation. - -2009-09-21 Felix Zielcke - - * util/hostdisk.c: Fix a comment. - -2009-09-20 Robert Millan - - Fix regression introduced in r2539. - - * term/usb_keyboard.c (USB_HID_DEVICE_TO_HOST): Change from 0x61 - to 0xA1. - -2009-09-19 Colin Watson - - * util/grub.d/30_os-prober.in: Don't throw away stderr from - os-prober. Under normal operation, it does not print anything to - stderr; if it does, we need to debug it, and throwing away stderr - makes that excessively difficult. - -2009-09-16 Vladimir Serbinenko - - * mmap/mmap.c (grub_cmd_badram): Fix off-by-one error. - -2009-09-16 Robert Millan - - * aclocal.m4 (AC_LANG_PROGRAM): New macro. Overrides stock - AC_LANG_PROGRAM from autoconf. - (grub_ASM_USCORE, grub_PROG_OBJCOPY_ABSOLUTE): Add missing - prototypes (fixes warning). - - * configure.ac: Add `-Werror' to TARGET_CFLAGS unless - `--disable-werror' was used. - -2009-09-16 Robert Millan - - * partmap/msdos.c (pc_partition_map_iterate): Fix possible use of - uninitialized `lastaddr'. - -2009-09-15 Vladimir Serbinenko - - * partmap/msdos.c (pc_partition_map_iterate): Detect and break loops. - -2009-09-14 Colin Watson - - * commands/test.c (get_fileinfo): Return immediately if - grub_fs_probe fails. - -2009-09-14 JosĂ© MartĂ­nez - - * commands/acpi.c (grub_cmd_acpi): Fix loading ACPI tables from file. - -2009-09-14 Colin Watson - - * util/grub.d/30_os-prober.in: Cope with Windows 7 in os-prober - output. - -2009-09-13 Robert Millan - - * configure.ac: Remove --enable-grub-pe2elf. Only build - grub-pe2elf when needed by the build system itself. - * conf/common.rmk: Remove $(enable_grub_pe2elf) check. - -2009-09-12 Robert Millan - - * configure.ac: Bump version to 1.97~beta3. - * docs/version.texi: Likewise. - -2009-09-12 Robert Millan - - * video/i386/pc/vbe.c (grub_vbe_get_video_mode_info): Move packed - mode special handling (grub_vbe_bios_set_dac_palette_width() call) - from here ... - * loader/i386/linux.c [GRUB_MACHINE_PCBIOS] - (grub_linux_setup_video): ... to here (with some adjustments). - -2009-09-12 Robert Millan - - Fix memory corruption issue (spotted by Colin Watson). - - * kern/i386/pc/startup.S (grub_vbe_bios_getset_dac_palette): Fix bug - causing returned size to be stored in an incorrect memory location. - Fix use of uninitialized value when storing the returned size. - -2009-09-12 Yves Blusseau - - Change clean rules to properly remove files - - * genmk.rb: add new clean rules - * Makefile.in (clean): add the new targets - (mostlyclean): likewise - -2009-09-11 Colin Watson - - * include/grub/ntfs.h (struct grub_fshelp_node): Change `size' - to grub_uint64_t. - * fs/ntfs.c (init_file): Understand 64-bit sizes for - non-resident files. - -2009-09-11 Colin Watson - - * configure.ac: Don't look for help2man when cross-compiling. Fixes - part of bug #27349. - -2009-09-10 Felix Zielcke - - * util/grub-mkconfig.in: Make the created config mode 400 and - print a warning if it fails. - -2009-09-10 Robert Millan - - * util/grub.d/40_custom.in: Ask user to type custom entries below - comment, rather than below 'exec tail' line. - -2009-09-10 Colin Watson - - * util/grub.d/40_custom.in: Make sure that the explanatory text is - visible in grub.cfg. - -2009-09-10 Colin Watson - - * util/grub.d/40_custom.in: Make it a little clearer how to use this - file. - -2009-09-10 Felix Zielcke - - * docs/grub.cfg: Add an example menu entry for memtest86+. - -2009-09-09 Felix Zielcke - - * config.guess: Update to latest version from config git. - * config.sub: Likewise. - -2009-09-08 Colin Watson - - * script/sh/execute.c (grub_script_execute_cmdline): Set "?" in - unknown-command case. Fixes bug #27320. - -2009-09-08 Felix Zielcke - - * kern/rescue_parser.c (grub_rescue_parse_line): Only suggest to try - `help' if the command exists. - -2009-09-06 Robert Millan - - * INSTALL: Require GCC 4.1.3 or later. - -2009-09-06 Yves Blusseau - - * Makefile.in (RMKFILES): add i386-qemu.rmk - (MAINTAINER_CLEANFILES): add $(srcdir)/DISTLIST $(srcdir)/config.h.in - $(srcdir)/stamp-h.in - -2009-09-05 Robert Millan - - * util/grub-probe.c (probe): Comment out buggy codepath, which - was unexpectedly enabled by Colin Watson's 2009-09-02 fix. This - should be re-enabled after 1.97. - -2009-09-05 Felix Zielcke - - * gendistlist.sh: Add `grub-dumpdevtree' and `*.lua' to the list - find searches for. - -2009-09-04 Vladimir Serbinenko - - * loader/i386/xnu.c (grub_cpu_xnu_fill_devicetree): Remove - unnecessary calls to grub_error. - -2009-09-04 Colin Watson - - * NEWS: Mention `keystatus' and Unicode fonts. - -2009-09-04 Robert Millan - - * configure.ac: Bump version to 1.97~beta2. - * docs/version.texi: Likewise. - -2009-09-03 Colin Watson - - * configure.ac: By default, GCC 4.4 generates .eh_frame sections - containing unwind information in some cases where it previously did - not. Use -fno-dwarf2-cfi-asm if available to restore the old - behaviour. See http://patchwork.kernel.org/patch/8555/ for related - discussion. - -2009-09-02 Yves BLUSSEAU - - Embedding loadenv module into grub-emu - - * conf/i386-pc.rmk (grub_emu_SOURCES): add lib/envblk.c and - commands/loadenv.c - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise - -2009-09-03 Magnus Granberg - - * aclocal.m4: Add grub_CHECK_PIE. It check if the compiler - include -fPIE in the default specs. - * configure.ac: Check if pie_possible is yes and add -fno-PIE - to TARGET_CFLAGS. - -2009-09-03 Felix Zielcke - - * INSTALL: Note that GNU Bison 2.3 or later is required. - -2009-09-03 Colin Watson - - * kern/i386/pc/startup.S: Fix typo. - -2009-09-02 Vladimir Serbinenko - - * efiemu/loadcore.c (SUFFIX (grub_efiemu_loadcore_load)): Fix style - according to GCS. - -2009-09-02 Colin Watson - - * docs/grub.texi (Naming convention): Describe one-based partition - numbering. - (Device syntax): Likewise. - (File name syntax): Likewise. - (Block list syntax): Likewise. - (Making a GRUB bootable CD-ROM): Talk about grub.cfg rather than - menu.lst. - (File name syntax): Likewise. - (Command-line and menu entry commands): Document acpi, blocklist, - crc, export, insmod, keystatus, ls, set, and unset commands. - -2009-09-02 Colin Watson - - * commands/keystatus.c (GRUB_MOD_INIT (keystatus)): Adjust summary - to avoid implying that only one of --shift, --ctrl, or --alt may be - used. - -2009-09-02 Colin Watson - - * util/grub-probe.c (probe): Test st.st_mode using S_ISREG macro - rather than comparing against S_IFREG, which will almost never work. - -2009-09-01 Vladimir Serbinenko - - * commands/loadenv.c (check_blocklists): Fix off-by-one error. - (write_blocklists): Likewise. - -2009-09-01 Colin Watson - - * script/lua/grub_lua.h (fputs): Supply a format string as the first - argument to grub_printf. - -2009-09-01 Felix Zielcke - - * genmk.rb: Add quotes around $(TARGET_OBJ2ELF) to cope with - non GNU test. - -2009-08-30 Vladimir Serbinenko - - * kern/file.c (grub_file_read): Spelling fix - -2009-08-30 Vladimir Serbinenko - - * loader/i386/bsdXX.c (SUFFIX (grub_freebsd_load_elfmodule)): Fix - loading of headers in some cases. - -2009-08-30 Robert Millan - - * configure.ac: Bump version to 1.97~beta1. - * docs/version.texi: Likewise. - -2009-08-29 Vladimir Serbinenko - - * include/grub/i386/xnu.h: Add license header. - include grub/err.h explicitly. - -2009-08-29 Robert Millan - - * util/grub.d/10_freebsd.in: Detect `ufs1' and `ufs2' and map them - to `ufs' in the vfs.root.mountfrom kernel parameter. - -2009-08-29 Robert Millan - - * term/i386/pc/serial.c: Include `'. - - [GRUB_MACHINE_PCBIOS] (serial_hw_io_addr): Macroify initialization - value (0x0400 -> GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR). - - [! GRUB_MACHINE_PCBIOS] (GRUB_SERIAL_PORT_NUM): Calculate using - `ARRAY_SIZE' macro. - -2009-08-28 Vladimir Serbinenko - - * kern/file.c (grub_file_read): Check offset. - * fs/hfs.c (grub_hfs_read_file): Remove unnecessary offset check. - * fs/jfs.c (grub_jfs_read_file): Likewise. - * fs/ntfs.c (grub_ntfs_read): Likewise. - * fs/reiserfs.c (grub_reiserfs_read): Likewise. - * fs/minix.c (grub_minix_read_file): Correct offset check. - * fs/ufs.c (grub_ufs_read_file): Likewise. - -2009-08-28 Colin Watson - - * term/i386/pc/console.c (bios_data_area): Cast - GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR explicitly. - -2009-08-28 Vladimir Serbinenko - - 1-bit optimised blitters. - - * include/grub/fbblit.h (grub_video_fbblit_replace_32bit_1bit): New - prototype. - (grub_video_fbblit_replace_24bit_1bit): Likewise. - (grub_video_fbblit_replace_16bit_1bit): Likewise. - (grub_video_fbblit_replace_8bit_1bit): Likewise. - (grub_video_fbblit_blend_XXXA8888_1bit): Likewise. - (grub_video_fbblit_blend_XXX888_1bit): Likewise. - (grub_video_fbblit_blend_XXX565_1bit): Likewise. - * video/fb/fbblit.c (grub_video_fbblit_replace_32bit_1bit): New - function. - (grub_video_fbblit_replace_24bit_1bit): Likewise. - (grub_video_fbblit_replace_16bit_1bit): Likewise. - (grub_video_fbblit_replace_8bit_1bit): Likewise. - (grub_video_fbblit_blend_XXXA8888_1bit): Likewise. - (grub_video_fbblit_blend_XXX888_1bit): Likewise. - (grub_video_fbblit_blend_XXX565_1bit): Likewise. - * video/fb/video_fb.c (common_blitter): Use 1-bit optimised blitters - when possible. - * video/video.c (grub_video_get_blit_format): Return - GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED if bpp = 1. - -2009-08-28 Colin Watson - - * normal/cmdline.c (grub_cmdline_get): Supply a format string as - the first argument to grub_printf. - -2009-08-28 Colin Watson -2009-08-28 Robert Millan - - Add `getkeystatus' terminal method. Add a new `keystatus' command - to query it. - - * include/grub/term.h (GRUB_TERM_STATUS_SHIFT, - GRUB_TERM_STATUS_CTRL, GRUB_TERM_STATUS_ALT): Definitions for - modifier key bitmasks. - (struct grub_term_input): Add `getkeystatus' member. - (grub_getkeystatus): Add prototype. - * kern/term.c (grub_getkeystatus): New function. - - * include/grub/i386/pc/memory.h - (GRUB_MEMORY_MACHINE_BIOS_DATA_AREA_ADDR): New macro. - (struct grub_machine_bios_data_area): Define necessary parts of BIOS - Data Area layout. - * term/i386/pc/console.c (grub_console_getkeystatus): New function. - (grub_console_term_input): Set `getkeystatus' member. - * term/usb_keyboard.c (grub_usb_hid): Macroify HID protocol - constants. - (grub_usb_keyboard_getreport): Likewise. - (grub_usb_keyboard_checkkey): Likewise. - (grub_usb_keyboard_getkeystatus): New function. - (grub_usb_keyboard_term): Set `getkeystatus' member. - - * commands/keystatus.c: New file. - * conf/common.rmk (pkglib_MODULES): Add keystatus.mod. - (keystatus_mod_SOURCES): New variable. - (keystatus_mod_CFLAGS): Likewise. - (keystatus_mod_LDFLAGS): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add - commands/keystatus.c. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - -2009-08-28 Vladimir Serbinenko - - Split befs.mod and afs.mod into *_be.mod and *.mod - - * conf/common.rmk (grub_probe_SOURCES): Add afs_be.c and befs_be.c. - (grub_fstest_SOURCES): Likewise. - (pkglib_MODULES): Add afs_be.mod and befs_be.mod. - (afs_be_mod_SOURCES): New variable. - (afs_be_mod_CFLAGS): Likewise. - (afs_be_mod_LDFLAGS): Likewise. - (befs_be_mod_SOURCES): Likewise. - (befs_be_mod_CFLAGS): Likewise. - (befs_be_mod_LDFLAGS): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add afs_be.c and befs_be.c. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_setup_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * fs/afs_be.c: New file. - * fs/befs_be.c: New file. - * fs/afs.c (GRUB_AFS_FSNAME_SUFFIX): New definition. - (GRUB_AFS_FSNAME): Use GRUB_AFS_FSNAME_SUFFIX. - (U16): Replaced with ... - (grub_afs_to_cpu16): ...this. All users updated. - (U32): Replaced with ... - (grub_afs_to_cpu32): ...this. All users updated. - (U64): Replaced with ... - (grub_afs_to_cpu64): ...this. All users updated. - (GRUB_AFS_BO_LITTLE_ENDIAN): Remove. - (GRUB_AFS_BO_BIG_ENDIAN): Likewise. - (grub_afs_validate_sblock): Check only one endianness. - (GRUB_MOD_INIT (befs)) [MODE_BIGENDIAN && MODE_BFS]: Rename to .. - (GRUB_MOD_INIT (befs_be)) [MODE_BIGENDIAN && MODE_BFS]: ... this. - (GRUB_MOD_INIT (afs)) [MODE_BIGENDIAN && !MODE_BFS]: Rename to .. - (GRUB_MOD_INIT (afs_be)) [MODE_BIGENDIAN && !MODE_BFS]: ... this. - (GRUB_MOD_FINI (befs)) [MODE_BIGENDIAN && MODE_BFS]: Rename to .. - (GRUB_MOD_FINI (befs_be)) [MODE_BIGENDIAN && MODE_BFS]: ... this. - (GRUB_MOD_FINI (afs)) [MODE_BIGENDIAN && !MODE_BFS]: Rename to .. - (GRUB_MOD_FINI (afs_be)) [MODE_BIGENDIAN && !MODE_BFS]: ... this. - -2009-08-26 Bean - - * fs/xfs.c (GRUB_XFS_INO_INOINAG): Replace 1L with 1LL to support - 64-bit number. - (GRUB_XFS_FSB_TO_BLOCK): Likewise. - (grub_xfs_inode_block): Change return type to grub_uint64_t. - (grub_xfs_read_inode): Change type of block to grub_uint64_t. - -2009-08-25 Vladimir Serbinenko - - NetBSD memory map support. - - * include/grub/i386/bsd.h (NETBSD_BTINFO_MEMMAP): New definition. - (grub_netbsd_btinfo_mmap_header): New structure. - (grub_netbsd_btinfo_mmap_entry): Likewise. - * loader/i386/bsd.c (grub_netbsd_boot): Pass memory map. - -2009-08-25 Vladimir Serbinenko - - Enable bsd.mod on coreboot. - - * conf/i386-coreboot.rmk (pkglib_MODULES): Add bsd.mod. - (bsd_mod_SOURCES): New variable. - (bsd_mod_CFLAGS): Likewise. - (bsd_mod_LDFLAGS): Likewise. - (bsd_mod_ASFLAGS): Likewise. - * loader/i386/bsd.c [!GRUB_MACHINE_PCBIOS]: Fix includes. - (grub_bsd_get_device) [!GRUB_MACHINE_PCBIOS]: Set *biosdev to 0xff. - -2009-08-25 Vladimir Serbinenko - - Cleanup NetBSD root support. - - * loader/i386/bsd.c (grub_netbsd_boot): Remove call to - grub_bsd_get_device. - Fix typo. - -2009-08-25 Felix Zielcke - - * util/grub.d/00_header.in: Move check for the video backend of - gfxterm from here ... - * util/grub-mkconfig.in: ... to here. Enable gfxterm if there's - a suitable video backend. - -2009-08-25 Vladimir Serbinenko - - Fix breakage in grub-setup. - - * util/i386/pc/grub-setup.c (setup): Use "part_msdos" instead of - "msdos_partition_map". - -2009-08-25 Vladimir Serbinenko - - Fix breakage in normal/auth.c. - - * normal/auth.c (grub_iswordseparator): New function. - -2009-08-25 Vladimir Serbinenko - - Authentication support. - - * commands/password.c: New file. - * conf/common.rmk (pkglib_MODULES): Add password.mod. - (password_mod_SOURCES): New variable. - (password_mod_CFLAGS): Likewise. - (password_mod_LDFLAGS): Likewise. - (normal_mod_SOURCES): Add normal/auth.c. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add commands/password.c and - normal/auth.c. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * include/grub/auth.h: New file. - * include/grub/err.h (grub_err_t): New enum value - GRUB_ERR_ACCESS_DENIED. - * include/grub/menu.h (grub_menu_entry): New fields 'restricted' and - 'users'. - * include/grub/normal.h (grub_cmdline_get): New argument 'history'. - * normal/cmdline.c (grub_cmdline_get): New argument 'history'. All - users updated. - * normal/auth.c: New file. - * normal/main.c (grub_normal_add_menu_entry): Handle --users option. - (grub_cmdline_run): Don't allow to go to command line without - authentication. - * normal/menu.c (grub_menu_execute_entry): Handle restricted entries. - * normal/menu_entry.c (grub_menu_entry_run): Don't allow editing - menuentry without superuser rights. - * normal/menu_viewer.c (grub_menu_viewer_show_menu): Don't exit if - user isn't a superuser. - -2009-08-24 Vladimir Serbinenko - - Save space by inlining misc.c functions. - - * kern/misc.c (grub_iswordseparator): Made static. - * kern/misc.c (grub_strcat): Moved from here ... - * include/grub/misc.h (grub_strcat): ... here. Inlined. - * kern/misc.c (grub_strncat): Moved from here ... - * include/grub/misc.h (grub_strncat): ... here. Inlined. - * kern/misc.c (grub_strcasecmp): Moved from here ... - * include/grub/misc.h (grub_strcasecmp): ... here. Inlined. - * kern/misc.c (grub_strncasecmp): Moved from here ... - * include/grub/misc.h (grub_strncasecmp): ... here. Inlined. - * kern/misc.c (grub_isalpha): Moved from here ... - * include/grub/misc.h (grub_isalpha): ... here. Inlined. - * kern/misc.c (grub_isdigit): Moved from here ... - * include/grub/misc.h (grub_isdigit): ... here. Inlined. - * kern/misc.c (grub_isgraph): Moved from here ... - * include/grub/misc.h (grub_isgraph): ... here. Inlined. - * kern/misc.c (grub_tolower): Moved from here ... - * include/grub/misc.h (grub_tolower): ... here. Inlined. - -2009-08-24 Vladimir Serbinenko - - * script/sh/function.c (grub_script_function_find): Cut error message - not to flood terminal. - * script/sh/lexer.c (grub_script_yylex): Remove command line length - limit. - * script/sh/script.c (grub_script_arg_add): Duplicate string. - -2009-08-24 Colin Watson - - * term/usb_keyboard.c (grub_usb_keyboard_getreport): Make - `report' grub_uint8_t *. - (grub_usb_keyboard_checkkey): Make `data' elements grub_uint8_t. - Use a 50-millisecond timeout rather than just repeating - grub_usb_keyboard_getreport 50 times. - (grub_usb_keyboard_getkey): Make `data' elements grub_uint8_t. - -2009-08-24 Vladimir Serbinenko - - Rename *_partition_map to part_* - - * partmap/acorn.c (grub_acorn_partition_map): Set name to 'part_acorn'. - * partmap/amiga.c (grub_amiga_partition_map): Set name to 'part_amiga'. - * partmap/apple.c (grub_apple_partition_map): Set name to 'part_apple'. - * partmap/gpt.c (grub_gpt_partition_map): Set name to 'part_gpt'. - All users updated. - * partmap/msdos.c (grub_msdos_partition_map): Set name to 'part_msdos'. - All users updated. - * partmap/sun.c (grub_sun_partition_map): Set name to 'part_sun'. - * util/grub-probe.c (probe_partmap): Don't transform partition name - to get module name. - -2009-08-24 Vladimir Serbinenko - - Fix OpenBSD and NetBSD support. - - * include/grub/i386/bsd.h (GRUB_BSD_TEMP_BUFFER): Change to resolve - memory address conflict. - (OPENBSD_MMAP_ACPI): New definition. - (OPENBSD_MMAP_NVS): Likewise. - * loader/i386/bsd.c (grub_openbsd_boot): Support OPENBSD_MMAP_ACPI - and OPENBSD_MMAP_NVS. - Add memory map terminator - Explicit cast when calling grub_unix_real_boot. - (grub_netbsd_boot): Explicit cast when calling grub_unix_real_boot. - -2009-08-24 Vladimir Serbinenko - - Let user specify NetBSD root device. - - * loader/i386/bsd.c (netbsd_root): New variable. - (netbsd_opts): New option 'root'. - (NETBSD_ROOT_ARG): New macro. - (grub_netbsd_boot): Use 'netbsd_root'. - (grub_bsd_unload): Free 'netbsd_root'. - (grub_cmd_netbsd): Fill 'netbsd_root'. - -2009-08-24 Vladimir Serbinenko - - Support for 64-bit NetBSD. - - * loader/i386/bsd.c (grub_bsd_load_elf): Apply correct mask to entry - point when booting non-FreeBSD. - -2009-08-24 Vladimir Serbinenko - - Support --no-smp and --no-acpi for NetBSD. - - * include/grub/i386/bsd.h (NETBSD_AB_NOSMP): New definition. - (NETBSD_AB_NOACPI): Likewise. - * loader/i386/bsd.c (netbsd_opts): New entries no-smp and no-acpi. - (netbsd_flags): Add NETBSD_AB_NOSMP, NETBSD_AB_NOACPI. - -2009-08-23 Vladimir Serbinenko - - * fs/hfsplus.c (grub_hfsplus_mount): Don't ignore grub_hfsplus_read_file - errors. - (grub_hfsplus_btree_iterate_node): Don't ignore grub_hfsplus_read_file - errors. Call grub_error when needed. - -2009-08-23 Vladimir Serbinenko - - * commands/search.c (search_fs): Try searching without autoload first. - * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Load - filesystem module explicitly for faster booting. - -2009-08-23 Colin Watson - - * util/grub-mkconfig.in: Export GRUB_DISABLE_OS_PROBER. - -2009-08-23 Colin Watson - - * util/grub.d/30_os-prober.in: Disable os-prober if - `GRUB_DISABLE_OS_PROBER' was set to true. - -2009-08-23 Robert Millan - - * partmap/pc.c: Rename to ... - * partmap/msdos.c: ... this. Update all users. - (grub_pc_partition_map): Rename to ... - (grub_msdos_partition_map): ... this. Update all users. - - * parttool/pcpart.c: Rename to ... - * parttool/msdospart.c: ... this. Update all users. - - * include/grub/pc_partition.h: Rename to ... - * include/grub/msdos_partition.h: ... this. Update all users. - (grub_pc_partition_bsd_entry): Rename to ... - (grub_msdos_partition_bsd_entry): ... this. Update all users. - (grub_pc_partition_disk_label): Rename to ... - (grub_msdos_partition_disk_label): ... this. Update all users. - (grub_pc_partition_entry): Rename to ... - (grub_msdos_partition_entry): ... this. Update all users. - (grub_pc_partition_mbr): Rename to ... - (grub_msdos_partition_mbr): ... this. Update all users. - (grub_pc_partition): Rename to ... - (grub_msdos_partition): ... this. Update all users. - (grub_pc_partition_is_empty): Rename to ... - (grub_msdos_partition_is_empty): ... this. Update all users. - (grub_pc_partition_is_extended): Rename to ... - (grub_msdos_partition_is_extended): ... this. Update all users. - (grub_pc_partition_is_bsd): Rename to ... - (grub_msdos_partition_is_bsd): ... this. Update all users. - - * conf/common.rmk (amiga_mod_SOURCES, amiga_mod_CFLAGS) - (amiga_mod_LDFLAGS, apple_mod_SOURCES, apple_mod_CFLAGS) - (apple_mod_LDFLAGS, msdos_mod_SOURCES, msdos_mod_CFLAGS) - (msdos_mod_LDFLAGS, sun_mod_SOURCES, sun_mod_CFLAGS) - (sun_mod_LDFLAGS, acorn_mod_SOURCES, acorn_mod_CFLAGS) - (acorn_mod_LDFLAGS, gpt_mod_SOURCES, gpt_mod_CFLAGS) - (gpt_mod_LDFLAGS): Rename to ... - (part_amiga_mod_SOURCES, part_amiga_mod_CFLAGS, part_amiga_mod_LDFLAGS) - (part_apple_mod_SOURCES, part_apple_mod_CFLAGS, part_apple_mod_LDFLAGS) - (part_msdos_mod_SOURCES, part_msdos_mod_CFLAGS, part_msdos_mod_LDFLAGS) - (part_sun_mod_SOURCES, part_sun_mod_CFLAGS, part_sun_mod_LDFLAGS) - (part_acorn_mod_SOURCES, part_acorn_mod_CFLAGS, part_acorn_mod_LDFLAGS) - (part_gpt_mod_SOURCES, part_gpt_mod_CFLAGS) - (part_gpt_mod_LDFLAGS): ... this. - (pkglib_MODULES): Prefix partition modules with `part_'. Rename - `pcpart.mod' to `msdospart.mod'. - (pcpart_mod_SOURCES, pcpart_mod_CFLAGS, pcpart_mod_LDFLAGS): Rename - to ... - (msdospart_mod_SOURCES, msdospart_mod_CFLAGS) - (msdospart_mod_LDFLAGS): ... this. - -2009-08-23 Vladimir Serbinenko - - * loader/i386/bsd.c (freebsd_opts): Rewritten to use extcmd. - (openbsd_opts): Likewise. - (netbsd_opts): Likewise. - (freebsd_flags): Added 0 terminator. - (openbsd_flags): Likewise. - (netbsd_flags): Likewise. - (grub_bsd_parse_flags): Rewritten to use extcmd. All users updated. - (grub_cmd_freebsd): Transformed into extended command. - (grub_cmd_openbsd): Likewise. - (grub_cmd_netbsd): Likewise. - (cmd_freebsd): Changed type to grub_extcmd_t. - (cmd_openbsd): Likewise. - (cmd_netbsd): Likewise. - (GRUB_MOD_INIT (bsd)): Register grub_cmd_freebsd, grub_cmd_netbsd and - grub_cmd_openbsd as extended commands. - (GRUB_MOD_FINI (bsd)): Use grub_unregister_extcmd for cmd_freebsd, - cmd_netbsd and cmd_openbsd - -2009-08-22 Vladimir Serbinenko - - * commands/xnu_uuid.c (transform): Use grub_memcpy instead of memcpy. - -2009-08-21 Pavel Roskin - - * Makefile.in (install-local): When checking if a file is in the - build directory, use "test -e" to detect symlinks. - - * Makefile.in (install-local): Remove all files in - $(DESTDIR)$(pkglibdir) before installing new files there. - -2009-08-18 Felix Zielcke - - * util/powerpc/ieee1275/grub-mkrescue.in (grub_mkimage): Use - grub-mkelfimage. - -2009-08-18 Felix Zielcke - - * util/grub-mkconfig.in: Don't use gfxterm by default if not - explicitly specified by the user. - -2009-08-18 Pavel Roskin - - * include/grub/fbfill.h (struct grub_video_fbrender_target): Use - grub_uint8_t pointer for data. - * include/grub/fbutil.h (struct grub_video_fbblit_info): - Likewise. - * video/fb/fbutil.c: Remove unnecessary casts. - -2009-08-17 Michal Suchanek - - VBE cleanup. - - * video/i386/pc/vbe.c (vbe_mode_in_use): Removed (duplicate). - (grub_vbe_set_video_mode): Save active mode info - only after setting the mode. - (grub_video_vbe_setup): Call 'grub_vbe_set_video_mode' with NULL as - second argument. - -2009-08-17 Michal Suchanek - - Rename variables for clarity. - - * video/i386/pc/vbe.c (active_mode_info): Renamed to ... - (active_vbe_mode_info): ... this. All users updated. - (framebuffer): Rename 'active_mode' to 'active_vbe_mode'. - All users updated. - (initial_mode): Rename to ... - (initial_vbe_mode): ... this. All users updated. - (mode_in_use): Rename to .. - (vbe_mode_in_use): ... this. All users updated. - (mode_list): Rename to .. - (vbe_mode_list): ... this. All users updated. - (grub_vbe_set_video_mode): Rename 'mode' to 'vbe_mode', 'mode_info' to - 'vbe_mode_info' and 'old_mode' to 'old_vbe_mode'. - (grub_video_vbe_init): Rename 'rm_mode_list' to 'rm_vbe_mode_list' and - 'mode_list_size' to 'vbe_mode_list_size'. - (grub_video_vbe_setup): Rename 'mode_info' to 'vbe_mode_info', - 'best_mode_info' to 'best_vbe_mode_info' and - 'best_mode' to 'best_vbe_mode' - -2009-08-17 Michal Suchanek - - Remove duplicate grub_video_fb_get_video_ptr. - - * include/grub/fbutil.h (get_data_ptr): Rename to ... - (grub_video_fb_get_video_ptr): ... this. - * include/grub/video_fb.h (grub_video_fb_get_video_ptr): Removed. - * video/fb/fbutil.c: Add comment about addressing. - (get_data_ptr): Rename to ... - (grub_video_fb_get_video_ptr): ... this. All users updated. - * video/fb/video_fb.c (grub_video_fb_get_video_ptr): Remove. - -2009-08-17 Robert Millan - - * fs/fat.c (grub_fat_read_data): Remove `#if 0' braces around the - grub_dprintf() that was just added. - -2009-08-17 Robert Millan - - * loader/i386/linux.c (GRUB_ASSUME_LINUX_HAS_FB_SUPPORT) - (DEFAULT_VIDEO_MODE): Remove macros. - (grub_linux_boot): Remove assumption that Linux has FB support, - and use "text" as default video mode. - -2009-08-15 Vladimir Serbinenko - - * fs/affs.c (grub_affs_read_symlink): Change leftover grub_printf into - grub_dprintf. - * fs/fat.c (grub_fat_read_data): Likewise. - -2009-08-14 Vladimir Serbinenko - - * loader/i386/multiboot.c (grub_multiboot): Don't pass filename to - payload. - (grub_module): Likewise. - -2009-08-14 Vladimir Serbinenko - - * loader/i386/multiboot.c (grub_multiboot_unload): Don't free mbi and - mbi->cmdline but free playground. - -2009-08-14 Vladimir Serbinenko - - Handle group offset on UFS1. - - * fs/ufs.c (grub_ufs_sblock): New field 'cylg_mask'. - (grub_ufs_read_inode) [!MODE_UFS2]: handle cylg_offset and cylg_mask. - -2009-08-14 Vladimir Serbinenko - - Split ufs.mod into ufs1.mod and ufs2.mod. - - * conf/common.rmk (grub_probe_SOURCES): Add fs/ufs2.c. - (grub_fstest_SOURCES): Likewise. - (pkglib_MODULES): Remove ufs.mod. Add ufs1.mod and ufs2.mod. - (ufs_mod_SOURCES): Remove. - (ufs_mod_CFLAGS): Likewise. - (ufs_mod_LDFLAGS): Likewise. - (ufs1_mod_SOURCES): New variable. - (ufs1_mod_CFLAGS): Likewise. - (ufs1_mod_LDFLAGS): Likewise. - (ufs2_mod_SOURCES): New variable. - (ufs2_mod_CFLAGS): Likewise. - (ufs2_mod_LDFLAGS): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add fs/ufs2.c. - * conf/i386-efi.rmk (util/i386/efi/grub-mkimage.c_DEPENDENCIES): - Likewise. - (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - (grub_setup_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64.rmk (grub_emu_SOURCES): Likewise. - (grub_setup_SOURCES): Likewise. - * conf/x86_64-efi.rmk (util/i386/efi/grub-mkimage.c_DEPENDENCIES): - Likewise. - * fs/ufs2.c: New file. - * fs/ufs.c: Separate UFS1 from UFS2 by using preprocessor. - -2009-08-14 Vladimir Serbinenko - - Framebuffer split. - - * commands/i386/pc/vbetest.c (grub_cmd_vbetest): Restore video - subsystem at the end. - * conf/common.rmk (pkglib_MODULES): Add video_fb.mod. - (video_fb_mod_SOURCES): New variable. - (video_fb_mod_CFLAGS): Likewise. - (video_fb_mod_LDFLAGS): Likewise. - * conf/i386-pc.rmk (vbe_mod_SOURCES): Remove video/i386/pc/vbeblit.c, - video/i386/pc/vbefill.c and video/i386/pc/vbeutil.c. - * video/i386/pc/vbeblit.c: Moved from here ... - * video/fb/fbblit.c: ..here. Replaced 'vbe' with 'fb'. - * video/i386/pc/vbefill.c: Moved from here ... - * video/fb/fbfill.c: ..here. Replaced 'vbe' with 'fb'. - * video/i386/pc/vbeutil.c: Moved from here ... - * video/fb/fbutil.c: ..here. Replaced 'vbe' with 'fb'. - * include/grub/i386/pc/vbeblit.h: Moved from here ... - * include/grub/fbblit.h: ... here. Replaced 'vbe' with 'fb'. - * include/grub/i386/pc/vbefill.h: Moved from here ... - * include/grub/fbfill.h: ... here. Replaced 'vbe' with 'fb'. - * include/grub/i386/pc/vbeutil.h: Moved from here ... - * include/grub/fbutil.h: ... here. Replaced 'vbe' with 'fb'. - * include/grub/i386/pc/vbe.h: Moved framebuffer part ... - * include/grub/video_fb.h: ... here. Replaced 'vbe' with 'fb'. - * include/grub/video.h (GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER): Removed. - (GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER): Likewise. - (grub_video_adapter): Added 'get_info_and_fini'. - (grub_video_get_info_and_fini): New prototype. - (grub_video_set_mode): make modestring const char *. - * loader/i386/linux.c (grub_linux_setup_video): Use - grub_video_get_info_and_fini. - (grub_linux_boot): Move modesetting just before booting. - * loader/i386/pc/xnu.c (grub_xnu_set_video): Use - grub_video_get_info_and_fini. - * video/i386/pc/vbe.c: Moved framebuffer part ... - * video/fb/video_fb.c: ... here. Replaced 'vbe' with 'fb'. - * video/i386/pc/vbe.c (grub_vbe_set_video_mode): Use - grub_video_fbstd_colors and grub_video_fb_set_palette. - (grub_video_vbe_init): Clear 'framebuffer' variable and use - grub_video_fb_init. - (grub_video_vbe_fini): Use grub_video_fb_fini. - (grub_video_vbe_setup): Use framebuffer.render_target instead of - render_target and use grub_video_fb_set_active_render_target and - grub_video_fb_set_palette. - (grub_video_vbe_set_palette): Use grub_video_fb_set_palette. - (grub_video_vbe_set_viewport): Use grub_video_fb_set_viewport. - (grub_video_vbe_adapter): Use framebuffer. - * video/video.c (grub_video_get_info_and_fini): New function. - (grub_video_set_mode): Make modestring const char *. - (GRUB_MOD_INIT(video_video)): Don't set variables to 0 since these - values are already initialised. - -2009-08-14 Pavel Roskin - - * boot/i386/pc/cdboot.S: Use LOCAL for local labels. Eliminate - ABS and APPLE_CC. - * boot/i386/pc/diskboot.S: Likewise. - * boot/i386/pc/lnxboot.S: Likewise. Hardcode the number of code - sectors allow compilation on MacOSX. - * conf/i386-pc.rmk: Enable unconditional compilation of - lnxboot.img. - -2009-08-13 Colin Watson - - * util/grub-mkconfig.in: Export GRUB_HIDDEN_TIMEOUT. - * util/grub.d/00_header.in: Enter interruptible sleep if - GRUB_HIDDEN_TIMEOUT is set. - -2009-08-13 Yves Blusseau - - * include/grub/symbol.h: Add the LOCAL macro. - * boot/i386/pc/boot.S: Use the LOCAL macro for all labels - starting with "L_". - -2009-08-13 Pavel Roskin - - * boot/i386/pc/boot.S: Remove ABS macro, it's not required by - any modern compilers we support. - - * boot/i386/pc/boot.S: Remove all code dependent on APPLE_CC. - Use local labels starting with "L_" so that Apple assembler - knows they are local. - -2009-08-10 Robert Millan - - * include/grub/i386/bsd.h (KERNEL_TYPE_NONE, KERNEL_TYPE_FREEBSD) - (KERNEL_TYPE_OPENBSD, KERNEL_TYPE_NETBSD): Convert to ... - (bsd_kernel_types): ... this enum. - - * loader/i386/bsd.c (grub_cmd_freebsd_loadenv, grub_cmd_freebsd_module) - (grub_cmd_freebsd_module_elf): Abort with "You need to load the - kernel first." when `kernel_type' is set to KERNEL_TYPE_NONE. - - (grub_bsd_load_aout, grub_bsd_load, grub_cmd_freebsd_loadenv) - (grub_cmd_freebsd_module, grub_cmd_freebsd_module_elf) - (GRUB_MOD_INIT (bsd)): Fix capitalization in a few error - messages. - -2009-08-08 Robert Millan - - * util/grub-dumpdevtree: Moved from here ... - * util/i386/efi/grub-dumpdevtree: ... to here. - (hexify): New function. Converts a string to its hex version. - Generate hex versions of "efi" and "device-properties" by calling - hexify() on the ASCII strings rather than by hardcoding numbers. - -2009-08-08 Robert Millan - - * fs/jfs.c: Update copyright year. - -2009-08-08 Felix Zielcke - - * util/grub.d/00_header.in: Fix a comment. - * util/grub.d/10_linux.in: Likewise. - * util/grub.d/10_windows.in: Likewise. - * util/grub.d/10_hurd.in: Likewise. - -2009-08-08 Felix Zielcke - - * util/grub-mkconfig.in: Allow the user to specify the used font - with GRUB_FONT. - -2009-08-08 Pavel Roskin - - * include/grub/powerpc/libgcc.h: Export __ashrdi3() if - available, xfs.mod needs it now. - - * util/grub-mkconfig_lib.in (version_test_numeric): Don't use - the "g" modifier in sed when the intention is to strip something - once. This fixes comparison of kernels with multiple dashes. - - * util/grub-mkconfig.in: Define datarootdir, datadir may depend - on it. Add missing space before closing bracket. Fix - misleading formatting. - -2009-08-07 Robert Millan - - * docs/grub.texi: Major overhaul. Remove all sections that are - specific to GRUB Legacy, or mostly composed of Legacy-specific - information. - -2009-08-07 Robert Millan - - * docs/version.texi: New file. Provides version information for - grub.texi. - -2009-08-07 Robert Millan - - * docs/grub.texi: Update CVS information to SVN. - Replace outdated "GRUB 2 will include" phrase with "GRUB 2 includes". - -2009-08-07 Felix Zielcke - - * util/grub-mkconfig.in: Remove a wrong `fi'. - -2009-08-07 Felix Zielcke - - * fs/uuid.c (grub_jfs_superblock): New fields unused2 and uuid. - (grub_jfs_uuid): New function. - (grub_jfs_fs): Set uuid field to grub_jfs_uuid. - -2009-08-07 Felix Zielcke - - * util/grub-mkconfig_lib.in (font_path): Move the functionality - of it to ... - * util/grub-mkconfig.in: ... here. Prefer unicode.pf2 and - unifont.pf2 over ascii.pf2. Export LANG=C in case ascii.pf2 gets used. - -2009-08-07 Robert Millan - - * util/grub.d/10_linux.in (test_numeric): Moved from here ... - * util/grub-mkconfig_lib.in (version_test_numeric): ... to here. - Update all users. - - * util/grub.d/10_linux.in (test_gt): Strip any basename prefix, - not just "vmlinu[zx]". - Moved from here ... - * util/grub-mkconfig_lib.in (version_test_gt): ... to here. Update - all users. - - * util/grub.d/10_linux.in (find_latest): Moved from here ... - * util/grub-mkconfig_lib.in (version_find_latest): ... to here. Update - all users. - -2009-08-07 Robert Millan - - * util/grub.d/10_freebsd.in: Use an absolute device path for - `vfs.root.mountfrom'. Set `vfs.root.mountfrom.options=rw'. - -2009-08-06 Felix Zielcke - - * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Fix - handling of multiple abstraction modules. - -2009-08-04 Robert Millan - - Fix a bug resulting in black screen when loading Linux using a - packed video mode. - - * kern/i386/pc/startup.S (grub_vbe_bios_getset_dac_palette_width): New - function. - - * include/grub/i386/pc/vbe.h (GRUB_VBE_CAPABILITY_DACWIDTH): New macro. - (grub_vbe_bios_getset_dac_palette_width): New function. - (grub_vbe_bios_get_dac_palette_width) - (grub_vbe_bios_set_dac_palette_width): New macros (act as wrappers for - grub_vbe_bios_getset_dac_palette_width()). - - * video/i386/pc/vbe.c (grub_vbe_probe): Use `GRUB_VBE_STATUS_OK' to - check for return status. - (grub_vbe_get_video_mode_info): When getting information for a packed - mode (<= 8 bpp), obtain DAC palette width using - grub_vbe_bios_getset_dac_palette_width(), and use that for initializing - {red,green,blue}_mark_size. - -2009-08-04 Felix Zielcke - - * commands/search.c (options): Fix help output to match actual code. - -2009-08-02 Vladimir Serbinenko - - * commands/hexdump.c (grub_cmd_hexdump): Use grub_disk_read instead - of homegrown code. - -2009-08-01 Vladimir Serbinenko - - * util/hostfs.c (grub_hostfs_dir): Don't use DT_DIR: It doesn't work - on XFS or ReiserFS. - -2009-08-01 Vladimir Serbinenko - - Support Apple partition map with sector size different from 512 bytes. - - * partmap/apple.c (grub_apple_header): New field 'blocksize'. - (apple_partition_map_iterate): Respect 'aheader.blocksize' - and 'apart.partmap_size'. - -2009-08-01 Vladimir Serbinenko -2009-08-01 Robert Millan - - Fix cpuid command. - - * commands/i386/cpuid.c (options): New variable. - (grub_cmd_cpuid): Return real error. - (GRUB_MOD_INIT(cpuid)): Declare options. - -2009-07-31 Vladimir Serbinenko - - * partmap/pc.c (pc_partition_map_iterate): Check that boot flags are - valid. - -2009-07-31 Bean - - * fs/xfs.c (grub_xfs_sblock): Change unused5 field to log2_sect and - log2_inode. - (grub_fshelp_node): Move inode field to the end. - (grub_xfs_data): Remove inode field. - (grub_xfs_inode_block): Calculate inode size using sblock. - (grub_xfs_inode_offset): Likewise. - (grub_xfs_read_inode): Calculate inode size using sblock. - (grub_xfs_read_block): Replace XFS_INODE_EXTENTS with nrec. - (grub_xfs_iterate_dir): Calculate inode size using sblock. - (grub_xfs_mount): Use grub_zalloc instead of grub_malloc. Realloc data - to match inode size. - (grub_xfs_dir): goto mount_fail when mount fails, as data->diropen is - not accessible when data is null. - (grub_xfs_open): Likewise. - -2009-07-31 Bean - - * disk/lvm.c (grub_lvm_scan_device): Ignore extra copy of metadata. - Don't change pv->disk if it's already set. - - * disk/raid.c (grub_raid_scan_device): Merge this function into ... - (grub_raid_register): ... here. - (grub_raid_rescan): Removed. - - * include/grub/raid.h (grub_raid_rescan): Removed. - - * util/grub-fstest.c: Remove include file . - (fstest): Replace grub_raid_rescan with module fini function followed - by init function. - - * util/grub-probe.c: Add include file . - (probe_raid_level): New function. - (probe): Detect abstraction by walking the disk device, support two - level of abstraction (LVM on RAID) when detecting partition map. - -2009-07-31 Pavel Roskin - - * disk/raid5_recover.c (grub_raid5_recover): Revert conversion - to grub_zalloc(), it was erroneous. - Reported by Bean - -2009-07-30 Vladimir Serbinenko - - * util/i386/pc/grub-setup.c (setup): Check that no partition is in - embedding zone, not only the first one. - -2009-07-29 Joe Auricchio - - * term/gfxterm.c (clear_char): New function. - (grub_virtual_screen_setup): Use clear_char. - (scroll_up): Likewise. - (grub_virtual_screen_cls): Likewise. - -2009-07-29 Felix Zielcke - - * util/deviceiter.c (get_acceleraid_disk_name): New static - function. - (grub_util_iterate_devices): Handle Accelraid devices. - * util/hostdisk.c (convert_system_partition_to_system_disk): Likewise. - -2009-07-28 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): Use ',' rather than ';' as - separator for the suggested gfxpayload string (';' collides with the - parser and needs escaping). - -2009-07-28 Vladimir Serbinenko - - * loader/i386/multiboot_helper.S (grub_multiboot_backward_relocator): - Clear direction flag before jumping to OS. - (grub_multiboot2_real_boot): Likewise. - -2009-07-28 Felix Zielcke - - * util/i386/pc/grub-install: Fix parsing of --disk-module - option. - -2009-07-28 Felix Zielcke - - * util/i386/pc/grub-setup.c (setup): Fix 2 incorrect checks - when embedding. - -2009-07-26 Felix Zielcke - - * util/grub-mkconfig.in (package_version): New variable. - Use it do display the version. - -2009-07-25 Felix Zielcke - - * kern/file.c (grub_file_open): Revert to previous check with - grub_errno. - -2009-07-25 Vladimir Serbinenko - - * commands/probe.c (GRUB_MOD_INIT (probe)): Remove "[--target=target]" - from help line. It's out of sync with code. - -2009-07-25 Vladimir Serbinenko - - * kern/parser.c (grub_parser_execute): Fix a bug causing truncated - entries on failed boot. - -2009-07-25 Felix Zielcke - - * kern/file.c (grub_file_open): Fix an error check. - -2009-07-24 Vladimir Serbinenko - - * util/i386/pc/grub-setup.c (setup): Fix segmentation fault when - partition map couldn't be identified. - -2009-07-23 Pavel Roskin - - * commands/xnu_uuid.c (transform): Use GRUB_CPU_WORDS_BIGENDIAN - instead of WORDS_BIGENDIAN. Use grub_le_to_cpu32(), so that the - case of little endian words becomes just an optimization. - Respect const modifier. - (md5_final): Use code that doesn't depend on endianness. - - * include/grub/misc.h (ALIGN_UP): Cast align to the type of addr - to avoid loss of upper bits if align is unsigned and shorter - than addr. - -2009-07-21 Vladimir Serbinenko - - UUID support for UFS - - * fs/ufs.c (grub_ufs_sblock): Add uuidhi and uuidlow. - (grub_ufs_uuid): New function. - (grub_ufs_fs): add .uuid - -2009-07-21 Pavel Roskin - - * kern/dl.c (grub_dl_check_header): Make static. - -2009-07-21 Felix Zielcke - - * util/grub.d/30_os-prober.in: Remove unused CHAINROOT. Don't - add drivemap for Vista. It breaks Windows 7. - -2009-07-21 Vladimir Serbinenko - - * fs/ufs.c (grub_ufs_sblock): Fix offset of mtime2 which was off by - 128 bytes - -2009-07-20 Vladimir Serbinenko - - Add BFS support - - * conf/common.rmk (grub_probe_SOURCES): Add fs/befs.c. - (grub_fstest_SOURCES): Likewise. - (pkglib_MODULES): Add befs.mod. - (befs_mod_SOURCES): New variable. - (befs_mod_CFLAGS): Likewise. - (befs_mod_LDFLAGS): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - (grub_setup_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (grub_setup_SOURCES): Likewise. - * fs/befs.c: New file. - * fs/afs.c (GRUB_AFS_FSNAME): New declaration. - (GRUB_AFS_SBLOCK_SECTOR): Likewise. - (GRUB_AFS_SBLOCK_MAGIC1) [MODE_BFS]: New conditional declaration. - (GRUB_AFS_BTREE_MAGIC) [MODE_BFS]: Likewise - (B_KEY_INDEX_ALIGN): New declaration. - (B_KEY_INDEX_OFFSET): Use B_KEY_INDEX_ALIGN. - (grub_afs_bnode) [MODE_BFS]: Make key_count and key_size 16-bit - (grub_afs_btree) [MODE_BFS]: New conditional declaration. - (grub_afs_sblock) [MODE_BFS]: Remove link_count. - (grub_afs_validate_sblock) [MODE_BFS]: Support BFS - (grub_afs_mount) [MODE_BFS]: Likewise. - (grub_afs_dir) [MODE_BFS]: Divide mtime by 65536 and not 1000000. - (grub_afs_fs): Use GRUB_AFS_FSNAME - (GRUB_MOD_INIT (afs)) [MODE_BFS]: Rename to ... - (GRUB_MOD_INIT (befs)) [MODE_BFS]: ... this - (GRUB_MOD_FINI (afs)) [MODE_BFS]: Rename to ... - (GRUB_MOD_FINI (befs)) [MODE_BFS]: ... this - -2009-07-19 Yves BLUSSEAU - - * util/getroot.c (find_root_device): Add support for MacOSX. - * util/hostdisk.c: Likewise. - -2009-07-20 Vladimir Serbinenko - - * font/font.c (find_glyph): Check whether a font is present to avoid - segmentation fault. - -2009-07-20 Joe Auricchio - - * term/gfxterm.c (grub_virtual_screen_setup): Clear virtual_screen. - -2009-07-20 Pavel Roskin - - * configure.ac: Trim excessively wordy excuses. - -2009-07-20 Vladimir Serbinenko - - Add symlink, mtime and label support to AtheFS. - - * fs/afs.c (grub_afs_sblock): Declare `name' as char. - (grub_afs_iterate_dir): Handle symlinks. - (grub_afs_open): Use grub_afs_read_symlink. - (grub_afs_dir): Likewise. - Pass mtime. - (grub_afs_label): New function. - (grub_afs_fs): Add grub_afs_label. - (grub_afs_read_symlink): New function. - -2009-07-20 Vladimir Serbinenko - - Fix AtheFS support. - - * fs/afs.c: Fix comments style. - (grub_afs_blockrun): Declare as packed. - (grub_afs_datastream): Likewise. - (grub_afs_bnode): Likewise. - (grub_afs_btree): Likewise. - (grub_afs_sblock): Likewise. - Declare `name' as char. - (grub_afs_inode): Declare as packed. - Change void *vnode to grub_uint32_t unused. - (grub_afs_iterate_dir): Check that key_size is positive. - (grub_afs_mount): Don't read superblock twice. - (grub_afs_dir): Don't free node in case of error, - grub_fshelp_find_file already handles this. - (grub_afs_open): Likewise. - -2009-07-19 Pavel Roskin - - * Makefile.in: Remove LIBLZO and enable_lzo. - * conf/i386-pc.rmk: Remove lzo support. - * configure.ac: Remove checks for lzo, don't define ENABLE_LZMA. - * include/grub/i386/pc/kernel.h: Define ENABLE_LZMA. Remove lzo - support. - * kern/i386/pc/lzo1x.S: Remove. - * kern/i386/pc/startup.S: Remove lzo support. - * util/i386/pc/grub-mkimage.c: Likewise. - -2009-07-19 Vladimir Serbinenko - - * disk/usbms.c (grub_usbms_transfer): Fix double semicolon. - * fs/xfs.c (grub_xfs_dir): Likewise. - * fs/afs.c (grub_afs_dir): Likewise. - * fs/iso9660.c (grub_iso9660_iterate_dir): Likewise. - (grub_iso9660_open): Likewise. - * fs/jfs.c (grub_jfs_open): Likewise. - * fs/ext2.c (grub_ext2_dir): Likewise. - * include/grub/macho.h (grub_macho_fat_arch): Likewise. - * script/sh/lexer.c (grub_script_yylex): Likewise. - -2009-07-16 Pavel Roskin - - * configure.ac: Never add "-c" to CFLAGS. - - * configure.ac: Fix incorrect comparison for grub_cv_cc_efiemu. - - * configure.ac: Fix wrong use of grub_cv_cc_no_red_zone where - grub_cv_cc_efiemu should be used. - - * configure.ac: Typo fixes. - - * kern/mm.c (grub_zalloc): New function. - (grub_debug_zalloc): Likewise. - * include/grub/mm.h: Declare grub_zalloc() and - grub_debug_zalloc(). - * util/misc.c (grub_zalloc): New function. - * bus/usb/uhci.c (grub_uhci_pci_iter): Use grub_zalloc() - instead of grub_malloc(), remove unneeded initializations. - * bus/usb/usbhub.c (grub_usb_hub_add_dev): Likewise. - * commands/extcmd.c (grub_extcmd_dispatcher): Likewise. - * commands/parttool.c (grub_cmd_parttool): Likewise. - * disk/i386/pc/biosdisk.c (grub_biosdisk_open): Likewise. - * disk/raid5_recover.c (grub_raid5_recover): Likewise. - * disk/raid6_recover.c (grub_raid6_recover): Likewise. - * disk/usbms.c (grub_usbms_finddevs): Likewise. - * efiemu/mm.c (grub_efiemu_request_memalign): Likewise. - * efiemu/pnvram.c (grub_efiemu_pnvram): Likewise. - (grub_cmd_efiemu_pnvram): Likewise. - * fs/i386/pc/pxe.c (grub_pxefs_open): Likewise. - * fs/iso9660.c (grub_iso9660_mount): Likewise. - (grub_iso9660_iterate_dir): Likewise. - * fs/jfs.c (grub_jfs_opendir): Likewise. - * fs/ntfs.c (list_file): Likewise. - (grub_ntfs_mount): Likewise. - * kern/disk.c (grub_disk_open): Likewise. - * kern/dl.c (grub_dl_load_core): Likewise. - * kern/elf.c (grub_elf_file): Likewise. - * kern/env.c (grub_env_context_open): Likewise. - (grub_env_set): Likewise. - (grub_env_set_data_slot): Likewise. - * kern/file.c (grub_file_open): Likewise. - * kern/fs.c (grub_fs_blocklist_open): Likewise. - * loader/i386/multiboot.c (grub_module): Likewise. - * loader/xnu.c (grub_xnu_create_key): Likewise. - (grub_xnu_create_value): Likewise. - * normal/main.c (grub_normal_add_menu_entry): Likewise. - (read_config_file): Likewise. - * normal/menu_entry.c (make_screen): Likewise. - * partmap/sun.c (sun_partition_map_iterate): Likewise. - * script/sh/lexer.c (grub_script_lexer_init): Likewise. - * script/sh/script.c (grub_script_parse): Likewise. - * video/bitmap.c (grub_video_bitmap_create): Likewise. - * video/readers/jpeg.c (grub_video_reader_jpeg): Likewise. - * video/readers/png.c (grub_png_output_byte): Likewise. - (grub_video_reader_png): Likewise. - -2009-07-16 Vladimir Serbinenko - - Enable all targets that can be built by default - - * configure.ac: enable efiemu runtime, grub-emu, grub-emu-usb, - grub-mkfont and grub-fstest if they can be built - -2009-07-16 Vladimir Serbinenko - - Fix hang and segmentation fault in grub-emu-usb - - * disk/scsi.c (grub_scsi_open): return err and not grub_errno - * util/usb.c (grub_libusb_devices): likewise - (grub_libusb_init): rename to ... - (GRUB_MOD_INIT (libusb)):...this - (grub_libusb_fini): rename to .. - (GRUB_MOD_FINI (libusb)):...this - * disk/usbms.c (grub_usbms_transfer): fix retry logic - * include/grub/disk.h (grub_raid_init): removed, it's useless - (grub_raid_fini): likewise - (grub_lvm_init): likewise - (grub_lvm_fini): likewise - * util/grub-emu.c (main): don't call grub_libusb_init, it's done - by grub_init_all - -2009-07-16 Vladimir Serbinenko - - Fix libusb - - * Makefile.in (LIBUSB): new macro - * genmk.rb (Utility/print_tail): new method - (Utility/rule): use intermediary variable #{prefix}_OBJECTS - (top level): call util.print_tail at the end. - -2009-07-16 Vladimir Serbinenko - - Make FreeBSD accept zpool.cache - - * loader/i386/bsd.c (grub_freebsd_add_meta_module): spoof filename if - type is /boot/zfs/zpool.cache - -2009-07-16 Vladimir Serbinenko - - Fix 64-bit efiemu - - * include/grub/efiemu/efiemu.h (grub_efiemu_configuration_table64_t): - correct wrong typedef - * efiemu/prepare.c (SUFFIX (grub_efiemu_prepare)): minor style fixes - -2009-07-15 Pavel Roskin - - * include/grub/disk.h (struct grub_disk_dev): Use enum for id. - * kern/disk.c (struct grub_disk_cache): Likewise. - - * commands/probe.c (options): Typo fix. - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_BPB_END): - Increase to 0x5a to accommodate FAT32. Adjust other offsets - accordingly. - Original patch by Yves Blusseau - - * boot/i386/pc/boot.S (general_error_string): Add DOS newline at - the end of "Error" to make the message more readable. - - * boot/i386/pc/boot.S (kernel_segment): Remove. - (copy_buffer): Use GRUB_BOOT_MACHINE_KERNEL_ADDR in segment 0 - for destination. - - * boot/i386/pc/boot.S (boot_version): Remove. - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_VER_MAJ): - Remove. - - * include/grub/i386/pc/boot.h: Sort all offsets. - (GRUB_BOOT_MACHINE_KERNEL_ADDRESS): Remove, it's unused. - (GRUB_BOOT_MACHINE_KERNEL_SEGMENT): Likewise. - * boot/i386/pc/boot.S: Assert location of every offset listed in - include/grub/i386/pc/boot.h. - -2009-07-13 Pavel Roskin - - * include/grub/i386/coreboot/machine.h: Rename - GRUB_MACHINE_LINUXBIOS to GRUB_MACHINE_COREBOOT. - * loader/multiboot_loader.c (grub_cmd_multiboot_loader): Allow - multiboot 1 for GRUB_MACHINE_COREBOOT and GRUB_MACHINE_QEMU. - - * kern/dl.c: Force native word size to suppress warnings when - compiling grub-emu. - - * kern/device.c (grub_device_iterate): Change struct part_ent to - hold the name, not a pointer to it. Use one grub_malloc() per - partition, not two. Free partition_name if grub_malloc() fails. - Set ents to NULL only before grub_partition_iterate() is called. - -2009-07-11 Bean - - * kern/ieee1275/openfw.c (grub_children_iterate): Fix size of - childname. - -2009-07-10 Bean -2009-07-10 Robert Millan - - * kern/ieee1275/openfw.c (grub_children_iterate) - (grub_devalias_iterate): Fix size evaluation for property or path - strings, which was broken since r2132. - -2009-07-07 Pavel Roskin - - * commands/search.c (search_file): Merge into ... - (search_fs): ... this. Accept search type as argument. - (grub_cmd_search): Pass search type to search_fs(). - - * include/grub/util/console.h: New file. - * util/console.c: Use it instead of grub/machine/console.h. - * util/grub-emu.c: Likewise. - - * lib/arg.c (find_long_option): Remove. - (find_long): Add `len' argument, make `s' const char *. - (grub_arg_parse): Parse long options in place, not in a - temporary buffer. - -2009-07-06 Pavel Roskin - - * commands/search.c (search_fs): Fix potential NULL pointer - dereference. - - * commands/search.c (search_fs): Replace QUID macro with quid_fn - function pointer. - -2009-07-06 Daniel Mierswa - - * commands/search.c (search_fs): Use grub_strcasecmp() for UUID - comparison. - -2009-07-05 Pavel Roskin - - * include/grub/i386/linux.h (struct linux_kernel_params): - Restore padding3, it's still needed. - - * util/grub.d/10_freebsd.in: Fix spelling of `device.hints' on - FreeBSD. - * util/osdetect.lua: Likewise. - -2009-07-05 Bean - - * conf/common.rmk (lua_mode_SOURCES): Add script/lua/lstrlib.c. - - * script/lua/grub_lib.c (grub_lua_run): Check input parameter. - (grub_lua_getenv): Likewise. - (grub_lua_setenv): Likewise. - (save_errno): New function. - (push_result): Likewise. - (grub_lua_enum_device): Likewise. - (grub_lua_enum_file): Likewise. - (grub_lua_file_open): Likewise. - (grub_lua_file_close): Likewise. - (grub_lua_file_seek): Likewise. - (grub_lua_file_read): Likewise. - (grub_lua_file_getline): Likewise. - (grub_lua_file_getsize): Likewise. - (grub_lua_file_getpos): Likewise. - (grub_lua_file_eof): Likewise. - (grub_lua_file_exist): Likewise. - (grub_lua_add_menu): Likewise. - - * script/lua/grub_lua.h (isupper): New inline function. - (islower): Likewise. - (ispunct): Likewise. - (isxdigit): Likewise. - (strcspn): Change to normal function. - (strpbkr): New function declaration. - (memchr): Likewise. - - * script/lua/grub_main.c (scan_str): New function. - (strcspn): Likewise. - (strpbrk): Likewise. - (memchr): Likewise. - - * script/lua/linit.c (lualibs): Enable the string library. - - * util/osdetect.lua: New file. - -2009-07-04 Robert Millan - - * include/grub/i386/linux.h (struct linux_kernel_params): Add - `capabilities' member. - -2009-07-02 Pavel Roskin - - * genparttoollist.sh: Add missing newline at the end. - -2009-07-01 Pavel Roskin - - * kern/x86_64/efi/callwrap.S: Add missing newline at the end. - - * util/hostdisk.c (open_device): Remove `const' from - `sysctl_size', as sysctlbyname() can change it (in this case it - doesn't actually happen). - - * include/grub/types.h: Define GRUB_LONG_MAX and GRUB_LONG_MIN - using signed long int constants. - - * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): Make `p' - constant to avoid a warning on FreeBSD. - - * util/hostdisk.c (device_is_wholedisk): Compile only on systems - where it's needed. - - * Makefile.in: Install include/grub/machine symlink. - - * Makefile.in: When installing symlinks, use "cp -fR", which - works on FreeBSD and MacOSX. - From Yves Blusseau - - * kern/dl.c (grub_dl_resolve_symbol): Make static. - * include/grub/dl.h: Remove grub_dl_resolve_symbol(). - - * util/misc.c: Move grub_reboot() and grub_halt() ... - * util/grub-emu.c: ... here. Make main_env static. - * include/grub/util/misc.h: Remove main_env. - - * kern/mm.c: Use correct format to print size_t. - - * include/grub/elf.h: Define Elf_Sword and Elf_Xword. - * kern/i386/dl.c: Use ELF symbols without "32" or "64". - * kern/powerpc/dl.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/x86_64/dl.c: Likewise. - -2009-07-01 Robert Millan - - Fix grub-emu build on sparc64-ieee1275. - - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Synchronize with ... - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): ... this. - -2009-07-01 Robert Millan - - * util/misc.c: Include `' and `'. - (grub_reboot, grub_halt): New functions. - - * util/i386/pc/misc.c: Delete. Update all users. - * util/sparc64/ieee1275/misc.c: Likewise. - * util/powerpc/ieee1275/misc.c: Likewise. - -2009-07-01 Robert Millan - - * conf/i386.rmk (setjmp_mod_SOURCES) - (setjmp_mod_ASFLAGS, setjmp_mod_LDFLAGS): Move to ... - * conf/common.rmk (setjmp_mod_SOURCES) - (setjmp_mod_ASFLAGS, setjmp_mod_LDFLAGS): ... here, and modify - to use $(target_cpu). - * conf/x86_64-efi.rmk (setjmp_mod_SOURCES) - (setjmp_mod_ASFLAGS, setjmp_mod_LDFLAGS): Remove. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - - * conf/i386-pc.rmk (kernel_img_SOURCES): Use - $(target_cpu) for kern/$(target_cpu)/dl.c. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/powerpc-ieee1275.rmk (kernel_img_SOURCES): Use - $(target_cpu) for kern/$(target_cpu)/dl.c and for - kern/$(target_cpu)/cache.S. - * conf/sparc64-ieee1275.rmk: Likewise. - -2009-07-01 Robert Millan - - * include/grub/i386/linux.h (linux_kernel_params): Change `mmap_size' - type to `grub_uint8_t', and adjust `padding9' accordingly. - -2009-06-29 Robert Millan - - * include/grub/i386/linux.h (GRUB_VIDEO_TYPE_TEXT): New macro. - - * loader/i386/linux.c [__i386__] (grub_linux_boot): Simplify inline - assembly in final jump, using register constraints. - - (grub_linux_boot): For text mode, initialize `have_vga' using - GRUB_VIDEO_TYPE_TEXT rather than 0 (this changes its value to 1). - - Initialize `video_cursor_x' and `video_cursor_y' as late as possible, - right before the final jump. - - Set `video_mode' to 0x3. - - Document initialization of `video_page', `video_mode' and - `video_ega_bx'. - -2009-06-29 Robert Millan - - * include/grub/i386/linux.h (GRUB_LINUX_FLAG_QUIET): New macro. - * loader/i386/linux.c (grub_cmd_linux): Recognize "quiet" option, - and set GRUB_LINUX_FLAG_QUIET appropriately. - -2009-06-29 Robert Millan - - Fix build on Debian / sparc. - - * configure.ac: Recognize `sparc' target_cpu (as sparc64). - -2009-06-28 Pavel Roskin - - * kern/i386/qemu/mmap.c (grub_machine_mmap_iterate): Use cast to - fix a warning. - - * util/grub.d/10_linux.in: Match SUSE style initrd names. - -2009-06-27 Robert Millan - - * loader/i386/linux.c (grub_linux_boot): Fix uninitialized use of - `err'. - -2009-06-27 Robert Millan - - Revert r2338. - - * loader/i386/linux.c (grub_cmd_linux): Don't call grub_error when - file can't be opened. grub_file_open() is already supposed to set - grub_errno / grub_errmsg appropriately. - * loader/i386/pc/linux.c (grub_cmd_linux): Likewise. - -2009-06-27 Pavel Roskin -2009-06-27 Robert Millan - - * include/grub/dl.h: Include grub/elf.h. - (struct grub_dl): Add symtab field. - * kern/dl.c [GRUB_MACHINE_QEMU]: Define - GRUB_MODULES_MACHINE_READONLY. - (grub_dl_resolve_symbols): Populate mod->symtab, making a copy - of the header for read-only modules. - (grub_dl_unload): Free mod->symtab for read-only modules. - * kern/i386/dl.c: Use mod->symtab. - * kern/powerpc/dl.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/x86_64/dl.c: Likewise. - - * conf/i386-qemu.rmk: New file. - * kern/i386/qemu/startup.S: Likewise. - * kern/i386/qemu/mmap.c: Likewise. - * boot/i386/qemu/boot.S: Likewise. - * include/grub/i386/qemu/time.h: Likewise. - * include/grub/i386/qemu/serial.h: Likewise. - * include/grub/i386/qemu/kernel.h: Likewise. - * include/grub/i386/qemu/console.h: Likewise. - * include/grub/i386/qemu/boot.h: Likewise. - * include/grub/i386/qemu/init.h: Likewise. - * include/grub/i386/qemu/machine.h: Likewise. - * include/grub/i386/qemu/loader.h: Likewise. - * include/grub/i386/qemu/memory.h: Likewise. - - * conf/i386-coreboot.rmk (GRUB_BOOT_MACHINE_LINK_ADDR) - (GRUB_KERNEL_MACHINE_LINK_ADDR): New variables. - [qemu] (pkglib_IMAGES): Add `boot.img'. - [qemu] (boot_img_SOURCES, boot_img_ASFLAGS, boot_img_LDFLAGS) - [qemu] (boot_img_FORMAT): New variables. - [qemu] (bin_UTILITIES): Add `grub-mkimage'. - [qemu] (grub_mkimage_SOURCES, grub_mkimage_CFLAGS): New variables. - [qemu] (kernel_img_SOURCES, kernel_img_HEADERS, kernel_img_CFLAGS) - [qemu] (kernel_img_ASFLAGS, kernel_img_LDFLAGS) - [qemu] (kernel_img_FORMAT): New variables. - - * configure.ac: Recognise `i386-qemu'. - - * util/i386/pc/grub-mkimage.c (compress_kernel): Add dummy variant - (for no compression). - [GRUB_MACHINE_QEMU] (generate_image): Misc adjustments to produce - a valid i386 ROM image. Make `GRUB_KERNEL_MACHINE_COMPRESSED_SIZE', - `GRUB_KERNEL_MACHINE_INSTALL_DOS_PART' and - `GRUB_KERNEL_MACHINE_INSTALL_BSD_PART' optional features (with - ifdefs). - -2009-06-27 Pavel Roskin - - * efiemu/prepare.c: Eliminate TYPE macro, it makes code hard to - read. - * efiemu/prepare32.c: Likewise. - * efiemu/prepare64.c: Likewise. - -2009-06-26 Pavel Roskin - - * include/grub/types.h: Define GRUB_TARGET_WORDSIZE. - * include/grub/elf.h: Define symbols without "32" or "64" based - on GRUB_TARGET_WORDSIZE. - * include/grub/multiboot2.h: Use GRUB_TARGET_WORDSIZE. - * efiemu/loadcore32.c: Redefine GRUB_TARGET_WORDSIZE, remove own - ELF definitions. - * efiemu/loadcore64.c: Likewise. - * loader/i386/bsd32.c: Likewise. - * loader/i386/bsd64.c: Likewise. - * kern/dl.c: Remove own ELF definitions. - * util/i386/efi/grub-mkimage.c: Likewise. - -2009-06-23 Robert Millan - - * kern/i386/pc/startup.S (real_to_prot): Access `gdtdesc' using - segment 0x0 unconditionally, because the reference generated by - GAS is an absolute address. - -2009-06-22 Robert Millan - - * include/grub/i386/kernel.h: Include `'. - [! GRUB_MACHINE_IEEE1275]: Set `GRUB_MOD_ALIGN' to 0x1. - -2009-06-22 Robert Millan - - * commands/search.c (grub_cmd_search): Macroify hardcoded args[] - indexes. Check for -f explicitly. - (search_file): Improve error message. - (GRUB_MOD_INIT(search)): Add missing `-n' to help output. - -2009-06-22 Robert Millan - - * conf/i386-pc.rmk (GRUB_MEMORY_MACHINE_LINK_ADDR): Rename to ... - (GRUB_KERNEL_MACHINE_LINK_ADDR): ... this. Update all users. - -2009-06-22 Robert Millan - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/i386/misc.S'. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-coreboot.rmk: Likewise. - - * kern/i386/pc/startup.S (grub_stop): Remove function. - * kern/i386/ieee1275/startup.S: Likewise. - * kern/i386/coreboot/startup.S: Likewise. - * kern/i386/misc.S (grub_stop): New function. - -2009-06-22 Robert Millan - - * kern/i386/pc/startup.S (real_to_prot): Move from here ... - * kern/i386/realmode.S (real_to_prot): ... to here. - -2009-06-22 Robert Millan - - * conf/i386-ieee1275.rmk (pkglib_PROGRAMS): Replace `kernel.elf' - with `kernel.img'. - (kernel_elf_SOURCES): Rename to ... - (kernel_img_SOURCES): ... this. - (kernel_elf_HEADERS): Rename to ... - (kernel_img_HEADERS): ... this. Update all users. - (kernel_elf_ASFLAGS): Rename to ... - (kernel_img_ASFLAGS): ... this. - (kernel_elf_CFLAGS): Rename to ... - (kernel_img_CFLAGS): ... this. - (kernel_elf_LDFLAGS): Rename to ... - (kernel_img_LDFLAGS): ... this. - * conf/i386-coreboot.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - - * util/elf/grub-mkimage.c (add_segments): Replace "kernel.elf" - with "kernel.img". - -2009-06-21 Pavel Roskin - - * loader/powerpc/ieee1275/linux.c (offset_phdr): Fix prototypes - to match nested functions. - * loader/sparc64/ieee1275/linux.c: Likewise. - - * conf/i386-ieee1275.rmk: Define kernel_elf_ASFLAGS. - -2009-06-21 Robert Millan - - * configure.ac: Enable `END_SYMBOL' / `BSS_START_SYMBOL' test on - all i386 platforms. - -2009-06-21 Robert Millan - - Fix asm file handling on ELF, and remove workarounds. - - * genmk.rb (class Programs): Detect assembly files, and set ASFLAGS - and -DASM_FILE=1 appropriately (copied from `class Images' stanza). - * kern/i386/ieee1275/startup.S (ASM_FILE): Remove macro. - * kern/i386/coreboot/startup.S (ASM_FILE): Likewise. - -2009-06-21 Vladimir Serbinenko - - Load BSD ELF modules - - * conf/i386-pc.rmk (bsd_mod_SOURCES): Add loader/i386/bsd32.c - and loader/i386/bsd64.c - * include/grub/i386/bsd.h (FREEBSD_MODTYPE_MODULE): Remove - (FREEBSD_MODTYPE_ELF_MODULE): New definition - (FREEBSD_MODTYPE_ELF_MODULE_OBJ): Likewise - (grub_freebsd_load_elfmodule32): New declaration - (grub_freebsd_load_elfmoduleobj64): Likewise - (grub_freebsd_load_elf_meta32): Likewise - (grub_freebsd_load_elf_meta64): Likewise - (grub_freebsd_add_meta): Likewise - (grub_freebsd_add_meta_module): Likewise - * loader/i386/bsd.c (grub_freebsd_add_meta): Make global - (grub_freebsd_add_meta_module): Likewise and move module-specific - parts to grub_cmd_freebsd and grub_cmd_freebsd_module - (grub_cmd_freebsd): Add elf-kernel specific parts - based on grub_freebsd_add_meta_module - (grub_cmd_freebsd_module): Add type parsing moved from - grub_freebsd_add_meta_module - (grub_cmd_freebsd_module_elf): New function - (cmd_freebsd_module_elf): New variable - (GRUB_MOD_INIT): Register freebsd_module_elf - * loader/i386/bsd32.c: New file - * loader/i386/bsd64.c: Likewise - * loader/i386/bsdXX.c: Likewise - * kern/elf.c (grub_elf32_load): Let hook decide which pheaders to load - (grub_elf64_load): Likewise - * include/grub/elfload.h (grub_elf32_load_hook_t): New parameter do_load - All users updated - (grub_elf64_load_hook_t): Likewise - -2009-06-21 Colin Watson - - * util/grub-mkconfig.in (GRUB_DISABLE_LINUX_RECOVERY): Export - variable. - * util/grub.d/10_linux.in: If GRUB_DISABLE_LINUX_RECOVERY is true, - don't write a menu entry for recovery mode. - -2009-06-20 Robert Millan - - * util/i386/pc/grub-mkimage.c (main): Oops, free `output' only - after it's no longer needed. - -2009-06-20 Robert Millan - - * include/grub/i386/loader.h (grub_linux_prot_size) - (grub_linux_tmp_addr, grub_linux_real_addr) - (grub_linux_is_bzimage, grub_linux16_boot): Declare only on - GRUB_MACHINE_PCBIOS. - * util/i386/pc/grub-mkimage.c (compress_kernel): Move - common grub_util_info() call to ... - (generate_image): ... here. - Fix use of uninitialized memory, comparison of signed with - unsigned integers and memory leak. - Remove bogus module address message. - -2009-06-20 Vladimir Serbinenko - - * disk/mdraid_linux.c (GRUB_MOD_FINI): use grub_raid_unregister and not - grub_raid_register - * disk/dmraid_nvidia.c (GRUB_MOD_FINI): likewise - -2009-06-19 Pavel Roskin - - * configure.ac: Remove stray AC_MSG_CHECKING. - -2009-06-19 Vladimir Serbinenko - - * disk/scsi.c (grub_scsi_open): use continue instead of big if - -2009-06-18 Pavel Roskin - - * conf/common.rmk: Add fs_file.mod. - * disk/fs_file.c: New file. - * include/grub/disk.h (enum grub_disk_dev_id): Add - GRUB_DISK_DEVICE_FILE_ID. - -2009-06-18 Vladimir Serbinenko - - Fix build with Apple's toolchain. Part 2 - - * aclocal.m4 (grub_PROG_TARGET_CC): add missing prototype for main and - a fake start - -2009-06-18 Vladimir Serbinenko - - Fix build with Apple's toolchain. Part 1 - - * commands/i386/pc/drivemap_int13h.S: use assembly-time constants - for long calls - * configure.ac: remove a leftover AC_MSG_RESULT - (CFLAGS): don't add -Wl,--defsym,___main=0x8100 when building with - Apple's toolchain - -2009-06-18 Vladimir Serbinenko - - Fix warnings - - * fs/ntfscomp.c (decomp_get16): initialize c1 and c2 - (decomp_block): initialize ch - use grub_memcpy instead of memcpy - -2009-06-17 Pavel Roskin - - * include/grub/i386/coreboot/console.h: Don't use the i386-pc - version, use declarations needed to use vga_text as the startup - console. - - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Remove - term/i386/pc/at_keyboard.c, it doesn't need to be compiled into - the kernel. - * kern/i386/coreboot/init.c: Don't call grub_at_keyboard_init() - and grub_at_keyboard_fini(), it's done on module load and - unload. - -2009-06-17 Felix Zielcke - - * loader/i386/linux.c (grub_cmd_linux): Set grub_error if the - file can't be found. - * loader/i386/pc/linux.c (grub_cmd_linux): Likewise. - -2009-06-17 Vladimir Serbinenko - - Fix newline handling - - * include/grub/script_sh.h (grub_lexer_param): new field was_newline - * script/sh/lexer.c (grub_script_lexer_init): initialize was_newline - (grub_script_yylex): don't segfault on unterminated script - newline terminates command and variable - -2009-06-17 Vladimir Serbinenko - - avoid double grub_adjust_range call. Bug reported by David Simner - - * kern/disk.c (grub_disk_write): change to raw disk access before - calling disk_read - -2009-06-17 Colin Watson - - * util/elf/grub-mkimage.c (usage): Prefix each option line with two - spaces, for the benefit of help2man. - * util/i386/efi/grub-mkimage.c (usage): Likewise. - -2009-06-16 Pavel Roskin - - * kern/i386/halt.c: Include grub/machine/init.h. - * kern/i386/reboot.c: Include grub/cpu/reboot.h. - -2009-06-16 Felix Zielcke - - * util/grub.d/30_os-prober.in: Use ${root} in the generated - drivemap menuentry. - -2009-06-16 James Jarvis - - * commands/help.c GRUB_MOD_INIT(echo): Fix the help output of - `echo' command. - -2009-06-16 Pavel Roskin - - * boot/i386/pc/boot.S: Remove root_drive. Assert offset of - boot_drive_check by using GRUB_BOOT_MACHINE_DRIVE_CHECK. Don't - save %dx, we only need %dl and we never change it. - * boot/i386/pc/cdboot.S: Don't set the root drive. - * boot/i386/pc/pxeboot.S: Likewise. - * include/grub/i386/pc/boot.h: Remove - GRUB_BOOT_MACHINE_ROOT_DRIVE, adjust - GRUB_BOOT_MACHINE_DRIVE_CHECK. - * include/grub/i386/pc/kernel.h: Remove grub_root_drive. - * kern/i386/pc/init.c (make_install_device): Remove references - to grub_root_drive. - * kern/i386/pc/startup.S: Likewise. - * util/i386/pc/grub-setup.c (setup): Don't set root_drive. - -2009-06-16 Vladimir Serbinenko - - xnu_uuid command - - * commands/xnu_uuid.c: new file - * conf/common.rmk (pkglib_MODULES): add xnu_uuid.mod - (xnu_uuid_mod_SOURCES): new variable - (xnu_uuid_mod_CFLAGS): likewise - (xnu_uuid_mod_LDFLAGS): likewise - * conf/i386-coreboot.rmk (grub_emu_SOURCES): add commands/probe.c - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - * conf/sparc64-ieee1275.rmk: likewise - * util/grub.d/30_os-prober.in: use UUID for Mac OS X/Darwin - -2009-06-16 Pavel Roskin - - * configure.ac: Avoid '==' in test command, it's not portable. - -2009-06-16 Vladimir Serbinenko - - Probe command - - * commands/probe.c: new file - * conf/common.rmk (pkglib_MODULES): add probe.mod - (probe_mod_SOURCES): new variable - (probe_mod_CFLAGS): likewise - (probe_mod_LDFLAGS): likewise - * conf/i386-coreboot.rmk (grub_emu_SOURCES): add commands/probe.c - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - * conf/sparc64-ieee1275.rmk: likewise - -2009-06-15 Vladimir Serbinenko - - Fix handling of string like \"hello\" and "a - b" - - * script/sh/lexer.c (check_textstate): accept GRUB_PARSER_STATE_ESC - (grub_script_yylex): fix parsing of quoting, escaping and newline - -2009-06-13 Vladimir Serbinenko - - * loader/i386/multiboot.c (grub_multiboot_get_bootdev): fix partition - handling - -2009-06-13 Jun Inoue - - * util/grub-mkconfig.in: Fix parsing of --output option. - -2009-06-12 Pavel Roskin - - * Makefile.in (pkgdata_SRCDIR): Remove. genmodsrc.sh and - genmk.rb don't need to be generated or installed. - -2009-06-12 Vladimir Serbinenko - - * commands/i386/pc/drivemap_int13h.S: add more comments - -2009-06-11 Pavel Roskin - - * Makefile.in (uninstall): Uninstall manuals. - - * Makefile.in: Rename lib_DATA to lib_SCRIPTS, move it from - PKGLIB to SCRIPTS. This fixes installation of grub-mkconfig_lib - and update-grub_lib in two places. - * conf/common.rmk: Rename lib_DATA to lib_SCRIPTS. - - * disk/usbms.c (grub_usbms_transfer): Initialize `err' to fix - a compiler warning. - - * loader/i386/bsd.c (grub_freebsd_boot): Rename `entry' to - `entry_lo' to fix variable shadowing. - -2009-06-11 Christian Franke - - * kern/misc.c (__enable_execute_stack): Add missing return type - to prevent gcc warning. - -2009-06-11 Felix Zielcke - - * conf/i386-ieee1275.rmk (COMMON_LDFLAGS): Remove `-static -lgcc'. - -2009-06-11 Pavel Roskin - - * Makefile.in: Don't rely on any scripts being executable. - Always use $(SHELL) to run shell scripts. - - * configure.ac: Always define ___main if using -nostdlib. This - fixes tests on Cygwin. - -2009-06-11 Giuseppe Caizzone - - UDF fix - - * fs/udf.c (grub_udf_read_block): handle the fact that ad->length - is in bytes and not in blocks - -2009-06-11 Pavel Roskin - - * kern/i386/halt.c (grub_halt): Make `i' unsigned to fix a - warning. - -2009-06-11 Felix Zielcke - - * util/grub.d/30_os-prober.in: Fix a comment. Source - ${libdir}/grub/grub-mkconfig_lib. Use prepare_grub_to_access_device - to set the root device. Place drivemap command in the generated - chain entry. - -2009-06-11 Pavel Roskin - - * configure.ac: Remove host_m32. Issues with 64-bit utilities - have long been resolved. - -2009-06-11 Colin Watson - - * util/grub.d/10_linux.in: Capitalise "Linux". - - * util/grub-pe2elf.c (usage): Fix references to grub-editenv. - -2009-06-11 Pavel Roskin - - * kern/efi/efi.c (grub_exit): Add infinite loop at the end to - fix a gcc warning and ensure that the function won't ever exit. - - * kern/i386/ieee1275/init.c: Add missing prototype for - grub_stop_floppy(). - - * loader/ieee1275/multiboot2.c [__i386__]: Include - grub/cpu/multiboot.h. - - * term/i386/pc/serial.c (serial_translate_key_sequence): Avoid - casts to short - they are not portable and cause warnings. Fix - use of uninitialized values in input_buf. Use ARRAY_SIZE. - -2009-06-11 Vladimir Serbinenko - - Drivemap fixes - - * commands/i386/pc/drivemap.c (grub_get_root_biosnumber_drivemap): - new function - (grub_get_root_biosnumber_saved): new variable - (GRUB_MOD_INIT): register grub_get_root_biosnumber_drivemap - (GRUB_MOD_FINI): unregister grub_get_root_biosnumber_drivemap - * commands/i386/pc/drivemap_int13h.S (grub_drivemap_handler): restore - %dx after the call if necessary - * conf/common.rmk (pkglib_MODULES): remove boot.mod - (boot_mod_SOURCES): remove - (boot_mod_CFLAGS): remove - (boot_mod_LDFLAGS): remove - * conf/i386-coreboot.rmk (pkglib_MODULES): add boot.mod - (boot_mod_SOURCES): new variable - (boot_mod_CFLAGS): likewise - (boot_mod_LDFLAGS): likewise - * conf/i386-efi.rmk: likewise - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - * conf/sparc64-ieee1275.rmk: likewise - * conf/x86_64-efi.rmk: likewise - * include/grub/i386/pc/biosnum.h: new file - * lib/i386/pc/biosnum.c: likewise - * loader/i386/bsd.c (grub_bsd_get_device): use grub_get_root_biosnumber - * loader/i386/multiboot.c (grub_multiboot_get_bootdev): likewise - * loader/i386/pc/chainloader.c (grub_chainloader_cmd): likewise - -2009-06-10 Pavel Roskin - - * io/gzio.c (test_header): Don't reuse one buffer for all data. - Use separate variables. Read only the file size at the end, but - not the checksum that we don't use. - - * kern/file.c (grub_file_read): Use void pointer for the buffer. - Adjust all callers. - - * kern/ieee1275/openfw.c: Remove libc includes. - * kern/ieee1275/cmain.c: Likewise. - * include/grub/ieee1275/ieee1275.h: Likewise. - - * kern/i386/coreboot/init.c: Include grub/cpu/tsc.h to fix - compiler warnings. - -2009-06-10 Felix Zielcke - - * Makefile.in: Remove all trailing whitespace. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * docs/grub.texi: Likewise. - * docs/texinfo.tex: Likewise. - * disk/fs_uuid.c: Likewise. - * disk/lvm.c: Likewise. - * disk/scsi.c: Likewise. - * disk/ata.c: Likewise. - * disk/ieee1275/ofdisk.c: Likewise. - * disk/i386/pc/biosdisk.c: Likewise. - * disk/host.c: Likewise. - * disk/raid.c: Likewise. - * disk/efi/efidisk.c: Likewise. - * disk/usbms.c: Likewise. - * disk/memdisk.c: Likewise. - * disk/loopback.c: Likewise. - * kern/powerpc/dl.c: Likewise. - * kern/device.c: Likewise. - * kern/dl.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/ieee1275/ieee1275.c: Likewise. - * kern/term.c: Likewise. - * kern/fs.c: Likewise. - * kern/i386/dl.c: Likewise. - * kern/i386/pc/startup.S: Likewise. - * kern/i386/pc/init.c: Likewise. - * kern/i386/pc/mmap.c: Likewise. - * kern/i386/pc/lzo1x.S: Likewise. - * kern/i386/ieee1275/init.c: Likewise. - * kern/i386/realmode.S: Likewise. - * kern/i386/tsc.c: Likewise. - * kern/partition.c: Likewise. - * kern/corecmd.c: Likewise. - * kern/file.c: Likewise. - * kern/efi/efi.c: Likewise. - * kern/efi/init.c: Likewise. - * kern/efi/mm.c: Likewise. - * kern/main.c: Likewise. - * kern/err.c: Likewise. - * kern/env.c: Likewise. - * kern/disk.c: Likewise. - * kern/generic/millisleep.c: Likewise. - * kern/generic/rtc_get_time_ms.c: Likewise. - * kern/misc.c: Likewise. - * kern/parser.c: Likewise. - * genmk.rb: Likewise. - * configure.ac: Likewise. - * boot/i386/pc/diskboot.S: Likewise. - * boot/i386/pc/pxeboot.S: Likewise. - * boot/i386/pc/boot.S: Likewise. - * boot/i386/pc/lnxboot.S: Likewise. - * boot/i386/pc/cdboot.S: Likewise. - * parttool/pcpart.c: Likewise. - * video/readers/tga.c: Likewise. - * video/video.c: Likewise. - * video/bitmap.c: Likewise. - * lib/envblk.c: Likewise. - * lib/i386/setjmp.S: Likewise. - * fs/xfs.c: Likewise. - * fs/afs.c: Likewise. - * fs/fat.c: Likewise. - * fs/ntfs.c: Likewise. - * fs/udf.c: Likewise. - * fs/affs.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/hfs.c: Likewise. - * fs/fshelp.c: Likewise. - * fs/ext2.c: Likewise. - * fs/jfs.c: Likewise. - * fs/reiserfs.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/minix.c: Likewise. - * fs/cpio.c: Likewise. - * fs/sfs.c: Likewise. - * fs/ufs.c: Likewise. - * efiemu/prepare.c: Likewise. - * efiemu/loadcore_common.c: Likewise. - * efiemu/runtime/efiemu.sh: Likewise. - * efiemu/runtime/efiemu.S: Likewise. - * efiemu/runtime/efiemu.c: Likewise. - * efiemu/pnvram.c: Likewise. - * efiemu/main.c: Likewise. - * efiemu/i386/pc/cfgtables.c: Likewise. - * efiemu/i386/loadcore64.c: Likewise. - * efiemu/i386/loadcore32.c: Likewise. - * efiemu/loadcore.c: Likewise. - * efiemu/symbols.c: Likewise. - * efiemu/mm.c: Likewise. - * include/grub/autoefi.h: Likewise. - * include/grub/datetime.h: Likewise. - * include/grub/term.h: Likewise. - * include/grub/hfs.h: Likewise. - * include/grub/lvm.h: Likewise. - * include/grub/i386/tsc.h: Likewise. - * include/grub/i386/linux.h: Likewise. - * include/grub/i386/xnu.h: Likewise. - * include/grub/i386/efiemu.h: Likewise. - * include/grub/i386/pc/biosdisk.h: Likewise. - * include/grub/i386/pc/memory.h: Likewise. - * include/grub/i386/pc/vbe.h: Likewise. - * include/grub/parttool.h: Likewise. - * include/grub/video.h: Likewise. - * include/grub/memory.h: Likewise. - * include/grub/fs.h: Likewise. - * include/grub/partition.h: Likewise. - * include/grub/xnu.h: Likewise. - * include/grub/efi/api.h: Likewise. - * include/grub/efi/pe32.h: Likewise. - * include/grub/efi/memory.h: Likewise. - * include/grub/multiboot.h: Likewise. - * include/grub/usbdesc.h: Likewise. - * include/grub/multiboot2.h: Likewise. - * include/grub/acpi.h: Likewise. - * include/grub/efiemu/efiemu.h: Likewise. - * include/grub/disk.h: Likewise. - * include/grub/ieee1275/ieee1275.h: Likewise. - * include/grub/net.h: Likewise. - * include/grub/machoload.h: Likewise. - * include/grub/macho.h: Likewise. - * include/multiboot.h: Likewise. - * genmoddep.awk: Likewise. - * normal/main.c: Likewise. - * normal/menu_entry.c: Likewise. - * normal/menu_viewer.c: Likewise. - * normal/completion.c: Likewise. - * normal/cmdline.c: Likewise. - * normal/misc.c: Likewise. - * normal/datetime.c: Likewise. - * bus/usb/usbtrans.c: Likewise. - * bus/usb/ohci.c: Likewise. - * bus/usb/uhci.c: Likewise. - * bus/usb/usb.c: Likewise. - * mmap/efi/mmap.c: Likewise. - * mmap/i386/pc/mmap_helper.S: Likewise. - * mmap/i386/pc/mmap.c: Likewise. - * mmap/i386/mmap.c: Likewise. - * mmap/i386/uppermem.c: Likewise. - * mmap/mmap.c: Likewise. - * commands/acpi.c: Likewise. - * commands/echo.c: Likewise. - * commands/blocklist.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/usbtest.c: Likewise. - * commands/boot.c: Likewise. - * commands/parttool.c: Likewise. - * commands/search.c: Likewise. - * commands/cat.c: Likewise. - * commands/i386/pc/play.c: Likewise. - * commands/i386/pc/drivemap.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/i386/pc/acpi.c: Likewise. - * commands/i386/pc/vbetest.c: Likewise. - * commands/ls.c: Likewise. - * commands/cmp.c: Likewise. - * commands/test.c: Likewise. - * commands/efi/acpi.c: Likewise. - * commands/gptsync.c: Likewise. - * commands/help.c: Likewise. - * partmap/amiga.c: Likewise. - * partmap/apple.c: Likewise. - * partmap/acorn.c: Likewise. - * partmap/pc.c: Likewise. - * partmap/sun.c: Likewise. - * partmap/gpt.c: Likewise. - * script/sh/lexer.c: Likewise. - * script/sh/function.c: Likewise. - * font/font.c: Likewise. - * font/font_cmd.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * loader/efi/chainloader.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * loader/macho.c: Likewise. - * loader/i386/multiboot.c: Likewise. - * loader/i386/linux.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/i386/pc/multiboot2.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/pc/xnu.c: Likewise. - * loader/i386/bsd_trampoline.S: Likewise. - * loader/i386/efi/linux.c: Likewise. - * loader/i386/multiboot_elfxx.c: Likewise. - * loader/i386/bsd_helper.S: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/i386/linux_trampoline.S: Likewise. - * loader/i386/xnu_helper.S: Likewise. - * loader/i386/xnu.c: Likewise. - * loader/i386/bsd_pagetable.c: Likewise. - * loader/i386/multiboot_helper.S: Likewise. - * loader/xnu.c: Likewise. - * loader/xnu_resume.c: Likewise. - * io/gzio.c: Likewise. - * term/efi/console.c: Likewise. - * term/terminfo.c: Likewise. - * term/ieee1275/ofconsole.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * term/i386/pc/vga.c: Likewise. - * term/usb_keyboard.c: Likewise. - * term/gfxterm.c: Likewise. - * aclocal.m4: Likewise. - * util/lvm.c: Likewise. - * util/grub.d/30_os-prober.in: Likewise. - * util/grub.d/10_hurd.in: Likewise. - * util/console.c: Likewise. - * util/grub-macho2img.c: Likewise. - * util/grub-probe.c: Likewise. - * util/hostfs.c: Likewise. - * util/i386/pc/grub-mkimage.c: Likewise. - * util/i386/pc/grub-setup.c: Likewise. - * util/i386/efi/grub-mkimage.c: Likewise. - * util/grub-mkconfig.in: Likewise. - * util/raid.c: Likewise. - * util/resolve.c: Likewise. - * util/grub-mkdevicemap.c: Likewise. - * util/grub-emu.c: Likewise. - * util/getroot.c: Likewise. - * util/hostdisk.c: Likewise. - * util/usb.c: Likewise. - * util/grub-editenv.c: Likewise. - * util/misc.c: Likewise. - -2009-06-10 Felix Zielcke - - * gendistlist.sh (EXTRA_DISTFILES): Add `genhandlerlist.sh' and - `genparttoollist.sh'. - (DISTDIRS): Add `efiemu', `mmap', `parttool' and `script'. - Add `*.sh' to the list find searches for and change `mdate.sh' - to `mdate-sh'. - -2009-06-10 Pavel Roskin - - * include/grub/multiboot2.h: Provide compatibility defines for - multiboot2.h. - * include/multiboot2.h: Include stdint.h only if needed, using - angle brackets. - * loader/i386/pc/multiboot2.c: Include multiboot2.h after - grub/multiboot2.h. - * loader/ieee1275/multiboot2.c: Likewise. - * loader/multiboot2.c: Likewise. - * loader/multiboot_loader.c: Likewise. - - * configure.ac: Use -nostdlib when probing for the target. It - should not be required to have libc for the target. - - * configure.ac: Remove checks for __bswapsi2 and __bswapdi2, - they fail without libc headers for the target. - * include/grub/powerpc/libgcc.h: Use weak attribute for all - exports. - * include/grub/sparc64/libgcc.h: Likewise. Don't use - preprocessor conditionals. - - * conf/common.rmk: Compile tar.mod from tar.c, not cpio.c. The - build system doesn't need to be aware of the tar.c internals. - -2009-06-09 Michel Hermier - - * fs/i386/pc/pxe.c (grub_pxefs_read): Fix returned values. - -2009-06-09 Robert Millan - - * util/deviceiter.c (grub_util_iterate_devices): Increase number of - disk limit to 26 for IDE, Virtio, Xen and SCSI. - -2009-06-09 Felix Zielcke - - * util/i386/pc/grub-install.in: Change the error message if UUIDs - aren't available if ata.mod gets used. - -2009-06-09 Oliver Henshaw - - * bus/usb/ohci.c (grub_ohci_pci_iter): Link struct only after - initialising controller. - * bus/usb/uhci.c (grub_uhci_pci_iter): Likewise. - -2009-06-08 Felix Zielcke - - * util/i386/pc/grub-install.in: Add a parameter --disk-module - to choose between ata and biosdisk module on i386-pc. - -2009-06-08 Oliver Henshaw - - * bus/usb/ohci.c (grub_ohci_pci_iter): Define the Class, - Subclass and Programming Interface fields in terms of the 3 byte - Class Code register. - * bus/usb/uhci.c (grub_uhci_pci_iter): Likewise. - - * bus/usb/ohci.c (grub_ohci_pci_iter): Check that programming - interface is OHCI. Add grub_dprintf for symmetry with - bus/usb/uhci.c. - * bus/usb/uhci.c (grub_uhci_pci_iter): Check that programming - interface is UHCI. Add interf variable for programming - interface. Print interface with class/subclass. - - * bus/usb/ohci.c: Set interf with correct field. - - * bus/usb/uhci.c: Remove unneeded doubled lines. - * bus/usb/ohci.c: Likewise. Change interf to grub_uint32_t. - Remove whitespace inside comment. - -2009-06-08 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): When processing `vga=', use - as fallback an equivalent option without depth. - -2009-06-08 Vladimir Serbinenko - - Not fail if unable to retrieve C/H/S on LBA disks - - * disk/i386/pc/biosdisk.c (grub_biosdisk_open): behave gracefully - if unable to retrieve C/H/S on LBA disks - -2009-06-08 Pavel Roskin - - * fs/hfs.c (grub_hfs_find_dir): Use union to avoid a warning - about aliasing. - -2009-06-08 Felix Zielcke - - * Makefile.in (uninstall): Remove all $lib_DATA files. - -2009-06-08 Vladimir Serbinenko - - Bugfix: install on partitionless device - - * util/hostdisk.c (grub_util_biosdisk_get_grub_dev): check if os_dev - is a whole disk - -2009-06-08 Felix Zielcke - - * Makefile.in (uninstall): Remove all $include_DATA files. - -2009-06-08 Felix Zielcke - - * commands/true.c: New file. Implement the true and false commands. - * conf/common.rmk.c (pkglib_MODULES): Add `true.mod'. - (true_mod_SOURCES): New variable. - (true_mod_CFLAGS): Likewise. - (true_mod_LDFLAGS): Likewise. - -2009-06-05 Colin D Bennett - - Optimized font character lookup using binary search instead of linear - search. Fonts now are required to have the character index ordered by - code point. - - * font/font.c (load_font_index): Verify that fonts have ordered - character indices. - (find_glyph): Use binary search instead of linear search to find a - character in a font. - -2009-06-05 Michael Scherer - - * fs/hfsplus.c (grub_hfsplus_mount): Determine if the filesystem - uses case sensitive btree. - (grub_hfsplus_iterate_dir): Use GRUB_FSHELP_CASE_INSENSITIVE - only for case insensitive filesystems. - -2009-06-05 Vladimir Serbinenko - - * conf/i386-pc.rmk (efiemu_mod_CFLAGS): remove -Werror -Wall - * conf/common.rmk (search_mod_CFLAGS): likewise - -2009-06-04 Vladimir Serbinenko - - * kern/i386/pc/startup.S [APPLE_CC]: block of nops to - compensate a compiler bug - -2009-06-04 Vladimir Serbinenko - - * include/grub/term.h (GRUB_TERM_BACKSPACE): explicitly define as 8 - instead of '\b' - -2009-06-04 Vladimir Serbinenko - - Definitions for creating asm symbols with Apple's CC - - * include/grub/symbol.h [APPLE_CC] (FUNCTION): new macro - [APPLE_CC] (VARIABLE): likewise - -2009-06-04 Vladimir Serbinenko - - Disable lnxboot.img when compiled - with Apple's CC - - * conf/i386-pc.rmk (pkglib_IMAGES): remove lnxboot.img - pkglib_IMAGES [! TARGET_APPLE_CC] (pkglib_IMAGES): add lnxboot.img - * boot/i386/pc/lnxboot.S [APPLE_CC]: define an #error - [! APPLE_CC] (CODE_LENG): skip - [! APPLE_CC] (setup_sects): likewise - [! APPLE_CC]: skip filling - -2009-06-04 Vladimir Serbinenko - - Address in trampolines based on 32-bit registers when compiled - with Apple's CC - - * loader/i386/xnu_helper.S [APPLE_CC]: use 32-bit registers - for addresses - * loader/i386/linux_trampoline.S [APPLE_CC]: likewise - -2009-06-04 Vladimir Serbinenko - - Avoid aliases when compiling with Apple's CC for PCBIOS machine - - * kern/misc.c [APPLE_CC] (memcpy): new function - [APPLE_CC] (memmove): likewise - [APPLE_CC && !GRUB_UTIL] (grub_err_printf): likewise - (memcpy): define alias conditionally on !APPLE_CC - (memset): likewise - (abort): likewise - * include/grub/misc.h (memove): don't define when both GRUB_UTIL and - APPLE_CC are defined - * include/grub/list.h [APPLE_CC] (grub_assert_fail): new function - (grub_assert_fail): make prototype conditional - -2009-06-04 Vladimir Serbinenko - - Use grub-macho2img when compiling with Apple's CC for PCBIOS machine - - * conf/common.rmk (bin_UTILITIES): add (on false on condition) - grub-macho2img - (CLEANFILES): add grub-macho2img - (grub_macho2img_SOURCES): new variable - * kern/i386/pc/startup.S (bss_start): new variable - (bss_end): likewise - * genmk.rb: use grub-macho2img for *.img when compiled with Apple's CC - * util/grub-macho2img.c: new file - -2009-06-04 Vladimir Serbinenko - - Use objconv when compiling with Apple's CC - - * conf/i386-pc.rmk (efiemu32.o): use OBJCONV if defined - (efiemu64.o): likewise - (efiemu64_c.o): omit -mcmodel=large and add -DAPPLE_CC=1 - when compiling with Apple's CC - (efiemu64_s.o): likewise - * configure.ac: check for objconv when compiling with Apple's CC - * genmk.rb: use objconv for modules when compiled with Apple's CC - -2009-06-04 Vladimir Serbinenko - - Define segment as well as section when compiling with - Apple's CC - - * efiemu/runtime/efiemu.c (PHYSICAL_ATTRIBUTE): new definition - (efiemu_set_virtual_address_map): declare with PHYSICAL_ATTRIBUTE - (efiemu_convert_pointer): likewise - (efiemu_set_virtual_address_map): likewise - (efiemu_convert_pointer): likewise - (efiemu_getcrc32): likewise - (init_crc32_table): likewise - (reflect): likewise - * include/grub/dl.h (GRUB_MOD_NAME): define segment with Apple's CC - (GRUB_MOD_DEP): likewise - -2009-06-04 Vladimir Serbinenko - - Allow a compilation without -mcmodel=large - - * kern/efi/mm.c (grub_efi_allocate_pages): don't allocate >4GiB - when compiled without -mcmodel=large - (filter_memory_map): remove memory post 4 GiB when compiled - without -mcmodel=large - * configure.ac: fail gracefully and add -DMCMODEL_SMALL=1 to - TARGET_CFLAGS when -mcmodel=large isn't supported - -2009-06-04 Vladimir Serbinenko - - Remove nested functions in efiemu core - - * efiemu/runtime/efiemu.c (reflect): make static instead of nested - -2009-06-04 Vladimir Serbinenko - - Avoid clobbering %ebx/%rbx in inline assembly with Apple's CC - - * efiemu/runtime/efiemu.c (write_cmos): use %cl instead of %bl as - temporary storage - * include/grub/i386/tsc.h (grub_get_tsc): restore %rbx/%ebx when - using Apple's CC - (grub_cpu_is_tsc_supported): likewise - * loader/i386/xnu.c (guessfsb): restore %rbx/%ebx in inline assembly - -2009-06-04 Vladimir Serbinenko - - Absolute addressing through constant with Apple's cc - - * kern/i386/pc/startup.S: Define necessary constants - and address through it when using ABS with Apple's CC - * boot/i386/pc/diskboot.S: likewise - * boot/i386/pc/boot.S: likewise - * boot/i386/pc/lnxboot.S: likewise - * boot/i386/pc/cdboot.S: likewise - * mmap/i386/pc/mmap_helper.S: likewise - * commands/i386/pc/drivemap_int13h.S: likewise - -2009-06-04 Vladimir Serbinenko - - Check if compiler is apple cc - - * Makefile.in (ASFLAGS): new variable - (TARGET_ASFLAGS): likewise - (TARGET_MODULE_FORMAT): likewise - (TARGET_APPLE_CC): likewise - (OBJCONV): likewise - (TARGET_IMG_CFLAGS): likewise - (TARGET_CPPFLAGS): add includedir - * configure.ac: call grub_apple_cc and grub_apple_target_cc - (TARGET_IMG_LDFLAGS): Add -Wl,-Ttext,. All users updated - Check for linker script only if compiler isn't Apple's CC - (TARGET_MODULE_FORMAT): set - (TARGET_APPLE_CC): likewise - (TARGET_ASFLAGS): likewise - (ASFLAGS): likewise - Check for objcopy only if compiler isn't Apple's CC - Check for BSS symbol only if compiler isn't Apple's CC - * genmk.rb: adapt nm options if we use Apple's utils - * aclocal.m4 (grub_apple_cc): new test - (grub_apple_target_cc): likewise - -2009-06-04 Vladimir Serbinenko - - Simplify sed expressions and improve awk - - * Makefile.in (install-local): simplify sed expression - * gencmdlist.sh: likewise - * genmoddep.awk: avoid adding module as a dependency of itself - -2009-06-04 Vladimir Serbinenko - - Add missing start symbols - - * boot/i386/pc/boot.S: add start - * boot/i386/pc/pxeboot.S: likewise - -2009-06-04 Vladimir Serbinenko - - Fix wrong assumptions with grub-mkimage on EFI - - * i386/efi/grub-mkimage.c (read_kernel_module): don't write prefix here - (relocate_addresses): consider both r_addend and value at offset - (make_mods_section): zerofill modinfo and header - (convert_elf): write prefix here - -2009-06-04 Vladimir Serbinenko - - Use .asciz instead of .string - - * i386/pc/diskboot.S: use .asciz instead of .string - * i386/pc/boot.S: likewise - * include/grub/dl.h (GRUB_MOD_DEP): likewise - (GRUB_MOD_NAME): likewise - -2009-06-04 Vladimir Serbinenko - - gfxpayload support - - * commands/videotest.c (grub_cmd_videotest): use grub_video_set_mode - * include/grub/video.h (GRUB_VIDEO_MODE_TYPE_PURE_TEXT): new definition - (grub_video_setup): remove - (grub_video_set_mode): new prototype - * loader/i386/linux.c (DEFAULT_VIDEO_MODE): new definition - (vid_mode): remove - (linux_vesafb_res): compile only on PCBIOS - (grub_linux_boot): support gfxpayload - * loader/i386/pc/xnu.c (video_hook): new function - (grub_xnu_set_video): support gfxpayload - * term/gfxterm.c (DEFAULT_VIDEO_WIDTH): removed - (DEFAULT_VIDEO_HEIGHT): likewise - (DEFAULT_VIDEO_FLAGS): likewise - (DEFAULT_VIDEO_MODE): new definition - (video_hook): new function - (grub_gfxterm_init): use grub_video_set_mode - * util/grub.d/30_os-prober.in: remove explicit modesetting before - loading xnu - * video/video.c (grub_video_setup): removed - (grub_video_set_mode): new function based on grub_gfxterm_init and - grub_video_setup - -2009-06-04 Vladimir Serbinenko - - Avoid calling biosdisk in drivemap - - * commands/i386/pc/drivemap.c (parse_biosdisk): remove - (revparse_biosdisk): likewise - (list_mappings): derive name from id directly - (grub_cmd_drivemap): use tryparse_diskstring - -2009-06-04 Vladimir Serbinenko - - Script fixes - - * include/grub/script_sh.h (grub_script_cmdline): remove cmdline - (grub_lexer_param): add tokenonhold - (grub_script_create_cmdline): remove cmdline. All callers updated - (grub_script_function_create): make functionname - grub_script_arg. All callers updated - (grub_script_execute_argument_to_string): new prototype - * kern/parser.c (state_transitions): reorder - (grub_parser_cmdline_state): fix a bug and make more compact - * script/sh/execute.c (grub_script_execute_argument_to_string): - make global - (grub_script_execute_cmdline): use new format - * script/sh/function.c (grub_script_function_create): make functionname - grub_script_arg. All callers updated - * script/sh/lexer.c (grub_script_lexer_init): initialize tokenonhold - (grub_script_yylex): remove - (grub_script_yylex2): renamed to ... - (grub_script_yylex): ...renamed - parse the expressions like a${b}c - * script/sh/parser.y (GRUB_PARSER_TOKEN_ARG): new typed terminal - (GRUB_PARSER_TOKEN_VAR): remove - (GRUB_PARSER_TOKEN_NAME): likewise - ("if"): declare as typeless - ("while"): likewise - ("function"): likewise - ("else"): likewise - ("then"): likewise - ("fi"): likewise - (text): remove - (argument): likewise - (script): accept empty scripts and make exit on error - (arguments): use GRUB_PARSER_TOKEN_ARG - (function): likewise - (command): move error handling to script - (menuentry): move grub_script_lexer_ref before - * script/sh/script.c (grub_script_create_cmdline): remove cmdline - argument. All callers updated - -2009-06-04 Robert Millan - - Prevent GRUB from probing floppies during boot. - - * conf/common.rmk (search_mod_CFLAGS): Use `-Werror -Wall'. - * commands/search.c (options): Add --no-floppy. - (search_fs, search_file, grub_cmd_search): Support --no-floppy. - * util/grub-mkconfig_lib.in (prepare_grub_to_access_device): Use - --no-floppy when searching for UUIDs. - -2009-06-04 Robert Millan - - Simplify the code duplication in commands/search.c. - - * commands/search.c (search_label, search_fs_uuid): Merge into ... - (search_fs): ... this. Update all users. - -2009-06-03 Felix Zielcke - - * util/grub-mkconfig.in (update_grub_dir): Rename to grub_mkconfig_dir. - -2009-05-28 Pavel Roskin - - * Makefile.in: Don't use "cp -d", it doesn't work on FreeBSD. - Remove the original symlink explicitly. - - * fs/hfs.c (grub_hfs_find_dir): Skip sequences of slashes, not - just one slash. That's how grub_fshelp_find_file() does it. - -2009-05-26 Pavel Roskin - - * genmk.rb: Avoid shadowing variable `s', rename the outer `s' - to `str'. - - * util/getroot.c (grub_util_get_dev_abstraction): Mark os_dev as - possibly unused. - -2009-05-25 Christian Franke - - * disk/ata.c (grub_ata_wait_not_busy): Add debug output of status - register. - (grub_atapi_identify): Add wait after drive select. - (grub_ata_identify): Do more strict status register check before - calling grub_atapi_identify (). Suppress error message if status - register is 0x00 after command failure. Add status register - check after PIO read to avoid bogus identify due to stuck DRQ. - Thanks to Pavel Roskin for testing. - (grub_device_initialize): Remove unsafe status register check. - Thanks to 'phcoder' for problem report and patch. - Prevent sign extension in debug message. - -2009-05-23 Colin D Bennett - - Cleaned up `include/grub/normal.h'. Grouped prototypes by - definition file, and functions defined in `normal/menu.c' have had - their prototypes moved to `include/grub/menu.h' for consistency. - - * include/grub/menu.h (grub_menu_execute_callback): Added; moved - from normal.h. - (grub_menu_get_entry): Likewise. - (grub_menu_get_timeout): Likewise. - (grub_menu_set_timeout): Likewise. - (grub_menu_execute_entry): Likewise. - (grub_menu_execute_with_fallback): Likewise. - (grub_menu_entry_run): Likewise. - - * include/grub/normal.h: Re-ordered and grouped function - prototypes by file that the function is defined in. - (grub_menu_execute_callback): Removed; moved to menu.h. - (grub_menu_get_entry): Likewise. - (grub_menu_get_timeout): Likewise. - (grub_menu_set_timeout): Likewise. - (grub_menu_execute_entry): Likewise. - (grub_menu_execute_with_fallback): Likewise. - (grub_menu_entry_run): Likewise. - (grub_menu_addentry): Renamed from this ... - (grub_normal_add_menu_entry): ... to this. - - * normal/main.c (grub_menu_addentry): Renamed from this ... - (grub_normal_add_menu_entry): ... to this. - - * script/sh/execute.c (grub_script_execute_menuentry): Update - reference to renamed grub_menu_addentry function. - -2009-05-23 Felix Zielcke - - * commands/i386/pc/drivemap.c (MODNAME): Remove. Update all users. - -2009-05-22 Pavel Roskin - - * aclocal.m4 (grub_I386_CHECK_REGPARM_BUG): Remove. - * configure.ac: Don't call grub_I386_CHECK_REGPARM_BUG. Define - NESTED_FUNC_ATTR using AH_BOTTOM. Use regparm(1) only when - compiling for the i386 targets, but not for the utilities. - - * include/grub/i386/pc/kernel.h (grub_boot_drive): Change type - to grub_uint8_t. - (grub_root_drive): Likewise. - * kern/i386/pc/startup.S (grub_boot_drive): Change size to byte, - remove alignment. - (grub_root_drive): Change size to byte. - (grub_start_addr): Remove. - (grub_end_addr): Likewise. - (grub_apm_bios_info): Likewise. - -2009-05-21 Felix Zielcke - - * normal/i386: Remove. - * normal/powerpc: Likewise. - * normal/sparc64: Likewise. - * normal/x86_64: Likewise. - -2009-05-19 Vladimir Serbinenko - - * conf/x86_64-efi.rmk (linux_mod_ASFLAGS): Add missing variable - * loader/i386/linux_trampoline.S: Fix indentation - * loader/i386/xnu_helper.S: Likewise - -2009-05-18 Colin D Bennett - - Display error messages when parsing a Lua statement fails. - Previously, executing a syntactically invalid statement like - ")foo" or "bar;" would silently fail. - - * script/lua/grub_main.c (handle_lua_error): New function. - (grub_lua_parse_line): Improved reporting of Lua parser and - execution errors. - -2009-05-17 Vladimir Serbinenko - - Remove -Werror which causes build to fail on some systems - - * conf/i386-pc.rmk (xnu_mod_CFLAGS): Remove -Werror -Wall - * conf/i386-efi.rmk (xnu_mod_CFLAGS): Likewise - * conf/x86_64-efi.rmk (xnu_mod_CFLAGS): Likewise - -2009-05-17 Vladimir Serbinenko - - trampoline for linux on 64-bit platform - - * conf/x86_64-efi.rmk (linux_mod_SOURCES): added - loader/i386/efi/linux_trampoline.S - * include/grub/x86_64/efi/loader.h (grub_linux_real_boot): removed - declaration - * kern/x86_64/efi/startup.S (grub_linux_real_boot): moved from - here - * loader/i386/linux_trampoline.S: moved here - * loader/i386/efi/linux.c (allocate_pages): reserve space for - trampoline - (jumpvector): removed - (grub_linux_trampoline_start): new declaration - (grub_linux_trampoline_end): likewise - (grub_linux_boot): use trampoline when on 64-bit platform - * loader/i386/linux.c: likewise - -2009-05-16 Pavel Roskin - - * script/lua/grub_lib.c (grub_lua_getenv): Make name and value - const to avoid a warning. - (grub_lua_setenv): Likewise. - * script/lua/grub_main.c (grub_lua_parse_line): Use size_t for - lmsg to fix a warning. - -2009-05-16 Felix Zielcke - - * conf/i386.rmk (setjmp_mod_CFLAGS): Rename to ... - (setjmp_mod_ASFLAGS): ... this. Set to $(COMMON_ASFLAGS). - * conf/x86_64-efi.rmk (setjmp_mod_CFLAGS): Rename to ... - (setjmp_mod_ASFLAGS): ... this. Set to $(COMMON_ASFLAGS). - * conf/powerpc-ieee1275.rmk (setjmp_mod_CFLAGS): Rename to ... - (setjmp_mod_ASFLAGS): ... this. Set to $(COMMON_ASFLAGS). - * conf/sparc64-ieee1275.rmk (setjmp_mod_CFLAGS): Rename to ... - (setjmp_mod_ASFLAGS): ... this. Set to $(COMMON_ASFLAGS). - -2009-05-16 Felix Zielcke - - * util/grub-mkconfig.in: Export GRUB_TERMINAL_INPUT. - -2009-05-16 Bean - - * conf/common.rmk (pkglib_MODULES): Add lua.mod. - (lua_mod_SOURCES): New variable. - (lua_mod_CFLAGS): Likewise. - (lua_mod_LDFLAGS): Likewise. - - * conf/i386.rmk (pkglib_MODULES): Add setjmp.mod. - (setjmp_mod_SOURCES): New variable. - (setjmp_mod_CFLAGS): Likewise. - (setjmp_LDFLAGS): Likewise. - - * conf/x86_64-efi.rmk (pkglib_MODULES): Add setjmp.mod. - (setjmp_mod_SOURCES): New variable. - (setjmp_mod_CFLAGS): Likewise. - (setjmp_LDFLAGS): Likewise. - - * conf/powerpc-ieee1275.rmk (pkglib_MODULES): Add setjmp.mod. - (setjmp_mod_SOURCES): New variable. - (setjmp_mod_CFLAGS): Likewise. - (setjmp_LDFLAGS): Likewise. - - * conf/sparc64-ieee1275.rmk (pkglib_MODULES): Add setjmp.mod. - (setjmp_mod_SOURCES): New variable. - (setjmp_mod_CFLAGS): Likewise. - (setjmp_LDFLAGS): Likewise. - - * normal/i386/setjmp.S: Moved from here ... - * lib/i386/setjmp.S: ... Moved here - * normal/x86_64/setjmp.S: Moved from here ... - * lib/x86_64/setjmp.S: ... Moved here - * normal/powerpc/setjmp.S: Moved from here ... - * lib/powerpc/setjmp.S: ... Moved here - * normal/sparc64/setjmp.S: Moved from here ... - * lib/sparc64/setjmp.S: ... Moved here - - * include/grub/i386/setjmp.h (grub_setjmp): Don't use attribute - returns_twice in mingw. - - * script/lua/grub_lib.c: New file. - * script/lua/grub_lib.h: Likewise. - * script/lua/grub_lua.h: Likewise. - * script/lua/grub_main.c: Likewise. - * script/lua/lapi.c: Likewise. - * script/lua/lapi.h: Likewise. - * script/lua/lauxlib.c: Likewise. - * script/lua/lauxlib.h: Likewise. - * script/lua/lbaselib.c: Likewise. - * script/lua/lcode.c: Likewise. - * script/lua/lcode.h: Likewise. - * script/lua/ldblib.c: Likewise. - * script/lua/ldebug.c: Likewise. - * script/lua/ldebug.h: Likewise. - * script/lua/ldo.c: Likewise. - * script/lua/ldo.h: Likewise. - * script/lua/ldump.c: Likewise. - * script/lua/lfunc.c: Likewise. - * script/lua/lfunc.h: Likewise. - * script/lua/lgc.c: Likewise. - * script/lua/lgc.h: Likewise. - * script/lua/linit.c: Likewise. - * script/lua/liolib.c: Likewise. - * script/lua/llex.c: Likewise. - * script/lua/llex.h: Likewise. - * script/lua/llimits.h: Likewise. - * script/lua/lmathlib.c: Likewise. - * script/lua/lmem.c: Likewise. - * script/lua/lmem.h: Likewise. - * script/lua/loadlib.c: Likewise. - * script/lua/lobject.c: Likewise. - * script/lua/lobject.h: Likewise. - * script/lua/lopcodes.c: Likewise. - * script/lua/lopcodes.h: Likewise. - * script/lua/loslib.c: Likewise. - * script/lua/lparser.c: Likewise. - * script/lua/lparser.h: Likewise. - * script/lua/lstate.c: Likewise. - * script/lua/lstate.h: Likewise. - * script/lua/lstring.c: Likewise. - * script/lua/lstring.h: Likewise. - * script/lua/lstrlib.c: Likewise. - * script/lua/ltable.c: Likewise. - * script/lua/ltable.h: Likewise. - * script/lua/ltablib.c: Likewise. - * script/lua/ltm.c: Likewise. - * script/lua/ltm.h: Likewise. - * script/lua/lua.h: Likewise. - * script/lua/luaconf.h: Likewise. - * script/lua/lualib.h: Likewise. - * script/lua/lundump.c: Likewise. - * script/lua/lundump.h: Likewise. - * script/lua/lvm.c: Likewise. - * script/lua/lvm.h: Likewise. - * script/lua/lzio.c: Likewise. - * script/lua/lzio.h: Likewise. - -2009-05-16 Bean - - * include/grub/kernel.h (grub_module_header_types): Add type - OBJ_TYPE_CONFIG. - - * kern/main.c (grub_load_config): New function. - (grub_main): Call grub_load_config to read boot config. - - * grub-mkimage (generate_image): New parameter config_path. - (options): New option --config. - (main): Parse --config option, and pass it to generate_image. - -2009-05-14 Christian Franke - - * commands/i386/pc/drivemap_int13h.S: Add missing EXT_C for symbols. - This fixes build on Cygwin. - -2009-05-14 Pavel Roskin - - * commands/i386/pc/drivemap_int13h.S: Eliminate unconditional - jump. This saves two bytes, so the typical case of 2 swapped - drives would fit 32 bytes. - -2009-05-13 Pavel Roskin - - * loader/i386/multiboot.c (grub_multiboot): Cast mmap_addr to - grub_uint32_t to avoid a warning. - - * loader/i386/linux.c (allocate_pages): When assigning - real_mode_mem, cast through grub_size_t to fix a warning. The - code already makes sure that the value would fit a pointer. - (grub_linux_setup_video): Cast render_target->data to - grub_size_t to fix a warning. - -2009-05-13 Javier MartĂ­n - - * commands/i386/pc/drivemap.c: New file - implement drivemap - command. - * commands/i386/pc/drivemap_int13h.S: New file - int13 handler. - * conf/i386-pc.rmk: Add drivemap.c and drivemap_int13h.S. - -2009-05-13 Pavel Roskin - - * util/i386/pc/grub-setup.c (setup): Remove unused variable - embedding_area_exists. - -2009-05-13 Robert Millan - - * util/i386/pc/grub-setup.c (setup): Restructure code flow to make - it easier to understand / work with. - Improve warning messages for cases where there's no embedding area, - or when it is too small (or core.img too large). - -2009-05-13 Pavel Roskin - - * loader/i386/pc/multiboot2.c: Add necessary includes for - grub_multiboot2_real_boot(). - - * fs/iso9660.c (grub_iso9660_iterate_dir): The file mode in the - PX record is always little-endian. We only need the lower 2 - bytes of the mode. - - * fs/cpio.c: Use the same name "struct head" for tar and cpio to - facilitate code reuse. - (grub_cpio_mount): Use "struct head", not a char buffer. This - fixes a warning reported by gcc 4.4. - - * kernel/disk.c (grub_disk_read): Use void pointer for the - buffer. - (grub_disk_write): Use const void pointer for the buffer. - Adjust all callers. Remove unnecessary casts. - -2009-05-10 Robert Millan - - * util/i386/pc/grub-install.in: Update copyright year. - -2009-05-09 Vladimir Serbinenko - - gptsync - - * commands/gptsync.c: new file - * conf/common.rmk (pkglib_MODULES): add gptsync.mod - (gptsync_mod_SOURCES): new variable - (gptsync_mod_CFLAGS): likewise - (gptsync_mod_LDFLAGS): likewise - * include/grub/pc_partition.h (GRUB_PC_PARTITION_TYPE_NTFS): - new definition - (GRUB_PC_PARTITION_TYPE_HFS): likewise - * conf/i386-coreboot.rmk (grub_emu_SOURCES): add commands/gptsync.c - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - -2009-05-09 Vladimir Serbinenko - - Fixed grub-emu - - * kern/dl.c (grub_dl_ref): omit when compiling grub-emu - (grub_dl_ref): likewise - -2009-05-08 Robert Millan - - * util/i386/pc/grub-setup.c (setup): Factorize find_usable_region(), - split in two functions (one for msdos and one for gpt). - -2009-05-08 Pavel Roskin - - * disk/raid.c (grub_raid_block_xor): Make buf2 constant, it's - not modified. - - * disk/raid6_recover.c (grub_raid6_recover): Fix warnings about - uninitialized err[0] and err[1]. Rename them to bad1 and bad2. - Initialize them with -1. Add sanity check for bad1. Eliminate - nerr variable. - -2009-05-08 David S. Miller - - * util/sparc64/ieee1275/grub-ofpathname.c (main): Set progname. - -2009-05-06 Robert Millan - - * util/i386/pc/grub-setup.c (setup): Fix check for embed region - existence. - -2009-05-05 Felix Zielcke - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `kern/rescue_reader.c', `kern/rescue_parser.c' and `normal/autofs.c'. - -2009-05-05 David S. Miller - - * util/sparc64/ieee1275/grub-install.in: Fix sed arg quoting. - -2009-05-05 Pavel Roskin - - * include/grub/dl.h [GRUB_UTIL]: Provide inline implementations - of grub_dl_ref() and grub_dl_unref(). - * commands/parttool.c: Remove preprocessor conditionals around - grub_dl_ref() and grub_dl_unref(). - * fs/affs.c: Likewise. - * fs/afs.c: Likewise. - * fs/cpio.c: Likewise. - * fs/ext2.c: Likewise. - * fs/fat.c: Likewise. - * fs/hfs.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/jfs.c: Likewise. - * fs/minix.c: Likewise. - * fs/ntfs.c: Likewise. - * fs/reiserfs.c: Likewise. - * fs/sfs.c: Likewise. - * fs/udf.c: Likewise. - * fs/ufs.c: Likewise. - * fs/xfs.c: Likewise. - * include/grub/dl.h: Likewise. - * loader/xnu.c: Likewise. - -2009-05-04 Pavel Roskin - - * commands/acpi.c: Remove unused variable my_mod. - * partmap/amiga.c: Likewise. - * partmap/apple.c: Likewise. - * partmap/gpt.c: Likewise. - * partmap/pc.c: Likewise. - * partmap/sun.c: Likewise. - * term/gfxterm.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * term/i386/pc/vga.c: Likewise. - -2009-05-04 David S. Miller - - * kern/ieee1275/openfw.c (grub_children_iterate): Fix string - pointer args to grub_ieee1275_get_property(). - - * conf/sparc64-ieee1275.rmk: Fix build due to missing '\'. - - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Bypass cdrom - devices, and do not traverse down under controller nodes. - - * disk/ieee1275/ofdisk.c (compute_dev_path): New. - (grub_ofdisk_open): Use it to un-escape "," characters. - * kern/disk.c (find_part_sep): New. - (grub_disk_open): Use it to find the first non-escaped ',' - character in the disk name. - * util/ieee1275/devicemap.c (escape_of_path): New. - (grub_util_emit_devicemap_entry): Use it. - * util/sparc64/ieee1275/grub-install.in: Update script to - strip partition specifiers properly by not triggering on - '\' escaped ',' characters. - -2009-05-04 Robert Millan - - * include/grub/i386/linux.h (GRUB_LINUX_VID_MODE_VESA_START): Set - to 0x300. - * loader/i386/linux.c (vga_modes, linux_vesafb_res): Add a few - resolutions. - (linux_vesafb_modes): Add a lot of additional modes to the list (based - on documentation from Wikipedia). - -2009-05-04 Pavel Roskin - - * disk/ata.c: Spelling fixes. - * disk/raid.c: Likewise. - * disk/usbms.c: Likewise. - * disk/dmraid_nvidia.c: Likewise. - * kern/ieee1275/openfw.c: Likewise. - * kern/ieee1275/init.c: Likewise. - * kern/ieee1275/cmain.c: Likewise. - * boot/i386/pc/cdboot.S: Likewise. - * video/readers/png.c: Likewise. - * video/i386/pc/vbe.c: Likewise. - * fs/udf.c: Likewise. - * fs/hfs.c: Likewise. - * fs/reiserfs.c: Likewise. - * efiemu/runtime/efiemu.c: Likewise. - * efiemu/main.c: Likewise. - * efiemu/mm.c: Likewise. - * include/grub/elf.h: Likewise. - * include/grub/xnu.h: Likewise. - * include/grub/usbdesc.h: Likewise. - * include/grub/usb.h: Likewise. - * include/grub/script_sh.h: Likewise. - * include/grub/lib/LzmaEnc.h: Likewise. - * include/grub/efiemu/efiemu.h: Likewise. - * include/grub/command.h: Likewise. - * normal/menu.c: Likewise. - * normal/main.c: Likewise. - * normal/datetime.c: Likewise. - * bus/usb/uhci.c: Likewise. - * mmap/i386/uppermem.c: Likewise. - * mmap/mmap.c: Likewise. - * commands/acpi.c: Likewise. - * commands/test.c: Likewise. - * partmap/apple.c: Likewise. - * font/font.c: Likewise. - * loader/sparc64/ieee1275/linux.c: Likewise. - * loader/macho.c: Likewise. - * loader/i386/bsd_trampoline.S: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/xnu.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * term/usb_keyboard.c: Likewise. - * util/resolve.c: Likewise. - * util/getroot.c: Likewise. - -2009-05-04 Felix Zielcke - - * conf/i386-pc.rmk (libpkg_DATA): Rename to pkglib_DATA. - -2009-05-04 Robert Millan - - * loader/i386/linux.c [GRUB_MACHINE_PCBIOS] (grub_cmd_linux): Fix - build error. - -2009-05-04 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): Make "vga=" compatibility - parameter only available on BIOS. - -2009-05-04 Vladimir Serbinenko - - Removed wrong semicolon in declaration - - * grub/misc.h (grub_dprintf): remove semicolon - -2009-05-04 Robert Millan - - * loader/i386/linux.c (GRUB_ASSUME_LINUX_HAS_FB_SUPPORT): New macro. - (grub_linux_boot): Don't check for `linux_vesafb_modes' bounds (this - is done by grub_cmd_linux() now). - [! GRUB_ASSUME_LINUX_HAS_FB_SUPPORT]: If "vga=" parameter wasn't set, - restore video to text mode. - (grub_cmd_linux): Default `vid_mode' initialization to 0, which - indicates lack of "vga=" parameter. "vga=0" is mapped to - `GRUB_LINUX_VID_MODE_NORMAL'. - -2009-05-04 Felix Zielcke - - * conf/i386-efi.rmk (grub_emu_SOURCES): Remove `normal/execute.c', - `normal/lexer.c', `kern/rescue.c', `normal/function.c', `normal/misc.c' - and `normal/script.c'. Add `kern/rescue_reader.c', - `kern/rescue_parser.c', `script/sh/main.c', `script/sh/execute.c', - `script/sh/function.c', `script/sh/lexer.c', `script/sh/script.c' and - `grub_script.tab.c'. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * Makefile.in: Remove duplicated 2008 in Copyright line. - -2009-05-04 Robert Millan - - * util/misc.c (grub_util_warn): New function. Emits a warning - unconditionally. - * include/grub/util/misc.h (grub_util_warn): New declaration. - - * util/i386/pc/grub-install.in: Understand --force and pass it down - to grub-setup. - - * util/i386/pc/grub-setup.c (main): Understand --force and pass it - down to setup(). - (setup): Improve error messages and add warnings when requested to - install in odd layouts. Refuse to install using blocklists unless - --force was set. - -2009-05-04 martin f. krafft - - * disk/raid.c (grub_raid_scan_device): Improve debug message. - -2009-05-04 Vladimir Serbinenko - - Updated copyright year - - * fs/hfsplus.c: updated copyright year - -2009-05-04 Vladimir Serbinenko - - HFS+ UUID - - * fs/hfsplus.c (grub_hfsplus_volheader): added num_serial field - in the space previously used by unused3 - (grub_hfsplus_uuid): new function - (grub_hfsplus_fs): added uuid field - -2009-05-03 Pavel Roskin - - * disk/ata.c: Don't cast mod to void in GRUB_MOD_INIT to - suppress warnings. It's no longer needed. - * disk/host.c: Likewise. - * disk/ata_pthru.c: Likewise. - * disk/loopback.c: Likewise. - * hook/datehook.c: Likewise. - * parttool/pcpart.c: Likewise. - * fs/i386/pc/pxe.c: Likewise. - * fs/ntfscomp.c: Likewise. - * efiemu/main.c: Likewise. - * mmap/mmap.c: Likewise. - * commands/crc.c: Likewise. - * commands/hexdump.c: Likewise. - * commands/hdparm.c: Likewise. - * commands/acpi.c: Likewise. - * commands/echo.c: Likewise. - * commands/minicmd.c: Likewise. - * commands/blocklist.c: Likewise. - * commands/memrw.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/usbtest.c: Likewise. - * commands/lsmmap.c: Likewise. - * commands/boot.c: Likewise. - * commands/parttool.c: Likewise. - * commands/configfile.c: Likewise. - * commands/search.c: Likewise. - * commands/ieee1275/suspend.c: Likewise. - * commands/cat.c: Likewise. - * commands/i386/pc/pxecmd.c: Likewise. - * commands/i386/pc/play.c: Likewise. - * commands/i386/pc/halt.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/i386/pc/vbetest.c: Likewise. - * commands/lspci.c: Likewise. - * commands/date.c: Likewise. - * commands/handler.c: Likewise. - * commands/ls.c: Likewise. - * commands/test.c: Likewise. - * commands/cmp.c: Likewise. - * commands/efi/loadbios.c: Likewise. - * commands/efi/fixvideo.c: Likewise. - * commands/halt.c: Likewise. - * commands/help.c: Likewise. - * commands/reboot.c: Likewise. - * hello/hello.c: Likewise. - * script/sh/main.c: Likewise. - * loader/xnu.c: Likewise. - * term/terminfo.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/usb_keyboard.c: Likewise. - -2009-05-03 David S. Miller - - * normal/menu.c: Include grub/parser.h - -2009-05-03 Pavel Roskin - - * mmap/efi/mmap.c (grub_mmap_malign_and_register): Return void*, - not char*. - * mmap/i386/mmap.c (grub_mmap_malign_and_register): Likewise. - Suggested by Javier MartĂ­n - - * util/i386/pc/grub-mkrescue.in: Allow for the case when - efiemu??.o doesn't exist. - * util/i386/pc/grub-install.in: Likewise. Use "cp -f" for - copying. - -2009-05-03 Bean Vladimir Serbinenko - - FreeBSD 64-bit support - - * conf/i386-pc.rmk (bsd_mod_SOURCES): add loader/i386/bsd_helper.S - and loader/i386/bsd_trampoline.S - (bsd_mod_ASFLAGS): new variable - * include/grub/i386/bsd.h (FREEBSD_MODINFOMD_SMAP): new definition - (FREEBSD_MODTYPE_KERNEL64): likewise - (grub_bsd64_trampoline_start): likewise - (grub_bsd64_trampoline_end): likewise - (grub_bsd64_trampoline_selfjump): likewise - (grub_bsd64_trampoline_gdt): likewise - * include/grub/i386/loader.h (grub_unix_real_boot): moved from here ... - * include/grub/i386/bsd.h (grub_unix_real_boot): ... moved here - * kern/i386/loader.S (grub_unix_real_boot): moved from here ... - * loader/i386/bsd_helper.S (grub_unix_real_boot): moved here - * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type - of "attrib" member - * loader/i386/bsd_pagetable.c: new file - * loader/i386/bsd_trampoline.S: likewise - * loader/i386/bsd.c (ALIGN_QWORD): new macro - (ALIGN_VAR): likewise - (entry_hi): new variable - (kern_end_mdofs): likewise - (is_64bit): likewise - (grub_freebsd_add_meta): use ALIGN_VAR - (grub_e820_mmap): new declaration - (grub_freebsd_add_mmap): new function - (grub_freebsd_add_meta_module): support 64 bit kernels - (grub_freebsd_list_modules): use ALIGN_VAR - (gdt_descriptor): new declaration - (grub_freebsd_boot): support 64 bit kernels - (grub_bsd_elf64_hook): new function - (grub_bsd_load_elf): support elf64 - -2009-05-03 Bean - - * script/sh/execute.c (grub_script_execute_cmdif): Reset grub_errno - after we get the result of if statement. - -2009-05-03 Bean - - * Makefile.in (enable_efiemu): New variable. - - * conf/i386-pc.rmk: Only compile efiemu runtimes when enable_efiemu is - set. - (efiemu32.o): Use macro $< for source file, add $(srcdir) to include - path. - (efi64_c.o): Use macro $< for source file, add $(srcdir) to include - path, add -mno-red-zone option. - (efiemu64_s.o): Likewise. - (efiemu64.o): Use macro $^ for source file. - - * configure.ac (--enable-efiemu): New option. - -2009-05-03 Vladimir Serbinenko - - xnu support - - * conf/i386-efi.rmk (kernel_mod_HEADERS): added i386/pit.h - (pkglib_MODULES): add xnu.mod - (xnu_mod_SOURCES): new variable - (xnu_mod_CFLAGS): likewise - (xnu_mod_LDFLAGS): likewise - (xnu_mod_ASFLAGS): likewise - * conf/i386-pc.rmk: likewise - * conf/x86_64-efi.rmk: likewise - * include/grub/efi/efi.h (grub_efi_finish_boot_services): - new declaration - * include/grub/i386/macho.h: new file - * include/grub/i386/xnu.h: likewise - * include/grub/macho.h: likewise - * include/grub/machoload.h: likewise - * include/grub/x86_64/macho.h: likewise - * include/grub/x86_64/xnu.h: likewise - * include/grub/xnu.h: likewise - * kern/efi/efi.c (grub_efi_finish_boot_services): new function - * kern/efi/mm.c (MAX_HEAP_SIZE): increase - * loader/i386/efi/xnu.c: new file - * loader/i386/pc/xnu.c: likewise - * loader/i386/xnu.c: likewise - * loader/i386/xnu_helper.S: likewise - * loader/macho.c: likewise - * loader/xnu.c: likewise - * loader/xnu_resume.c: likewise - * util/grub-dumpdevtree: likewise - * include/grub/i386/pit.h: include grub/err.h - (grub_pit_wait): export - * util/grub.d/30_os-prober.in: support Darwin/Mac OS X - -2009-05-02 Vladimir Serbinenko - - Efiemu - - * conf/i386-pc.rmk: new modules efiemu, efiemu_acpi, efiemu_pnvram, - _linux_efi, linux_efi. - new files in grub-emu - new targets efiemu32.o and efiemu64.o - * loader/linux_normal_efiemu.c: likewise - * loader/i386/efi/linux.c: added preliminary efiemu support - * util/i386/pc/grub-install.in: add efiemu??.o to the list of - files to copy - * include/grub/autoefi.h: new file - * include/grub/i386/efiemu.h: likewise - * include/grub/i386/pc/efiemu.h: likewise - * include/grub/efi/api.h: add LL suffix when necessary - new definitions relating to tables - * include/grub/efiemu/efiemu.h: new file - * include/grub/efiemu/runtime.h: likewise - * efiemu/prepare.c: likewise - * efiemu/loadcore_common.c: likewise - * efiemu/loadcore64.c: likewise - * efiemu/runtime/efiemu.sh: likewise - * efiemu/runtime/efiemu.S: likewise - * efiemu/runtime/efiemu.c: likewise - * efiemu/runtime/config.h: likewise - * efiemu/prepare32.c: likewise - * efiemu/main.c: likewise - * efiemu/modules/pnvram.c: likewise - * efiemu/modules/i386: likewise - * efiemu/modules/i386/pc: likewise - * efiemu/modules/acpi.c: likewise - * efiemu/i386/pc/cfgtables.c: likewise - * efiemu/i386/loadcore64.c: likewise - * efiemu/i386/loadcore32.c: likewise - * efiemu/prepare64.c: likewise - * efiemu/loadcore.c: likewise - * efiemu/symbols.c: likewise - * efiemu/mm.c: likewise - * efiemu/loadcore32.c: likewise - -2009-05-02 Vladimir Serbinenko - - ACPI spoofing - - * commands/acpi.c: new file - * commands/i386/pc/acpi.c: likewise - * commands/efi/acpi.c: likewise - * include/grub/acpi.h: likewise - * conf/i386-pc.rmk (pkglib_MODULES): added acpi.mod - (acpi_mod_SOURCES): new variable - (acpi_mod_CFLAGS): likewise - (acpi_mod_LDFLAGS): likewise - * conf/i386-efi.rmk: likewise - * conf/x86_64-efi.rmk: likewise - -2009-05-02 Vladimir Serbinenko - - Missing part from mmap patch - - * mmap/efi/mmap.c (grub_machine_mmap_unregister): renamed to - (grub_mmap_unregister) - (grub_mmap_free_and_unregister): use grub_mmap_register - -2009-05-02 Vladimir Serbinenko - - Mmap services - - * loader/i386/efi/linux.c (grub_linux_boot): use grub_mmap_iterate - * loader/i386/linux.c (find_mmap_size): likewise - (allocate_pages): likewise - * loader/i386/multiboot.c (grub_get_multiboot_mmap_len): likewise - (grub_fill_multiboot_mmap): likewise - (grub_multiboot): use grub_mmap_get_lower and grub_mmap_get_upper - * loader/i386/pc/linux.c (grub_cmd_linux): use grub_mmap_get_lower - * include/grub/i386/bsd.h (OPENBSD_MMAP_AVAILABLE): new definition - (OPENBSD_MMAP_RESERVED): likewise - * include/grub/i386/pc/memory.h: include grub/memory.h - (grub_lower_mem): removed - (grub_upper_mem): likewise - (GRUB_MACHINE_MEMORY_ACPI): new definition - (GRUB_MACHINE_MEMORY_NVS): likewise - (GRUB_MACHINE_MEMORY_MAX_TYPE): likewise - (GRUB_MACHINE_MEMORY_HOLE): likewise - (grub_machine_mmap_register): likewise - (grub_machine_mmap_unregister): likewise - (grub_machine_get_upper): likewise - (grub_machine_get_lower): likewise - (grub_machine_get_post64): likewise - * include/grub/i386/efi/memory.h: new file - * include/grub/x86_64/efi/memory.h: likewise - * include/grub/efi/memory.h: likewise - * conf/i386-pc.rmk (pkglib_MODULES): added mmap.mod - (mmap_mod_SOURCES): new variable - (mmap_mod_LDFLAGS): likewise - (mmap_mod_ASFLAGS): likewise - * conf/i386-coreboot.rmk: likewise - * conf/i386-ieee1275.rmk: likewise - * conf/i386-efi.rmk: likewise - * conf/x86_64-efi.rmk: likewise - * include/grub/types.h (UINT_TO_PTR): new macro - (PTR_TO_UINT32): likewise - (PTR_TO_UINT64): likewise - * include/grub/memory.h: new file - * mmap/i386/pc/mmap.c: likewise - * mmap/i386/pc/mmap_helper.S: likewise - * mmap/i386/uppermem.c: likewise - * mmap/mmap.c: likewise - * mmap/efi/mmap.c: likewise - * kern/i386/coreboot/init.c (grub_machine_init): don't use - grub_upper_mem - * kern/i386/pc/init.c (grub_lower_mem): removed variable - (grub_upper_mem): likewise - (grub_machine_init): don't use grub_upper_mem, - make grub_lower_mem local - * loader/i386/bsd.c (grub_openbsd_boot): use grub_mmap_get_lower, - grub_mmap_iterate and grub_mmap_get_upper - (grub_netbsd_boot): use grub_mmap_get_lower and grub_mmap_get_upper - -2009-05-02 Bean - - * conf/common.rmk (grub_script.tab.c): Change normal/parser.y to - script/sh/parser.y. - (pkglib_MODULES): Add normal.mod and sh.mod. - (normal_SOURCES): New variable. - (normal_mod_CFLAGS): Likewise. - (normal_mod_LDFLAGS): Likewise. - (sh_mod_SOURCES): Likewise. - (sh_mod_CFLAGS): Likewise. - (sh_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (normal/lexer.c_DEPENDENCIES): Changed to - script/sh/lexer.c_DEPENDENCIES. - (kernel_img_SOURCES): Remove kern/rescue.c, and kern/reader.c, - kern/rescue_reader.c and kern/rescue_parser.c. - (kernel_img_HEADERS): Remove rescue.h, add reader.h. - (grub_emu_SOURCES): Change source files. - (pkglib_MODULES): Remove normal.mod. - (normal_SOURCES): Removed. - (normal_mod_CFLAGS): Likewise. - (normal_mod_LDFLAGS): Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1276.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - - * include/grub/command.h (grub_command_execute): New inline function. - - * include/grub/menu.h (grub_menu_entry): Removed commands field. - - * include/grub/normal.h: Remove . - (grub_fs_module_list): Moved to normal/autofs.c. - (grub_exit_env): Removed. - (grub_command_execute): Likewise. - (grub_normal_menu_addentry): Renamed to grub_menu_addentry, removed - parameter script. - (read_command_list): New function declaration. - (read_fs_list): Likewise. - - * include/parser.h: Include . - (grub_parser_split_cmdline): Change type of getline parameter. - (grub_parser): New structure. - (grub_parser_class): New variable. - (grub_parser_execute): New function declaration. - (grub_register_rescue_parser): Likewise. - (grub_parser_register): New inline function. - (grub_parser_unregister): Likewise. - (grub_parser_get_current): Likewise. - (grub_parser_set_current): Likewise. - - * include/grub/reader.h: New file. - * kern/reader.c: Likewise. - * kern/rescue_parser.c: Likewise. - * kern/rescue_reader.c: Likewise. - * normal/autofs.c: Likewise. - * normal/dyncmd.c: Likewise. - - * include/grub/rescue.h: Removed. - * normal/command.h: Likewise. - - * include/grub/script.h: Moved to ... - * include/grub/script_sh.h: ... Moved here. - * normal/execute.c: Moved to ... - * script/sh/execute.c: ... Moved here. - * normal/function.c: Moved to ... - * script/sh/function.c: ... Moved here. - * normal/lexer.c: Moved to ... - * script/sh/lexer.c: ... Moved here. - * normal/parser.y: Moved to ... - * script/sh/parser.y: ... Moved here. - * normal/script.c: Moved to ... - * script/sh/script.c: ... Moved here. - - * normal/main.c: Remove and , include - . - (grub_exit_env): Removed. - (fs_module_list): Moved to normal/autofs.c. - (grub_file_getline): Don't handle comment here. - (free_menu): Skip removed field entry->commands. - (grub_normal_menu_addentry): Removed as grub_menu_entry, removed - script parameter. - (read_config_file): Removed nested parameter, change getline function. - (grub_enter_normal_mode): Removed. - (grub_dyncmd_dispatcher): Moved to normal/dyncmd.c. - (read_command_list): Likewise. - (autoload_fs_module): Moved to normal/autofs.c. - (read_fs_list): Likewise. - (reader_nested): New variable. - (grub_normal_execute): Run parser.sh to switch to sh parser. - (grub_cmd_rescue): Removed. - (cmd_normal): Removed. - (grub_cmd_normal): Unregister itself at the beginning. Don't register - rescue command. - (grub_cmdline_run): New function. - (grub_normal_reader_init): Likewise. - (grub_normal_read_line): Likewise. - (grub_env_write_pager): Likewise. - (cmdline): New variable. - (grub_normal_reader): Likewise. - (GRUB_MOD_INIT): Register normal reader and set as current, register - pager hook, register normal command with grub_register_command_prio, - so that it won't show up in command.lst. - (GRUB_MOD_FINI): Unregister normal reader, unhook pager, clear - grub_fs_autoload_hook. - - * normal/menu.c: Remove , add . - (grub_menu_execute_entry): Replace grub_script_execute with - grub_parser_execute, change parameter to grub_command_execute. - - * normal/menu_text.c: Remove . - - * normal/menu_entry.c: Remove , add - and . - (run): Change editor_getline to use new parser interface. Change - parameter to grub_command_execute. - - * kern/main.c: Remove , include , - and . - (grub_load_normal_mode): Execute normal command. - (grub_main): Call grub_register_core_commands, - grub_register_rescue_parser and grub_register_rescue_reader, use - grub_reader_loop to enter input loop. - - * kern/parser.c (grub_parser_split_cmdline): Change type of - getline parameter. - (grub_parser_class): New variable. - (grub_parser_execute): New function. - - * loader/i386/multiboot.c: Remove . - * loader/multiboot2.c: Likewise. - * loader/sparc64/ieee1275/linux.c: Likewise. - - * util/grub-emu.c (read_command_list): New dummy function. - -2009-05-02 Robert Millan - - * util/deviceiter.c (grub_util_iterate_devices): Increase max drive - count to 16 for CCISS and IDA. - -2009-05-02 Robert Millan - - * normal/menu_text.c (grub_wait_after_message): Print a newline - after waiting for user input. - - * loader/i386/linux.c: Include `'. - (grub_cmd_linux): Improve the error message about `ask' mode, by - waiting for user input so it's not missed (we can do this, since - user requested interaction). - -2009-05-02 Vladimir Serbinenko - - Added missing lst to grub-mkrescue - - * util/i386/pc/grub-mkrescue.in: added ${input_dir}/handler.lst - and ${input_dir}/parttool.lst - -2009-04-30 David S. Miller - - * util/hostdisk.c (device_is_wholedisk): New function. - (grub_util_biosdisk_get_grub_dev): Shortcut when hdg.start is - zero only if device_is_wholedisk() returns true. - - * util/hostdisk.c (convert_system_partition_to_system_disk): - Handle virtual disk devices named /dev/vdiskX as found on sparc - and powerpc. - - * kern/sparc64/ieee1275/init.c (grub_machine_set_prefix): If - lettered partition specifier is found, convert to numbered. - -2009-04-29 David S. Miller - - * include/grub/powerpc/ieee1275/memory.h: Include ieee1275.h. - * include/grub/sparc64/ieee1275/memory.h: Likewise. - - * normal/command.c: Add missing newline at end of file. - - * commands/lsmmap.c (grub_cmd_lsmmap): Add casts to avoid printf - warnings. - * kern/ieee1275/openfw.c (grub_claimmap): Likewise. - * disk/ieee1275/ofdisk.c (grub_ofdisk_open, grub_ofdisk_close, - grub_ofdisk_read): Likewise, and deal similarly with the fact that - ihandles have a 32-bit type but need to be stored in a "void *". - -2009-04-28 Pavel Roskin - - * disk/fs_uuid.c (grub_fs_uuid_open): Use parent->data for dev, - not disk. Adjust all dependencies. - (grub_fs_uuid_close): Use grub_device_close(), not - grub_disk_close(). - - * disk/fs_uuid.c (grub_fs_uuid_open): Allocate memory to copy - parent's partition, don't copy it by reference, as it gets freed - on close. - -2009-04-27 Vladimir Serbinenko - - Preboot hooks support - - * commands/boot.c (struct grub_preboot_t): new declaration - (preboots_head): new variable - (preboots_tail): likewise - (grub_loader_register_preboot_hook): new function - (grub_loader_unregister_preboot_hook): likewise - (grub_loader_set): launch preboot hooks - * include/grub/loader.h (grub_loader_preboot_hook_prio_t): new type - (grub_loader_register_preboot_hook): new declaration - (grub_loader_unregister_preboot_hook): likewise - -2009-04-27 Vladimir Serbinenko - - Warning fix - - * disk/scsi.c (grub_scsi_open): added missing cast when - calling grub_dprintf - -2009-04-26 Vladimir Serbinenko - - Bug and warning fixes - - * include/grub/i386/pc/init.h (grub_stop_floppy): added missing - declaration - * commands/test.c (test_parse): fixed bug with file tests and corrected - declaration of find_file - -2009-04-26 Pavel Roskin - - * Makefile.in: Don't install empty manual pages if help2man is - missing. Use help2man option for output, not shell redirection. - -2009-04-26 David S. Miller - - * util/grub-mkdevicemap.c (make_device_map): Add missing - NESTED_FUNC_ATTR to process_device(). - -2009-04-25 Vladimir Serbinenko - - Test command - - * commands/test.c: rewritten to use bash-like test - -2009-04-25 Vladimir Serbinenko - - Parttool autoloading and improvements - - * Makefile.in (pkglib_DATA): add parttool.lst - (parttool.lst): new target - * genmk.rb: generate parttool-* - (CLEANFILES): add #{parttool} - (PARTTOOLFILES): new variable - * genparttoollist.sh: new file - * parttool/pcpart.c (grub_pcpart_boot): more feedback - (grub_pcpart_type): likewise - * commands/parttool.c (helpmsg): new variable - (grub_cmd_parttool): output help if not enough arguments are supplied - autoload modules - (GRUB_MOD_INIT(parttool)): use helpmsg - -2009-04-24 David S. Miller - - Avoiding opening same device multiple times in device iterator. - - * kern/device.c: (grub_device_iterate): Define struct part_ent, - and use it to build a list of partitions in iterate_disk() and - iterate_partition(). - - * disk/fs_uuid.c (grub_fs_uuid_close): Call grub_disk_close() - on disk->data. - - * disk/ieee1275/nand.c (grub_nand_iterate): Return - grub_devalias_iterate() result instead of unconditional 0. - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Likewise. - Also, capture hook return value, either directly or via - grub_children_iterate(), and propagate to caller. - * include/grub/ieee1275/ieee1275.h (grub_devalias_iterate, - grub_children_iterate): Return value is now 'int' instead of - 'grub_err_t'. - * kern/ieee1275/openfw.c (grub_children_iterate): Fix to behave - like a proper iterator, stopping when hooks return non-zero. - (grub_devalias_iterate): Likewise. - -2009-04-23 David S. Miller - - * kern/sparc64/ieee1275/openfw.c: Unused, delete. - -2009-04-22 David S. Miller - - * kern/ieee1275/mmap.c (grub_machine_mmap_iterate): If size_cells - is larger than address_cells, use that value for address_cells too. - - * include/grub/ieee1275/ieee1275.h (IEEE1275_MAX_PROP_LEN, - IEEE1275_MAX_PATH_LEN): Define. - * kern/ieee1275/openfw.c (grub_children_iterate): Dynamically - allocate 'childtype', 'childpath', 'childname', and 'fullname'. - (grub_devalias_iterate): Dynamically allocate 'aliasname' and - 'devtype'. Explicitly NULL terminate devalias expansion. - - * util/sparc64/ieee1275/misc.c: New file. - * util/sparc64/ieee1275/grub-setup.c: New file. - * util/sparc64/ieee1275/grub-ofpathname.c: New file. - * util/sparc64/ieee1275/grub-mkimage.c: New file. - * util/sparc64/ieee1275/grub-install.in: New file. - * util/ieee1275/ofpath.c: New file. - * util/ieee1275/devicemap.c: New file. - * util/devicemap.c: New file. - * util/deviceiter.c: New file. - * kern/sparc64/ieee1275/init.c: New file. - * include/grub/util/ofpath.h: New file. - * include/grub/util/deviceiter.h: New file. - * util/grub-mkdevicemap.c: Include deviceiter.h. - Implement using grub_util_emit_devicemap_entry and - grub_util_iterate_devices. - * conf/i386-corebook.rmk: Build util/deviceiter.c and - util/devicemap.c into grub-mkdevicemap - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Add rules to build boot block - images and installation utilities. Build kernel as image - instead of as elf binary. Use common rules as much as possible. - -2009-04-19 Vladimir Serbinenko - - Correct GPT definition - - * include/grub/gpt_partition.h (grub_gpt_partentry): Corrected the type - of "attrib" member - -2009-04-19 Felix Zielcke - - * INSTALL: Replace `autogen.sh' with `./autogen.sh'. - -2009-04-19 David S. Miller - - * loader/sparc64/ieee1275/linux.c: Include grub/command.h - (grub_rescue_cmd_linux): Rename to... - (grub_cmd_linux): and fix prototype. - (grub_rescue_cmd_initrd): Rename to... - (grub_cmd_initrd): and fix prototype. - (cmd_linux, cmd_initrd): New. - (GRUB_MOD_INIT(linux)): Use grub_register_command(). - (GRUB_MOD_FINI(linux): Use grub_unregister_command(). - -2009-04-17 Pavel Roskin - - * bus/usb/ohci.c (grub_ohci_transaction): Fix incorrect printf - format. - (grub_ohci_transfer): Likewise. - - * bus/usb/usbtrans.c (grub_usb_control_msg): Warning fix. - - * loader/multiboot_loader.c (grub_cmd_multiboot_loader): Fix - return without a value. Fix inconsistent indentation. - - * fs/i386/pc/pxe.c (grub_pxefs_dir): Fix function prototype to - match struct grub_fs. - - * disk/ata.c (grub_ata_pciinit): Use NESTED_FUNC_ATTR. - * bus/usb/ohci.c (grub_ohci_pci_iter): Likewise. - * bus/usb/uhci.c (grub_uhci_pci_iter): Likewise. - * commands/lspci.c (grub_lspci_iter): Likewise. - -2009-04-16 Bean - - * commands/efi/loadbios.c (grub_cmd_fakebios): Add missing return - value. - -2009-04-15 Pavel Roskin - - * include/grub/types.h: Rename ULONG_MAX to GRUB_ULONG_MAX and - LONG_MAX to GRUB_LONG_MAX. Introduce GRUB_LONG_MIN. Update all - users of ULONG_MAX, LONG_MAX and LONG_MIN to use the new - definitions. - -2009-04-15 Felix Zielcke - - * disk/lvm.c (grub_lvm_scan_device): Add `LVM' to the error messages, - that no multiple data or metadata areas are supported and `Unknown - metadata header'. - -2009-04-15 Vladimir Serbinenko - - Move loader out of the kernel - - * kern/loader.c: moved to ... - * commands/boot.c: ... moved here - * commands/minicmd.c (grub_mini_cmd_boot): moved to ... - * commands/boot.c (grub_cmd_boot): moved here. All users updated - * include/grub/kernel.h (grub_machine_fini): export - * include/grub/loader.h (grub_loader_is_loaded): update declaration - (grub_loader_set): likewise - (grub_loader_unset): likewise - (grub_loader_boot): likewise - * conf/common.rmk: new module boot.mod - (pkglib_MODULES): add boot.mod - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): remove kern/loader.c - (grub_emu_SOURCES): likewise - * conf/i386-efi.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - * conf/i386-pc.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - * conf/sparc64-ieee1275.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - * conf/x86_64-efi.rmk (kernel_elf_SOURCES): likewise - (grub_emu_SOURCES): likewise - -2009-04-15 Vladimir Serbinenko - - use grub_lltoa instead of grub_itoa and grub_ltoa for all purposes - - * kern/misc.c (grub_itoa): Removed function - (grub_ltoa): likewise - (grub_vsprintf): use grub_lltoa - -2009-04-15 Vladimir Serbinenko - - Restore grub-emu - - * conf/i386-pc.rmk (grub_emu_SOURCES): add normal/handler.c - * conf/i386-coreboot.rmk: likewise - * conf/i386-ieee1275.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - -2009-04-15 Felix Zielcke - - * INSTALL: Add that `./autogen.sh' needs to be run before - `./configure.'. - -2009-04-14 Bean - - * Makefile.in (pkglib_DATA): Add handler.lst. - (handler.lst): New rule. - - * conf/i386-pc.rmk (normal_mod_SOURCES): Add normal/handler.c. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-efi.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - - * genhandlerlist.sh: New file. - - * genmk.rb: Add rules to generate handler.lst. - - * include/grub/normal.h (grub_file_getline): New function definition. - (read_handler_list): Likewise. - (free_handler_list): Likewise. - - * include/grub/term.h (grub_term_register_input): Add name parameter - for auto generation of handler.lst. - (grub_term_register_output): Likewise. - - * normal/handler.c: New file. - - * normal/main.c (get_line): Renamed to grub_file_getline. - (read_config_file): Use the newly renamed grub_file_getline. - (read_command_list): Likewise. - (read_fs_list): Likewise. - (grub_normal_execute): Call read_handler_list to parse handler.lst. - (GRUB_MOD_FINI): Call free_handler_list to free handler list. - - * term/efi/console.c (grub_console_init): Add name parameter for auto - generation of handler.lst. - * term/gfxterm.c: Likewise. - * term/i386/pc/at_keyboard.c: Likewise. - * term/i386/pc/console.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * term/i386/pc/vga.c: Likewise. - * term/i386/pc/vga_text.c: Likewise. - * term/ieee1275/ofconsole.c: Likewise. - * term/usb_keyboard.c: Likewise. - -2009-04-14 Bean - - * util/grub-pe2elf.c (write_symbol_table): Terminate short name symbol - properly with null character. - -2009-04-14 Felix Zielcke - - * configure: Remove. - * config.h.in: Likewise. - * stamp-h.in: Likewise. - * DISTLIST: Likewise. - * conf/common.mk: Likewise. - * conf/i386-coreboot.mk: Likewise. - * conf/i386-efi.mk: Likewise. - * conf/i386-ieee1275.mk: Likewise. - * conf/i386.mk: Likewise. - * conf/i386-pc.mk: Likewise. - * conf/powerpc-ieee1275.mk: Likewise. - * conf/sparc64-ieee1275.mk: Likewise. - * conf/x86_64-efi.mk: Likewise. - - * INSTALL: Remove the sentence that Ruby and autoconf are only required if you - develop on GRUB. - -2009-04-14 John Stanley - David S. Miller - - * util/hostdisk.c (make_device_name): Fix buffer length - calculations. - -2009-04-14 Felix Zielcke - - * util/hostdisk.c [__FreeBSD__ || __FreeBSD_kernel__]: Include - and . - (open_device) [__FreeBSD__ || __FreeBSD_kernel_]: Use sysctlgetbyname() - to add 0x10 to `kern.geom.debugflags' if it's not already set, before - opening the device and reset them afterwards. - -2009-04-13 Pavel Roskin - - * conf/common.rmk (grub_fstest_SOURCES): Add normal/datetime.c. - Reported by John Stanley - -2009-04-13 Robert Millan - - * util/grub.d/10_freebsd.in: Detect Debian GNU/kFreeBSD and use - that name for menuentries when appropriate. - -2009-04-13 Felix Zielcke - - * util/grub.d/10_freebsd.in: Add a missing `fi'. - -2009-04-13 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): Don't pass `vga=ask' parameter - to Linux, simply abort telling the user it's no longer supported. - -2009-04-13 Felix Zielcke - - * util/grub.d/10_freebsd.in: Don't exit if /boot/devices.hints - doesn't exist. Check also for /boot/kernel/kernel.gz. Print - `freebsd_loadenv' only when devices.hints exist. - -2009-04-13 Pavel Roskin - - * term/usb_keyboard.c (grub_usb_keyboard_getkey): Warning fixes. - -2009-04-13 Felix Zielcke - - * util/i386/pc/grub-install.in (install_drive): Remove the BSD - partition number. - (grub_drive): Likewise. - -2009-04-13 David S. Miller - - * kern/sparc64/ieee1275/ieee1275.c: New file. - * include/grub/sparc64/ieee1275/ieee1275.h (IEEE1275_MAP_WRITE, - IEEE1275_MAP_READ, IEEE1275_MAP_EXEC, IEEE1275_MAP_LOCKED, - IEEE1275_MAP_CACHED, IEEE1275_MAP_SE, IEEE1275_MAP_GLOBAL, - IEEE1275_MAP_IE, IEEE1275_MAP_DEFAULT): Define. - (grub_ieee1275_map_physical, grub_ieee1275_claim_vaddr, - grub_ieee1275_alloc_physmem): Declare new exported functions. - - * include/grub/sparc64/ieee1275/loader.h: New file. - * include/grub/sparc64/ieee1275/memory.h: Likewise. - * include/grub/sparc64/kernel.h: Likewise. - * loader/sparc64/ieee1275/linux.c: Likewise. - - * conf/common.rmk (grub_probe_SOURCES): Add Sun partition module. - (grub_fstest_SOURCES): Likewise. - - * util/hostdisk.c (make_device_name): Do not make any assumptions - about the length of drive names. - - * kern/dl.c (grub_dl_load_file): Close file immediately when - we are done using it. - -2009-04-12 David S. Miller - - * kern/misc.c (grub_ltoa): Fix cast when handling negative - values. Noticed by Pavel Roskin. - - * configure.ac: Check for __bswapsi2 and__bswapdi2 using - target compiler. - - * genmk.rb: Add more flexible image type specification, also - pass --strip-unneeded to objcopy. - * conf/i386-pc.rmk: Use *_FORMAT. - * conf/i386-pc.mk: Rebuilt. - - * disk/ieee1275/ofdisk.c (struct ofdisk_hash_ent): New struct. - (OFDISK_HASH_SZ): Define. - (ofdisk_hash): New hash table. - (ofdisk_hash_fn, ofdisk_hash_find, ofdisk_hash_add): New functions. - (grub_ofdisk_open): Use ofdisk_hash_ent address as disk->id - instead of device phandle which is not unique. - - * kern/sparc64/ieee1275/init.c: Delete, replace with... - * kern/sparc64/ieee1275/crt0.S: assembler implementation. - * include/grub/sparc64/ieee1275/kernel.h: Declare grub_prefix[]. - (GRUB_MOD_ALIGN, GRUB_MOD_GAP, GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE, - GRUB_KERNEL_MACHINE_KERNEL_IMAGE_SIZE, - GRUB_KERNEL_MACHINE_COMPRESSED_SIZE, GRUB_KERNEL_MACHINE_PREFIX, - GRUB_KERNEL_MACHINE_DATA_END): Define. - (grub_kernel_image_size, grub_total_module_size): Declare. - -2009-04-12 Pavel Roskin - - * configure.ac: Change the logic when we check for target tools. - Do it when the target is specified and it's different from the - specified value of the host. - -2009-04-11 Felix Zielcke - - * util/hostdisk.c [__FreeBSD_kernel__]: Include sys/disk.h. - (grub_util_biosdisk_open) [__FreeBSD_kernel__]: Add support for - GNU/kFreeBSD. Check if a device is a character device. Use - DIOCGMEDIASIZE to get the size. - (convert_system_partition_to_system_disk) [__FreeBSD_kernel__]: Add - support for GNU/kFreeBSD. - (grub_util_biosdisk_get_grub_dev) [__FreeBSD_kernel__]: Check if OS_DEV - is a character device instead of a block device. Add support for - FreeBSD device names. - - * util/getroot.c (find_root_device) [__FreeBSD_kernel__]: Check if ENT - is a character device instead of a block device. - - * util/grub-probe.c (probe) [__FreeBSD_kernel__]: Check if DEVICE_NAME - is a character device instead of a block device. - -2009-04-11 Andrey Shuvikov - - * util/hostdisk.c [__FreeBSD__]: Include sys/disk.h. - (grub_util_biosdisk_open) [__FreeBSD__]: Add support for - FreeBSD. Check if a device is a character device. Use - DIOCGMEDIASIZE to get the size. - (convert_system_partition_to_system_disk) [__FreeBSD__]: Add - support for FreeBSD. - (grub_util_biosdisk_get_grub_dev) [__FreeBSD__]: Check if OS_DEV - is a character device instead of a block device. Add support for - FreeBSD device names. - - * util/getroot.c (find_root_device) [__FreeBSD__]: Check if ENT is - a character device instead of a block device. - (grub_util_check_char_device): New function. - - * util/grub-probe.c (probe) [__FreeBSD__]: Check if DEVICE_NAME is - a character device instead of a block device. - - * include/grub/util/getroot.h (grub_util_check_char_device): New - prototype. - -2009-04-11 David S. Miller - - * conf/sparc64-ieee1275.rmk (kernel_img_LDFLAGS): Link with - static libgcc. - * configure.ac: Check for __bswapsi2 and __bswapdi2 presence. - * include/grub/sparc64/libgcc.h (__bswapsi2): Export libgcc - function, if present. - (__bswapdi2): Likewise. - - * include/grub/sparc64/ieee1275/boot.h: New file. - * boot/sparc64/ieee1275/boot.S: Likewise. - * boot/sparc64/ieee1275/diskboot.S: Likewise. - - * kern/misc.c (grub_ltoa): New function. - (grub_vsprintf): Use it to format 'long' integers. - -2009-04-10 David S. Miller - - * disk/ieee1275/nand.c (grub_nand_open): All ieee1275 call arg - slots are of type grub_ieee1275_cell_t. - (grub_nand_read): Likewise. - * kern/ieee1275/ieee1275.c (IEEE1275_PHANDLE_INVALID, - IEEE1275_IHANDLE_INVALID): Use grub_ieee1275_cell_t since these - macros are used to compare values in arg/ret block of the call. - (grub_ieee1275_finddevice, grub_ieee1275_get_property, - grub_ieee1275_next_property, grub_ieee1275_get_property_length, - grub_ieee1275_instance_to_package, grub_ieee1275_package_to_path, - grub_ieee1275_instance_to_path, grub_ieee1275_write, - grub_ieee1275_read, grub_ieee1275_seek, grub_ieee1275_peer, - grub_ieee1275_child, grub_ieee1275_parent, grub_ieee1275_open, - grub_ieee1275_close, grub_ieee1275_set_property, - grub_ieee1275_set_color): All ieee1275 call arg slots are of type - grub_ieee1275_cell_t. - * kern/ieee1275/openfw.c (grub_map): Likewise. - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_ihandle_t, - grub_ieee1275_phandle_t): Define as grub_unit32_t type. - - * kern/ieee1275/init.c (grub_machine_init): Make 'actual' grub_ssize_t. - * kern/ieee1275/openfw.c (grub_children_iterate): Likewise. - (grub_devalias_iterate): Likewise. - -2009-04-10 Vladimir Serbinenko - - UFS improvements - - * fs/ufs.c (INODE_NBLOCKS): new definition - (struct grub_ufs_dirent): added fields for non-BSD dirents - (grub_ufs_get_file_block): fixed double indirect handling - (grub_ufs_lookup_symlink): use more robust way to determine whether - symlink is inline - (grub_ufs_find_file): support for non-BSD dirents - (grub_ufs_dir): support for non-BSD dirents - -2009-04-10 Bean - - * include/grub/efi/api.h (grub_efi_configuration_table): Add packed - attribute, otherwise the size would be wrong for i386 platform. - - * include/grub/pci.h (grub_pci_read_word): New inline function. - (grub_pci_read_byte): Likewise. - (grub_pci_write): Likewise. - (grub_pci_write_word): Likewise. - (grub_pci_write_byte): Likewise. - - * include/grub/pci.h (grub_pci_iteratefunc_t): Add NESTED_FUNC_ATTR. - - * loader/i386/efi/linux.c (fake_bios_data): Moved to loadbios module. - (find_framebuf): Scan pci to locate the frame buffer address. - - * commands/efi/fixvideo.c: New file. - - * commands/efi/loadbios.c: Likewise. - - * commands/memrw.c: Likewise. - - * util/grub-dumpbios.in: Likewise. - - * conf/common.rmk (grub-dumpbios): New utility. - (pkglib_MODULES): New module memrw.mod. - (memrw_mod_SOURCE): New macro. - (memrw_mod_CFLAGS): Likewise. - (memrw_mod_LDFLAGS): Likewise. - - * conf/i386-efi.rmk (pkglib_MODULES): New module loadbios.mod and - fixvideo.mod. - (loadbios_mod_SOURCE): New macro. - (loadbios_mod_CFLAGS): Likewise. - (loadbios_mod_LDFLAGS): Likewise. - (fixvideo_mod_SOURCE): Likewise. - (fixvideo_mod_CFLAGS): Likewise. - (fixvideo_mod_LDFLAGS): Likewise. - - * conf/x86_64.rmk (pkglib_MODULES): New module loadbios.mod and - fixvideo.mod. - (loadbios_mod_SOURCE): New macro. - (loadbios_mod_CFLAGS): Likewise. - (loadbios_mod_LDFLAGS): Likewise. - (fixvideo_mod_SOURCE): Likewise. - (fixvideo_mod_CFLAGS): Likewise. - (fixvideo_mod_LDFLAGS): Likewise. - -2009-04-08 Felix Zielcke - - * disk/lvm.c (grub_lvm_scan_device): Add a missing NULL check. - -2009-04-07 David S. Miller - - * kern/sparc64/dl.c (grub_arch_dl_relocate_symbols): Add - support for R_SPARC_OLO10 relocations. Fix compile warning for - R_SPARC_WDISP30 case. - * kern/sparc64/cache.S: Fix grub_arch_sync_caches implementation. - -2009-04-06 Pavel Roskin - - * include/grub/misc.h (ARRAY_SIZE): New macro. - * include/grub/i386/linux.h (GRUB_LINUX_VID_MODE_VESA_START): - New macro. - * loader/i386/linux.c (allocate_pages): Use free_pages(). - (grub_linux_unload): Don't use free_pages(). - (grub_linux_boot): Prevent accessing linux_vesafb_modes with a - wrong index. Treat all other modes as text modes. - (grub_cmd_linux): Initialize vid_mode unconditionally to - GRUB_LINUX_VID_MODE_NORMAL. Recognize and support "vga=ask". - - * commands/help.c (print_command_help): Use cmd->prio, not - cmd->flags to check for GRUB_PRIO_LIST_FLAG_ACTIVE. - -2009-04-06 Vladimir Serbinenko - - Parttool - - * parttool/pcpart.c: new file - * commands/parttool.c: likewise - * conf/common.rmk (pkglib_MODULES): Added parttool.mod and pcpart.mod - (parttool_mod_SOURCES): new variable - (parttool_mod_CFLAGS): likewise - (parttool_mod_LDFLAGS): likewise - (pcpart_mod_SOURCES): likewise - (pcpart_mod_CFLAGS): likewise - (pcpart_mod_LDFLAGS): likewise - * conf/i386-coreboot.rmk (grub_emu_SOURCES): added commands/parttool.c - and parttool/pcpart.c - * conf/i386-efi.rmk: likewise - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - * conf/sparc64-ieee1275.rmk: likewise - * conf/x86_64-ieee1275.rmk: likewise - -2009-04-05 Vladimir Serbinenko - - Support for mtime and further expandability of dir command - - * include/grub/lib/datetime.h: moved to ... - * include/grub/datetime.h: ... moved here and added - declaration of grub_unixtime2datetime. All users updated - * include/grub/fs.h: new syntax for dir and mtime functions in - struct grub_fs - * include/grub/fshelp.h: new declarations of GRUB_FSHELP_TYPE_MASK - and GRUB_FSHELP_FLAGS_MASK - * commands/ls.c (grub_ls_list_files): Write mtime in long format - * fs/ext2.c (grub_ext2_dir): use new dir syntax and supply mtime - (grub_ext2_mtime): new function - * fs/hfsplus.c (grub_hfsplus_dir): use new dir syntax and supply mtime - (grub_hfsplus_mtime): new function - * fs/ufs.c (GRUB_UFS_ATTR_TYPE): new definition - (GRUB_UFS_ATTR_FILE): likewise - (GRUB_UFS_ATTR_LNK): likewise - (struct grub_ufs_sblock): new fields mtime - (grub_ufs_read_inode): new parameter to read inode to a separate buffer - all users updated - (grub_ufs_dir): mtime support - (grub_ufs_mtime): new function - * fs/affs.c (grub_affs_dir): use new dir syntax - * fs/afs.c (grub_afs_dir): likewise - * fs/cpio.c (grub_cpio_dir): likewise - * fs/fat.c (grub_fat_find_dir): likewise - * fs/hfs.c (grub_hfs_dir): likewise - * fs/iso9660.c (grub_iso9660_dir): likewise - * fs/jfs.c (grub_jfs_dir): likewise - * fs/minix.c (grub_minix_dir): likewise - * fs/ntfs.c (grub_ntfs_dir): likewise - * fs/reiserfs.c (grub_reiserfs_dir): likewise - * fs/sfs.c (grub_sfs_dir): likewise - * fs/xfs.c (grub_xfs_dir): likewise - * util/hostfs.c (grub_hostfs_dir): likewise - * lib/datetime.c: moved to ... - * normal/datetime.c: ... moved here - (grub_unixtime2datetime): new function - * kern/rescue.c (grub_rescue_print_files): use new dir syntax - * normal/completion.c (iterate_dir): use new dir syntax - * normal/misc.c (grub_normal_print_device_info): tell the - last modification time of a volume - * kern/fs.c (grub_fs_probe): updated dummy function to use new syntax - * conf/common.rmk: added lib/datetime.c to ls.mod - * conf/i386-coreboot.rmk (grub_emu_SOURCES): add normal/datetime.c - (normal_mod_SOURCES): likewise - (datetime_mod_SOURCES): Removed lib/datetime.c - * conf/i386-efi.rmk: likewise - * conf/i386-ieee1275.rmk: likewise - * conf/i386-pc.rmk: likewise - * conf/powerpc-ieee1275.rmk: likewise - * conf/sparc64-ieee1275.rmk: likewise - * conf/x86_64-efi.rmk: likewise - -2009-04-05 Vladimir Serbinenko - - Trim trailing spaces in FAT label and support mtools-like labels - - * fs/fat.c (grub_fat_iterate_dir): New function based - on grub_fat_find_dir - (grub_fat_find_dir): use grub_fat_iterate_dir - (grub_fat_label): likewise - -2009-04-04 Vladimir Serbinenko - - * conf/powerpc-ieee1275.rmk (kernel_elf_HEADERS): add list.h - and command.h - remove extraneous kernel_elf_HEADERS - -2009-04-04 Bean - - * include/grub/util/misc.h: Add dummy function fsync for mingw. - - * util/misc.c: Likewise. - -2009-04-04 Yoshinori K. Okuji - - * loader/i386/efi/linux.c (fake_bios_data): Use grub_dprintf - instead of grub_printf. - -2009-04-03 Robert Millan - - * loader/i386/linux.c (grub_linux_setup_video): Fill - `params->{red,green,blue,reserved}_{mask_size,field_pos}' with - values from `mode info' structure instead of hardcoded - values. - -2009-04-01 Pavel Roskin - - * Makefile.in: Remove all references to MODULE_LDFLAGS, it's - unused now. - * genmk.rb: Likewise. - * configure.ac: Likewise. - -2009-04-01 Manoel Abranches - - * aclocal.m4: Move --build-id=none from MODULE_LDFLAGS to - TARGET_LDFLAGS. This corrects a problem with grub-mkelfimage. - -2009-04-01 David S. Miller - - * normal/sparc64/setjmp.S: Fix setjmp implementation. - * include/grub/sparc64/setjmp.h (grub_jmp_buf): Update. - (grub_setjmp): Mark with 'returns_twice' attribute. - * include/grub/i386/setjmp.h (grub_setjmp): Likewise - * include/grub/powerpc/setjmp.h (grub_setjmp): Likewise. - * include/grub/x86_64/setjmp.h (grub_setjmp): Likewise. - -2009-04-01 Robert Millan - - Reapply fix from 2008-07-28 which was accidentally reverted; also - perform the same fix to a similar check in same function. - - * disk/raid.c (grub_raid_scan_device): Do not abort when two disks - with the same number are found, just use issue a warning with - grub_dprintf(), as this error has been reported to be non-fatal. - -2009-03-31 Pavel Roskin - - * aclocal.m4 (grub_I386_CHECK_REGPARM_BUG): Provide safe default - for cross-compilation. - -2009-03-30 Robert Millan - - Fix i386-ieee1275 build. - - * include/grub/i386/ieee1275/loader.h (grub_multiboot2_real_boot): - Remove declaration. - -2009-03-30 Pavel Roskin - - * fs/hfs.c (grub_hfs_strncasecmp): Integrate into ... - (grub_hfs_cmp_catkeys): ... this. Don't assume strings to be - zero-terminated, rely only on the strlen value. Fix comparison - of strings differing in length. - -2009-03-30 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): Check for zImage before - checking for abi version. Improve error messages on BIOS to notify - user about `linux16' command. - -2009-03-29 Vladimir Serbinenko - - Leak fixes - - * kern/disk.c (grub_disk_cache_store): Invalidate previous cache - in case of collision - * disk/scsi.c (grub_scsi_open): free scsi in case of error - -2009-03-29 Robert Millan - - * loader/i386/linux.c (grub_cmd_linux): Parse "vga=" parameter and - set `vid_mode' accordingly. - (grub_linux_boot): Process `vid_mode' and set video mode. - -2009-03-29 Robert Millan - - * util/grub.d/10_linux.in (linux_entry): New function. - Factorize generation of Linux boot entries. - -2009-03-29 Yoshinori K. Okuji - - Make the format of Environment Block plain text. The boot loader - part is not tested well yet. - - * util/grub-editenv.c (DEFAULT_ENVBLK_SIZE): New macro. - (buffer): Removed. - (envblk): Likewise. - (usage): Remove "info" and "clear". Add "unset". Update the - description of "set", as this does not delete variables any - longer. - (create_envblk_file): Complete rewrite. - (open_envblk_file): Likewise. - (cmd_info): Removed. - (cmd_list): Likewise. - (cmd_set): Likewise. - (cmd_clear): Likewise. - (list_variables): New function. - (write_envblk): Likewise. - (set_variables): Likewise. - (unset_variables): Likewise. - (main): Complete rewrite. - - * commands/loadenv.c (buffer): Removed. - (envblk): Likewise. - (open_envblk_file): New function. - (read_envblk_file): Complete rewrite. - (grub_cmd_load_env): Likewise. - (grub_cmd_list_env): Likewise. - (struct blocklist): New struct. - (free_blocklists): New function. - (check_blocklists): Likewise. - (write_blocklists): Likewise. - (grub_cmd_save_env): Complete rewrite. - - * include/grub/lib/envblk.h (GRUB_ENVBLK_SIGNATURE): Replaced with - a plain text signature. - (GRUB_ENVBLK_MAXLEN): Removed. - (struct grub_envblk): Complete rewrite. - (grub_envblk_find): Removed. - (grub_envblk_insert): Likewise. - (grub_envblk_open): New prototype. - (grub_envblk_set): Likewise. - (grub_envblk_delete): Put const to VALUE. - (grub_envblk_iterate): Put const to NAME and VALUE. - (grub_envblk_close): New prototype. - (grub_envblk_buffer): New inline function. - (grub_envblk_size): Likewise. - - * lib/envblk.c: Include grub/mm.h. - (grub_env_find): Removed. - (grub_envblk_open): New function. - (grub_envblk_close): Likewise. - (escaped_value_len): Likewise. - (find_next_line): Likewise. - (grub_envblk_insert): Removed. - (grub_envblk_set): New function. - (grub_envblk_delete): Complete rewrite. - (grub_envblk_iterate): Likewise. - -2009-03-28 Robert Millan - - * conf/i386-pc.rmk (pkglib_MODULES): Add `linux16.mod'. - (linux16_mod_SOURCES, linux16_mod_CFLAGS, linux16_mod_LDFLAGS): New - variables. Use 16-bit loader. - (linux_mod_SOURCES, linux_mod_CFLAGS, linux_mod_LDFLAGS): Use 32-bit - loader. - * kern/i386/loader.S (grub_linux_boot): Rename to ... - (grub_linux16_boot): ... this. Update all users. - * loader/i386/linux.c (grub_linux32_boot): Rename to ... - (grub_linux_boot): ... this. Update all users. - - * loader/i386/pc/linux.c (GRUB_MOD_INIT(linux)): Rename to ... - (GRUB_MOD_INIT(linux16)): ... this. Rename `linux' and `initrd' - commands to `linux16' and `initrd16'. - (GRUB_MOD_FINI(linux)): Rename to ... - (GRUB_MOD_FINI(linux16)): ... this. - -2009-03-24 Pavel Roskin - - * genmk.rb: Define ASM_FILE for *.S files for *.lst generation, - not just for compilation. - -2009-03-22 Vladimir Serbinenko - - Move multiboot helper out of kernel - - * conf/i386-pc.rmk (multiboot_mod_SOURCES): Add - `loader/i386/multiboot_helper.S'. - * conf/i386-coreboot.rmk: Likewise - * conf/i386-ieee1275.rmk: Likewise - - * kern/i386/loader.S: Move multiboot helpers from here... - * loader/i386/multiboot_helper.S: ...moved here - * include/grub/i386/loader.h: Move declarations of multiboot - helpers from here... - * include/grub/i386/multiboot.h: ...moved here - * loader/i386/multiboot.c: Added include of grub/cpu/multiboot.h - -2009-03-22 Yoshinori K. Okuji - - * kern/env.c (grub_env_context_open): Added an argument to specify - whether a new context inherits exported variables from current - one. This is useful when making a sandbox to interpret a config - file. - All callers updated. - - * include/grub/env.h (grub_env_context_open): Updated the prototype. - -2009-03-22 Yoshinori K. Okuji - - * kern/env.c (grub_env_context_close): Fix memory leaks. - -2009-03-22 Yoshinori K. Okuji - - * normal/main.c (grub_normal_execute): Added an argument - BATCH to specify if an interactive interface should be provided - after reading a config file. - All callers updated. - (read_command_list): Prevent being executed twice. - (read_fs_list): Likewise. - - * include/grub/normal.h (grub_normal_execute): Updated the - prototype. - -2009-03-22 Pavel Roskin - - * kern/powerpc/ieee1275/startup.S: Replace EXT_C(start) with - _start. - * kern/i386/pc/startup.S: Likewise. - * kern/i386/efi/startup.S: Likewise. - * kern/i386/ieee1275/startup.S: Likewise. - * kern/i386/coreboot/startup.S: Likewise. - * kern/x86_64/efi/startup.S: Likewise. - - * aclocal.m4 (grub_CHECK_START_SYMBOL): Remove. - * configure.ac: Don't call grub_CHECK_START_SYMBOL. - * kern/i386/pc/startup.S: Use _start instead of START_SYMBOL. - -2009-03-21 Vladimir Serbinenko - - Bugfixes in multiboot for bugs uncovered by solaris kernel. - - * loader/i386/multiboot_elfxx.c (grub_multiboot_load_elf): Corrected - limit detection. - Use vaddr of correct segment for entry_point. - -2009-03-21 Bean - - * commands/blocklist.c: Add include file , remove - and . - (grub_cmd_blocklist): Use the new command interface. - (GRUB_MOD_INIT): Likewise. - (GRUB_MOD_FINI): Likewise. - * commands/boot.c: Likewise. - * commands/cat.c: Likewise. - * commands/cmp.c: Likewise. - * commands/configfile.c: Likewise. - * commands/crc.c: Likewise. - * commands/echo.c: Likewise. - * commands/halt.c: Likewise. - * commands/handler.c: Likewise. - * commands/hdparm.c: Likewise. - * commands/help.c: Likewise. - * commands/hexdump.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/ls.c: Likewise. - * commands/lsmmap.c: Likewise. - * commands/lspci.c: Likewise. - * commands/loadenv.c: Likewise. - * commands/read.c: Likewise. - * commands/reboot.c: Likewise. - * commands/search.c: Likewise. - * commands/sleep.c: Likewise. - * commands/test.c: Likewise. - * commands/usbtest.c: Likewise. - * commands/videotest.c: Likewise. - * commands/i386/cpuid.c: Likewise. - * commands/i386/pc/halt.c: Likewise. - * commands/i386/pc/play.c: Likewise. - * commands/i386/pc/pxecmd.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/i386/pc/vbetest.c: Likewise. - * commands/ieee1275/suspend.c: Likewise. - * disk/loopback.c: Likewise. - * font/font_cmd.c: Likewise. - * hello/hello.c: Likewise. - * loader/efi/appleloader.c: Likewise. - * loader/efi/chainloader.c: Likewise. - * loader/i386/bsd.c: Likewise. - * loader/i386/efi/linux.c: Likewise. - * loader/i386/ieee1275/linux.c: Likewise. - * loader/i386/linux.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * term/gfxterm.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/terminfo.c: Likewise. - - * term/i386/pc/vesafb.c: Removed . - * term/i386/pc/vga.c: Likewise. - * video/readers/jpeg.c: Likewise. - * video/readers/png.c: Likewise. - * video/readers/tga.c: Likewise. - - * util/grub-fstest (cmd_loopback): Removed. - (cmd_blocklist): Likewise. - (cmd_ls): Likewise. - (grub_register_command): Likewise. - (grub_unregister_command): Likewise. - (execute_command): Use grub_command_find to locate command and execute - it. - - * include/grub/efi/chainloader.h: Removed. - * loader/efi/chainloader_normal.c: Likewise. - * loader/i386/bsd_normal.c: Likewise. - * loader/i386/pc/chainloader_normal.c: Likewise. - * loader/i386/pc/multiboot_normal.c: Likewise. - * loader/linux_normal.c: Likewise. - * loader/multiboot_loader_normal.c: Likewise. - * loader/powerpc/ieee1275/linux_normal.c: Likewise. - - * gencmdlist.sh: Scan new registration command grub_register_extcmd - and grub_register_command_p1. - - * conf/common.rmk (grub_fstest_SOURCES): Add kern/list.c, - kern/command.c, lib/arg.c and commands/extcmd.c. - (pkglib_MODULES): Remove boot.mod, and minicmd.mod and extcmd.mod. - (minicmd_mod_SOURCES): New variable. - (minicmd_mod_CFLAGS): Likewise. - (minicmd_mod_LDFLAGS): Likewise. - (extcmd_mod_SOURCES): Likewise. - (extcmd_mod_CFLAGS): Likewise. - (extcmd_mod_LDFLAGS): Likewise. - (boot_mod_SOURCES): Removed. - (boot_mod_CFLAGS): Likewise. - (boot_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add kern/command.c and - kern/corecmd.c. - (kernel_img_HEADERS): Add command.h. - (grub_emu_SOURCES): Remove commands/boot.c and normal/arg.c, add - commands/minicmd.c, kern/command.c, kern/corecmd.c, commands/extcmd.c - and lib/arg.c. - (pkglib_MODULES): Change _linux.mod, _chain.mod, _bsd.mod and - _multiboot.mod as linux.mod, chain.mod, bsd.mod and multiboot.mod, - remove the corresponding normal mode command. - (normal_mod_SOURCES): Remove normal/arg.c. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-efi.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - - * include/grub/arg.h: Move from here ... - * include/grub/lib/arg.h: ... to here. - - * normal/arg.c: Move from here ... - * lib/arg.c: ... to here. - - * commands/extcmd.c: New file. - * commands/minicmd.c: Likewise. - * include/grub/command.h: Likewise. - * include/grub/extcmd.h: Likewise. - * kern/command.c: Likewise. - * kern/corecmd.c: Likewise. - - * kern/list.c (grub_list_iterate): Return int instead of void. - (grub_list_insert): New function. - (grub_prio_list_insert): Likewise. - - * kern/rescue.c (grub_rescue_command): Removed. - (grub_rescue_command_list): Likewise. - (grub_rescue_register_command): Likewise. - (grub_rescue_unregister_command): Likewise. - (grub_rescue_cmd_boot): Move to minicmd.c - (grub_rescue_cmd_help): Likewise. - (grub_rescue_cmd_info): Likewise. - (grub_rescue_cmd_boot): Likewise. - (grub_rescue_cmd_testload): Likewise. - (grub_rescue_cmd_dump): Likewise. - (grub_rescue_cmd_rmmod): Likewise. - (grub_rescue_cmd_lsmod): Likewise. - (grub_rescue_cmd_exit): Likewise. - (grub_rescue_print_devices): Moved to corecmd.c. - (grub_rescue_print_files): Likewise. - (grub_rescue_cmd_ls): Likewise. - (grub_rescue_cmd_insmod): Likewise. - (grub_rescue_cmd_set): Likewise. - (grub_rescue_cmd_unset): Likewise. - (attempt_normal_mode): Use grub_command_find to get normal module. - (grub_enter_rescue_mode): Use grub_register_core_commands to register - commands, remove grub_rescue_register_command calls. - - * normal/command.c (grub_register_command): Removed. - (grub_unregister_command): Likewise. - (grub_command_find): Likewise. - (grub_iterate_commands): Likewise. - (rescue_command): Likewise. - (export_command): Moved to corecmd.c. - (set_command): Removed. - (unset_command): Likewise. - (insmod_command): Likewise. - (rmmod_command): Likewise. - (lsmod_command): Likewise. - (grub_command_init): Likewise. - - * normal/completion.c (iterate_command): Use cmd->prio to check for - active command. - (complete_arguments): Use grub_extcmd_t structure to find options. - (grub_normal_do_completion): Change function grub_iterate_commands to - grub_command_iterate. - - * normal/execute.c (grub_script_execute_cmd): No need to parse - argument here. - - * normal/main.c (grub_dyncmd_dispatcher): New function. - (read_command_list): Register unload commands as dyncmd. - (grub_cmd_normal): Use new command interface, register rescue, - unregister normal at entry, register normal, unregister rescue at exit. - - * include/grub/list.h (grub_list_test_t): New type. - (grub_list_iterate): Return int instead of void. - (grub_list_insert): New function. - (GRUB_AS_NAMED_LIST_P): New macro. - (GRUB_AS_PRIO_LIST): Likewise. - (GRUB_AS_PRIO_LIST_P): Likewise. - (GRUB_PRIO_LIST_PRIO_MASK): New constant. - (GRUB_PRIO_LIST_FLAG_ACTIVE): Likewise. - (grub_prio_list): New structure. - (grub_prio_list_insert): New function. - (grub_prio_list_remove): New inline function. - - * include/grub/normal.h: Remove , add . - (GRUB_COMMAND_FLAG_CMDLINE): Moved to command.h. - (GRUB_COMMAND_FLAG_MENU): Likewise. - (GRUB_COMMAND_FLAG_BOTH): Likewise. - (GRUB_COMMAND_FLAG_TITLE): Likewise. - (GRUB_COMMAND_FLAG_NO_ECHO): Likewise. - (GRUB_COMMAND_FLAG_NO_ARG_PARSE): Removed. - (GRUB_COMMAND_FLAG_NOT_LOADED): Likewise. - (grub_command): Likewise. - (grub_register_command): Likewise. - (grub_command_find): Likewise. - (grub_iterate_commands): Likewise. - (grub_command_init): Likewise. - (grub_arg_parse): Likewise. - (grub_arg_show_help): Likewise. - - * include/grub/rescue.h (grub_rescue_register_command): Removed. - (grub_rescue_unregister_command): Likewise. - - * include/grub/i386/bsd.h: Remove grub_rescue_cmd_freebsd, - grub_rescue_cmd_openbsd, grub_rescue_cmd_netbsd, - grub_rescue_cmd_freebsd_loadenv and grub_rescue_cmd_freebsd_module. - - * include/grub/i386/efi/loader.h: Remove grub_rescue_cmd_linux and - grub_rescue_cmd_initrd. - * include/grub/i386/loader.h: Likewise. - * include/grub/x86_64/loader.h: Likewise. - - * include/grub/i386/pc/chainloader.h: Remove grub_chainloader_cmd. - -2009-03-21 Bean - - * util/hostdisk.c (read_device_map): Use grub_util_get_disk_size - instead of stat in mingw environment. - - * util/misc.c (grub_millisleep): Use Sleep in mingw environment. - - * aclocal.m4 (grub_CHECK_LINK_DIR): New function. - - * configure.ac: Use grub_CHECK_LINK_DIR to determine whether to use - AC_CONFIG_LINKS. - -2009-03-21 Bean - - * fs/ext2.c (grub_ext2_mount): Change errno to GRUB_ERR_BAD_FS for - out of range error. - -2009-03-18 Michel Dänzer - - * fs/ext2.c (grub_ext2_read_block): Take endianness into account when - checking inode flags for EXT4_EXTENTS_FLAG. - -2009-03-18 Robert Millan - - * loader/i386/linux.c: Include `' and - `'.. - (grub_linux_setup_video): New function. Loosely based on the EFI one. - (grub_linux32_boot): Attempt to configure video settings with - grub_linux_setup_video(). - (grub_rescue_cmd_linux): Set noreturn=0 in grub_loader_set, in order - to avoid grub_console_fini() which would step out of graphical mode - unconditionally. - -2009-03-14 Robert Millan - - Fix build on powerpc. - * conf/powerpc-ieee1275.rmk (kernel_elf_HEADERS): Add `handler.h'. - -2009-03-12 Vladimir Serbinenko - - * term/gfxterm.c (GRUB_MOD_FINI(term_gfxterm)): Correct name of - background image command. - -2009-03-12 Colin D Bennett - - * term/gfxterm.c (draw_cursor): Ensure character is redrawn. - (grub_gfxterm_putchar): Extract pairs of identical calls to - draw_cursor out of conditional blocks. - -2009-03-11 Pavel Roskin - - * fs/hfs.c (grub_hfs_strncasecmp): New function. - (grub_hfs_cmp_catkeys): Use HFS specific string comparison. - -2009-03-11 Robert Millan - - * loader/i386/multiboot_elfxx.c - (CONCAT(grub_multiboot_load_elf, XX)): Do not reject ET_DYN files. - -2009-03-11 Felix Zielcke - - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): Add `kern/list.c' and - `kern/handler.c'. - -2009-03-11 Robert Millan - - * loader/i386/multiboot.c (code_size): New variable. - (grub_multiboot): Define offsets by adding to `code_size' rather - than subtracting from `grub_multiboot_payload_size'. Provide - 4-byte alignment to MBI and others by increasing - `boot_loader_name_length' appropriately. - - * loader/i386/multiboot_elfxx.c - (CONCAT(grub_multiboot_load_elf, XX)): Initialize `code_size'. - -2009-03-09 Felix Zielcke - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Remove duplicated - `fs/ext2.c'. - -2009-03-08 Robert Millan - - Make loader/i386/linux.c usable on i386-pc again. - - * kern/i386/pc/init.c (grub_machine_init): Disable addition of low - memory to heap. - * loader/i386/linux.c [GRUB_MACHINE_PCBIOS] (allocate_pages): Remove - `#error' stanza. - -2009-03-07 Bean - - * loader/i386/efi/linux.c (grub_rescue_cmd_initrd): Fix a bug in initrd - allocation. - -2009-03-06 Robert Millan - - Fix display issue on terminals with screen size other than 80x25 - (e.g. gfxterm with resolution higher than 640x480). - - * normal/main.c (grub_normal_init_page): Display title text in a - position relative to the center of the terminal instead of relying - on a hardcoded offset. - -2009-03-04 Robert Millan - - Filter /etc/grub.d/10_* so that only add-ons for native kernels are - installed. - - * Makefile.in (host_kernel): New variable. - * conf/common.rmk (grub-mkconfig_SCRIPTS): Conditionalize all 10_*.in - scripts instead of just the windows one. - * configure.ac: Initialize and AC_SUBST `host_kernel'. - -2009-03-04 Felix Zielcke - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `kern/list.c' and - `kern/handler.c'. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - -2009-03-04 Felix Zielcke - - * partmap/pc.c (pc_partition_map_iterate): Skip over invalid BSD partitions - or if there's no space for the disk label and print the partition number on a - invalid magic. - -2009-03-04 Felix Zielcke - - * util/misc.c: Include . - (grub_millisleep): New function. - -2009-03-04 Bean - - * configure.ac: Only test -mcmodel=large option in x86_64-efi, also add - another option -mno-red-zone. - - * commands/handler.c: Change module description. - - * kern/handler.c: Add missing space at the end of description line. - - * kern/list.c: Likewise. - -2009-03-03 Robert Millan - - Move more components to the relocation area, and fix mbi pointer - handling to use the destination rather than the origin (thanks to - Vladimir Serbinenko for spotting). - - * loader/i386/multiboot.c (mbi_dest): New variable. - (grub_multiboot_boot): Use `mbi_dest' instead of `mbi'. - (grub_multiboot): Put cmdline, boot_loader_name and mbi in the - relocation area. - -2009-03-01 Bean - - * include/grub/efi/api.h (GRUB_EFI_MPS_TABLE_GUID): New constant. - (GRUB_EFI_ACPI_TABLE_GUID): Likewise. - (GRUB_EFI_ACPI_20_TABLE_GUID): Likewise. - (GRUB_EFI_SMBIOS_TABLE_GUID): Likewise. - - * loader/i386/efi/linux.c (acpi_guid): New variable. - (acpi_guid): Likewise. - (EBDA_SEG_ADDR): New constant. - (LOW_MEM_ADDR): Likewise. - (FAKE_EBDA_SEG): Likewise. - (fake_bios_data): New function. - (grub_linux_boot): Call fake_bios_data. - -2009-03-01 Bean - - * commands/terminal.c: Removed. - - * commands/handler.c: New file. - - * include/grub/list.h: Likewise. - - * include/grub/handler.h: Likewise. - - * kern/list.c: Likewise. - - * kern/handler.c: Likewise. - - * kern/term.h: Include header file . - (grub_term_input): Move next field to the beginning. - (grub_term_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - (grub_term_register_input): Changed to inline function. - (grub_term_register_output): Likewise. - (grub_term_unregister_input): Likewise. - (grub_term_unregister_output): Likewise. - (grub_term_set_current_input): Likewise. - (grub_term_set_current_output): Likewise. - (grub_term_get_current_input): Likewise. - (grub_term_get_current_output): Likewise. - (grub_term_iterate_input): Removed. - (grub_term_iterate_output): Likewise. - - * kern/term.c (grub_term_list_input): Removed. - (grub_term_list_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - (grub_cur_term_input): Change variable as macro. - (grub_cur_term_output): Likewise. - (grub_term_register_input): Removed. - (grub_term_register_output): Likewise. - (grub_term_unregister_input): Likewise. - (grub_term_unregister_output): Likewise. - (grub_term_set_current_input): Likewise. - (grub_term_set_current_output): Likewise. - (grub_term_iterate_input): Likewise. - (grub_term_iterate_output): Likewise. - (grub_term_get_current_input): Likewise. - (grub_term_get_current_output): Likewise. - - * util/grub-editenv.c: Include header file . - (grub_term_get_current_input): Removed. - (grub_term_get_current_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - - * util/grub-fstest.c (grub_term_get_current_input): Removed. - (grub_term_get_current_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - - * util/grub-probe.c (grub_term_get_current_input): Removed. - (grub_term_get_current_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - - * util/i386/pc/grub-setup.c (grub_term_get_current_input): Removed. - (grub_term_get_current_output): Likewise. - (grub_term_input_class): New variable. - (grub_term_output_class): Likewise. - - * conf/common.rmk (pkglib_MODULES): Replace terminal with handler. - (terminal_mod_SOURCES): Likewise. - (terminal_mod_CFLAGS): Likewise. - (terminal_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_img_SOURCES): Add list.c and handler.c. - (kernel_img_HEADERS): Add list.h and handler.h. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_mod_SOURCES): Add list.c and handler.c. - (kernel_mod_HEADERS): Add list.h and handler.h. - - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_elf_SOURCES): Add list.c and handler.c. - (kernel_elf_HEADERS): Add list.h and handler.h. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_elf_SOURCES): Add list.c and handler.c. - (kernel_elf_HEADERS): Add list.h and handler.h. - - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_mod_SOURCES): Add list.c and handler.c. - (kernel_mod_HEADERS): Add list.h and handler.h. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Replace terminal.c with - handler.c. - (kernel_elf_SOURCES): Add list.c and handler.c. - (kernel_elf_HEADERS): Add list.h and handler.h. - -2009-02-27 Robert Millan - - Factorize elf32 / elf64 code in Multiboot loader. This will - prevent it from getting out of sync again. - - * loader/i386/multiboot.c (grub_multiboot_is_elf32, - grub_multiboot_load_elf32, grub_multiboot_is_elf64, - grub_multiboot_load_elf64): Move from here ... - * loader/i386/multiboot_elfxx.c (grub_multiboot_is_elf, - grub_multiboot_load_elf): ... to here (new file). - -2009-02-27 Robert Millan - - * util/grub.d/10_linux.in: Rename "single-user mode" to - "recovery mode". - -2009-02-27 Vladimir Serbinenko - - Don't leak in SCSI code. - * disk/scsi.c (grub_scsi_close): free `scsi'. - -2009-02-27 Robert Millan - - * loader/i386/pc/multiboot.c: Move from here ... - * loader/i386/multiboot.c: ... to here. Update all users. - -2009-02-27 Robert Millan - - Patch from Alexandre Bique - * util/i386/pc/grub-setup.c (setup): Fix directory path. - -2009-02-27 Krzysztof Smiechowicz - - * fs/sfs.c (grub_sfs_read_extent): Correction to traversing extent - b-tree. - -2009-02-27 Robert Millan - - * kern/misc.c (grub_strtoull): Fix bug (it mistakenly parsed the - `0x' qualifier as 0 when base is specified as parameter). - -2009-02-24 Bean - - * configure.ac: Check for -mcmodel=large in x86_64 target. - - * include/grub/efi/api.h (efi_call_10): New macro. - (efi_wrap_10): New function. - - * include/grub/efi/pe32.h (GRUB_PE32_REL_BASE_HIGH): New macro. - (GRUB_PE32_REL_BASED_HIGH): Likewise. - (GRUB_PE32_REL_BASED_LOW): Likewise. - (GRUB_PE32_REL_BASED_HIGHLOW): Likewise. - (GRUB_PE32_REL_BASED_HIGHADJ): Likewise. - (GRUB_PE32_REL_BASED_MIPS_JMPADDR): Likewise. - (GRUB_PE32_REL_BASED_SECTION): Likewise. - (GRUB_PE32_REL_BASED_REL): Likewise. - (GRUB_PE32_REL_BASED_IA64_IMM64): Likewise. - (GRUB_PE32_REL_BASED_DIR64): Likewise. - (GRUB_PE32_REL_BASED_HIGH3ADJ): Likewise. - - * kern/x86_64/dl.c (grub_arch_dl_relocate_symbols): Fixed relocation - issue. - - * kern/x86_64/efi/callwrap.S (efi_wrap_6): Bug fix. - (efi_wrap_10): New function. - - * kern/x86_64/efi/startup.S (codestart): Use relative addressing. - - * loader/efi/appleloader.c (devpath_5): Add support for late 2008 - MB/MBP model (NV chipset). - (devdata_devs): Add devpath_5 to the list. - - * load/i386/efi/linux.c (video_base): Remove variable. - (RGB_MASK): New macro. - (RGB_MAGIC): Likewise. - (LINE_MIN): Likewise. - (LINE_MAX): Likewise. - (FBTEST_STEP): Likewise. - (FBTEST_COUNT): Likewise. - (fb_list): New variable. - (grub_find_video_card): Remove function. - (find_framebuf): New function. - (grub_linux_setup_video): Use find_framebuf to get frame buffer and - line length. - - * util/i386/efi/grub-mkimage.c (grub_reloc_section): Fix relocation - problem for x86_64. - -2009-02-22 Vesa Jääskeläinen - - Patch #25624 by Kevin Lacquement . - - * util/grub-mkconfig.in: Use ${grub_mkdevicemap} instead of hard - coding tool name. - -2009-02-22 Robert Millan - - * include/multiboot.h (MULTIBOOT_INFO_ALIGN): New macro. - * loader/i386/pc/multiboot.c (grub_multiboot): Include the MBI - in our relocation, instead of using it directly from heap. Also - use `MULTIBOOT_INFO_ALIGN' to ensure it is aligned. - -2009-02-21 Robert Millan - - Implement USB keyboard support (based on patch by Marco Gerards) - - * conf/i386-pc.rmk (pkglib_MODULES): Add `usb_keyboard.mod'. - (usb_keyboard_mod_SOURCES, usb_keyboard_mod_CFLAGS) - (usb_keyboard_mod_LDFLAGS): New variables. - - * term/usb_keyboard.c: New file. - -2009-02-14 Vladimir Serbinenko - - Corrected wrong declaration - - * kern/disk.c: corrected declaration of grub_disk_ata_pass_through. - -2009-02-14 Christian Franke - - * commands/lspci.c (grub_pci_classes): Add `SATA Controller'. - (grub_lspci_iter): Print class code and programming interface byte. - -2009-02-14 Christian Franke - - * gendistlist.sh: Ignore `.svn' directories. - -2009-02-14 Felix Zielcke - - * fs/fat.c: Add 2009 to Copyright line. - -2009-02-14 Christian Franke - - * commands/hdparm.c: New file. Provides `hdparm' command - which sends ATA commands via grub_disk_ata_pass_through (). - - * conf/i386-pc.rmk: Add ata_pthru.mod and hdparm.mod. - - * disk/ata.c: Include . Move - and to include/grub/ata.h. - (enum grub_ata_addressing_t): Move to include/grub/ata.h. - (GRUB_CDROM_SECTOR_SIZE): Remove. - (GRUB_ATA_*): Move to include/grub/ata.h. - (GRUB_ATAPI_*): Likewise. - (enum grub_ata_commands): Likewise. - (enum grub_ata_timeout_milliseconds): Likewise. - (struct grub_ata_device): Likewise. - (grub_ata_regset): Likewise. - (grub_ata_regget): Likewise. - (grub_ata_regset2): Likewise. - (grub_ata_regget2): Likewise. - (grub_ata_check_ready): Likewise. - (grub_ata_wait_not_busy): Remove static, exported in - include/grub/ata.h. - (grub_ata_wait_drq): Likewise. - (grub_ata_pio_read): Likewise. - - * disk/ata_pthru.c: New file. Provides grub_ata_pass_through () - function for hdparm.mod. - - * include/grub/ata.h: New file, contains declarations from - disk/ata.c. - (enum grub_ata_commands): Add new commands for commands/hdparm.c. - - * include/grub/disk.h (grub_disk_ata_pass_through_parms): New struct. - (grub_disk_ata_pass_through): New exported variable. - - * kern/disk.c (grub_disk_ata_pass_through): New variable. - -2009-02-13 Colin D Bennett - - Support multiple fallback entries, and provide an API to support - executing default+fallback menu entries. Renamed the `terminal' menu - viewer to `text'. - - * include/grub/normal.h (grub_normal_text_menu_viewer): New global - variable declaration. - (grub_menu_execute_callback): New structure declaration. - (grub_menu_execute_callback_t): New typedef. - (grub_menu_execute_with_fallback): New function declaration. - (grub_menu_get_entry): Likewise. - (grub_menu_get_timeout): Likewise. - (grub_menu_set_timeout): Likewise. - - * normal/main.c (GRUB_MOD_INIT(normal)): Refer to new variable name. - - * normal/menu.c (grub_wait_after_message): Moved to - `normal/menu_text.c'. - (draw_border): Likewise. - (print_message): Likewise. - (print_entry): Likewise. - (print_entries): Likewise. - (grub_menu_init_page): Likewise. - (get_entry_number): Likewise. - (print_timeout): Likewise. - (run_menu): Likewise. - (grub_menu_execute_entry): Likewise. - (show_text_menu): Likewise. - (get_and_remove_first_entry_number): New function. - (grub_menu_execute_with_fallback): Likewise. - (get_entry): Renamed to ... - (grub_menu_get_entry): .. this and made it global. - (get_timeout): Renamed to ... - (grub_menu_get_timeout): ... this and made it global. - (set_timeout): Renamed to ... - (grub_menu_set_timeout): ... this and made it global. - (grub_normal_terminal_menu_viewer): Renamed to ... - (grub_normal_text_menu_viewer): ... this. - - * normal/menu_text.c: New file. Extracted text-menu-specific code - from normal/menu.c. - - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add `normal/menu_text.c'. - (normal_mod_SOURCES): Likewise. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * conf/i386-pc.rmk, (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - -2009-02-11 Robert Millan - - * util/grub.d/00_header.in: Update old reference to `font' command. - -2009-02-10 Felix Zielcke - - * fs/fat.c (grub_fat_mount): Fix wrong comparison. - - Based on patch from Javier MartĂ­n. - -2009-02-09 Felix Zielcke - - * conf/common.rmk (grub_probe_SOURCES): Move fs/ext2.c before fs/fat.c - to avoid false positives with FAT. - (grub_fstest_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - -2009-02-09 Felix Zielcke - - * fs/fat.c (grub_fat_mount): Try to avoid false positives by checking - bpb.version_specific.fat12_or_fat16.fstype and - bpb.version_specific.fat32.fstype. - -2009-02-08 Robert Millan - - * fs/tar.c: Replace "fs/cpio.c" with "cpio.c". - -2009-02-08 Robert Millan - - * Makefile.in (host_os, host_cpu): New variables. - (target_os): Remove. Update all users. - -2009-02-08 Marco Gerards - - * Makefile.in (enable_grub_emu_usb): New variable. - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `disk/scsi.c'. - (grub_emu_SOURCES) [grub_emu_SOURCES]: Add `disk/usbms.c', - `util/usb.c', `bus/usb/usb.c' and `commands/usbtest.c'. - (grub_emu_LDFLAGS): Add `$(LIBUSB)'. - (pkglib_MODULES): Add `usb.mod', `uhci.mod', `ohci.mod', - `usbtest.mod' and `usbms.mod'. - (usb_mod_SOURCES, usb_mod_CFLAGS, usb_mod_LDFLAGS) - (usbtest_mod_SOURCES, usbtest_mod_CFLAGS, usbtest_mod_LDFLAGS) - (uhci_mod_SOURCES, uhci_mod_CFLAGS, uhci_mod_LDFLAGS, - (ohci_mod_SOURCES, ohci_mod_CFLAGS, ohci_mod_LDFLAGS) - (usbms_mod_SOURCES, usbms_mod_CFLAGS, usbms_mod_LDFLAGS): New - variables. - - * disk/usbms.c: New file. - - * include/grub/usb.h: Likewise. - - * include/grub/usbtrans.h: Likewise. - - * include/grub/usbdesc.h: Likewise. - - * bus/usb/usbtrans.c: Likewise. - - * bus/usb/ohci.c: Likewise. - - * bus/usb/uhci.c: Likewise. - - * bus/usb/usbhub.c: Likewise. - - * bus/usb/usb.c: Likewise. - - * commands/usbtest.c: Likewise. - - * util/usb.c: Likewise. - - * include/grub/err.h (grub_err_t): Add `GRUB_ERR_IO'. - - * configure.ac: Test for libusb presence. - - * util/grub-emu.c (main) [HAVE_LIBUSB_H]: Call `grub_libusb_init'. - -2009-02-08 Vesa Jääskeläinen - - * kern/mm.c: Add more comments. - -2009-02-08 Robert Millan - - Patch from Javier MartĂ­n. - * fs/ext2.c (EXT2_DRIVER_SUPPORTED_INCOMPAT): Add - `EXT4_FEATURE_INCOMPAT_FLEX_BG'. - -2009-02-08 Robert Millan - - * fs/cpio.c: Split tar functionality to ... - * fs/tar.c: ... here (new file). Update all users. - -2009-02-07 Robert Millan - - * fs/ext2.c (grub_ext2_mount): Avoid mounting filesystems with - backward-incompatible features. - - Based on patch from Javier MartĂ­n, with some adjustments. - -2009-02-07 Michael Scherer - - * fs/hfs.c (grub_hfsplus_iterate_dir): Treat hfs+ as case insensitive. - -2009-02-07 Robert Millan - - * conf/common.rmk (grub_probe_SOURCES, grub_fstest_SOURCES): Move - position of `disk/lvm.c' to ensure grub_init_all() always picks it - after the RAID stuff. - -2009-02-05 Vesa Jääskeläinen - - Fixes problem when running vbetest command as reported by - Vladimir Serbinenko . - - * (grub_vbe_set_video_mode): Fixed problem with text modes. - -2009-02-04 Felix Zielcke - - util/getroot.c (grub_util_get_grub_dev): Add support for /dev/mdNpN and - /dev/md/NpN style mdraid devices. - -2009-02-03 Felix Zielcke - - * util/unifont2pff.rb: Remove. - -2009-02-03 Felix Zielcke - - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Add a missing trailing - `#'. - -2009-02-03 Felix Zielcke - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `normal/menu_viewer.c'. - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - -2009-02-02 Christian Franke - - * lib/hexdump.c (hexdump): Print at most 3 lines if data is identical. - -2009-02-01 Felix Zielcke - - * INSTALL: Note that we now require at least autoconf 2.59 and - that LZO is optional. - -2009-02-01 Vesa Jääskeläinen - - Base on patch on bug #24154 created by Tomas Tintera - . - - * video/i386/pc/vbe.c (grub_video_vbe_scroll): Fix downward scrolling. - -2009-02-01 Vesa Jääskeläinen - - Based on patch on bug #25318 created by Bernhard Rosenkraenzer - . - - * normal/parser.y (script_init): Add missing semicolon. - -2009-01-31 Colin D Bennett - - * normal/main.c: Add include to grub/menu_viewer.h. - (free_menu_entry_classes): Added. - (grub_normal_menu_addentry): Added class property handling. - (grub_normal_execute): Changed to use new menu viewer for menu viewing. - (GRUB_MOD_INIT(normal)): Added register for text based menu viewer. - - * normal/menu_viewer.c: New file. - - * normal/menu.c (run_menu_entry): Renamed to ... - (grub_menu_execute_entry): ... this and made it as global. - (grub_menu_run): Renamed to ... - (show_text_menu): ... this and made it local. - (show_text_menu): Adapt to new function names. - (grub_normal_terminal_menu_viewer): New global variable. - - * include/grub/menu.h: New file. - - * include/grub/menu_viewer.h: New file. - - * include/grub/normal.h: Added include to grub/menu.h. - (grub_menu_entry): Moved to include/grub/menu.h. - (grub_menu_entry_t): Likewise. - (grub_menu): Likewise. - (grub_menu_t): Likewise. - (grub_normal_terminal_menu_viewer): Added. - (grub_menu_execute_entry): Likewise. - (grub_menu_run): Removed. - - * DISTLIST: Added include/grub/menu.h. - Added include/grub/menu_viewer.h. - Added normal/menu_viewer.c. - -2009-01-31 Vesa Jääskeläinen - - * normal/execute.c (grub_script_execute_menuentry): Changed to use - arglist for menutitle arguments. - - * normal/main.c (grub_normal_menu_addentry): Likewise. - - * normal/parser.y (menuentry): Likewise. - - * normal/script.c (grub_script_create_cmdmenu): Likewise. - - * include/grub/script.h (grub_script_cmd_menuentry): Likewise. - (grub_script_create_cmdmenu): Likewise. - - * include/grub/normal.h (grub_normal_menu_addentry): Likewise. - - * conf/i386-pc.rmk (normal_mod_SOURCES): Adapt Colin D Bennett's - changes. - - * conf/x86_64-efi.rmk (normal_mod_SOURCES): Likewise. - - * conf/i386-coreboot.rmk (normal_mod_SOURCES): Likewise. - - * conf/i386-efi.rmk (normal_mod_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (normal_mod_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (normal_mod_SOURCES): Likewise. - - * conf/sparc64-ieee1275.rmk (normal_mod_SOURCES): Likewise. - -2009-01-30 Christian Franke - - * normal/arg.c (grub_arg_show_help): Add indentation if '\n' appears - in option help text. - -2009-01-27 Pavel Roskin - - * disk/fs_uuid.c (search_fs_uuid): Ignore case of the UUID. - -2009-01-27 Vesa Jääskeläinen - - * commands/lsmmap.c: Add include to grub/machine/memory.h. - - * fs/i386/pc/pxe.c (grub_pxefs_open): Fix sign problem. - - * term/i386/pc/at_keyboard.c (GRUB_MOD_FINI(at_keyboard)): Use proper - unregister function. - -2009-01-27 Vesa Jääskeläinen - - * disk/scsi.c (grub_scsi_read): Fix sign problem. - - * term/i386/pc/vga_text.c (grub_vga_text_init_fini). Fix declaration. - - * util/grub-mkfont.c (usage): Fix typo. - - * util/elf/grub-mkimage.c (load_modules): Fix warning. - -2009-01-26 Daniel Mierswa - - * fs/fat.c (grub_fat_uuid): Fix shift of the first two bytes. - - * commands/search.c (search_fs_uuid): Ignore case of the UUID. - - * kern/misc.c (grub_strcasecmp): New function. - (grub_strcasecmp): Use grub_size_t instead of int for length. - Fix return value. - * include/grub/misc.h: Update function prototypes. - -2009-01-26 Robert Millan - - * configure.ac: Fix cross-compilation check. - -2009-01-22 Christian Franke - - * kern/misc.c (grub_vsprintf): Fix size and termination of `format2' - (precision) digit string. Allow `.format2' without `format1' (width). - Limit input chars for `%s' output to `format2' if specified. This is - compatible with standard printf (). - -2009-01-22 Christian Franke - - * disk/ata.c (grub_ata_wait_status): Replace by ... - (grub_ata_wait_not_busy): ... this function. Checks only BSY bit, - other status bits may be invalid while BSY is asserted. - (grub_ata_check_ready): New function. - (grub_ata_cmd): Removed. - (grub_ata_wait_drq): New function. - (grub_ata_strncpy): Remove inline. - (grub_ata_pio_read): Reduce to actual block transfer. BSY wait - and error check now done by grub_ata_wait_drq (). - (grub_ata_pio_write): Likewise. - (grub_atapi_identify): Set DEV before check for !BSY. Use - grub_ata_wait_drq () to wait for data. - (grub_ata_device_initialize): Add status register check to - detect missing SATA slave devices. Add debug messages. - (grub_atapi_wait_drq): Use grub_ata_wait_not_busy (). - (grub_atapi_packet): Set DEV before check for !BSY. Replace - transfer loop by grub_ata_pio_write (). - (grub_ata_identify): Set DEV before check for !BSY. Use - grub_ata_wait_drq () to wait for data. - (grub_ata_setaddress): Set DEV before check for !BSY. - (grub_ata_readwrite): Remove duplicate code, handle batch/rest and - read/write in one loop. Fix invalid command on write. Fix incomplete - command on (size % batch) == 0. Add missing error check after write of - last block. Add debug messages. - (grub_atapi_read): Replace transfer loop by grub_ata_pio_read (). - -2009-01-19 Christian Franke - - * disk/ata.c (GRUB_ATAPI_REG_*): New defines. - (GRUB_ATAPI_IREASON_*): Likewise. - (grub_ata_pio_write): Fix timeout error return. - (grub_atapi_identify): Add grub_ata_wait () after cmd. - (grub_atapi_wait_drq): New function. - (grub_atapi_packet): New parameter `size'. - Use grub_atapi_wait_drq () and direct write instead of - grub_ata_pio_write (). - (grub_atapi_read): Replace grub_ata_pio_read () by a loop which - reads the number of bytes requested by the device for each DRQ - assertion. - (grub_atapi_write): Remove old implementation, return not - implemented instead. - -2009-01-19 Christian Franke - - * disk/scsi.c (grub_scsi_read10): Use scsi->blocksize instead - of 512 to calculate data size. - (grub_scsi_read12): Likewise. - (grub_scsi_write10): Likewise. - (grub_scsi_write12): Likewise. - (grub_scsi_read): Adjust size according to blocksize. - Add checks for invalid blocksize and unaligned transfer. - -2009-01-19 Vesa Jääskeläinen - - * font/font.c (grub_font_loader_init): Re-position unknown glyph. - - * term/gfxterm.c (write_char): Fix background rendering for wide - width glyphs. - -2009-01-19 Robert Millan - - * config.guess: Update to latest version from config git. - * config.sub: Likewise. - -2009-01-17 Felix Zielcke - - * Makefile.in: Change font compilation to use new grub-mkfont instead - of java version. - - * util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java: Remove. - * util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/Converter.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/Font.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/Glyph.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java: Likewise. - * util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java: Likewise. - -2009-01-16 Christian Franke - - * disk/ata.c (enum grub_ata_commands): Remove EXEC_DEV_DIAGNOSTICS. - (enum grub_ata_timeout_milliseconds): New enum. - (grub_ata_wait_status): Add parameter milliseconds. - (grub_ata_cmd): Remove variable `err'. Remove wait for !DRQ to allow - recovery from timed-out commands. - (grub_ata_pio_read): Add parameter milliseconds. Fix error return, - return grub_errno instead of REG_ERROR. - (grub_ata_pio_write): Add parameter milliseconds. - (grub_atapi_identify): Fix size of ATAPI IDENTIFY sector. - Pass milliseconds to grub_ata_wait_status () and - grub_ata_pio_read (). - (grub_atapi_packet): Pass milliseconds to grub_ata_pio_write (). - (grub_ata_identify): Remove variable `ataerr'. Pass milliseconds to - grub_ata_wait_status (). Fix IDENTIFY timeout check. - (grub_ata_device_initialize): Remove EXECUTE DEVICE DIAGNOSTICS. - It is not suitable for device detection, because DEV bit is ignored, - the command may run too long, and not all devices set the signature - properly. - (grub_ata_pciinit): Clear grub_errno before grub_ata_device_initialize (). - (grub_ata_setaddress): Pass milliseconds to grub_ata_wait_status (). - Fix device selection, DEV bit must be set first to address the registers - of the correct device. - (grub_ata_readwrite): Pass milliseconds to grub_ata_wait_status () and - grub_ata_pio_read/write (). - (grub_atapi_read): Pass milliseconds to grub_ata_pio_read (). - (grub_atapi_write): Pass milliseconds to grub_ata_pio_write (). - -2009-01-13 Carles Pina i Estany - - * util/grub-editenv.c (main): Use fseeko(), not fseek(). - -2009-01-13 Bean - - * util/grub-mkfont.c (write_font): forget to remove some debug code. - -2009-01-13 Bean - - * Makefile.in: (enable_grub_mkfont): New variable. - (freetype_cflags): Likewise. - (freetype_libs): Likewise. - - * common.rmk (bin_UTILITIES): Add `grub-mkfont' if requested. - (grub_mkfont_SOURCES): New variable. - (grub_mkfont_CFLAGS): Likewise. - (grub_mkfont_LDFLAGS): Likewise. - - * configure.ac (--enable-grub-mkfont): New option. Check for freetype2 - library if `--enable-grub-mkfont' is requested. - (enable_grub_mkfont): New variable. - (freetype_cflags): Likewise. - (freetype_libs): Likewise. - - * util/grub-mkfont.c: New file. - -2009-01-12 Christian Franke - - * disk/ata.c (grub_ata_pciinit): Fix bit numbers of compatibility - mode check. Fix setting of compat_use[]. - -2009-01-10 Robert Millan - - Update a few copyright years which we forgot to do in 2008 (only for - files whose changes made in 2008 were copyright-significant) - - * Makefile.in: Add 2008 to Copyright line. - * disk/ieee1275/ofdisk.c: Likewise. - * disk/efi/efidisk.c: Likewise. - * kern/dl.c: Likewise. - * kern/sparc64/ieee1275/init.c: Likewise. - * kern/mm.c: Likewise. - * kern/efi/mm.c: Likewise. - * boot/i386/pc/boot.S: Likewise. - * genfslist.sh: Likewise. - * fs/iso9660.c: Likewise. - * fs/hfs.c: Likewise. - * fs/jfs.c: Likewise. - * fs/minix.c: Likewise. - * fs/ufs.c: Likewise. - * gensymlist.sh.in: Likewise. - * genkernsyms.sh.in: Likewise. - * include/grub/misc.h: Likewise. - * include/grub/types.h: Likewise. - * include/grub/symbol.h: Likewise. - * include/grub/elf.h: Likewise. - * include/grub/kernel.h: Likewise. - * include/grub/disk.h: Likewise. - * include/grub/dl.h: Likewise. - * include/grub/i386/linux.h: Likewise. - * include/grub/i386/pc/biosdisk.h: Likewise. - * include/grub/efi/api.h: Likewise. - * include/grub/efi/pe32.h: Likewise. - * include/grub/util/misc.h: Likewise. - * normal/execute.c: Likewise. - * normal/arg.c: Likewise. - * normal/completion.c: Likewise. - * normal/lexer.c: Likewise. - * normal/parser.y: Likewise. - * normal/misc.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/hexdump.c: Likewise. - * commands/terminal.c: Likewise. - * commands/ls.c: Likewise. - * commands/help.c: Likewise. - * partmap/pc.c: Likewise. - * loader/efi/chainloader.c: Likewise. - * loader/multiboot_loader.c: Likewise. - * loader/i386/pc/multiboot2.c: Likewise. - * term/efi/console.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * util/lvm.c: Likewise. - * util/console.c: Likewise. - * util/i386/efi/grub-mkimage.c: Likewise. - * util/raid.c: Likewise. - -2009-01-06 Vesa Jääskeläinen - - * commands/videotest.c: Removed include to grub/machine/memory.h. - - * conf/i386-pc.rmk (pkglib_MODULES): Removed video.mod, gfxterm.mod, - videotest.mod, bitmap.mod, tga.mod, jpeg.mod, png.mod. - (video_mod_SOURCES): Removed. - (video_mod_CFLAGS): Likewise. - (video_mod_LDFLAGS): Likewise. - (gfxterm_mod_SOURCES): Likewise. - (gfxterm_mod_CFLAGS): Likewise. - (gfxterm_mod_LDFLAGS): Likewise. - (videotest_mod_SOURCES): Likewise. - (videotest_mod_CFLAGS): Likewise. - (videotest_mod_LDFLAGS): Likewise. - (bitmap_mod_SOURCES): Likewise. - (bitmap_mod_CFLAGS): Likewise. - (bitmap_mod_LDFLAGS): Likewise. - (tga_mod_SOURCES): Likewise. - (tga_mod_CFLAGS): Likewise. - (tga_mod_LDFLAGS): Likewise. - (jpeg_mod_SOURCES): Likewise. - (jpeg_mod_CFLAGS): Likewise. - (jpeg_mod_LDFLAGS): Likewise. - (png_mod_SOURCES): Likewise. - (png_mod_CFLAGS): Likewise. - (png_mod_LDFLAGS): Likewise. - - * conf/common.rmk (pkglib_MODULES): Added video.mod, videotest.mod, - bitmap.mod, tga.mod, jpeg.mod, png.mod, font.mod, gfxterm.mod - (video_mod_SOURCES): Added. - (video_mod_CFLAGS): Likewise. - (video_mod_LDFLAGS): Likewise. - (videotest_mod_SOURCES): Likewise. - (videotest_mod_CFLAGS): Likewise. - (videotest_mod_LDFLAGS): Likewise. - (bitmap_mod_SOURCES): Likewise. - (bitmap_mod_CFLAGS): Likewise. - (bitmap_mod_LDFLAGS): Likewise. - (tga_mod_SOURCES): Likewise. - (tga_mod_CFLAGS): Likewise. - (tga_mod_LDFLAGS): Likewise. - (jpeg_mod_SOURCES): Likewise. - (jpeg_mod_CFLAGS): Likewise. - (jpeg_mod_LDFLAGS): Likewise. - (png_mod_SOURCES): Likewise. - (png_mod_CFLAGS): Likewise. - (png_mod_LDFLAGS): Likewise. - (gfxterm_mod_SOURCES): Likewise. - (gfxterm_mod_CFLAGS): Likewise. - (gfxterm_mod_LDFLAGS): Likewise. - - * term/gfxterm.c: Removed include to grub/machine/memory.h, - grub/machine/console.h. - -2009-01-04 Jerone Young - - Make on screen instructions clearer - - Based on patch created by Jidanni - - * normal/menu.c: print clearer instructions on the screen - -2009-01-02 Colin D Bennett - - New font engine. - - Additional changes by Vesa Jääskeläinen to adapt to - build system and fixed gfxterm.c to work with different sized fonts. - - * configure.ac: Changed UNIFONT_HEX to UNIFONT_BDF. - - * configure: Re-generated. - - * DISTLIST: Removed font/manager.c. - Added font/font.c. - Added font/font_cmd.c. - - * Makefile.in: Changed UNIFONT_HEX to UNIFONT_BDF. Added Font tool - compilation. - - * include/grub/misc.h (grub_utf8_to_ucs4): Changed prototype. Changed users. - - * kern/misc.c (grub_utf8_to_ucs4): Changed prototype. - - * kern/term.c: Changed users of grub_utf8_to_ucs4. - - * normal/menu.c: Likewise. - - * conf/common.rmk (font_mod_SOURCES): Removed font/manager.c. - (font_mod_SOURCES): Added font/font_cmd.c, font/font.c. - - * include/grub/font.h: Replaced with new file. - - * include/grub/video.h (GRUB_VIDEO_MODE_TYPE_ALPHA): Changed value. - (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED): Likewise. - (GRUB_VIDEO_MODE_TYPE_COLOR_MASK): Likewise. - (GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP): Added. - (grub_video_blit_format): Added GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED. - (grub_video_mode_info): Added bg_red, bg_green, bg_blue, bg_alpha, - fg_red, fg_green, fg_blue, fg_alpha. - (grub_video_adapter): Removed blit_glyph. - (grub_video_blit_glyph): Removed. - - * font/manager.c: Removed file. - - * font/font.c: New file. - - * font/font_cmd.c: Likewise. - - * video/video.c (grub_video_blit_glyph): Removed. - - * video/i386/pc/vbe.c (grub_video_vbe_map_rgb): Added 1-bit support. - (grub_video_vbe_map_rgba): Likewise. - (grub_video_vbe_unmap_color_int): Likewise. - (grub_video_vbe_blit_glyph): Removed. - (grub_video_vbe_adapter): Removed blit_glyph. - - * video/i386/pc/vbeutil.c (get_data_ptr): Added 1-bit support. - (get_pixel): Likewise. - (set_pixel): Likewise. - - * commands/videotest.c (grub_cmd_videotest): Added more tests for fonts. - - * term/gfxterm.c: Adapted to new font engine. - - * term/i386/pc/vesafb.c: Marked as deprecated. Made it compile. - - * term/i386/pc/vga.c: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java: New file. - - * util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/Converter.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/Font.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/Glyph.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java: Likewise. - - * util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java: Likewise. - - * util/grub.d/00_header.in: Changed to use new loadfont command. - - * util/grub-mkconfig_lib.in: Changed font extension. - -2008-12-28 Felix Zielcke - - * util/getroot.c (grub_util_get_grub_dev): Add support for - /dev/md/dNNpNN style partitionable mdraid devices. - -2008-12-12 Alex Smith - - * fs/i386/pc/pxe.c (grub_pxefs_open): Handle the one open connection - at a time limit of the PXE TFTP API correctly. - (grub_pxefs_close): Likewise. - -2008-11-29 Robert Millan - - * disk/ata.c (grub_ata_pciinit): Handle errors raised by - grub_ata_device_initialize() calls. - -2008-11-28 Krzysztof Smiechowicz - - * fs/affs.c (grub_affs_iterate_dir): Return failure when directory - iteration failed. - * fs/sfs.c (grub_sfs_iterate_dir): Likewise. - -2008-11-28 Robert Millan - - Fix build on powerpc-ieee1275. Based on patch created by - Manoel Abranches . - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): Add - `kern/ieee1275/mmap.c'. - * include/grub/powerpc/ieee1275/memory.h: New file. - - Provide grub-install on coreboot. - * conf/i386-coreboot.rmk (sbin_SCRIPTS): Add `grub-install'. - (grub_install_SOURCES): New variable. - * util/i386/pc/grub-install.in: Add a few condition checks to make it - usable on coreboot. - -2008-11-25 Felix Zielcke - - * util/grub-fstest.c (grub_term_get_current_input): Change return type - to `grub_term_input_t'. - (grub_term_get_current_output): Change return type to - `grub_term_output_t'. - -2008-11-22 Robert Millan - - Fix breakage on coreboot due to declaration mismatch. - * term/i386/pc/vga_text.c (grub_vga_text_init_fini): New function. - (grub_vga_text_term): Use grub_vga_text_init_fini() instead of - grub_vga_text_cls(). - - * kern/i386/loader.S (grub_multiboot_backward_relocator): Improve - comments. Avoid copying one more byte than necessary (just in case). - - * conf/powerpc-ieee1275.rmk (kernel_elf_LDFLAGS): Change link address - to 0x200000 (avoids trouble with some OFW implementations, and matches - with the one in Yaboot). - Reported by Manoel Abranches - -2008-11-20 Robert Millan - - * kern/i386/coreboot/init.c (grub_time_tics): Remove variable. - (grub_get_rtc, grub_exit): Abort with grub_fatal() if called. - - * util/grub-mkconfig_lib.in (grub_warn): New function. - (convert_system_path_to_grub_path): Use grub_warn() when issuing - warnings, to obtain consistent formatting. - * util/grub.d/00_header.in: Likewise. - * util/update-grub_lib.in: Likewise. - - * loader/i386/linux.c (allocate_pages): Fix a warning. - Move comment text to `#error' stanza. - - Harmonize ieee1275's grub_available_iterate() with the generic - grub_machine_mmap_iterate() interface (fixes a recently-introduced - build problem on i386-ieee1275): - * kern/ieee1275/openfw.c (grub_available_iterate): Moved from here ... - * kern/ieee1275/mmap.c (grub_machine_mmap_iterate): ... here. Add third - parameter `type'. Update all users of this function. - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Add - `kern/ieee1275/mmap.c'. - * kern/ieee1275/init.c - * include/grub/ieee1275/ieee1275.h (grub_available_iterate): Replace - with ... - (grub_machine_mmap_iterate): ... this. - * include/grub/i386/pc/memory.h (grub_machine_mmap_iterate): Change - return type to `grub_err_t'. Update all implementations of this - function prototype. - * include/grub/i386/coreboot/memory.h (grub_machine_mmap_iterate): - Likewise. - - Add `lsmmap' command (lists firmware-provided memory map): - * commands/lsmmap.c: New file. - * conf/i386-pc.rmk (pkglib_MODULES): Add `lsmmap.mod'. - (lsmmap_mod_SOURCES, lsmmap_mod_CFLAGS, lsmmap_mod_LDFLAGS): New - variables. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - -2008-11-19 Robert Millan - - * loader/i386/pc/linux.c (grub_rescue_cmd_initrd): Fix a typo. - * loader/i386/linux.c (grub_rescue_cmd_initrd): Implement a few needed - constraints to initrd allocation (based on code from - loader/i386/pc/linux.c). Without them, initrd was allocated too high - for Linux to find it. - -2008-11-14 Robert Millan - - * fs/cpio.c (grub_cpio_open): Compare `name' and `fn' by hand in - order to cope with duplicate slashes. - -2008-11-14 Robert Millan - - * include/grub/i386/coreboot/memory.h (GRUB_MEMORY_MACHINE_LOWER_SIZE): - Redefine to match with GRUB_MEMORY_MACHINE_UPPER_START (0x100000). We - don't want to mess with lower memory, because it is used in the Linux - loader. - - * loader/i386/linux.c (allocate_pages): Allocate `real_mode_mem' in - an appropriate place in lower memory, between 0x10000 and 0x90000, - like loader/i386/efi/linux.c does. Linux often panics if real_mode_mem - is in our heap (probably as a result of it being corrupted during - decompression). Add #error instance with comment to explain why this - loader isn't currently usable on PC/BIOS. - -2008-11-14 Robert Millan - - * term/i386/pc/serial.c [! GRUB_MACHINE_PCBIOS] - (GRUB_SERIAL_PORT_NUM): Fix miscalculation. - -2008-11-12 Robert Millan - - Make loader/i386/linux.c buildable on i386-pc (although disabled). - - * include/grub/i386/pc/init.h: Include `'. - (struct grub_machine_mmap_entry, grub_machine_mmap_iterate): Move - from here ... - * include/grub/i386/pc/memory.h: ... to here. - -2008-11-12 Robert Millan - - Fix build problems on i386-ieee1275 and *-efi (introduced by vga_text - split). - - * include/grub/i386/pc/console.h: Include `'. - (grub_console_cur_color, grub_console_real_putchar) - (grub_console_putchar, grub_console_getcharwidth, grub_console_getwh) - (grub_console_setcolorstate, grub_console_setcolor) - (grub_console_getcolor): Move from here ... - * include/grub/i386/vga_common.h: ... to here (new file). - - * term/i386/pc/vga_text.c: Replace `' with - `' and `' with - `'. - * term/i386/vga_common.c: Replace `' with - `'. - -2008-11-12 Robert Millan - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `term/i386/vga_common.c'. - * conf/i386.rmk (pkglib_MODULES): Add `vga_text.mod'. - (vga_text_mod_SOURCES, vga_text_mod_CFLAGS, vga_text_mod_LDFLAGS): New - variables. - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Replace - `term/i386/pc/console.c' with `term/i386/vga_common.c'. - - * kern/i386/coreboot/init.c (grub_machine_init): Replace call to - grub_console_init() with call to grub_vga_text_init(). - (grub_machine_fini): Replace call to - grub_console_fini() with call to grub_vga_text_fini() and - grub_at_keyboard_fini(). - - * include/grub/i386/pc/console.h: Include `'. - (grub_console_putchar, grub_console_getcharwidth, grub_console_getwh) - (grub_console_setcolorstate, grub_console_setcolor) - (grub_console_getcolor): New function prototypes. - - * term/i386/pc/vga_text.c: Include `'. - (grub_vga_text_getxy, grub_vga_text_gotoxy, grub_vga_text_cls) - (grub_vga_text_setcursor): Static-ize. - (grub_vga_text_term): New structure. - (GRUB_MOD_INIT(vga_text), GRUB_MOD_FINI(vga_text)): New functions. - - * term/i386/pc/console.c: Remove `'. - (grub_console_cur_color, grub_console_standard_color) - (grub_console_normal_color, grub_console_highlight_color) - (map_char, grub_console_putchar, grub_console_getcharwidth) - (grub_console_getwh, grub_console_setcolorstate, grub_console_setcolor) - (grub_console_getcolor): Move from here ... - * term/i386/vga_common.c: ... to here (same function names). - -2008-11-12 Robert Millan - - Use newly-added Multiboot support in coreboot. - - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Replace - `kern/i386/coreboot/mmap.c' with `kern/i386/multiboot_mmap.c'. - - * kern/i386/coreboot/startup.S: Enable Multiboot header, fix its - alignment, set `MULTIBOOT_MEMORY_INFO' flag. - (codestart): Store the MBI in `startup_multiboot_info' when we're - being loaded using Multiboot. - - * kern/i386/coreboot/init.c (grub_machine_init): Move - grub_at_keyboard_init() call to beginning of function (useful for - debugging). Call grub_machine_mmap_init() before attempting to use - grub_machine_mmap_iterate(). - (grub_lower_mem, grub_upper_mem): Move from here ... - * kern/i386/multiboot_mmap.c (grub_lower_mem, grub_upper_mem): ... to - here (new file). - - * include/grub/i386/coreboot/memory.h (grub_machine_mmap_init): New - function prototype. - -2008-11-12 Robert Millan - - Fix a regression introduced by the at_keyboard.mod split. Because - some terminals are default on some platforms and non-default on - others, the first terminal being registered determines which is - going to be default. - - * kern/term.c (grub_term_register_input): If this is the first - terminal being registered, set it as the current one. - (grub_term_register_output): Likewise. - - * term/efi/console.c (grub_console_init): Do not call - grub_term_set_current_output() or grub_term_set_current_input(). - * term/ieee1275/ofconsole.c (grub_console_init): Likewise. - * term/i386/pc/console.c (grub_console_init): Likewise. - (grub_console_fini): Do not call grub_term_set_current_input() - (but leave grub_term_set_current_output() to restore text mode). - -2008-11-10 Robert Millan - - * util/grub.d/00_header.in: Add backward compatibility check for - versions of terminal.mod that don't understand `terminal_input' or - `terminal_output'. - -2008-11-09 Robert Millan - - * commands/terminal.c (GRUB_MOD_FINI(terminal)): Unregister - `terminal_input' / `terminal_output', not `terminal'. - -2008-11-08 Robert Millan - - * Makefile.in (include_DATA): Fix srcdir=. assumption. - (DISTCLEANFILES): Add `build_env.mk'. - -2008-11-08 Robert Millan - - * term/i386/pc/vesafb.c (grub_vesafb_term): Change type to - `struct grub_term_output'. Remove `.checkkey' and `.getkey' - members. Update all users. - * util/console.c (grub_ncurses_term): Split in ... - (grub_ncurses_term_input): ... this, and ... - (grub_ncurses_term_output): ... this. Update all users. - * term/ieee1275/ofconsole.c: Remove stale `#endif'. - -2008-11-08 Robert Millan - - * Makefile.in (PKGLIB): Add $(pkglib_BUILDDIR). - (PKGDATA): Add $(pkgdata_SRCDIR). - (pkglib_BUILDDIR): New variable. - (pkgdata_SRCDIR): New variable. - (build_env.mk): New target. - (include_DATA): New variable. - (install-local): Install $(include_DATA) files in $(includedir). - -2008-11-07 Pavel Roskin - - * gendistlist.sh: Use C locale for sorting to ensure consistent - output on all systems. - - * util/grub.d/00_header.in: Remove incorrect space before - "serial". - -2008-11-07 Robert Millan - - * include/multiboot2.h (struct multiboot_header): Add `flags' member as - per specification. - * loader/multiboot2.c (grub_multiboot2): Fix Multiboot2 header check. - * loader/multiboot_loader.c (find_multi_boot2_header): New function - (based on find_multi_boot1_header). - (grub_rescue_cmd_multiboot_loader): Check for Multiboot2 header, - using find_multi_boot2_header(), and abort if neither Multiboot or - Multiboot headers were found. - -2008-11-07 Robert Millan - - Modularize at_keyboard.mod: - - * conf/i386.rmk (pkglib_MODULES): Add `at_keyboard.mod'. - (at_keyboard_mod_SOURCES, at_keyboard_mod_CFLAGS) - (at_keyboard_mod_LDFLAGS): New variables. - - Actual terminal split: - - * include/grub/term.h (struct grub_term): Split in ... - (struct grub_term_input): ... this, and ... - (struct grub_term_output): ... this. Update all users. - (grub_term_set_current): Split in ... - (grub_term_set_current_input): ... this, and ... - (grub_term_set_current_output): ... this. - (grub_term_get_current): Split in ... - (grub_term_get_current_input): ... this, and ... - (grub_term_get_current_output): ... this. - (grub_term_register): Split in ... - (grub_term_register_input): ... this, and ... - (grub_term_register_output): ... this. - (grub_term_unregister): Split in ... - (grub_term_unregister_input): ... this, and ... - (grub_term_unregister_output): ... this. - (grub_term_iterate): Split in ... - (grub_term_iterate_input): ... this, and ... - (grub_term_iterate_output): ... this. - - * kern/term.c (grub_term_list): Split in ... - (grub_term_list_input): ... this, and ... - (grub_term_list_output): ... this. Update all users. - (grub_cur_term): Split in ... - (grub_cur_term_input): ... this, and ... - (grub_cur_term_output): ... this. Update all users. - (grub_term_set_current): Split in ... - (grub_term_set_current_input): ... this, and ... - (grub_term_set_current_output): ... this. - (grub_term_get_current): Split in ... - (grub_term_get_current_input): ... this, and ... - (grub_term_get_current_output): ... this. - (grub_term_register): Split in ... - (grub_term_register_input): ... this, and ... - (grub_term_register_output): ... this. - (grub_term_unregister): Split in ... - (grub_term_unregister_input): ... this, and ... - (grub_term_unregister_output): ... this. - (grub_term_iterate): Split in ... - (grub_term_iterate_input): ... this, and ... - (grub_term_iterate_output): ... this. - - * kern/misc.c (grub_abort): Split use of grub_term_get_current() into - a check for input and one for output (and only attempt to get keys - from user when input works). - - * util/grub-probe.c (grub_term_get_current): Split in ... - (grub_term_get_current_input): ... this, and ... - (grub_term_get_current_output): ... this. - * util/grub-fstest.c: Likewise. - * util/i386/pc/grub-setup.c: Likewise. - * util/grub-editenv.c: Likewise. - - Portability adjustments: - - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Remove - `term/i386/pc/at_keyboard.c'. - * kern/ieee1275/init.c [__i386__] (grub_machine_init): Remove call to - grub_keyboard_controller_init() (now handled by terminal .init). - * kern/i386/coreboot/init.c (grub_machine_init): Add call to - grub_at_keyboard_init(). - * include/grub/i386/ieee1275/console.h (grub_keyboard_controller_init) - (grub_console_checkkey, grub_console_getkey): Remove (now provided by - at_keyboard.mod via input terminal interface). - * include/grub/i386/coreboot/console.h: Convert into a stub for - `'. - - Migrate full terminals to new API: - - * term/efi/console.c (grub_console_term): Split into ... - (grub_console_term_input): ... this, and ... - (grub_console_term_output): ... this. Update all users. - * term/ieee1275/ofconsole.c: Remove __i386__ hack. - (grub_ofconsole_init): Split into ... - (grub_ofconsole_init_input): ... this, and ... - (grub_ofconsole_init_output): ... this. - (grub_ofconsole_term): Split into ... - (grub_ofconsole_term_input): ... this, and ... - (grub_ofconsole_term_output): ... this. Update all users. - * term/i386/pc/serial.c (grub_serial_term): Split into ... - (grub_serial_term_input): ... this, and ... - (grub_serial_term_output): ... this. Update all users. - * term/i386/pc/console.c (grub_console_term): Split into ... - (grub_console_term_input): ... this, and ... - (grub_console_term_output): ... this. Update all users. - (grub_console_term_input): Only enable it on PC/BIOS platform. - (grub_console_init): Remove grub_keyboard_controller_init() call. - - Migrate input terminals to new API: - - * term/i386/pc/at_keyboard.c: Replace `cpu' and `machine' with - `i386' and `i386/pc' to enable build on x86_64 (this driver is - i386-specific anyway). - (grub_console_checkkey): Rename to ... - (grub_at_keyboard_checkkey): ... this. Static-ize. Update all - users. - (grub_keyboard_controller_orig): New variable. - (grub_console_getkey): Rename to ... - (grub_at_keyboard_getkey): ... this. Static-ize. Update all - users. - (grub_keyboard_controller_init): Static-ize. Save original - controller value so that it can be restored ... - (grub_keyboard_controller_fini): ... here (new function). - (grub_at_keyboard_term): New structure. - (GRUB_MOD_INIT(at_keyboard), GRUB_MOD_FINI(at_keyboard)): New - functions. - - Migrate output terminals to new API: - - * term/i386/pc/vga.c (grub_vga_term): Change type to - `struct grub_term_output'. Remove `.checkkey' and `.getkey' - members. Update all users. - * term/gfxterm.c (grub_video_term): Change type to - `struct grub_term_output'. Remove `.checkkey' and `.getkey' - members. Update all users. - * include/grub/i386/pc/console.h (grub_console_checkkey) - (grub_console_getkey): Do not export (no longer needed by gfxterm, - etc). - - Migrate `terminal' command and userland tools to new API: - - * commands/terminal.c (grub_cmd_terminal): Split into ... - (grub_cmd_terminal_input): ... this, and ... - (grub_cmd_terminal_output): ... this. - (GRUB_MOD_INIT(terminal)): Split `terminal' command in two commands: - `terminal_input' and `terminal_output'. - * util/grub.d/00_header.in: Adjust `terminal' calls to new - `terminal_input' / `terminal_output' API. - * util/grub-mkconfig.in: Export ${GRUB_TERMINAL_INPUT} and - ${GRUB_TERMINAL_OUTPUT} instead of ${GRUB_TERMINAL} (and if user - provided ${GRUB_TERMINAL}, convert it). - -2008-11-04 Robert Millan - - * util/grub.d/10_freebsd.in: New file. Generate grub configuration - for FreeBSD. - * conf/common.rmk (grub-mkconfig_SCRIPTS): Add 10_freebsd. - -2008-11-03 Bean - - * kern/elf.c (grub_elf32_load): Revert to previous code. - (grub_elf64_load): Likewise. - - * loader/i386/bsd.c (grub_bsd_elf32_hook): Change return address. - -2008-11-01 Robert Millan - - * Makefile.in (CPPFLAGS): Fix builddir=. assumption. - (TARGET_CPPFLAGS): Likewise. - * genmk.rb (mod_src): Fix builddir=. and srcdir=. assumptions. - -2008-11-01 Carles Pina i Estany - - * normal/menu.c (run_menu): Add Previous and Next Page keys in menu. - -2008-10-29 Guillem Jover - - * disk/lvm.c (grub_lvm_scan_device): Fix error recovery by delaying the - addition of objects until the code is not going to be able to fail. - -2008-10-29 Guillem Jover - - * disk/lvm.c (grub_lvm_scan_device): Fix possible NULL value handling - (add a missing NULL check, and correct them by moving the pointer - operations after the actual check). - -2008-10-29 Robert Millan - - * util/i386/pc/grub-install.in: Handle empty string as output from - make_system_path_relative_to_its_root(). - -2008-10-05 Hans Lambermont - - * disk/lvm.c (grub_lvm_scan_device): Allocate buffer space for the - circular metadata worst case scenario. If the metadata is circular - then copy the wrap in place. - * include/grub/lvm.h: Add GRUB_LVM_MDA_HEADER_SIZE, from the LVM2 - project lib/format_text/layout.h - Circular metadata bug found and patch debugged by Jan Derk Gerlings. - -2008-10-03 Felix Zielcke - - * util/i386/pc/grub-install.in: Source grub-mkconfig_lib instead of update-grub_lib. - -2008-10-03 Felix Zielcke - - * util/update-grub_lib.in: Mention filename in warning message. - -2008-09-29 Felix Zielcke - - * NEWS: Update for rename of update-grub to grub-mkconfig. - -2008-09-29 Felix Zielcke - - * util/update-grub_lib.in: Copy to ... - * util/grub-mkconfig_lib.in: ... this. Update all users. - * util/update-grub_lib.in: Make it a stub to `grub-mkconfig_lib.in'. - * util/update-grub.in: Rename to ... - * util/grub-mkconfig.in: ... this. Update all users. Remove `-y' - option. Add `--output' option to allow users to specify the generated - configuration file. Default to stdout. - (update_grub_dir): Rename to ... - (grub_mkconfig_dir): ... this. - (grub_cfg): Default to an empty string. - * conf/common.rmk (update-grub): Rename to ... - (grub-mkconfig): ... this. - (update-grub_lib): Copy to ... - (grub-mkconfig_lib): ... this. - (update-grub_SCRIPTS): Copy to ... - (grub-mkconfig_SCRIPTS): ... this. Update all users. - (update-grub_DATA): Rename to ... - (grub-mkconfig_DATA): ... this. - -2008-09-28 Robert Millan - - * fs/iso9660.c (struct grub_iso9660_primary_voldesc): Rename `created' - to `modified'. Add the real `created' field. - (grub_iso9660_uuid): Use `modified' rather than `created' for - constructing the UUID. - -2008-09-28 Felix Zielcke - - fs/jfs.c (grub_jfs_find_file): Treat multiple slashes like one. - Based on code from Tomas Ebenlendr . - -2008-09-28 Bean - - * fs/ntfs.c (grub_ntfs_iterate_dir): Fix a bug in the previous patch. - Thanks to Christian Franke for finding this bug. - -2008-09-25 Robert Millan - - * util/grub-mkdevicemap.c (make_device_map): Actually replace all - instances of grub_util_get_disk_name() (see previous commit). - -2008-09-25 Robert Millan - - * conf/i386-pc.rmk (grub_mkdevicemap_SOURCES): Remove - `util/i386/get_disk_name.c'. - * conf/i386-efi.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/powerpc-ieee1275.rmk (grub_mkdevicemap_SOURCES): Remove - `util/ieee1275/get_disk_name.c'. - * include/grub/util/misc.h (grub_util_get_disk_name): Remove. - * util/ieee1275/get_disk_name.c: Remove file. - * util/i386/get_disk_name.c: Remove file. - * util/grub-mkdevicemap.c (make_device_map): Back to hardcoding - "hd%d" for device.map entries, rather than using - grub_util_get_disk_name(). - -2008-09-24 Carles Pina i Estany - - * disk/dmraid_nvidia.c (grub_dmraid_nv_detect): Fix `unused parameter' - warning. - * commands/i386/pc/pxecmd.c (dmraid_nvidia): Likewise. - -2008-09-24 Carles Pina i Estany - - * include/grub/i386/pc/console.h (GRUB_TERM_NPAGE): - Changed to 0x5100. - (GRUB_TERM_PPAGE): Changed to 0x4900. - -2008-09-24 Robert Millan - - * include/grub/powerpc/ieee1275/console.h (GRUB_CONSOLE_KEY_*): Remove - macros (they were i386-pc specific). - * include/grub/sparc64/ieee1275/console.h: Likewise. - * include/grub/efi/console.h: Likewise. - -2008-09-22 Bean - - * fs/ntfs.c (grub_ntfs_iterate_dir): Fix a rare case where $BITMAP is - resident and in attribute list. - - * include/grub/ntfs.h (BMP_LEN): Removed. - -2008-09-22 Bean - - * disk/ata.c (grub_atapi_open): Initialize devfnd, no need to set - scsi->name and scsi->luns, as they will be set in grub_scsi_open. - - * disk/scsi.c (grub_scsi_open): Don't call p->close (scsi) here when - error occurs, as grub_disk_open will call grub_disk_close, which will - call p->close (scsi). - -2008-09-21 Felix Zielcke - - * configure.ac (AC_INIT): Quote `GRUB' string and version number. - (AC_PREREQ): Bumped to 2.59. - (AC_TRY_COMPILE): Replace obsolete macro with ... - (AC_COMPILE_IFELSE): ... this. - * aclocal.m4 (AC_TRY_LINK): Replace obsolete macro with ... - (AC_LINK_IFELSE): ... this. - -2008-09-21 Felix Zielcke - - * autogen.sh: Add a call to `gendistlist.sh'. - -2008-09-19 Christian Franke - - * aclocal.m4 (grub_CHECK_ENABLE_EXECUTE_STACK): New function. - * configure.ac: Call grub_CHECK_ENABLE_EXECUTE_STACK. - * include/grub/misc.h [NEED_ENABLE_EXECUTE_STACK]: - Export __enable_execute_stack() to modules. - * kern/misc.c [NEED_ENABLE_EXECUTE_STACK] (__enable_execute_stack): - New function. - -2008-09-09 Felix Zielcke - - * Makefile.in (RMKFILES): Add `i386.rmk' and `x86_64-efi.rmk'. - Sort the list. - -2008-09-09 Felix Zielcke - - * util/hostdisk.c: Replace #include with - #include . - -2008-09-08 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Skip - segments when their filesz is zero (grub_file_read() interprets - zero-size as "read until EOF", which results in memory corruption). - Use `lowest_segment' rather than 0 for calculating the current - segment load address. - -2008-09-08 Robert Millan - - * util/hostdisk.c (open_device): Replace a grub_util_info() call - with grub_dprintf("hostdisk", ...), as it was so verbose that it - clobbered useful information. - -2008-09-08 Robert Millan - - * include/grub/util/biosdisk.h: Move to ... - * include/grub/util/hostdisk.h: ... here. Update all users. - * util/biosdisk.c: Move to ... - * util/hostdisk.c: ... here. Update all users. - -2008-09-07 Robert Millan - - * loader/i386/pc/multiboot.c (mmap_addr, mmap_length): Remove - variables. - (grub_multiboot): Move `mbi' allocation upwards, so that mmap address - and length can be stored directly in the `mbi->mmap_addr' and - `mbi->mmap_length' struct fields. - -2008-09-07 Robert Millan - - * conf/i386.rmk: New file. Provides declaration for building - `cpuid.mod'. - * conf/i386-pc.rmk (pkglib_MODULES): Remove `cpuid.mod'. - (cpuid_mod_SOURCES, cpuid_mod_CFLAGS, cpuid_mod_LDFLAGS): Remove - variables. - Include `conf/i386.mk'. - * conf/i386-efi.rmk: Likewise. - * conf/x86_64-efi.rmk: Likewise. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - -2008-09-07 Vesa Jääskeläinen - - Based on patch created by Colin D Bennett . - Adds optimization support for BGR based modes. - - * include/grub/i386/pc/vbeblit.h (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8) Removed. - (grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_index): Likewise. - (grub_video_i386_vbeblit_replace_directN): Added. - (grub_video_i386_vbeblit_replace_BGRX8888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_BGRX8888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_BGR888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_BGR888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_RGBX8888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_RGB888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_index_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_index_RGB888): Likewise. - (grub_video_i386_vbeblit_blend_BGRA8888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_BGR888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_RGBA8888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_RGB888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_index_RGBA8888): Likewise. - - * include/grub/i386/pc/vbefill.h (grub_video_i386_vbefill_R8G8B8A8) Removed. - (grub_video_i386_vbefill_R8G8B8): Likewise. - (grub_video_i386_vbefill_index): Likewise. - (grub_video_i386_vbefill_direct32): Added. - (grub_video_i386_vbefill_direct24): Likewise. - (grub_video_i386_vbefill_direct16): Likewise. - (grub_video_i386_vbefill_direct8): Likewise. - - * include/grub/video.h (grub_video_blit_format): Removed - GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8, GRUB_VIDEO_BLIT_FORMAT_R8G8B8. - (grub_video_blit_format): Added GRUB_VIDEO_BLIT_FORMAT_RGBA_8888, - GRUB_VIDEO_BLIT_FORMAT_BGRA_8888, GRUB_VIDEO_BLIT_FORMAT_RGB_888, - GRUB_VIDEO_BLIT_FORMAT_BGR_888, GRUB_VIDEO_BLIT_FORMAT_RGB_565, - GRUB_VIDEO_BLIT_FORMAT_BGR_565. - - * video/video.c (grub_video_get_blit_format): Updated to use new - blit formats. Added handling for 16 bit color modes. - - * video/i386/pc/vbe.c (grub_video_vbe_fill_rect): Updated to use new - fillers. - (common_blitter): Updated to use new blitters. - - * video/i386/pc/vbeblit.c (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8): - Removed. - (grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_index): Likewise. - (grub_video_i386_vbeblit_replace_directN): Added. - (grub_video_i386_vbeblit_replace_BGRX8888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_BGRX8888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_BGR888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_BGR888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_RGBX8888_RGB888): Likewise. - (grub_video_i386_vbeblit_replace_RGB888_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_index_RGBX8888): Likewise. - (grub_video_i386_vbeblit_replace_index_RGB888): Likewise. - (grub_video_i386_vbeblit_blend_BGRA8888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_BGR888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_RGBA8888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_RGB888_RGBA8888): Likewise. - (grub_video_i386_vbeblit_blend_index_RGBA8888): Likewise. - - * video/i386/pc/vbefill.c (grub_video_i386_vbefill_R8G8B8A8): Removed. - (grub_video_i386_vbefill_R8G8B8): Likewise. - (grub_video_i386_vbefill_index): Likewise. - (grub_video_i386_vbefill_direct32): Added. - (grub_video_i386_vbefill_direct24): Likewise. - (grub_video_i386_vbefill_direct16): Likewise. - (grub_video_i386_vbefill_direct8): Likewise. - - * video/readers/jpeg.c (grub_jpeg_decode_sos): Adapt to new blitter - types. - - * video/readers/tga.c (grub_video_reader_tga): Adapt to new blitter - types. - - * video/readers/png.c (grub_png_decode_image_header): Adapt to new - blitter types. - - * video/bitmap.c (grub_video_bitmap_create): Adapt to new blitter - types. - -2008-09-06 Felix Zielcke - - * disk/raid.c (insert_array): Set `array->chunk_size' to 64 for - RAID level 1. - -2008-09-06 Felix Zielcke - - * fs/iso9660.c (grub_iso9660_date): New structure. - (grub_iso9660_primary_voldesc): Add `grub_iso9660_date' member. - (grub_iso9660_uuid): New function. - -2008-09-05 Bean - - * fs/fshelp.c (grub_fshelp_find_file): Handle case insensitive names. - - * fs/ntfs.c (list_file): Ignore names in DOS namespace, set the case - insensitive bit for names in Win32 and Win32 & DOS namespace. - - * include/grub/fshelp.h (GRUB_FSHELP_CASE_INSENSITIVE): New macro. - - * include/grub/types.h (LONG_MAX): Likewise. - -2008-09-04 Felix Zielcke - - * util/getroot.c: Include . - (grub_util_get_grub_dev): Rewrite to use asprintf for mdraid devices, - add support for /dev/md/N devices and handle LVM double dash escaping. - -2008-09-04 Felix Zielcke - - * config.guess: Update to latest version from config git. - * config.sub: Likewise. - -2008-09-03 Robert Millan - - * disk/scsi.c (grub_scsi_open): Remove size limit when printing - `disk->total_sectors'. - -2008-09-01 Colin D Bennett - - * include/grub/normal.h: Fixed incorrect comment for - GRUB_COMMAND_FLAG_NO_ARG_PARSE. - -2008-09-01 Colin D Bennett - - * commands/i386/pc/vbeinfo.c (grub_cmd_vbeinfo): Replaced constant - values with defines. - - * include/grub/i386/pc/vbe.h (GRUB_VBE_MODEATTR_SUPPORTED): Added. - (GRUB_VBE_MODEATTR_RESERVED_1): Likewise. - (GRUB_VBE_MODEATTR_BIOS_TTY_OUTPUT_SUPPORT): Likewise. - (GRUB_VBE_MODEATTR_COLOR): Likewise. - (GRUB_VBE_MODEATTR_GRAPHICS): Likewise. - (GRUB_VBE_MODEATTR_VGA_COMPATIBLE): Likewise. - (GRUB_VBE_MODEATTR_VGA_WINDOWED_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_LFB_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_DOUBLE_SCAN_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_INTERLACED_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_TRIPLE_BUF_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_STEREO_AVAIL): Likewise. - (GRUB_VBE_MODEATTR_DUAL_DISPLAY_START): Likewise. - (GRUB_VBE_MEMORY_MODEL_TEXT): Likewise. - (GRUB_VBE_MEMORY_MODEL_CGA): Likewise. - (GRUB_VBE_MEMORY_MODEL_HERCULES): Likewise. - (GRUB_VBE_MEMORY_MODEL_PLANAR): Likewise. - (GRUB_VBE_MEMORY_MODEL_NONCHAIN4_256): Likewise. - (GRUB_VBE_MEMORY_MODEL_YUV): Likewise. - -2008-08-31 Robert Millan - - * loader/i386/pc/multiboot.c (grub_get_multiboot_mmap_len): Fix - declaration. - (grub_multiboot): Fix a few warnings. - -2008-08-31 Robert Millan - - * loader/i386/pc/multiboot.c: Update comment not to say that - boot_device support is unimplemented. - -2008-08-31 Robert Millan - - * loader/i386/pc/multiboot.c: Update comment not to say that a.out - or memory map support are unimplemented. - -2008-08-31 Colin D Bennett - - * util/i386/pc/grub-mkrescue.in: Support multiple overlay directories. - -2008-08-31 Colin D Bennett - - * commands/i386/pc/vbeinfo.c (grub_cmd_vbeinfo): Show VBE version and - total video memory in 'vbeinfo' output; show color format details for - each video mode. - -2008-08-30 Pavel Roskin - - * util/genmoddep.c: Remove for real this time. - * DISTLIST: Remove util/genmoddep.c. - -2008-08-30 Robert Millan - - * kern/i386/pc/startup.S (multiboot_header): Force 4-byte alignment - as required by Multiboot spec (it was already 4-byte aligned, but - only by chance). - -2008-08-29 Pavel Roskin - - * kern/powerpc/ieee1275/crt0.S: Rename to ... - * kern/powerpc/ieee1275/startup.S: ... this. - * conf/powerpc-ieee1275.rmk: Adjust for the above. - * DISTLIST: Likewise. - - * kern/powerpc/ieee1275/crt0.S: Include grub/symbol.h and - grub/cpu/kernel.h. Add start label for consistency with other - platforms. Add grub_prefix immediately after start. Add jump - to the code after grub_prefix. - * include/grub/powerpc/kernel.h: Provide valid values for - GRUB_KERNEL_CPU_PREFIX and GRUB_KERNEL_CPU_DATA_END. - -2008-08-29 Bean - - * configure.ac: Change host_os to cygwin for mingw. - (asprintf): New check for function. - - * include/grub/symbol.h: Replace #ifndef __CYGWIN__ with - #if ! defined (__CYGWIN__) && ! defined (__MINGW32__). - - * include/grub/util/misc.h: #include and , - declare asprintf if HAVE_ASPRINTF is not set, declare fseeko, ftello, - sync, sleep and grub_util_get_disk_size for mingw. - - * util/biosdisk.c (grub_util_biosdisk_open): Use grub_util_get_disk_size - to get size in mingw. - (open_device): Use flag O_BINARY if it's defined. - (find_root_device): Add dummy code for mingw. - - * util/grub-mkdevicemap.c (get_floppy_disk_name): Return 0 for mingw. - (get_ide_disk_name): Return //./PHYSICALDRIVE%d for mingw. - (get_scsi_disk_name): Return 0 for mingw. - - * util/hostfs.c: #include . - (grub_hostfs_open): Use "rb" flag to open file, use - grub_util_get_disk_size to get disk size for mingw. - - * util/misc.c: #include and in mingw. - (asprintf): New function if HAVE_ASPRINTF is not set. - (sync): New function for mingw. - (sleep): Likewise. - (grub_util_get_disk_size): Likewise. - -2008-08-28 Pavel Roskin - - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): Add - kern/time.c. - -2008-08-28 Robert Millan - - * util/biosdisk.c (find_grub_drive): Declare missing `i' variable. - -2008-08-28 Robert Millan - - Change find_grub_drive() syntax so it doesn't prevent it from - detecting NULL names as errors. - - * util/biosdisk.c (find_grub_drive): Move free slot search code - from here ... - (find_free_slot): ... to here. - (read_device_map): Use find_free_slot() to search for free slots. - -2008-08-27 Marco Gerards - - * conf/common.rmk (pkglib_MODULES): Add scsi.mod. - (scsi_mod_SOURCES): New variable. - (scsi_mod_CFLAGS): Likewise - (scsi_mod_LDFLAGS): Likewise. - - * disk/scsi.c: New file. - - * include/grub/scsi.h: Likewise. - - * include/grub/scsicmd.h: Likewise. - - * disk/ata.c: Include . - (grub_atapi_packet): Do not use grub_ata_cmd, use registers - instead. - (grub_ata_iterate): Skip ATAPI devices. - (grub_ata_open): Only handle ATAPI devices. - (struct grub_atapi_read): Removed. - (grub_atapi_readsector): Likewise. - (grub_ata_read): No longer handle ATAPI devices. - (grub_ata_write): Likewise. - (grub_atapi_iterate): New function. - (grub_atapi_read): Likewise. - (grub_atapi_write): Likewise. - (grub_atapi_open): Likewise. - (grub_atapi_close): Likewise. - (grub_atapi_dev): New variable. - (GRUB_MOD_INIT(ata)): Register ATAPI as SCSI device. - (GRUB_MOD_FINI(ata)): Unregister ATAPI. - - * include/grub/disk.h (enum grub_disk_dev_id): Add - `GRUB_DISK_DEVICE_SCSI_ID'. - -2008-08-26 Robert Millan - - * util/biosdisk.c (grub_util_biosdisk_open, open_device) - (grub_util_biosdisk_get_grub_dev): Make error messages a bit more - descriptive. - -2008-08-23 Bean - - * conf/common.rmk (grub_probe_SOURCES): Add disk/mdraid_linux.c. - (grub_fstest_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c, - disk/mdraid_linux.c and disk/dmraid_nvidia.c and lib/crc.c. - (pkglib_MODULES): Add raid5rec.mod, raid6rec.mod, mdraid.mod and - dm_nv.mod. - (raid5rec_mod_SOURCES): New macro. - (raid5rec_mod_CFLAGS): Likewise. - (raid5rec_mod_LDFLAGS): Likewise. - (raid6rec_mod_SOURCES): Likewise. - (raid6rec_mod_CFLAGS): Likewise. - (raid6rec_mod_LDFLAGS): Likewise. - (mdraid_mod_SOURCES): Likewise. - (mdraid_mod_CFLAGS): Likewise. - (mdraid_mod_LDFLAGS): Likewise. - (dm_nv_mod_SOURCES): Likewise. - (dm_nv_mod_CFLAGS): Likewise. - (dm_nv_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add disk/mdraid_linux.c. - (grub_emu_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c, - disk/mdraid_linux.c and disk/dmraid_nvidia.c. - - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add disk/raid5_recover.c, - disk/raid6_recover.c, disk/mdraid_linux.c and disk/dmraid_nvidia.c. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * disk/raid5_recover.c: New file. - - * disk/raid6_recover.c: Likewise. - - * disk/mdraid_linux.c: Likewise. - - * disk/dmraid_nvidia.c: Likewise. - - * disk/i386/pc/biosdisk.c: Set total_sectors of cdrom device to - ULONG_MAX. - - * disk/raid.c (grub_raid_open): Use the size of the smallest disk to - calculate the size of raid device. - (grub_raid_read): Simplify raid0 code. Support raid4, raid6 and four - different layout of raid5. - (grub_raid_scan_device): Remove code specific to mdraid. - (grub_raid_list): New variable. - (free_array): New function. - (grub_raid_register): Likewise. - (grub_raid_unregister): Likewise. - (grub_raid_rescan): Likewise. - (GRUB_MOD_INIT): Don't iterate device here. - (GRUB_MOD_FINI): Use free_array to release resource. - - * include/grub/raid.h: Remove macro and structure specific to mdraid. - (grub_raid5_recover_func_t): New function variable type. - (grub_raid6_recover_func_t): Likewise. - (grub_raid5_recover_func): New variable. - (grub_raid6_recover_func): Likewise. - (grub_raid_register): New function. - (grub_raid_unregister): Likewise. - (grub_raid_rescan): Likewise. - (grub_raid_block_xor): Likewise. - - * util/grub-fstest.c: Add #include and . - (CMD_CRC): New macro. - (part): Removed. - (read_file): Handle device as well as file. - (cmd_crc): New function. - (fstest): Handle multiple disks. - (options): Remove part, raw and long, add root and diskcount. - (usage): Add crc, remove -p, -r, -l, add -r and -c. - (main): Find the first non option entry and ignore subsequent options, - add handling for the new options, support multiple disks. - - * util/grub-probe.c (probe): Add mdraid to abstraction_name. - -2008-08-23 Bean - - * normal/x86_64/setjmp.S (grub_longjmp): Return 1 when val = 0. - - * genfslist.sh: Ignore kernel.mod. - - * genpartmaplist.sh: Likewise. - -2008-08-23 Robert Millan - - * util/getroot.c (find_root_device): Skip anything that starts with - a dot, not just directories. This avoids things like /dev/.tmp.md0. - -2008-08-22 Felix Zielcke - - * util/update-grub.in (GRUB_GFXMODE): Export variable. - * util/grub.d/00_header.in: Allow the administrator to change default - gfxmode via ${GRUB_GFXMODE}. - -2008-08-21 Felix Zielcke - - * fs/ntfs.c (grub_ntfs_mount): Fix a memory leak. - -2008-08-21 Robert Millan - - * loader/i386/linux.c: New file. Implements generic 32-bit Linux - loader. - * conf/i386-coreboot.rmk (_linux_mod_SOURCES): Replace - `loader/i386/pc/linux.c' with `loader/i386/linux.c'. - -2008-08-20 Carles Pina i Estany - - * menu/normal.c (run_menu): Replace hardcoded numbers with macros - (16 for GRUB_TERM_UP and 14 for GRUB_TERM_DOWN) - -2008-08-19 Robert Millan - - * term/gfxterm.c (DEFAULT_CURSOR_COLOR): Remove. - (struct grub_virtual_screen): Remove `cursor_color'. - (grub_virtual_screen_setup): Remove `virtual_screen.cursor_color' - initialization. - (write_cursor): Use `virtual_screen.fg_color' to draw cursor. - -2008-08-18 Robert Millan - - Unify (identical) linux_normal.c files. - * loader/i386/efi/linux_normal.c: Move from here ... - * loader/linux_normal.c: ... to here. Update all users. - * loader/i386/pc/linux_normal.c: Delete. Update all users. - * loader/i386/ieee1275/linux_normal.c: Likewise. - -2008-08-18 Robert Millan - - * include/grub/i386/linux.h (LINUX_LOADER_ID_LILO) - (LINUX_LOADER_ID_LOADLIN, LINUX_LOADER_ID_BOOTSECT) - (LINUX_LOADER_ID_SYSLINUX, LINUX_LOADER_ID_ETHERBOOT) - (LINUX_LOADER_ID_ELILO, LINUX_LOADER_ID_GRUB, LINUX_LOADER_ID_UBOOT) - (LINUX_LOADER_ID_XEN, LINUX_LOADER_ID_GUJIN, LINUX_LOADER_ID_QEMU): - New macros. - (GRUB_LINUX_CL_OFFSET, GRUB_LINUX_CL_END_OFFSET): Move from here ... - * loader/i386/pc/linux.c (GRUB_LINUX_CL_OFFSET) - (GRUB_LINUX_CL_END_OFFSET): ... to here. - * loader/i386/efi/linux.c (GRUB_EFI_CL_OFFSET): Rename to ... - (GRUB_LINUX_CL_OFFSET): ... this. Update all users. - (GRUB_EFI_CL_END_OFFSET): Rename to ... - (GRUB_LINUX_CL_END_OFFSET): ... this. Update all users. - (grub_rescue_cmd_linux): Macroify `type_of_loader' initialization. - Initialize `params->video_cursor_x' and `params->video_cursor_y' - portably using grub_getxy(). - Replace `-EFI' with `-bzImage' in boot message. - -2008-08-17 Robert Millan - - * include/grub/x86_64/kernel.h: New file ( stub). - -2008-08-17 Robert Millan - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/i386/pc/mmap.c'. - - * include/grub/i386/pc/init.h (GRUB_MACHINE_MEMORY_AVAILABLE) - (GRUB_MACHINE_MEMORY_RESERVED): New macros. - (grub_machine_mmap_iterate): New function declaration. - * include/grub/multiboot.h (struct grub_multiboot_mmap_entry): New - structure. - (GRUB_MMAP_MEMORY_AVAILABLE, GRUB_MMAP_MEMORY_RESERVED): New - macros. - - * kern/i386/pc/init.c (grub_machine_init): Replace hardcoded region - type check value with `GRUB_MACHINE_MEMORY_AVAILABLE'. - Move e820 parsing from here ... - * kern/i386/pc/mmap.c: New file. - (grub_machine_mmap_iterate): ... to here. - - * include/grub/i386/coreboot/memory.h: Remove `'. - (GRUB_LINUXBIOS_MEMORY_AVAILABLE): Rename (for consistency) to ... - (GRUB_MACHINE_MEMORY_AVAILABLE): ... this. Update all users. - (grub_available_iterate): Redeclare to return `void', and redeclare - its hook to use grub_uint64_t as addr and size parameters, and rename - to ... - (grub_machine_mmap_iterate): ... this. Update all users. - - * kern/i386/coreboot/mmap.c (grub_mmap_iterate): Simplify parser loop - to make it more readable. Rename to ... - (grub_machine_mmap_iterate): ... this. - - * loader/i386/pc/multiboot.c (mmap_addr, mmap_length): New variables. - (grub_get_multiboot_mmap_len, grub_fill_multiboot_mmap): New functions. - (grub_multiboot): Allocate an extra region after the payload, and fill - it with a Multiboot memory map. Adjust a.out loader to calculate size - with the extra space. - (grub_multiboot_load_elf32): Adjust elf32 loader to calculate size - with the extra space. - -2008-08-17 Carles Pina i Estany - - * menu/normal.c (run_menu): Add Home and End keys in grub-menu. - -2008-08-17 Felix Zielcke - - * gendistlist.sh: Add *.y, *.tex, *.texi, grub.cfg, README, *.sc, - mdate-sh to the list `find' searches for. - * DISTLIST: Regenerated. - -2008-08-16 Felix Zielcke - - * gendistlist.sh (EXTRA_DISTFILES): Remove gensymlist.sh, - genkernsyms.sh. Add geninit.sh, geninitheader.sh, genkernsyms.sh.in, - genmoddep.awk, gensymlist.sh.in. - (DISTDIRS): Add bus, docs, hook, lib. - * DISTLIST: Regenerated. - * NEWS: Add cygwin support and change the `os-prober' entry a bit. - -2008-08-16 Robert Millan - - * disk/raid.c (grub_raid_init): Handle/report errors set by - grub_device_iterate(). - * disk/lvm.c (grub_lvm_init): Likewise. - -2008-08-15 Bean - - * conf/i386-pc.rmk (pkglib_MODULES): Add datetime.mod, date.mod - and datehook.mod. - (datetime_mod_SOURCES): New macro. - (datetime_mod_CFLAGS): Likewise. - (datetime_mod_LDFLAGS): Likewise. - (date_mod_SOURCES): Likewise. - (date_mod_CFLAGS): Likewise. - (date_mod_LDFLAGS): Likewise. - (datehook_mod_SOURCES): Likewise. - (datehook_mod_CFLAGS): Likewise. - (datehook_mod_LDFLAGS): Likewise. - - * conf/i386-coreboot.rmk (pkglib_MODULES): Add datetime.mod, date.mod - and datehook.mod. - (datetime_mod_SOURCES): New macro. - (datetime_mod_CFLAGS): Likewise. - (datetime_mod_LDFLAGS): Likewise. - (date_mod_SOURCES): Likewise. - (date_mod_CFLAGS): Likewise. - (date_mod_LDFLAGS): Likewise. - (datehook_mod_SOURCES): Likewise. - (datehook_mod_CFLAGS): Likewise. - (datehook_mod_LDFLAGS): Likewise. - - * conf/i386-ieee1275.rmk (pkglib_MODULES): Add datetime.mod, date.mod - and datehook.mod. - (datetime_mod_SOURCES): New macro. - (datetime_mod_CFLAGS): Likewise. - (datetime_mod_LDFLAGS): Likewise. - (date_mod_SOURCES): Likewise. - (date_mod_CFLAGS): Likewise. - (date_mod_LDFLAGS): Likewise. - (datehook_mod_SOURCES): Likewise. - (datehook_mod_CFLAGS): Likewise. - (datehook_mod_LDFLAGS): Likewise. - - * conf/i386-efi.rmk (pkglib_MODULES): Add datetime.mod, date.mod - and datehook.mod. - (datetime_mod_SOURCES): New macro. - (datetime_mod_CFLAGS): Likewise. - (datetime_mod_LDFLAGS): Likewise. - (date_mod_SOURCES): Likewise. - (date_mod_CFLAGS): Likewise. - (date_mod_LDFLAGS): Likewise. - (datehook_mod_SOURCES): Likewise. - (datehook_mod_CFLAGS): Likewise. - (datehook_mod_LDFLAGS): Likewise. - - * conf/x86_64-efi.rmk (pkglib_MODULES): Add datetime.mod, date.mod - and datehook.mod. - (datetime_mod_SOURCES): New macro. - (datetime_mod_CFLAGS): Likewise. - (datetime_mod_LDFLAGS): Likewise. - (date_mod_SOURCES): Likewise. - (date_mod_CFLAGS): Likewise. - (date_mod_LDFLAGS): Likewise. - (datehook_mod_SOURCES): Likewise. - (datehook_mod_CFLAGS): Likewise. - (datehook_mod_LDFLAGS): Likewise. - - * kern/env.c (grub_env_insert): Fix a bug in prevp pointer. - - * commands/date.c: New file. - - * hook/datehook.c: Likewise. - - * include/grub/lib/datetime.h: Likewise. - - * include/grub/i386/cmos.h: Likewise. - - * lib/datetime.c: Likewise. - - * lib/i386/datetime.c: Likewise. - - * lib/efi/datetime.c: Likewise. - -2008-08-14 Robert Millan - - * conf/common.rmk (bin_UTILITIES): Add `grub-mkelfimage'. - (grub_mkelfimage_SOURCES): New variable. - (util/elf/grub-mkimage.c_DEPENDENCIES): Likewise. - - * conf/i386-coreboot.rmk (bin_UTILITIES, grub_mkimage_SOURCES) - (grub_mkimage_LDFLAGS, util/elf/grub-mkimage.c_DEPENDENCIES): Remove. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - - * kern/ieee1275/init.c: Include `'. - * kern/i386/coreboot/init.c: Likewise. - - * kern/i386/ieee1275/startup.S: Replace `' - with `'. - (GRUB_KERNEL_MACHINE_PREFIX, GRUB_KERNEL_MACHINE_DATA_END): Renamed - to ... - (GRUB_KERNEL_CPU_PREFIX, GRUB_KERNEL_CPU_DATA_END): ... this. - * kern/i386/coreboot/startup.S: Likewise. - - * include/grub/powerpc/ieee1275/kernel.h (GRUB_MOD_ALIGN) - (GRUB_MOD_GAP): Remove. - * include/grub/powerpc/kernel.h: New file. - * include/grub/i386/ieee1275/kernel.h (GRUB_KERNEL_MACHINE_PREFIX) - (GRUB_KERNEL_MACHINE_DATA_END): Remove. - * include/grub/i386/kernel.h: New file. - * include/grub/i386/coreboot/kernel.h (GRUB_MOD_ALIGN) - (GRUB_MOD_GAP, GRUB_KERNEL_MACHINE_PREFIX) - (GRUB_KERNEL_MACHINE_DATA_END): Remove. - - * util/ieee1275/grub-install.in (grub_mkimage): Initialize to use - `grub-mkelfimage'. - Use --directory when invoking grub_mkimage. - - * util/elf/grub-mkimage.c: Include `'. - (add_segments): Replace GRUB_KERNEL_MACHINE_DATA_END and - GRUB_KERNEL_MACHINE_PREFIX with GRUB_KERNEL_CPU_DATA_END - and GRUB_KERNEL_CPU_PREFIX. - -2008-08-14 Felix Zielcke - - * include/grub/err.h (grub_err_printf): New function prototype. - * util/misc.c (grub_err_printf): New function. - * kern/misc.c [! GRUB_UTIL] (grub_err_printf): New alias for - grub_printf. - * kern/err.c (grub_print_error): Use grub_err_printf. - -2008-08-13 Robert Millan - - * docs/grub.cfg: Remove `/dev/' prefix in GNU/Hurd boot entry. - -2008-08-13 Robert Millan - - * docs/grub.cfg: Use the native device name for the example GNU/Hurd - boot entry. - -2008-08-12 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Move part - of the relocation code from here ... - (grub_multiboot): ... to here. - (forward_relocator, backward_relocator): Move from here ... - * kern/i386/loader.S (grub_multiboot_forward_relocator) - (grub_multiboot_backward_relocator): ... to here. - (grub_multiboot_real_boot): Use %edx for entry offset. Put Multiboot - magic in %eax. Use %ebp for jumping (so %edx is not trashed). - * include/grub/i386/loader.h (grub_multiboot_forward_relocator) - (grub_multiboot_forward_relocator_end) - (grub_multiboot_backward_relocator) - (grub_multiboot_backward_relocator_end): New variables. - -2008-08-12 Bean - - * disk/raid.c (grub_raid_read): Fix a bug in raid0 code. - -2008-08-11 Robert Millan - - * kern/i386/linuxbios/startup.S: Move from here ... - * kern/i386/coreboot/startup.S: ... to here. - - * kern/i386/linuxbios/init.c: Move from here ... - * kern/i386/coreboot/init.c: ... to here. - - * kern/i386/linuxbios/table.c: Move from here ... - * kern/i386/coreboot/mmap.c: ... to here. - - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Update moved files. - -2008-08-11 Robert Millan - - * kern/device.c (grub_device_open): Do not handle grub_disk_open() - errors. Leave it to the upper layer to handle them. - -2008-08-09 Christian Franke - - * Makefile.in: Add `target_os' and `enable_grub_pe2elf'. - * conf/common.rmk: Install `grub-pe2elf' only if requested. - Install `grub.d/10_windows' only on Cygwin. - * configure.ac: Add subst of `target_os'. - Check `target_os' also before setting TARGET_OBJ2ELF. - Add `--enable-grub-pe2elf'. - -2008-08-08 Robert Millan - - * kern/disk.c: Replace `' with `'. - (grub_last_time): Change type to grub_uint64_t. - (grub_disk_open): Migrate code from to using grub_get_time_ms(). - (grub_disk_close): Likewise. - - * normal/menu.c: Replace `' with `'. - (run_menu): Migrate code from to using grub_get_time_ms(). - - * util/misc.c (grub_get_time_ms): New function. - -2008-08-08 Marco Gerards - - * disk/ata.c (grub_ata_regget): Change return type to - `grub_uint8_t'. - (grub_ata_regget2): Likewise. - (grub_ata_wait_status): New function. - (grub_ata_wait_busy): Removed function, updated all users to use - `grub_ata_wait_status'. - (grub_ata_wait_drq): Likewise. - (grub_ata_cmd): New function. - (grub_ata_pio_read): Change return type to `grub_uint8_t'. Add - error handling. - (grub_ata_pio_write): Add error handling. - (grub_atapi_identify): Likewise. - (grub_atapi_packet): Use `grub_ata_cmd' and improve error - handling. - (grub_ata_identify): Use `grub_ata_cmd' and improve error - handling. Actually use the detected registers. Reorder the - detection logic such that it is easier to read. - (grub_ata_pciinit): Do not assign the same ID to each controller. - (grub_ata_setaddress): Use `grub_ata_cmd' and improve error - handling. - (grub_atapi_readsector): Check the result of `grub_ata_pio_read'. - - * include/grub/err.h (grub_err_t): Add `GRUB_ERR_TIMEOUT'. - -2008-08-08 Marco Gerards - - * NEWS: Update. - -2008-08-07 Bean - - * include/grub/x86_64/pci.h: New file. - -2008-08-07 Christian Franke - - * kern/i386/pit.c (TIMER2_SPEAKER): New define. - (TIMER2_GATE): Likewise. - (grub_pit_wait): Add enable/disable of the timer2 gate - bit of port 0x61. This fixes a possible infinite loop. - -2008-08-07 Bean - - * conf/x86_64-efi.rmk (kernel_mod_SOURCES): Add kern/time.c, - kern/i386/tsc.c and kern/i386/pit.c. - - * include/grub/i386/tsc.h (grub_cpu_is_cpuid_supported): Handle - x86_64 platform. - - * kern/i386/efi/init.c: Replace with - . - - * kern/i386/pit.c: Replace with . - -2008-08-07 Bean - - * conf/i386-efi.rmk (kernel_mod_SOURCES): Add kern/time.c. - - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Add kern/time.c, - - * include/grub/i386/pit.h: Use macro KERNEL_CPU_PIT_HEADER to avoid - multiple inclusion. Add #include . - -2008-08-06 Christian Franke - - * conf/common.rmk: Build and install `10_windows'. - * util/grub.d/10_windows.in: New script. - -2008-08-06 Pavel Roskin - - * kern/i386/pit.c: Include `'. - -2008-08-06 Robert Millan - - * conf/i386-coreboot.rmk (kernel_elf_ASFLAGS): New variable. - * kern/i386/tsc.c: Include `'. - -2008-08-06 Bean - - * fs/i386/pc/pxe.c (grub_pxe_data): New member block_size. - (grub_pxefs_fs_int): Remove dummy definition. - (grub_pxefs_open): Use data->block_size to store the current block - size setting. - (grub_pxefs_read): Use block size stored in data->block_size. As the - value of grub_pxe_blksize can be changed after the file is opened. - -2008-08-06 Bean - - * fs/i386/pc/pxe.c (curr_file): new variable. - (grub_pxefs_open): Simply the handling of pxe file system. Don't - require the dummy internal file system anymore. - (grub_pxefs_read): Removed. - (grub_pxefs_close): Likewise. - (grub_pxefs_fs_int): Likewise. - (grub_pxefs_read_int): Renamed to grub_pxefs_read. Reinitialize tftp - connection when we switch file. - (grub_pxefs_close_int): Renamed to grub_pxefs_close. - -2008-08-06 Robert Millan - - * conf/i386-coreboot.rmk (pkglib_MODULES): Add `reboot.mod' and - `halt.mod'. - (reboot_mod_SOURCES, reboot_mod_CFLAGS, reboot_mod_LDFLAGS) - (halt_mod_SOURCES, halt_mod_CFLAGS, halt_mod_LDFLAGS): New variables. - - * kern/i386/halt.c: New file. - * kern/i386/reboot.c: Likewise. - * include/grub/i386/reboot.h: Likewise. - * include/grub/i386/halt.h: Likewise. - - * commands/halt.c [! GRUB_MACHINE_IEEE1275 ! GRUB_MACHINE_EFI]: - Include `'. - * commands/reboot.c [! GRUB_MACHINE_IEEE1275 ! GRUB_MACHINE_EFI] - [! GRUB_MACHINE_PCBIOS]: Include `'. - - * term/i386/pc/at_keyboard.c: Include `'. - (SHIFT_L, SHIFT_R, CTRL, ALT, CAPS_LOCK, KEYBOARD_REG_DATA) - (KEYBOARD_REG_STATUS, KEYBOARD_COMMAND_ISREADY, KEYBOARD_COMMAND_READ) - (KEYBOARD_COMMAND_WRITE, KEYBOARD_COMMAND_REBOOT) - (KEYBOARD_SCANCODE_SET1, KEYBOARD_ISMAKE, KEYBOARD_ISREADY) - (KEYBOARD_SCANCODE, OLPC_UP, OLPC_DOWN, OLPC_LEFT, OLPC_RIGHT): Move - from here ... - * include/grub/i386/at_keyboard.h: ... to here. - -2008-08-05 Robert Millan - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/i386/pit.c'. - * conf/i386-efi.rmk (kernel_mod_SOURCES): Likewise. - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Likewise. Also add - `kern/i386/tsc.c', `kern/generic/rtc_get_time_ms.c' and - `kern/generic/millisleep.c'. - - * kern/i386/tsc.c (calibrate_tsc): Rewrite using grub_pit_wait() - instead of grub_get_rtc(). - (grub_tsc_init): Initialize `tsc_boot_time'. - - * kern/i386/linuxbios/init.c (grub_millisleep): Remove stub. - (grub_machine_init): Use grub_tsc_init() rather than - installing an RTC-based handler via grub_install_get_time_ms(). - - * kern/i386/pit.c: New file. - * include/grub/i386/pit.h: Likewise. - -2008-08-05 Bean - - * boot/i386/pc/pxeboot.S (_start): Use drive number 0x7F for pxe. - - * conf/i386-pc.rmk (kernel_img_HEADERS): Add machine/pxe.h. - (pkglib_MODULES): Add pxe.mod and pxecmd.mod. - (pxe_mod_SOURCES): New macro. - (pxe_mod_CFLAGS): Likewise. - (pxe_mod_LDFLAGS): Likewise. - (pxecmd_mod_SOURCES): Likewise. - (pxecmd_mod_CFLAGS): Likewise. - (pxecmd_mod_LDFLAGS): Likewise. - - * kern/i386/pc/startup.S (grub_pxe_scan): New function. - (grub_pxe_call): Likewise. - - * include/grub/disk.h (grub_disk_dev_id): Add GRUB_DISK_DEVICE_PXE_ID. - - * commands/i386/pc/pxecmd.c: New file. - - * fs/i386/pc/pxe.c: Likewise. - - * include/grub/i386/pc/pxe.h: Likewise. - -2008-08-05 Bean - - * util/console.c (grub_console_cur_color): New variable. - (grub_console_standard_color): Likewise. - (grub_console_normal_color): Likewise. - (grub_console_highlight_color): Likewise. - (color_map): Likewise. - (use_color): Likewise. - (NUM_COLORS): New macro. - (grub_ncurses_setcolorstate): Handle color properly. - (grub_ncurses_setcolor): Don't change color here, just remember the - settings, color will be set in grub_ncurses_setcolorstate. - (grub_ncurses_getcolor): New function. - (grub_ncurses_init): Initialize color pairs. - (grub_ncurses_term): New member grub_ncurses_getcolor. - -2008-08-05 Colin D Bennett - - High resolution timer support. Implemented for x86 CPUs using TSC. - Extracted generic grub_millisleep() so it's linked in only as needed. - This requires a Pentium compatible CPU; if the RDTSC instruction is - not supported, then it falls back on the generic grub_get_time_ms() - implementation that uses the machine's RTC. - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/time.c', - `kern/i386/tsc.c', `kern/generic/rtc_get_time_ms.c' and - `kern/generic/millisleep.c'. - - * conf/i386-efi.rmk (kernel_mod_SOURCES): Add `kern/i386/tsc.c', - `kern/generic/rtc_get_time_ms.c' and `kern/generic/millisleep.c'. - - * conf/x86_64-efi.rml (kernel_mod_SOURCES): Add - `kern/generic/millisleep.c' and `kern/generic/rtc_get_time_ms.c'. - - * conf/sparc64-ieee1275.rmk (kernel_elf_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): Add - `kern/generic/millisleep.c'. - - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Likewise. - - * conf/i386-coreboot.rmk (kernel_elf_SOURCES): Add `kern/time.c'. - - * kern/generic/rtc_get_time_ms.c: New file. - - * kern/generic/millisleep.c: New file. - - * kern/misc.c: Don't include - anymore. - (grub_millisleep_generic): Removed. - - * commands/sleep.c (grub_interruptible_millisleep): Uses - grub_get_time_ms() instead of grub_get_rtc(). - - * include/grub/i386/tsc.h (grub_get_tsc): New file. New inline - function. - (grub_cpu_is_cpuid_supported): New inline function. - (grub_cpu_is_tsc_supported): New inline function. - (grub_tsc_init): New function prototype. - (grub_tsc_get_time_ms): New function prototype. - - * kern/i386/tsc.c (grub_get_time_ms): New file. - - * include/grub/time.h: Include . Don't include - anymore. - (grub_millisleep): Removed. - (grub_machine_init): Call grub_tsc_init. - - * kern/i386/linuxbios/init.c (grub_machine_init): Install the RTC - get_time_ms() implementation. - - * kern/sparc64/ieee1275/init.c (grub_millisleep): Removed. - (ieee1275_get_time_ms): New function. - (grub_machine_init): Install get_time_ms() implementation. - - * kern/i386/pc/init.c: Include . - (grub_machine_init): Call grub_tsc_init(). - (grub_millisleep): Removed. - - * kern/ieee1275/init.c (grub_millisleep): Removed. - (grub_machine_init): Install ieee1275_get_time_ms() - implementation. - (ieee1275_get_time_ms): New function. - (grub_get_rtc): Now calls ieee1275_get_time_ms(), which does the - real work. - -2008-08-05 Marco Gerards - - * disk/ata.c: Include . - (enum grub_ata_commands): Add `GRUB_ATA_CMD_EXEC_DEV_DIAGNOSTICS'. - (grub_ata_initialize): Rewritten. - (grub_ata_device_initialize): New function. - -2008-08-04 Pavel Roskin - - * kern/main.c: Include grub/mm.h. - -2008-08-04 Robert Millan - - * conf/i386-coreboot.rmk (COMMON_ASFLAGS, COMMON_CFLAGS) - (COMMON_LDFLAGS): Harmonize with i386-pc version (fixes a code - corruption problem). - -2008-08-04 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Fix misc - warnings introduced in my last commit. - -2008-08-03 Robert Millan - - Make PCI available on all i386 architectures. - - * include/grub/i386/pc/pci.h: Move from here ... - * include/grub/i386/pci.h: ... to here. - - * include/grub/i386/pc/pci.h: Remove. - * include/grub/i386/efi/pci.h: Remove. - * include/grub/x86_64/efi/pci.h: Remove. - - * include/grub/pci.h: Replace `' with - `'. - - * conf/i386-coreboot.rmk (pkglib_MODULES): Add `pci' and `lspci'. - (pci_mod_SOURCES, pci_mod_CFLAGS, pci_mod_LDFLAGS, lspci_mod_SOURCES) - (lspci_mod_CFLAGS, lspci_mod_LDFLAGS): New variables. - - * conf/i386-ieee1275.rmk: Likewise. - -2008-08-03 Robert Millan - - * term/i386/pc/vga_text.c (CRTC_CURSOR_DISABLE): New macro. - (grub_console_setcursor): Make it possible to set cursor off. - -2008-08-03 Robert Millan - - * util/grub.d/00_header.in: Be platform-agnostic. Probe for existence - of modules instead of assuming which platform provides what. - * util/update-grub.in: Likewise. - -2008-08-03 Robert Millan - - * kern/i386/pc/init.c (make_install_device): Check for `grub_prefix' - instead of `grub_install_dos_part' to determine whether a drive needs - to be prepended to prefix (`grub_install_dos_part' is not reliable, - because it can be overridden when loading GRUB via Multiboot). - -2008-08-02 Robert Millan - - * util/i386/pc/grub-install.in: Remove trailing slash from prefix. - -2008-08-02 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Add a pair - of informational grub_dprintf() calls. - -2008-08-02 Robert Millan - - * disk/memdisk.c (memdisk_size): Don't initialize. - (GRUB_MOD_INIT(memdisk)): Find memdisk using grub_module_iterate(). - - * include/grub/i386/pc/kernel.h - (GRUB_KERNEL_MACHINE_MEMDISK_IMAGE_SIZE): Remove macro. - (GRUB_KERNEL_MACHINE_PREFIX, GRUB_KERNEL_MACHINE_DATA_END): Shift. - (grub_memdisk_image_size, grub_arch_memdisk_addr) - (grub_arch_memdisk_size): Remove. - - * include/grub/kernel.h (struct grub_module_header): Remove `offset' - field (was only used to transfer a constant). Add `type' field to - support multiple module types. - (grub_module_iterate): New function. - - * kern/device.c (grub_device_open): Do not hide error messages - when grub_disk_open() fails. Use grub_print_error() instead. - - * kern/i386/pc/init.c (grub_arch_modules_addr) - (grub_arch_memdisk_size): Remove functions. - (grub_arch_modules_addr): Return the module address in high memory - (now that it isn't copied anymore). - - * kern/i386/pc/startup.S (grub_memdisk_image_size): Remove variable. - (codestart): Don't add grub_memdisk_image_size to %ecx in LZMA - decompression routine (grub_total_module_size already includes that - now). Don't copy modules back to low memory. - - * kern/main.c: Include `'. - (grub_load_modules): Split out (and use) ... - (grub_module_iterate): ... this function, which iterates through - module objects and runs a hook. - Comment out grub_mm_init_region() call, as it would cause non-ELF - modules to be overwritten. - - * util/i386/pc/grub-mkimage.c (generate_image): Instead of appending - the memdisk image in its own region, make it part of the module list. - * util/elf/grub-mkimage.c (options): Add "memdisk"|'m' option. - (main): Parse --memdisk|-m option, and pass user-provided path as - parameter to generate_image(). - (add_segments): Pass `memdisk_path' down to load_modules(). - (load_modules): Embed memdisk image in module section when requested. - * util/i386/efi/grub-mkimage.c (make_mods_section): Initialize - `header.type' instead of `header.offset'. - - * conf/powerpc-ieee1275.rmk (pkglib_MODULES): Add `memdisk.mod'. - (memdisk_mod_SOURCES, memdisk_mod_CFLAGS) - (memdisk_mod_LDFLAGS): New variables. - * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - -2008-08-02 Robert Millan - - * loader/i386/pc/multiboot.c (playground, forward_relocator) - (backward_relocator): New variables. Used to allocate and relocate - the payload, respectively. - (grub_multiboot_load_elf32): Load into heap instead of requested - address, install the appropriate relocator code in each bound of - the payload, and set the entry point such that - grub_multiboot_real_boot() will jump to one of them. - - * kern/i386/loader.S (grub_multiboot_payload_size) - (grub_multiboot_payload_orig, grub_multiboot_payload_dest) - (grub_multiboot_payload_entry_offset): New variables. - (grub_multiboot_real_boot): Set cpu context to what the relocator - expects, and jump to the relocator instead of the payload. - - * include/grub/i386/loader.h (grub_multiboot_payload_size) - (grub_multiboot_payload_orig, grub_multiboot_payload_dest) - (grub_multiboot_payload_entry_offset): Export. - -2008-08-01 Bean - - * normal/menu_entry.c (editor_getline): Don't return the original - string as result, as it will be released by lexer once it has done - using it. - -2008-08-01 Robert Millan - - * util/grub.d/10_linux.in: Use prepare_grub_to_access_device() from - within menuentries, not before them. - util/grub.d/10_hurd.in: Likewise. - -2008-08-01 Bean - - * conf/common.rmk (pkglib_MODULES): Add bufio.mod. - (bufio_mod_SOURCES): New macro. - (bufio_mod_CFLAGS): Likewise. - (bufio_mod_LDFLAGS): Likewise. - - * include/grub/bufio.h: New file. - - * io/bufio.c: Likewise. - - * video/png.c: Replace with . - (grub_video_reader_png): Use grub_buffile_open to open file. - - * video/jpeg.c: Replace with . - (grub_video_reader_jpeg): Use grub_buffile_open to open file. - - * video/tga.c: Replace with . - (grub_video_reader_tga): Use grub_buffile_open to open file. - - * font/manager.c: Include . - (add_font): Use grub_buffile_open to open file. - -2008-07-31 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): When loading - ELF segments, use a macro for arbitrarily accessing any of them instead - of preparing a pointer that allows access to one at a time. - (grub_multiboot_load_elf64): Likewise. - -2008-07-31 Bean - - * boot/i386/pc/lnxboot.S (real_code_2): Replace 0x50 with - GRUB_KERNEL_MACHINE_DATA_END. - -2008-07-30 Robert Millan - - * include/grub/i386/pc/kernel.h (GRUB_KERNEL_MACHINE_DATA_END): - Increase from 0x50 to 0x60. - * util/i386/pc/grub-install.in: Detect cross-disk installs, and - use UUIDs to identify the root drive for them. If that's not - possible, abort. - * util/i386/pc/grub-setup.c (setup): Do not special-case, or even - check, for cross-disk installs. - -2008-07-30 Robert Millan - - * kern/ieee1275/init.c (grub_machine_set_prefix): If `grub_prefix' - is non-empty, use it to set the `prefix' environment variable instead - of the usual approach. - * kern/i386/linuxbios/init.c (make_install_device): Remove function. - (grub_machine_set_prefix): Use `grub_prefix' to set the `prefix' - environment variable instead of dummy make_install_device(). - - * kern/i386/ieee1275/startup.S: Include `'. - (start): Insert a data section, with `grub_prefix' variable. - * kern/i386/linuxbios/startup.S: Likewise. - - * include/grub/powerpc/ieee1275/kernel.h [!ASM_FILE] (grub_prefix): - New variable reference. - * include/grub/i386/ieee1275/kernel.h (GRUB_KERNEL_MACHINE_PREFIX): - New macro. Defines offset of `grub_prefix' within startup.S (relative - to `start'). - (GRUB_KERNEL_MACHINE_DATA_END): New macro. Defines the end of data - section within startup.S (relative to `start'). - * include/grub/i386/coreboot/kernel.h: Likewise. - - * util/elf/grub-mkimage.c (add_segments): Receive `prefix' parameter. - Overwrite grub_prefix with its contents, at the beginning of the - first segment. - (main): Understand -p|--prefix. - -2008-07-30 Robert Millan - - * util/grub.d/10_hurd.in: Source ${libdir}/grub/update-grub_lib. - -2008-07-30 Robert Millan - - * term/i386/pc/vga_text.c (grub_console_cls): Use - grub_console_gotoxy() to go back to beginning of the screen. - Found by Patrick Georgi - -2008-07-29 Christian Franke - - * util/update-grub_lib.in (make_system_path_relative_to_its_root): - Add conversion of emulated mount points on Cygwin. - -2008-07-29 Christian Franke - - * util/update-grub.in: Add a check for admin - group on Cygwin. - Remove old `grub.cfg.new' before creation. - Add `-f' to `mv' to handle the different filesystem - semantics of Windows. - -2008-07-29 Bean - - * normal/main.c (get_line): Fix buffer overflow bug. - -2008-07-28 Robert Millan - - * partmap/apple.c (GRUB_APPLE_HEADER_MAGIC): New macro. - (struct grub_apple_header): New struct. Describes the layout of - the partmap header. - (apple_partition_map_iterate): Check the header magic as well as the - partition magic (which was already being checked). - -2008-07-28 Pavel Roskin - - * genmk.rb: Add a warning to the beginning of the output that - it's a generated file and should not be edited. - -2008-07-28 Robert Millan - - * disk/raid.c (grub_raid_scan_device): Do not abort when two disks - with the same number are found, just use issue a warning with - grub_dprintf(), as this error has been reported to be non-fatal. - -2008-07-27 Robert Millan - - * disk/ata.c (grub_ata_dumpinfo): Use grub_dprintf() for debugging - information. - -2008-07-27 Bean - - * fs/fat.c (GRUB_FAT_MAXFILE): New constant. - (grub_fat_find_dir): Ignore case when comparing filename. - -2008-07-27 Bean - - * fs/xfs.c (grub_xfs_dir_header): Change field i8count back to - smallino, as it's more descriptive, and i8count can be confused with - the other field count. - (grub_xfs_iterate_dir): Adjust grub_xfs_dir_entry pointer for small - inode type. - -2008-07-27 Bean - - * commands/crc.c: New file. - - * lib/crc.c: Likewise. - - * include/grub/lib/crc.h: Likewise. - - * util/grub-fstest.c: grub/hexdump.h => grub/lib/hexdump.h. - - * commands/hexdump.c: grub/hexdump.h => grub/lib/hexdump.h. - (hexdump): Move this function to ... - - * lib/hexdump.c: ... here. - - * include/grub/hexdump.h: Renamed to ... - - * include/grub/lib/hexdump.h: ... this. - - * commands/loadenv.c: grub/envblk.h => grub/lib/envblk.h - - * util/grub-editenv.c: Likewise. - - * include/envblk.h: Renamed to ... - - * include/lib/envblk.h: ... this. - - * util/envblk.c: Renamed to ... - - * lib/envblk.c: ... this. - - * conf/common.rmk (grub_fstest_SOURCES): commands/hexdump.c => - lib/hexdump.c. - (grub_editenv_SOURCES): util/envblk.c => lib/envblk.c - (pkglib_MODULES): Add crc.mod. - (hexdump_mod_SOURCES): Add lib/hexdump.c. - (loadenv_mod_SOURCES): util/envblk.c => lib/envblk.c. - (crc_mod_SOURCES): New macro. - (crc_mod_CFLAGS): Likewise. - (crc_mod_LDFLAGS): Likewise. - - * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add lib/hexdump.c. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. - -2008-07-27 Felix Zielcke - - * commands/help.c: Include . - (TERM_WIDTH): Removed. Updated all users. - -2008-07-27 Pavel Roskin - - * util/getroot.c (find_root_device): Rephrase a comment to avoid - spurious warnings about a comment within a comment. - -2008-07-25 Robert Millan - - * util/getroot.c (find_root_device): Skip devices that match - /dev/dm-[0-9]. This lets the real device be found for any type of - abstraction (LVM, EVMS, RAID..). - (grub_guess_root_device): Do not traverse /dev/mapper (for LVM) - and /dev/evms (for EVMS) before traversing /dev. If a /dev/dm-[0-9] - device is found first, find_root_device() will now skip it. - -2008-07-24 Pavel Roskin - - * include/grub/types.h: Use __builtin_bswap32() and - __builtin_bswap64() with gcc 4.3 and newer. - -2008-07-24 Christian Franke - - * util/i386/pc/grub-install.in: If `--debug' is specified, - pass `--verbose' to grub-setup. - Abort script if make_system_path_relative_to_its_root() fails. - -2008-07-24 Bean - - * configure.ac: Fixed a bug caused by the previous cygwin patch, - variable `target_platform' should be `platform'. - -2008-07-24 Bean - - * video/reader/png.c (DEFLATE_HLIT_MAX): Change value. - (grub_png_init_fixed_block): New function. - (grub_png_decode_image_data): Handle fixed huffman code compression. - -2008-07-24 Bean - - * common.rmk (bin_UTILITIES): Add grub-pe2elf. - (grub_pe2elf_SOURCES): New macro. - (CLEANFILES): Add grub-pe2elf. - - * include/grub/efi/pe32.h (GRUB_PE32_SCN_ALIGN_1BYTES): New constant. - (GRUB_PE32_SCN_ALIGN_2BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_4BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_8BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_16BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_32BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_64BYTES): Likewise. - (GRUB_PE32_SCN_ALIGN_SHIFT): Likewise. - (GRUB_PE32_SCN_ALIGN_MASK): Likewise. - (GRUB_PE32_SYM_CLASS_EXTERNAL): Likewise. - (GRUB_PE32_SYM_CLASS_STATIC): Likewise. - (GRUB_PE32_SYM_CLASS_FILE): Likewise. - (GRUB_PE32_DT_FUNCTION): Likewise. - (GRUB_PE32_REL_I386_DIR32): Likewise. - (GRUB_PE32_REL_I386_REL32): Likewise. - (grub_pe32_symbol): New structure. - (grub_pe32_reloc): Likewise. - - * util/grub-pe2elf.c: New file. - - * configure.ac: Set TARGET_OBJ2ELF if host os is cygwin. Don't test for - start symbol in non pc platform. - - * genmk.rb: Use TARGET_OBJ2ELF to convert native object format to elf. - - The following patches are from Christian Franke. - - * include/grub/dl.h: Remove .previous, gas supports this only - for ELF format. - - * include/grub/symbol.h [__CYGWIN__] (#define FUNCTION/VARIABLE): - Remove .type, gas supports this only for ELF format. - - * kern/dl.c (grub_dl_resolve_dependencies): Add check for trailing - nullbytes in symbol table. This fixes an infinite loop if table is - zero filled. - - * Makefile.in: Add autoconf replacements TARGET_IMG_LDSCRIPT, - TARGET_IMG_LDFLAGS and EXEEXT. - - * aclocal.m4 (grub_PROG_OBJCOPY_ABSOLUTE): Replace -Wl,-N by - TARGET_IMG_LDFLAGS_AC. - (grub_CHECK_STACK_ARG_PROBE): New function. - - * conf/i386-pc.rmk: Replace -Wl,-N by TARGET_IMG_LDFLAGS. - - * conf/i386-pc-cygwin-ld-img.sc: New linker script. - - * configure.ac: Add check for linker script "conf/${target}-img-ld.c" - to set TARGET_IMG_LD* accordingly. - Add check for Cygwin to set TARGET_MOD_OBJCOPY accordingly. - Add call to grub_CHECK_STACK_ARG_PROBE. - Use TARGET_IMG_LDFLAGS to check start, bss_start, end symbols. - - * genkernsyms.sh.in: Handle HAVE_ASM_USCORE case. - - * genmk.rb: Add EXEEXT to CLEANFILES. - -2008-07-23 Robert Millan - - * Makefile.in (UNICODE_ARROWS, UNICODE_LINES): New variables (they - define the codes for arrows and lines used for the menu). - (ascii.pff): Generate fonts for $(UNICODE_ARROWS) and $(UNICODE_LINES) - as well. - - * util/update-grub_lib.in (font_path): Prefer ascii.pff over complete - fonts, because the latter are too slow. - -2008-07-21 Bean - - * kern/i386/pc/startup.S (gate_a20_try_bios): Change test order for - a20. Run keyboard test last, as it will cause macbook to halt. - -2008-07-18 Pavel Roskin - - * kern/dl.c: Go back to using GRUB_CPU_SIZEOF_VOID_P. We cannot - load foreign architecture modules correctly anyway. Keep - support for loading host architecture modules, whether we - compile them or not. - -2008-07-17 Pavel Roskin - - * configure.ac: Use -m32 or -m64 regardless of whether we had to - change target_cpu. The compiler default can mismatch target_cpu - in any case. - - * disk/efi/efidisk.c: Fix format warnings on x86_64. - * kern/efi/efi.c: Likewise. - - * aclocal.m4 (grub_PROG_TARGET_CC): New macro. Check if the - target compiler is functional. - * configure.ac: Call grub_PROG_TARGET_CC once all target flags - are set up. - - * configure.ac: Default to efi platform for x86_64-apple. Allow - powerpc64 CPU, default to ieee1275 platform for it. Split CPU - adjustments from the rest, only do them if target is not - explicitly given. Merge other adjustments with the final sanity - check. Remove an extraneous check for supported CPU. Be - specific which CPU and which platform is not supported. - - * configure.ac: Default to pc platform for x86_64. - -2008-07-17 Robert Millan - - Partial LinuxBIOS -> Coreboot rename. - - * conf/i386-linuxbios.rmk: Renamed to ... - * conf/i386-coreboot.rmk: ... this. - * Makefile.in (RMKFILES): s/i386-linuxbios.rmk/i386-coreboot.rmk/g. - * configure.ac: Accept "coreboot" as input platform (but maintain - compatibility with "linuxbios"). - * include/grub/i386/linuxbios: Renamed to ... - * include/grub/i386/coreboot: ... this. - -2008-07-17 Bean - - * conf/i386/efi.rmk (pkglib_MODULES): add pci.mod and lspci.mod. - (appleldr_mod_SOURCE): New variable. - (appleldr_mod_CFLAGS): Likewise. - (appleldr_mod_LDFLAGS): Likewise. - (pci_mod_SOURCES): Likewise. - (pci_mod_CFLAGS): Likewise. - (pci_mod_LDFLAGS): Likewise. - (lspci_mod_SOURCES): Likewise. - (lspci_mod_CFLAGS): Likewise. - (lspci_mod_LDFLAGS): Likewise. - - * conf/x86_64-efi.rmk: New file. - - * disk/efi/efidisk.c (grub_efidisk_read): Wrap efi calls with efi_call_N - macro. - (grub_efidisk_write): Likewise. - - * include/efi/api.h (efi_call_0): New macro. - (efi_call_1): Likewise. - (efi_call_2): Likewise. - (efi_call_3): Likewise. - (efi_call_4): Likewise. - (efi_call_5): Likewise. - (efi_call_6): Likewise. - - * include/grub/efi/chainloader.h (grub_chainloader_cmd): Rename to - grub_rescue_cmd_chainloader. - - * include/grub/efi/pe32.h (GRUB_PE32_MACHINE_X86_64): New macro. - (grub_pe32_optional_header): Change some fields based on i386 or - x86_64 platform. - (GRUB_PE32_PE32_MAGIC): Likewise. - - * include/grub/efi/uga_draw.h: New file. - - * include/grub/elf.h (STN_ABS): New constant. - (R_X86_64_NONE): Relocation constant for x86_64. - (R_X86_64_64): Likewise. - (R_X86_64_PC32): Likewise. - (R_X86_64_GOT32): Likewise. - (R_X86_64_PLT32): Likewise. - (R_X86_64_COPY): Likewise. - (R_X86_64_GLOB_DAT): Likewise. - (R_X86_64_JUMP_SLOT): Likewise. - (R_X86_64_RELATIVE): Likewise. - (R_X86_64_GOTPCREL): Likewise. - (R_X86_64_32): Likewise. - (R_X86_64_32S): Likewise. - (R_X86_64_16): Likewise. - (R_X86_64_PC16): Likewise. - (R_X86_64_8): Likewise. - (R_X86_64_PC8): Likewise. - - * include/grub/i386/efi/pci.h: New file. - - * include/grub/i386/linux.h (GRUB_LINUX_EFI_SIGNATURE): - Change it value based on platform. - (GRUB_LINUX_EFI_SIGNATURE_0204): New constant. - (GRUB_E820_RAM): Likewise. - (GRUB_E820_RESERVED): Likewise. - (GRUB_E820_ACPI): Likewise. - (GRUB_E820_NVS): Likewise. - (GRUB_E820_EXEC_CODE): Likewise. - (GRUB_E820_MAX_ENTRY): Likewise. - (grub_e820_mmap): New structure. - (linux_kernel_header): Change the efi field according to different - kernel version, also field from linux_kernel_header. - - * include/grub/kernel.h (grub_module_info): Add padding for x86_64. - - * include/grub/pci.h (GRUB_PCI_ADDR_SPACE_MASK): New constant. - (GRUB_PCI_ADDR_SPACE_MEMORY): Likewise. - (GRUB_PCI_ADDR_SPACE_IO): Likewise. - (GRUB_PCI_ADDR_MEM_TYPE_MASK): Likewise. - (GRUB_PCI_ADDR_MEM_TYPE_32): Likewise. - (GRUB_PCI_ADDR_MEM_TYPE_1M): Likewise. - (GRUB_PCI_ADDR_MEM_TYPE_64): Likewise. - (GRUB_PCI_ADDR_MEM_PREFETCH): Likewise. - (GRUB_PCI_ADDR_MEM_MASK): Likewise. - (GRUB_PCI_ADDR_IO_MASK): Likewise. - - * include/grub/x86_64/efi/kernel.h: New file. - - * include/grub/x86_64/efi/loader.h: Likewise. - - * include/grub/x86_64/efi/machine.h: Likewise. - - * include/grub/x86_64/efi/pci.h: Likewise. - - * include/grub/x86_64/efi/time.h: Likewise. - - * include/grub/x86_64/linux.h: Likewise. - - * include/grub/x86_64/setjmp.h: Likewise. - - * include/grub/x86_64/time.h: Likewise. - - * include/grub/x86_64/types.h: Likewise. - - * kern/dl.c (GRUB_CPU_SIZEOF_VOID_P): Changed to - GRUB_TARGET_SIZEOF_VOID_P. - - * kern/efi/efi.c (grub_efi_locate_protocol): Wrap efi calls. - (grub_efi_locate_handle): Likewise. - (grub_efi_open_protocol): Likewise. - (grub_efi_set_text_mode): Likewise. - (grub_efi_stall): Likewise. - (grub_exit): Likewise. - (grub_reboot): Likewise. - (grub_halt): Likewise. - (grub_efi_exit_boot_services): Likewise. - (grub_get_rtc): Likewise. - - * kern/efi/mm.c (MEMORY_MAP_SIZE): Change to 0x3000 for new models. - (GRUB_CPU_SIZEOF_VOID_P): Changed to GRUB_TARGET_SIZEOF_VOID_P. - (grub_efi_allocate_pages): Wrap efi calls. - (grub_efi_free_pages): Wrap efi calls. - (grub_efi_get_memory_map): Wrap efi calls. - - * kern/x86_64/dl.c: New file. - - * kern/x86_64/efi/callwrap.S: Likewise. - - * kern/x86_64/efi/startup.S: Likewise. - - * loader/efi/appleloader.c: Likewise. - - * loader/efi/chainloader.c (cmdline): New variable. - (grub_chainloader_unload): Wrap efi calls. - (grub_chainloader_boot): Likewise. - (grub_rescue_cmd_chainloader): Wrap efi calls, handle - command line. - - * loader/efi/chainloader_normal.c (chainloader_command): - Change grub_chainloader_cmd to grub_rescue_cmd_chainloader, pass - command line. - - * loader/i386/efi/linux.c (allocate_pages): Change allocation - method. - (grub_e820_add_region): New function. - (grub_linux_boot): Construct e820 map from efi map, handle x86_64 - booting. - (grub_find_video_card): New function. - (grub_linux_setup_video): New function. - (grub_rescue_cmd_linux): Probe for video information. - - * normal/x86_64/setjmp.S: New file. - - * term/efi/console.c (map_char): New function. - (grub_console_putchar): Map unicode char. - (grub_console_checkkey): Wrap efi calls. - (grub_console_getkey): Likewise. - (grub_console_getwh): Likewise. - (grub_console_gotoxy): Likewise. - (grub_console_cls): Likewise. - (grub_console_setcolorstate): Likewise. - (grub_console_setcursor): Likewise. - - * util/i386/efi/grub-mkimage.c: Add support for x86_64. - -2008-07-16 Pavel Roskin - - * loader/i386/efi/linux.c (allocate_pages): Fix warnings in - format strings. - - * util/i386/efi/grub-mkimage.c (get_target_address): Return a - pointer, not an integer. This fixes a warning and prevents - precision loss on 64-bit systems. - (relocate_addresses): Remove unneeded cast. - -2008-07-15 Pavel Roskin - - * kern/i386/ieee1275/init.c: Include grub/cache.h. - - * term/ieee1275/ofconsole.c: Disable code unused on i386. - - * kern/ieee1275/ieee1275.c (grub_ieee1275_get_integer_property): - Fix comparison between signed and unsigned. - - * include/grub/i386/ieee1275/console.h: Declare - grub_console_init() and grub_console_fini(). - - * loader/i386/ieee1275/linux.c (grub_set_bootpath): Remove. - It's empty and unused. - - * fs/ext2.c (grub_ext2_read_block): Initialize blknr in the - beginning to avoid warnings with some compilers. - - * loader/ieee1275/multiboot2.c: Include grub/machine/loader.h. - [__i386__] (grub_mb2_arch_boot): Avoid unnecessary cast. - -2008-07-14 Pavel Roskin - - * kern/env.c (grub_register_variable_hook): Don't copy empty - string, it leaks memory. Pass "" to grub_env_set(), it should - handle constant strings. - - * commands/blocklist.c (grub_cmd_blocklist): Fix format warning. - * commands/cmp.c (grub_cmd_cmp): Likewise. - * kern/dl.c (grub_dl_flush_cache): Likewise. - (grub_dl_load_core): Likewise. - * kern/elf.c (grub_elf32_load_phdrs): Likewise. - (grub_elf64_load_phdrs): Likewise. - -2008-07-13 Pavel Roskin - - * lib/LzmaEnc.c (LzmaEnc_SetProps): Fix warning about comparison - between signed and unsigned. - (LzmaEnc_Finish): Fix warning about an unused parameter. - -2008-07-13 Bean - - * Makefile.in (enable_lzo): New rule. - - * conf/i386-pc.rmk (grub_mkimage_SOURCES): New test with enable_lzo. - - * configure.ac (ENABLE_LZO): New option --enable-lzo. - - * boot/i386/pc/lnxboot.S: #include . - - * include/grub/i386/pc/kernel.h (GRUB_KERNEL_MACHINE_RAW_SIZE): Change - its value according to the compression algorithm used, lzo or lzma. - - * util/i386/pc/grub-mkimage.c (compress_kernel): Use different - compression algorithm according to configure macro. - - * kern/i386/pc/startup.S (codestart): Likewise. - - * kern/i386/pc/lzma_decode.S: New file. - - * include/grub/lib/LzFind.h: Likewise. - - * include/grub/lib/LzHash.h: Likewise. - - * include/grub/lib/LzmaDec.h: Likewise. - - * include/grub/lib/LzmaEnc.h: Likewise. - - * include/grub/lib/LzmaTypes.h: Likewise. - - * lib/LzFind.c: Likewise. - - * lib/LzmaDec.c: Likewise. - - * lib/LzmaEnc.c: Likewise. - -2008-07-13 Bean - - * fs/ext2.c (EXT4_EXTENTS_FLAG): New macro. - (grub_ext4_extent_header): New structure. - (grub_ext4_extent): Likewise. - (grub_ext4_extent_idx): Likewise. - (grub_ext4_find_leaf): New function. - (grub_ext2_read_block): Handle extents. - -2008-07-12 Robert Millan - - * util/i386/pc/grub-mkrescue.in: s/grub-install/grub-mkrescue/g. - -2008-07-11 Robert Millan - - * util/grub.d/40_custom.in: New file. Example on how to add custom - entries to /etc/grub.d. - * conf/common.rmk (%, update-grub_SCRIPTS, CLEANFILES): Install - 40_custom (implicitly, by merging all the grub.d rules). - -2008-07-11 Pavel Roskin - - * commands/read.c (grub_getline): Fix invalid memory access. - Don't add newline to the variable value. - - * term/i386/pc/serial.c (GRUB_SERIAL_PORT_NUM): New constant. - [!GRUB_MACHINE_PCBIOS] (serial_hw_io_addr): Add COM2 and COM3. - (serial_hw_get_port): Check validity of the port number. - (grub_cmd_serial): Check return value of serial_hw_get_port(). - -2008-07-07 Pavel Roskin - - * boot/i386/pc/diskboot.S (notification_string): Replace - "Loading kernel" with just "loading". This is shorter, less - confusing and saves a few bytes for possible future changes. - -2008-07-05 Pavel Roskin - - * disk/ata.c (grub_ata_dumpinfo): Don't output addressing and - size for ATAPI devices, they are undefined. Output sector - number in decimal form. - - * disk/ata.c: Use named constants for status bits. - -2008-07-04 Pavel Roskin - - * kern/i386/linuxbios/init.c (grub_machine_init): Cast addr to - grub_addr_t before casting it to the void pointer to fix a - warning. Non-addressable regions are discarded earlier. - (grub_arch_modules_addr): Cast _end to grub_addr_t. - * kern/i386/linuxbios/table.c: Include grub/misc.h. - (check_signature): Don't shadow table_header. - (grub_linuxbios_table_iterate): Cast numeric constants to - grub_linuxbios_table_header_t. - * include/grub/i386/linuxbios/init.h: Add noreturn attribute to - grub_stop(). - - * kern/ieee1275/init.c: Cast _start and _end to grub_addr_t to - prevent warnings. - - * include/grub/misc.h (ALIGN_UP): Avoid unnecessary cast to a - pointer, which can cause warnings. Support 64-bit addresses. - - * util/elf/grub-mkimage.c: Use GRUB_TARGET_SIZEOF_LONG instead - of sizeof(long). This fixes PowerPC image generation on x86_64. - -2008-07-04 Robert Millan - - This fixes a performance issue when pc & gpt partmap iterators - didn't abort iteration even after our hook found what it was - looking for (often causing expensive probes of non-existent drives). - - Some callers relied on previous buggy behaviour, since they would - raise an error when their own hooks caused early abortion of its - iteration. - - * kern/device.c (grub_device_open): Improve error message. - * disk/lvm.c (grub_lvm_open): Likewise. - * disk/raid.c (grub_raid_open): Likewise. - - * partmap/pc.c (pc_partition_map_iterate): Abort parent iteration - when hook requests it, independently of grub_errno. - (pc_partition_map_probe): Do not fail when find_func() caused - early abortion of pc_partition_map_iterate(). - - * partmap/gpt.c (gpt_partition_map_iterate): Abort parent iteration - when hook requests it, independently of grub_errno. - (gpt_partition_map_probe): Do not fail when find_func() caused - early abortion of gpt_partition_map_iterate(). - - * kern/partition.c (grub_partition_iterate): Abort parent iteration - when hook requests it, independently of grub_errno. Do not fail when - part_map_iterate_hook() caused early abortion of p->iterate(). - - * util/biosdisk.c (grub_util_biosdisk_get_grub_dev): Do not fail - when grub_partition_iterate() returned with non-zero. - -2008-07-03 Pavel Roskin - - * disk/ata.c (grub_ata_pio_write): Check status before writing, - like we do in grub_ata_pio_read(). - (grub_ata_readwrite): Always write individual sectors. Fix the - sector count for the remainder. - (grub_ata_write): Enable writing to ATA devices. Correctly - report error for ATAPI devices. - -2008-07-02 Pavel Roskin - - * boot/i386/pc/cdboot.S: Add _start entry to fix a linker - warning. - - * disk/ata.c (grub_ata_readwrite): Don't increment sector number - for every read sector, we already increment it for the whole - batch. This fixes reading more than 256 sectors at once. - - * util/grub-editenv.c (cmd_info): Cast argument to long - explicitly. ptrdiff_t reduces to int on i386. - - * util/grub-editenv.c (main): Be specific which parameter is - missing. - - * disk/memdisk.c (memdisk_addr): Make a pointer to fix warnings. - (memdisk): Make memdisk_orig_addr a pointer. - - * fs/reiserfs.c (grub_reiserfs_read): Fix misuse of grub_size_t - for file offsets, use grub_off_t instead. Fix printf format - warnings. - - * fs/reiserfs.c: Remove #warning, TODO list items don't belong - there. Real unexpected warnings should not drown in the noise - about known problems. - - * commands/hexdump.c (grub_cmd_hexdump): Fix misuse of - grub_disk_addr_t for memory addresses. - - * loader/aout.c (grub_aout_load): Cast load_addr to pointer - explicitly to fix a warning. - - * util/grub-editenv.c (cmd_info): Fix warning in printf format. - - * Makefile.in (MODULE_LDFLAGS): New variable. - * aclocal.m4 (grub_PROG_LD_BUILD_ID_NONE): New macro. Check if - the linker accepts --build-id=none. - * configure.ac: Call grub_PROG_LD_BUILD_ID_NONE. Substitute - MODULE_LDFLAGS. - * genmk.rb: Use MODULE_LDFLAGS when linking modules. - - * fs/xfs.c (struct grub_xfs_dir_header): Use names similar to - those in Linux XFS code. Provide a way to access 64-bit parent - inode. - (grub_xfs_iterate_dir): Use the new names. Avoid reading past - the end of struct grub_xfs_dir_header. - -2008-07-02 Bean - - * include/grub/ieee1275.h (grub_ieee1275_flag): New constant - GRUB_IEEE1275_FLAG_CANNOT_INTERPRET, GRUB_IEEE1275_FLAG_FORCE_CLAIM - and GRUB_IEEE1275_FLAG_NO_ANSI. - - * kern/ieee1275/cmain.c (grub_ieee1275_find_options): Set flag - GRUB_IEEE1275_FLAG_CANNOT_INTERPRET, GRUB_IEEE1275_FLAG_FORCE_CLAIM - and GRUB_IEEE1275_FLAG_NO_ANSI for Open Hackware. - - * kern/ieee1275/ieee1275.c (grub_ieee1275_interpret): Return - immediately if GRUB_IEEE1275_FLAG_CANNOT_INTERPRET is set. - - * kern/ieee1275/init.c (grub_claim_heap): Claim memory directly if - GRUB_IEEE1275_FLAG_FORCE_CLAIM is set. - - * term/ieee1275/ofconsole.c (grub_ofconsole_writeesc): Don't output - esc sequence on non ANSI terminal. - (grub_ofconsole_gotoxy): Emulate backspace key on non ANSI terminal. - - * util/elf/grub-mkimage.c (add_segments): Move ELF header to the - beginning of file. - -2008-07-02 Bean - - * conf/common.rmk (bin_UTILITIES): Add grub-editenv. - (grub_editenv_SOURCES): New variable. - (pkglib_MODULES): Add loadenv.mod. - (loadenv_mod_SOURCES): New variable. - (loadenv_mod_CFLAGS): Likewise. - (loadenv_mod_LDFLAGS): Likewise. - - * include/grub/envblk.h: New file. - - * util/envblk.c: New file. - - * util/grub-editenv.c: New file. - - * commands/loadenv.c: New file. - -2008-07-01 Pavel Roskin - - * include/multiboot2.h (struct multiboot_tag_module): Use char, - not unsigned char. This fixes warnings and is consistent with - other tags. - - * disk/fs_uuid.c (search_fs_uuid): Correctly increment count. - - * normal/parser.y: Define YYENABLE_NLS as 0 to fix warnings. - - * term/tparm.c (analyze): Always set *popcount. - - * loader/i386/pc/linux.c (grub_rescue_cmd_linux): Remove useless - cast to fix a warning. - - * loader/i386/pc/multiboot2.c (grub_mb2_arch_module_alloc): Use - cast to suppress a warning. - - * fs/afs.c (grub_afs_read_block): Return grub_disk_addr_t, as - grub_fshelp_read_file() expects. - - * fs/fat.c: Fix UUID calculation on big-endian systems. We - write uuid as a 32-bit value in CPU byte order, so declare and - use it as such. - - * disk/raid.c: Cast grub_dprintf() arguments to unsigned long - long if the format specifier expects it. - * partmap/gpt.c (gpt_partition_map_iterate): Likewise. - * partmap/pc.c (pc_partition_map_iterate): Likewise. - * fs/ntfs.c (grub_ntfs_uuid): Cast data->uuid to unsigned long - long to fix a warning. - * fs/reiserfs.c (grub_reiserfs_read): Change casts in - grub_dprintf() arguments to fix warnings. - -2008-06-30 Pavel Roskin - - * util/i386/pc/grub-setup.c (setup): Write install_dos_part and - install_bsd_part immediately before core.img is embedded or - modified on disk. This fixes core.img verification if core.img - cannot be embedded. - - * util/i386/pc/grub-setup.c (setup): Use core_path_dev, not - core_path to calculate the blocklist. - Patch from Javier MartĂ­n - -2008-06-29 Robert Millan - - * fs/xfs.c (GRUB_XFS_FSB_TO_BLOCK): New macro. Maps filesystem - block to disk block. - (grub_xfs_read_block): Use GRUB_XFS_FSB_TO_BLOCK() on result. - Patch from Niels Böhm - -2008-06-29 Robert Millan - - * util/update-grub_lib.in (font_path): Search for fonts in - /boot/grub first, which is more likely to be readable (we aren't - deciding where fonts live, just looking for them). - -2008-06-26 Pavel Roskin - - * util/biosdisk.c (read_device_map): Don't leave dead map - entries for devices failing stat() check. - - * util/i386/pc/grub-setup.c (setup): Don't reuse core_path, use - core_path_dev for the core.img path on the target device. - -2008-06-26 Robert Millan - - * disk/fs_uuid.c: New file. - * conf/common.rmk (pkglib_MODULES): Add `fs_uuid.mod'. - (fs_uuid_mod_SOURCES, fs_uuid_mod_CFLAGS) - (fs_uuid_mod_LDFLAGS): New variables. - * include/grub/disk.h (grub_disk_dev_id): Add - `GRUB_DISK_DEVICE_UUID_ID'. - * kern/disk.c (grub_disk_dev_iterate): Allow disk devices not to - implement iterate(). - -2008-06-26 Robert Millan - - * util/grub.d/10_linux.in: Avoid passing UUIDs to Linux when either - "/dev/disk/by-uuid/${GRUB_DEVICE_UUID}" does not exist, or when a - Linux image includes no initrd. - -2008-06-21 Javier MartĂ­n - - * util/i386/pc/grub-setup.c (setup): Remove literal "core.img" in a - call to resolve the core image location that effectively appended the - name twice. - -2008-06-21 Robert Millan - - * util/grub.d/00_header.in: Move last prepare_grub_to_access_device() - call from here ... - - * util/grub.d/10_hurd.in: ... to here ... - * util/grub.d/10_linux.in: ... and here. - -2008-06-19 Robert Millan - - * kern/main.c (grub_main): Export `prefix' variable immediately - after it has been set by grub_machine_set_prefix(). - -2008-06-19 Robert Millan - - * commands/search.c (search_label, search_fs_uuid, search_file): Print - search result when not saving to variable, not the other way around. - When saving to variable, abort iteration as soon as a match is found. - -2008-06-19 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): Remove - check for partition that provides /boot/grub. Its logic is flawed, - as it prevents prepare_grub_to_access_device() from being called - multiple times. - -2008-06-19 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): Issue - "insmod" command directly when abstraction modules are needed, - instead of relying on GRUB_PRELOAD_MODULES (which had no effect - since it had already been processed). - -2008-06-19 Pavel Roskin - - * conf/i386-efi.rmk: Recompile grub-mkimage.c if Makefile has - changed. This is needed in case GRUB_LIBDIR changes. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-linuxbios.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - -2008-06-18 Pavel Roskin - - * conf/powerpc-ieee1275.rmk (kernel_elf_SOURCES): Rename - kernel_elf_symlist.c to symlist.c for consistency with other - architectures. Update all users. - * conf/sparc64-ieee1275.rmk (kernel_elf_SOURCES): Likewise. - -2008-06-18 Robert Millan - - * util/i386/pc/grub-install.in: If the drive is LVM or RAID, prepend - it in prefix. - - * util/i386/pc/grub-setup.c (main): Don't handle prefix at all. Set - `must_embed' to 1 when root_dev is a RAID device. When dest_dev is - a RAID device, run setup() for all members independently on whether - LVM abstraction is being used. - (setup): Don't handle prefix at all; let grub-mkimage take care of it. - If grub-mkimage has set `*install_dos_part == -2', don't override this - value. - Perform *install_dos_part adjustments independently on whether - we're embedding or not. - Clarify error message when image is too big for embedding. - Remove duplicate *install_dos_part stanza. - -2008-06-17 Robert Millan - - * term/ieee1275/ofconsole.c (fgcolor, bgcolor): Remove variables. - (grub_ofconsole_normal_color, grub_ofconsole_highlight_color): New - variables. - (grub_ofconsole_setcolor, grub_ofconsole_getcolor): Load/store - values in grub_ofconsole_normal_color and - grub_ofconsole_highlight_color (they're not directly related to - background and foreground). - (grub_ofconsole_setcolorstate): Extract background and foreground - from grub_ofconsole_normal_color and grub_ofconsole_highlight_color. - -2008-06-17 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): Use - /boot/grub for the check in last commit, not /boot (they could be - different partitions). - -2008-06-16 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): If we were - asked to setup access for the same partition that provides /boot, - don't bother using UUIDs since our root already has the value we - want. - -2008-06-16 Robert Millan - - * util/biosdisk.c (convert_system_partition_to_system_disk): Detect - I2O devices. - Patch from Sven Mueller . - -2008-06-16 Robert Millan - - * util/update-grub.in: Check for $EUID instead of $UID. - Reported by Vincent Zweije. - -2008-06-16 Bean - - * fs/ext2.c (grub_ext2_blockgroup): Revert to pre-journal state. - (grub_ext2_read_block): Likewise. - (grub_ext2_read_inode): Likewise. - (grub_ext2_mount): Likewise. - (grub_ext2_close): Likewise. - (grub_ext3_get_journal): Removed. - - * fs/reiserfs.c (grub_reiserfs_get_item): Revert to pre-journal state. - (grub_reiserfs_read_symlink): Likewise. - (grub_reiserfs_mount): Likewise. - (grub_reiserfs_open): Likewise. - (grub_reiserfs_read): Likewise. - (grub_reiserfs_close): Likewise. - (grub_reiserfs_get_journal): Removed. - - * fs/fshelp.c (grub_fshelp_read): Removed. - (grub_fshelp_map_block): Likewise. - - * include/grub/fshelp.h (grub_fshelp_journal_type): Removed. - (grub_fshelp_journal): Likewise. - (grub_fshelp_read): Likewise. - (grub_fshelp_map_block): Likewise. - -2008-06-16 Pavel Roskin - - * conf/powerpc-ieee1275.rmk: Remove -msoft-float, we don't use - floating point anymore. - * include/grub/powerpc/libgcc.h: Leave only necessary exports. - -2008-06-15 Pavel Roskin - - * commands/ls.c (grub_ls_list_files): Use integer calculations - for human readable format, avoid floating point use. - * kern/misc.c (grub_ftoa): Remove. - (grub_vsprintf): Remove floating point support. - -2008-06-15 Robert Millan - - * util/grub.d/10_linux.in: Use the underlying device for loop-AES - devices. - Reported by Max Vozeler. - -2008-06-15 Robert Millan - - * util/i386/pc/grub-mkimage.c (generate_image): If we included a drive - in our prefix, set install_{dos,bsd}_part = -2 to indicate this can be - skipped later. - (main): If a memdisk was requested, add "(memdisk)" drive explicitly to - the beginning of the prefix. - - * kern/i386/pc/init.c (make_install_device): Remove memdisk check. - It is assumed that if we have a memdisk, grub-mkimage has set - grub_prefix to include the "(memdisk)" drive in it. - -2008-06-15 Robert Millan - - * term/i386/pc/console.c [GRUB_MACHINE_LINUXBIOS] (grub_console_init): - Initialize keyboard controller after registering the terminal, so that - grub_printf() can be called from grub_keyboard_controller_init(). - -2008-06-15 Robert Millan - - * fs/sfs.c (grub_sfs_read_extent): Fix the count of nodes in - extent-btree which is written as big endian on disk. - Reported by Alain Greppin . - -2008-06-14 Robert Millan - - * util/i386/efi/grub-install.in (modules): Remove `_chain'. - * util/i386/pc/grub-install.in (modules): Likewise. - -2008-06-13 Pavel Roskin - - * commands/ls.c (grub_ls_list_files): Fix format warnings. - -2008-06-13 Bean - - * commands/hexdump.c (grub_cmd_hexdump): Adjust offset for partition. - - * fs/ext2.c (grub_ext3_get_journal): Fix revoke block handling. - - * fs/fshelp.c (grub_fshelp_map_block): Don't map block 0 as it's used - to indicate sparse block. - -2008-06-12 Pavel Roskin - - * fs/ext2.c (grub_ext2_read_inode): Don't normalize block - number, grub_fshelp_read() does it for us. - - * fs/fshelp.c (grub_fshelp_read): New function. Implement - linear disk read with journal translation. - * fs/ext2.c: Use grub_fshelp_read() instead of grub_disk_read(). - * include/grub/fshelp.h: Declare grub_fshelp_read(). - -2008-06-09 Pavel Roskin - - * fs/minix.c (grub_minix_mount): Handle error reading - superblock. - -2008-06-08 Robert Millan - - * util/i386/pc/grub-setup.c (main): If install drive is an LVM, - don't append the RAID prefix afterwards. - Reported by Clint Adams. - -2008-06-08 Robert Millan - - Based on description from Pavel: - * kern/disk.c (grub_disk_check_range): Rename to ... - (grub_disk_adjust_range): ... this. Add a comment explaining the - tasks performed by this function. - -2008-06-08 Robert Millan - - * include/grub/ntfs.h (struct grub_ntfs_bpb): Rename `serial_number' to - `num_serial' (for consistency with other variables). - (struct grub_ntfs_data): Add `uuid' member. - * fs/ntfs.c (grub_ntfs_mount): Initialize `data->uuid'. - (grub_ntfs_uuid): New function. - (grub_ntfs_fs): Reference grub_ntfs_uuid() in `uuid' struct member. - -2008-06-07 Pavel Roskin - - * util/biosdisk.c (open_device): Revert last change to the - function, it broke installation. The sector needs to be - different dependent on which device is opened. - -2008-06-06 Robert Millan - - Ensure GRUB_KERNEL_MACHINE_DATA_END is always consistent with the - rest of GRUB, and breakage doesn't happen if its value were modified. - - * include/grub/i386/pc/kernel.h (GRUB_KERNEL_MACHINE_RAW_SIZE): - Redefine as an offset from `GRUB_KERNEL_MACHINE_DATA_END' instead of - a constant (same value). - * kern/i386/pc/startup.S: Replace hardcoded `0x50' with - `GRUB_KERNEL_MACHINE_DATA_END' (same value). - -2008-06-06 Robert Millan - - * util/biosdisk.c (open_device): Do not modify sector offset when - accessing a partition. kern/disk.c already handles this for us. - -2008-06-06 Robert Millan - - * util/grub-emu.c (grub_machine_init): Move code in this function from - here ... - (main): ... to here (before grub_util_biosdisk_init() call, to prevent - segfault in case grub_printf() is called). - - * util/i386/pc/grub-install.in: Append `--device-map=${device_map}' to - grub_probe. Update all users not to explicitly add it again. - (grub_device): New variable; contains corresponding device for grubdir. - (fs_module, partmap_module, devabstraction_module): Pass - `--device ${grub_device}' to grub_probe to avoid traversing /dev - every time. - -2008-06-05 Robert Millan - - * normal/misc.c (grub_normal_print_device_info): When a filesystem UUID - is found, print it (same layout as with labels). - -2008-06-04 Robert Millan - - * util/biosdisk.c (get_drive): Rename to ... - (find_grub_drive): ... this. Update all users. - - (get_os_disk): Rename to ... - (convert_system_partition_to_system_disk): ... this. Update all users. - - (find_drive): Rename to ... - (find_system_device): ... this. Update all users. - -2008-06-04 Robert Millan - - * util/biosdisk.c (get_os_disk): Handle IDA devices. - * util/grub-mkdevicemap.c (get_mmc_disk_name) - (make_device_map): Likewise. - -2008-06-01 Robert Millan - - * util/biosdisk.c (get_drive): Verify that `map[i].drive' is non-NULL - before dereferencing it. - - * fs/fat.c (struct grub_fat_bpb): Move fat32-specific fields into a - union with fat12/fat16-specific ones. Add some new fields, including - `num_serial' for both versions. - (struct grub_fat_data): Add `uuid' member. - (grub_fat_mount): Refer to fat32-specific fields in `bpb' by their new - names. Initialize `data->uuid' using `num_serial'. - (grub_fat_uuid): New function. - (grub_fat_fs): Reference grub_fat_uuid() in `uuid' struct member. - - * fs/reiserfs.c (grub_reiserfs_superblock): Add `uuid' field. - (grub_reiserfs_uuid): New function. - (grub_reiserfs_fs): Reference grub_reiserfs_uuid() in `uuid' struct - member. - - * fs/xfs.c (grub_xfs_sblock): Add `uuid' field. - (grub_xfs_uuid): New function. - (grub_xfs_fs): Reference grub_reiserfs_uuid() in `uuid' struct member. - -2008-06-01 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): Generate - code that is backward compatible with pre-uuid search command. - -2008-05-31 Robert Millan - - * disk/i386/pc/biosdisk.c (grub_biosdisk_iterate): Iterate through - floppies after everything else, to ensure floppy drive isn't accessed - unnecessarily (patch from Bean). - -2008-05-31 Robert Millan - - * commands/search.c (search_label, search_fs_uuid, search_file): Do - not print device names when we were asked to set a variable. - -2008-05-31 Robert Millan - - * term/ieee1275/ofconsole.c (grub_ofconsole_setcursor): Implement - using "cursor-on" and "cursor-off" commands (understood at least by - the Open Firmware flavour on OLPC). - -2008-05-31 Michael Gorven - - * term/terminfo.c (grub_terminfo_set_current): Correct vt100 cursor - on and off sequences. - -2008-05-31 Robert Millan - - * util/update-grub_lib.in: Replace `grub-probe' with `${grub_probe}'. - * util/update-grub.in: Likewise. - -2008-05-30 Pavel Roskin - - * util/biosdisk.c (linux_find_partition): Simplify logic and - make the code more universal. Keep special processing for - devfs, but use a simple rule for all other devices. If the - device ends with a number, append 'p' and the partition number. - Otherwise, append only the partition number. - -2008-05-30 Robert Millan - - * util/update-grub.in (GRUB_DISABLE_LINUX_UUID): Export variable. - * util/grub.d/10_linux.in: If GRUB_DEVICE_UUID is set, and - GRUB_DISABLE_LINUX_UUID isn't true, use the filesystem UUIDs as - the `root' parameter to Linux. - -2008-05-30 Robert Millan - - * commands/search.c (options): Rename --fs_uuid to --fs-uuid. - * util/update-grub_lib.in (prepare_grub_to_access_device): Replace - --fs_uuid with --fs-uuid. - * util/update-grub.in: Allow filesystem UUID probes to fail (since not - all filesystems support them). - -2008-05-30 Robert Millan - - * fs/ext2.c (grub_ext2_uuid): Use `04x' instead of '02x' as - grub_printf() flags, since we're printing in units of 2 bytes. - -2008-05-30 Robert Millan - - * util/grub.d/00_header.in: Remove obsolete comment referencing - convert_system_path_to_grub_path(). - * util/update-grub.in: Likewise. - * util/update-grub_lib.in (is_path_readable_by_grub): New function. - (convert_system_path_to_grub_path): Add a warning message explaining - that this function is deprecated. Rely on is_path_readable_by_grub() - for the readability checks. - (font_path): Use is_path_readable_by_grub() for the readability - check rather than convert_system_path_to_grub_path(). - -2008-05-30 Robert Millan - - * util/update-grub_lib.in (prepare_grub_to_access_device): New function. - * util/update-grub.in: Set `GRUB_FONT_PATH' to the system path, without - converting it first. - * util/grub.d/00_header.in: Use prepare_grub_to_access_device() to setup - grub.cfg for access to font file, and afterwards call it again to set - the root device. - -2008-05-30 Robert Millan - - * commands/search.c (options): Add --fs_uuid option. - (search_fs_uuid): New function. - (grub_cmd_search): Fix --set argument passing. - Use search_fs_uuid() when requested via --fs_uuid. - (grub_search_init): Update help message. - * fs/ext2.c (struct grub_ext2_sblock): Rename `unique_id' to `uuid' - and redeclare it as an array of 16-bit words. - (grub_ext2_uuid): New function. - (grub_ext2_fs): Reference grub_ext2_uuid() in `uuid' struct member. - * include/grub/fs.h (struct grub_fs): Add `uuid' struct member. - * util/update-grub.in (GRUB_DEVICE_UUID, GRUB_DEVICE_BOOT) - (GRUB_DEVICE_BOOT_UUID): New variables. - (GRUB_DRIVE. GRUB_DRIVE_BOOT. GRUB_DRIVE_BOOT_GRUB): Remove. - * util/grub.d/00_header.in: Set root using `search --fs_uuid' command - whenever possible. - * util/grub.d/10_hurd.in: Avoid explicit use of root drive. Instead, - just assume `root' variable has the right value. - * util/grub.d/10_linux.in: Likewise. - * util/grub-probe.c (probe): Probe for filesystem UUID when requested - via PRINT_FS_UUID. - (main): Recognise `-t fs_uuid' argument. - -2008-05-30 Robert Millan - - * util/biosdisk.c (map): Redefine structure to hold information - about GRUB drive name. - (get_drive): Reimplement without assuming (and verifying) BIOS-like - drive names. - (call_hook): Remove. - (grub_util_biosdisk_iterate): Access drive names via `.drive' struct - member. Assume drive has partitions. - (grub_util_biosdisk_open): Access device names via `.device' struct - member. - (open_device): Likewise. - (find_drive): Likewise. - (read_device_map): Adjust map[] usage to match the new struct - definition. Don't check for duplicates (still possible, but not cheap - anymore). - (grub_util_biosdisk_fini): Free malloced buffers referenced by map[]. - (make_device_name): Remove assumption of BIOS-like drive names. - -2008-05-30 Pavel Roskin - - * conf/i386-efi.rmk (normal/execute.c_DEPENDENCIES): Remove, as - compiling execute.c doesn't need grub_script.tab.h anymore. - (normal/command.c_DEPENDENCIES): Likewise. - (normal/function.c_DEPENDENCIES): Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-linuxbios.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - -2008-05-29 Pavel Roskin - - * disk/lvm.c (grub_lvm_scan_device): Check for the buffer end - when scanning metadata for volume group name. - - * include/grub/script.h: Don't include grub_script.tab.h. It's - a generated file, which may only be included from the files with - DEPENDENCIES rules in the makefile. Don't use typedef YYSTYPE, - use union YYSTYPE, as the later allows forward declaration. - * normal/lexer.c: Don't use typedef YYSTYPE, use union YYSTYPE. - -2008-05-29 Robert Millan - - * term/i386/pc/at_keyboard.c: Include `grub/machine/machine.h'. - (OLPC_UP, OLPC_DOWN, OLPC_LEFT, OLPC_RIGHT): New macros. - [GRUB_MACHINE_IEEE1275] (keyboard_map): Add OLPC scan codes - (grub_console_checkkey): Add grub_dprintf() call to report unknown - scan codes. - -2008-05-29 Robert Millan - - * term/i386/pc/at_keyboard.c (grub_console_checkkey): Add support for - control key combinations. - -2008-05-29 Robert Millan - - * util/powerpc/ieee1275/grub-install.in: Move from here ... - * util/ieee1275/grub-install.in: ... to here. - * powerpc-ieee1275.rmk (grub_install_SOURCES): Update location. - * i386-ieee1275.rmk (sbin_SCRIPTS): New variable. - (grub_install_SOURCES): Likewise. - -2008-05-29 Robert Millan - - * fs/affs.c: Update copyright year. - * fs/ext2.c: Likewise. - * fs/fshelp.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/ntfs.c: Likewise. - * fs/xfs.c: Likewise. - * include/grub/fshelp.h: Likewise. - * util/grub-mkdevicemap.c: Likewise. - -2008-05-28 Robert Millan - - * util/update-grub.in: Allow chmod call to fail, since /boot/grub/ - might need to be fatfs to support some firmware implementations - (e.g. OFW or EFI). - -2008-05-28 Robert Millan - - * util/biosdisk.c (linux_find_partition, get_os_disk): Handle MMC - devices. - * util/grub-mkdevicemap.c (get_mmc_disk_name) - (make_device_map): Likewise. - -2008-05-20 Bean - - * fs/fshelp.c (grub_fshelp_map_block): New function. - (grub_fshelp_find_file): Use 64-bit type for pos and block address. - Use `>>' and `&' operator to avoid 64-bit divide and modulo. - - * include/grub/fshelp.h (grub_fshelp_journal_type): New enum. - (GRUB_FSHELP_JOURNAL_UNUSED_MAPPING): New macro. - (grub_fshelp_journal): New structure. - (grub_fshelp_map_block): New function prototype. - (grub_fshelp_read_file): Use grub_disk_addr_t as block type. - (grub_fshelp_map_block): Likewise. - - * fs/ext2.c (EXT3_FEATURE_COMPAT_HAS_JOURNAL): New macro. - (EXT3_JOURNAL_MAGIC_NUMBER): Likewise. - (EXT3_JOURNAL_DESCRIPTOR_BLOCK): Likewise. - (EXT3_JOURNAL_COMMIT_BLOCK): Likewise. - (EXT3_JOURNAL_SUPERBLOCK_V1): Likewise. - (EXT3_JOURNAL_SUPERBLOCK_V2): Likewise. - (EXT3_JOURNAL_REVOKE_BLOCK): Likewise. - (EXT3_JOURNAL_FLAG_ESCAPE): Likewise. - (EXT3_JOURNAL_FLAG_SAME_UUID): Likewise. - (EXT3_JOURNAL_FLAG_DELETED): Likewise. - (EXT3_JOURNAL_FLAG_LAST_TAG): Likewise. - (grub_ext2_sblock): New members for journal support. - (grub_ext3_journal_header): New structure. - (grub_ext3_journal_revoke_header): Likewise. - (grub_ext3_journal_block_tag): Likewise. - (grub_ext3_journal_sblock): Likewise. - (grub_fshelp_node): New members logfile and journal. - (grub_ext2_read_block): Change block type to grub_disk_addr_t. Use - grub_fshelp_map_block to get real block number. - (grub_ext2_blockgroup): Use grub_fshelp_map_block to get real block - number. - (grub_ext2_read_inode): Likewise. - (grub_ext3_get_journal): New function. - (grub_read_inode): Initialize journal using grub_ext3_get_journal. - (grub_ext2_close): Release memory used by journal. - - * fs/reiserfs.c (REISERFS_MAGIC_STRING): Changed to "ReIsEr". - (REISERFS_MAGIC_DESC_BLOCK): New macro. - (grub_reiserfs_transaction_header): Renamed to - grub_reiserfs_description_block, replace field data with real_blocks. - (grub_reiserfs_commit_block): New structure. - (grub_reiserfs_data): New member journal. - (grub_reiserfs_get_item): Use grub_fshelp_map_block to get real block - number. - (grub_reiserfs_read_symlink): Likewise. - (grub_reiserfs_iterate_dir): Likewise. - (grub_reiserfs_open): Likewise. - (grub_reiserfs_read): Likewise. - (grub_reiserfs_get_journal): New function. - (grub_reiserfs_mount): Use "ReIsEr" as super block magic, as there are - three varieties ReIsErFs, ReIsEr2Fs and ReIsEr3Fs. Initialize journal - using grub_reiserfs_get_journal. - (grub_reiserfs_close): Release memory used by journal. - - * fs/affs.c (grub_affs_read_block): Change block type to - grub_disk_addr_t. Use grub_divmod64 to do 64-bit division. - - * fs/afs.c (grub_afs_read_block): Change block type to grub_disk_addr_t. - - * fs/hfsplus.c (grub_hfsplus_read_block): Likewise. - - * fs/ntfs.c (grub_ntfs_read_block): Likewise. - - * fs/udf.c (grub_udf_read_block): Change block type to - grub_disk_addr_t. Use type cast to avoid warning. - - * fs/xfs.c (grub_xfs_read_block): Likewise. - -2008-05-16 Christian Franke - - * commands/cat.c (grub_cmd_cat): Remove non-ESC keys from keyboard queue - to ensure that break with ESC will always work. - * commands/sleep.c (grub_interruptible_millisleep): Likewise. - Remove ESC from keyboard queue. - -2008-05-16 Christian Franke - - * util/biosdisk.c: [__CYGWIN__] Add includes. - (grub_util_biosdisk_open): Use Linux code also for Cygwin. - (get_os_disk): Move variable declarations to OS specific - parts to avoid warning. - [__GNU__] (get_os_disk): Fix /dev/sdXsN case. - [__CYGWIN__] (get_os_disk): Add Cygwin /dev/sdXN device names. - (grub_util_biosdisk_get_grub_dev): Use Linux code also for - Cygwin. - * util/getroot.c: [__CYGWIN__] Add includes. - (strip_extra_slashes): Fix "/" case. - [__CYGWIN__] (get_win32_path): New function. - [__CYGWIN__] (grub_get_prefix): Add conversion to win32 path. - [__CYGWIN__] (find_root_device): Disable. - [__CYGWIN__] (get_bootsec_serial): New function. - [__CYGWIN__] (find_cygwin_root_device): Likewise. - [__linux__] (grub_guess_root_device): Add early returns to simplify - structure. - [__CYGWIN__] (grub_guess_root_device): Call find_cygwin_root_device. - [__linux__] (grub_util_get_dev_abstraction): Enable LVM and RAID - check for Linux only. - -2008-05-15 Bean - - * kern/i386/pc/startup.S (grub_console_getkey): Workaround for the - keyboard hang problem in apple's intel mac. - -2008-05-09 Robert Millan - - * util/biosdisk.c (linux_find_partition, get_os_disk): Handle Virtio - devices. - * util/grub-mkdevicemap.c (get_virtio_disk_name) - (make_device_map): Likewise. - Reported by Aurelien Jarno - -2008-05-07 Ian Campbell - - * util/biosdisk.c (get_os_disk): Recognise xvd type disks. - * util/grub-mkdevicemap.c (get_xvd_disk_name): New function. - (make_device_map): Output entries for xvd type disks. - -2008-05-07 Robert Millan - - * util/biosdisk.c (linux_find_partition, get_os_disk): Handle CCISS - devices. - * util/grub-mkdevicemap.c (get_cciss_disk_name) - (make_device_map): Likewise. - Reported by Roland Dreier - -2008-05-07 Robert Millan - - * disk/lvm.c (grub_lvm_scan_device): Detect errors in an additional - grub_strstr() call. Correct a few mistakes in failure path handling. - -2008-05-06 Robert Millan - - * util/update-grub_lib.in (make_system_path_relative_to_its_root): - Do not print a trailing slash (therefore, the root directory is an - empty string). - (convert_system_path_to_grub_path): Do not remove trailing slash - from make_system_path_relative_to_its_root() output. - - * util/i386/pc/grub-install.in: Add trailing slash to output from - make_system_path_relative_to_its_root(). - -2008-05-06 Robert Millan - - * util/grub-fstest.c (grub_refresh): Call `fflush (stdout)'. This - ensures that output lines aren't intermangled with those sent to - stderr (via grub_util_info()). - * util/grub-probe.c (grub_refresh): Likewise. - * util/i386/pc/grub-setup.c (grub_refresh): Likewise. - -2008-05-05 Christian Franke - - * util/grub-mkdevicemap.c (get_floppy_disk_name) [__CYGWIN__]: - Add Cygwin device names. - (get_ide_disk_name) [__CYGWIN__]: Likewise. - (get_scsi_disk_name) [__CYGWIN__]: Likewise. - (check_device): Return error instead of success on empty name. - (make_device_map): Move label inside linux specific code to - prevent compiler warning. - -2008-04-30 Robert Millan - - Based on patch from Fabian Greffrath - * util/grub.d/10_linux.in: Add ${GRUB_CMDLINE_LINUX_DEFAULT} to the - first boot option. - * util/update-grub.in: Export GRUB_CMDLINE_LINUX_DEFAULT. - -2008-04-29 Robert Millan - - * docs/grub.cfg: New file (example GRUB configuration). - -2008-04-26 Robert Millan - - * DISTLIST: Sort (sort -u < DISTLIST | sponge DISTLIST). Add - `loader/i386/ieee1275/linux.c', `loader/i386/ieee1275/linux_normal.c' - and `disk/ieee1275/nand.c'. - -2008-04-25 Bean - - * Makefile.in (RMKFILES): Add missing arch i386-ieee1275 and - i386-linuxbios. - - * commands/hexdump.c (grub_cmd_hexdump): Support dumping of device, - change the buffer size to 4096 for cdrom device. - - * conf/i386-ieee1275.rmk (pkglib_MODULES): Add _linux.mod, linux.mod - and nand.mod. - (_linux_mod_SOURCES): New variable. - (_linux_mod_CFLAGS): Likewise. - (_linux_mod_LDFLAGS): Likewise. - (linux_mod_SOURCES): Likewise. - (linux_mod_CFLAGS): Likewise. - (linux_mod_LDFLAGS): Likewise. - (nand_mod_SOURCES): Likewise. - (nand_mod_CFLAGS): Likewise. - (nand_mod_LDFLAGS): Likewise. - - * disk/ieee1275/ofdisk.c (grub_ofdisk_open): Return - GRUB_ERR_UNKNOWN_DEVICE instead of GRUB_ERR_BAD_DEVICE if no device - type property. (nand device in olpc don't have this property) - - * include/grub/disk.h (grub_disk_dev_id): New macro - GRUB_DISK_DEVICE_NAND_ID. - - * include/grub/i386/ieee1275/loader.h (grub_rescue_cmd_linux): New - function prototype. - (grub_rescue_cmd_initrd): Likewise. - - * include/grub/i386/linux.h (GRUB_LINUX_OFW_SIGNATURE): New macro. - (linux_kernel_params): Add new member ofw_signature, ofw_num_items, - ofw_cif_handler and ofw_idt, adjust padding number. - - * include/grub/i386/pc/memory.h (grub_upper_mem): Export it if - GRUB_MACHINE_IEEE1275 is defined. - - * include/grub/ieee1275/ieee1275.h (grub_available_iterate): - Use NESTED_FUNC_ATTR attribute on the hook parameter. - - * kern/powerpc/ieee1275/init.c (grub_claim_heap): Use NESTED_FUNC_ATTR - on nested function heap_init. - (grub_upper_mem): New variable for i386-ieee1275. - (grub_get_extended_memory): New function for i386-ieee1275. - (grub_machine_init): Call grub_get_extended_memory for i386-ieee1275. - - * kern/powerpc/ieee1275/openfw.c (grub_available_iterate): Use - NESTED_FUNC_ATTR on the hook parameter. Don't quit if no device type - property. - - * loader/i386/ieee1275/linux.c: New file. - - * loader/i386/ieee1275/linux_normal.c: New file. - - * disk/ieee1275/nand.c: New file. - -2008-04-18 Thomas Schwinge - - * util/i386/pc/grub-mkrescue.in (grub_mkimage): Don't overwrite correct - value. - * util/powerpc/ieee1275/grub-mkrescue.in (grub_mkimage): Likewise. - -2008-04-18 Robert Millan - - Restructures early code path on ieee1275 to unify grub_main() as - the first C function that is executed in every platform. - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_init): New prototype. - * kern/i386/ieee1275/startup.S (_start): Jump to grub_main() instead of - cmain(). - * kern/powerpc/ieee1275/crt0.S (_start): Likewise. - * kern/ieee1275/cmain.c (cmain): Rename to ... - * kern/ieee1275/cmain.c (grub_ieee1275_init): ... this. - * kern/ieee1275/init.c (grub_machine_init): Call grub_ieee1275_init() - at the beginning. - -2008-04-18 Robert Millan - - * util/update-grub.in: Fix syntax error when setting - `GRUB_PRELOAD_MODULES'. - Reported by Stephane Chazelas - -2008-04-17 Lubomir Kundrak - - * aclocal.m4 (grub_PROG_OBJCOPY_ABSOLUTE): take only .text - section into account, newer toolchains generate unique build ids - * configure.ac: remove the test for --build-id=none acceptance, - we want build ids to be preserved - * genmk.rb: add -R .note.gnu.build-id to objcopy, so build id - far from other sections don't cause the raw binary images grow - size - -2008-04-15 Robert Millan - - * disk/lvm.c: Update copyright year. - * kern/misc.c: Likewise. - -2008-04-14 Vesa Jaaskelainen - - * disk/lvm.c (grub_lvm_scan_device): Add forgotten failure path when - there is no memory left for physical volume name. - -2008-04-14 Vesa Jaaskelainen - - * disk/lvm.c (grub_lvm_scan_device): Fix logical volume's physical - volume name mapping to support bigger than 9 character names properly. - -2008-04-13 Robert Millan - - * disk/i386/pc/biosdisk.c (grub_biosdisk_rw): Fix CHS limit check, - as per http://www.allensmith.net/Storage/HDDlimit/Int13h.htm - -2008-04-13 Christian Franke - - * util/i386/pc/grub-mkrescue.in: Add --emulation=floppy - to create a floppy emulation boot CD when non emulation mode - does not work. - Enable Joliet CD filesystem extension. - -2008-04-13 Robert Millan - - * kern/misc.c (grub_strncat): Fix off-by-one error. - Reported by Zhang Huan - - * kern/env.c (grub_env_context_close): Clear current context, not - previous one. - Patch from Zhang Huan - - * kern/misc.c (grub_strcat): Minor speed optimization (same code size). - -2008-04-13 Robert Millan - - Improve robustness when handling LVM. - - * disk/lvm.c (grub_lvm_getvalue): Return 0 when `*p' is NULL - (and leave `*p' unmodified). - (grub_lvm_iterate): Don't assume `vg->lvs != NULL' when iterating - through it. - (grub_lvm_memberlist): Don't assume `lv->vg->pvs != NULL' when - iterating through it. - (grub_lvm_open): Don't assume `vg->lvs != NULL' when iterating - through it. - (grub_lvm_scan_device): Check the return value (and fail gracefully - when due) on each grub_lvm_getvalue() or grub_strstr() call. - Don't assume `vg->pvs != NULL' when iterating through it. - -2008-04-13 Robert Millan - - * gendistlist.sh (EXTRA_DISTFILES): Add `genpartmaplist.sh'. - * genmk.rb (partmap): New variable. - (CLEANFILES, PARTMAPFILES): Add #{partmap}. - (#{partmap}): New target rule. - * genpartmaplist.sh: New file. - * Makefile.in (pkglib_DATA): Add partmap.lst. - (partmap.lst): New target rule. - * util/i386/pc/grub-mkrescue.in: Generate grub.cfg that loads needed - modules (including all partition maps), instead of preloading them. - -2007-04-13 Fabian Greffrath - - * util/grub.d/30_os-prober.in: New script. Use `os-prober' and - `linux-boot-prober' (if installed) to detect other operating - systems which are installed on the computer and add them to - the boot menu. - * conf/common.rmk: Build and install 30_os-prober. - -2008-04-12 Robert Millan - - * kern/powerpc/ieee1275/init.c: Move from here ... - * kern/ieee1275/init.c: ... to here. Update all users. - - * kern/powerpc/ieee1275/cmain.c: Move from here ... - * kern/ieee1275/cmain.c: ... to here. Update all users. - - * kern/powerpc/ieee1275/openfw.c: Move from here ... - * kern/ieee1275/openfw.c: ... to here. Update all users. - - * loader/powerpc/ieee1275/multiboot2.c: Move from here ... - * loader/ieee1275/multiboot2.c: ... to here. Update all users. - -2008-04-10 Pavel Roskin - - * configure.ac: Always use "_cv_" in cache variables for - compatibility with Autoconf 2.62. - -2008-04-07 Robert Millan - - Revert grub/machine/init.h addition by Pavel (since it breaks on - i386-ieee1275 and others): - * util/i386/pc/misc.c: Remove grub/machine/init.h. - * util/powerpc/ieee1275/misc.c: Likewise. - -2008-04-07 Robert Millan - - * util/grub-probe.c (probe): Improve error message. - -2008-04-07 Robert Millan - - * util/biosdisk.c (read_device_map): Skip devices that don't exist - (this prevents the presence of a bogus entry from ruining the whole - thing). - -2008-04-06 Pavel Roskin - - * util/biosdisk.c: Include grub/util/biosdisk.h. - * util/grub-fstest.c (execute_command): Make static. - * util/grub-mkdevicemap.c (check_device): Likewise. - * util/i386/pc/misc.c: Include grub/machine/init.h. - * util/powerpc/ieee1275/misc.c: Likewise. - * util/lvm.c: Include grub/util/lvm.h. - * util/misc.c: Include grub/kernel.h, grub/misc.h and - grub/cache.h. - * util/raid.c: Include grub/util/raid.h. - (grub_util_getdiskname): Make static. - - * util/grub-emu.c (main): Remove calls to grub_hostfs_init() and - grub_hostfs_fini(), as they are called from grub_init_all() and - grub_fini_all() respectively. This fixes an infinite loop in - grub-fstest due to double registration of hostfs. - Reported by Christian Franke - -2008-04-05 Pavel Roskin - - * bus/pci.c (grub_pci_iterate): For multifunction devices, probe - all 8 functions. Otherwise, probe function 0 only. - -2008-04-04 Pavel Roskin - - * commands/lspci.c (grub_lspci_iter): Print the bus number - correctly. - - * commands/lspci.c (grub_pci_classes): Fix typos. - (grub_lspci_iter): Don't print func twice. Print vendor ID - before device ID, as it's normally done. - - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_find_options): - Fix signedness warnings. - * kern/powerpc/ieee1275/openfw.c (grub_available_iterate): - Likewise. - * util/ieee1275/get_disk_name.c: Include config.h so that - _GNU_SOURCE is defined and getline() is declared. Mark an - unused argument as such. Fix a signedness warning. - -2008-04-02 Pavel Roskin - - * genkernsyms.sh.in: Use more robust assignments for CC and - srcdir. Quote srcdir. - * gensymlist.sh.in: Likewise. Assert at the compile time that - the symbol table is not empty. - - * disk/raid.c (grub_raid_memberlist): Fix a signedness warning. - * fs/cpio.c (grub_cpio_read): Likewise. - -2008-04-01 Pavel Roskin - - * disk/ata.c (grub_ata_open): Don't lose precision in disk->id. - * disk/host.c (grub_host_open): Likewise. - * disk/loopback.c (grub_loopback_open): Likewise. - * disk/memdisk.c (grub_memdisk_open): Use a string pointer for - disk->id as in disk/host.c, not a multi-character constant. - - * util/grub-fstest.c (cmd_cmp): Use fseeko(), not fseek(). The - later is obsolete, potentially dangerous and sets a bad example. - * util/i386/efi/grub-mkimage.c (make_header): Likewise. - * util/misc.c (grub_util_get_image_size): Likewise. - - * disk/loopback.c (options): Improve help for "--partitions". - - * normal/arg.c (grub_arg_show_help): Fix spacing of the long - options to align them with the short options, e.g. "echo -e". - -2008-03-31 Bean - - * video/reader/png.c (grub_png_data): New member is_16bit and - image_data. - (grub_png_decode_image_header): Detect 16 bit png image. - (grub_png_convert_image): New function to convert 16 bit image to 8 bit. - (grub_png_decode_png): Call grub_png_convert_image for 16 bit image. - (grub_video_reader_png): Release memory occupied by image_data. - - * fs/ntfs.c (find_attr): Handle non-resident attribute list larger than - 4096 bytes. - (grub_nfs_mount): Skip the test for sector per cluster. - - * include/grub/ntfs.h (MAX_SPC): Removed. - -2008-03-31 Bean - - * conf/common.rmk (pkgdata_MODULES): Add afs.mod. - (grub_probe_SOURCES): Add fs/afs.c. - (grub_fstest_SOURCES): Likewise. - (afs_mod_SOURCES): New variable. - (afs_mod_CFLAGS): Likewise. - (afs_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/afs.c. - (grub_emu_SOURCES): Likewise. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * fs/afs.c: New file. - -2008-03-30 Pavel Roskin - - * disk/host.c: Include grub/misc.h to fix a warning. - * util/hostfs.c: Use GRUB_MOD_INIT and GRUB_MOD_FINI to fix - warnings about implicit declarations. - - * fs/udf.c (grub_udf_mount): Fix warning about a shadowing a - variable. - * include/grub/i386/loader.h: Change declaration of - grub_linux_boot() to match what grub_loader_set() expects. - * util/getroot.c (grub_guess_root_device): Return const char* to - fix a warning. - * util/grub-probe.c (probe): Fix a warning about uninitialized - abstraction_name variable. - * util/i386/get_disk_name.c (grub_util_get_disk_name): Mark - second argument as unused to fix a warning. - - * loader/i386/pc/multiboot2.c (grub_mb2_arch_elf64_hook): Add - missing grub_error() call. - - * util/update-grub_lib.in: Define datarootdir, since Autoconf - 2.60 and newer uses it to define datadir. - - * commands/sleep.c: Fix warning about implicit declaration. - * disk/memdisk.c: Likewise. - * loader/aout.c: Likewise. - * loader/i386/bsd_normal.c: Likewise. - * util/grub-probe.c: Likewise. - - * commands/i386/cpuid.c (has_longmode): Make static. - * disk/i386/pc/biosdisk.c (cd_drive): Likewise. - * include/grub/i386/bsd.h (bios_memmap_t): Remove, it's unused. - - * kern/i386/pc/startup.S (real_to_prot): Use %cs prefix to load - GDT. This is more robust, as %ds can change. - (grub_biosdisk_rw_int13_extensions): Don't clear %ds before - calling real_to_prot(). - (grub_biosdisk_get_diskinfo_int13_extensions): Likewise. - -2008-03-28 Pavel Roskin - - * kern/i386/pc/startup.S: Assert that uncompressed functions - don't spill beyond GRUB_KERNEL_MACHINE_RAW_SIZE. - * kern/i386/pc/lzo1x.S: Remove all .align directives in the - code, as they push parts of the code (error handlers) beyond - GRUB_KERNEL_MACHINE_RAW_SIZE. Speed is not as important in this - code as correctness and size. - -2008-03-28 Pavel Roskin - - * kern/i386/pc/startup.S - (grub_biosdisk_get_diskinfo_int13_extensions): When converting - data block address to the real mode, keep offset minimal. This - works around a bug in AWARD BIOS on old Athlon systems, which - makes CD detection hang. - -2008-03-26 Pavel Roskin - - * normal/color.c (grub_parse_color_name_pair): Make `name' a - const. - * include/grub/normal.h: Add grub_parse_color_name_pair() - declaration. - -2008-03-24 Bean - - * disk/i386/pc/biosdisk.c (cd_start): Removed. - (cd_count): Removed. - (cd_drive): New variable. - (grub_biosdisk_get_drive): Don't check for (cdN) device. - (grub_biosdisk_call_hook): Likewise. - (grub_biosdisk_iterate): Change cdrom detection method. - (grub_biosdisk_open): Replace cd_start with cd_drive. - (GRUB_MOD_INIT): Use grub_biosdisk_get_cdinfo_int13_extension to - detect cdrom device. - - * include/grub/i386/pc/biosdisk.h (GRUB_BIOSDISK_MACHINE_CDROM_START): - Removed. - (GRUB_BIOSDISK_MACHINE_CDROM_END): Removed. - (GRUB_BIOSDISK_CDTYPE_NO_EMUL): New macro. - (GRUB_BIOSDISK_CDTYPE_1_2_M): Likewise. - (GRUB_BIOSDISK_CDTYPE_1_44_M): Likewise. - (GRUB_BIOSDISK_CDTYPE_2_88_M): Likewise. - (GRUB_BIOSDISK_CDTYPE_HARDDISK): Likewise. - (GRUB_BIOSDISK_CDTYPE_MASK): Likewise. - (grub_biosdisk_cdrp): New structure. - (grub_biosdisk_get_cdinfo_int13_extensions): New function. - - * include/grub/i386/pc/kernel.h (grub_boot_drive): Export this variable. - - * kern/i386/pc/init.c (make_install_device): Don't use (cdN) as root - device. - - * kern/i386/pc/startup.S (grub_biosdisk_get_cdinfo_int13_extensions): - New function. - -2008-03-20 Robert Millan - - Remove 2 TiB limit in ata.mod. - * disk/ata.c (grub_ata_device): Promote `size' to grub_uint64_t. - (grub_ata_dumpinfo): Print sector count with 0x%llx. - (grub_ata_identify): Interpret `&info16[100]' as a pointer to - grub_uint64_t instead of grub_uint32_t. - -2008-03-05 Bean - - * loader/i386/pc/multiboot.c (grub_multiboot_get_bootdev): New function. - (grub_multiboot): Set boot device. - - * boot/i386/pc/lnxboot.S (real_code_2): Set %dh to 0xFF. - -2008-03-02 Bean - - * fs/reiserfs.c (grub_reiserfs_read_symlink): Add 0 at the end of - symlink_buffer. - -2008-03-01 Yoshinori K. Okuji - - * DISTLIST: Added docs/fdl.texi, docs/grub.texi, docs/mdate-sh and - texinfo.tex. - - * docs/grub.texi: New file. Copied from GRUB Legacy, and slightly - modified. - - * docs/fdl.texi: New file. - - * docs/mdate-sh: New file. Copied from gnulib. - * docs/texinfo.tex: Likewise. - - * config.guess: Updated from gnulib. - * install-sh: Likewise. - -2008-02-28 Robert Millan - - * conf/i386-linuxbios.rmk (pkglib_MODULES): Add aout.mod. - (aout_mod_SOURCES): New variable. - (aout_mod_CFLAGS): Likewise. - (aout_mod_LDFLAGS): Likewise. - - * conf/i386-ieee1275.rmk: Likewise. - -2008-02-28 Robert Millan - - * util/update-grub.in: Reorganise terminal validity check. Accept - `ieee1275:console' (OLPC) and `*:gfxterm' as valid too. - Based on suggestion by Franklin PIAT. - -2008-02-28 Fabian Greffrath - - * include/grub/util/getroot.h (grub_util_check_block_device): Export new - function. - * util/getroot.c (grub_util_check_block_device): New function that - returns the given argument if it is a block device and returns NULL else. - * util/grub-probe.c (argument_is_device): New variable. - (probe): Promote device_name from a variable to an argument. Receive - device_name from grub_util_check_block_device() if path is NULL and from - grub_guess_root_device() else. Do not free() device_name anymore. - (options): Introduce new parameter '-d, --device'. - (main): Add description of the new parameter to the help screen. - Rename path variable to argument. Set argument_is_device if the '-d' - option is given. Pass argument to probe() depending on - argument_is_device. - -2008-02-24 Bean - - * fs/iso9660.c (GRUB_ISO9660_VOLDESC_BOOT): New macro. - (GRUB_ISO9660_VOLDESC_PRIMARY): Likewise. - (GRUB_ISO9660_VOLDESC_SUPP): Likewise. - (GRUB_ISO9660_VOLDESC_PART): Likewise. - (GRUB_ISO9660_VOLDESC_END): Likewise. - (grub_iso9660_primary_voldesc): New member escape. - (grub_iso9660_data): New member joliet. - (grub_iso9660_convert_string): New function. - (grub_iso9660_mount): Detect joliet extension. - (grub_iso9660_iterate_dir): Convert filename when joliet is detected. - (grub_iso9660_iso9660_label): Likewise. - - * conf/common.rmk (pkgdata_MODULES): Add udf.mod. - (grub_setup_SOURCES): Add fs/udf.c. - (grub_fstest_SOURCES): Likewise. - (udf_mod_SOURCES): New variable. - (udf_mod_CFLAGS): Likewise. - (udf_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/udf.c. - (grub_emu_SOURCES): Likewise. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * fs/udf.c: New file. - -2008-02-24 Robert Millan - - * conf/i386-efi.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): New variables. - * conf/i386-ieee1275.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): Likewise. - * conf/i386-linuxbios.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): Likewise. - * conf/i386-pc.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): Likewise. - * conf/powerpc-ieee1275.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): Likewise. - * conf/sparc64-ieee1275.rmk (normal/function.c_DEPENDENCIES) - (normal/lexer.c_DEPENDENCIES): Likewise. - -2008-02-23 Robert Millan - - * partmap/gpt.c (grub_gpt_magic): Add `0x' qualifier to each member, - since they were intended to be in hex. This didn't break previously - because of a bug in gpt_partition_map_iterate() (see below). - - (gpt_partition_map_iterate): Replace `grub_memcmp' with `! grub_memcmp' - when checking the validity of GPT header. - Remove `partno', since it always provides the same information as `i'. - -2008-02-21 Yoshinori K. Okuji - - * include/grub/efi/time.h: Fix a wrong comment. - -2008-02-19 Pavel Roskin - - * kern/rescue.c (grub_enter_rescue_mode): Improve initial - message. - -2008-02-19 Bean - - * conf/i386-pc.rmk (pkglib_MODULES): Add aout.mod _bsd.mod and bsd.mod. - (aout_mod_SOURCES): New variable. - (aout_mod_CFLAGS): Likewise. - (aout_mod_LDFLAGS): Likewise. - (_bsd_mod_SOURCES): New variable. - (_bsd_mod_CFLAGS): Likewise. - (_bsd_mod_LDFLAGS): Likewise. - (bsd_mod_SOURCES): New variable. - (bsd_mod_CFLAGS): Likewise. - (bsd_mod_LDFLAGS): Likewise. - - * include/grub/aout.h: New file. - - * include/grub/i386/loader.h (grub_unix_real_boot): New function. - - * include/grub/i386/bsd.h: New file. - - * include/grub/i386/pc/init.h (grub_get_mmap_entry): Use EXPORT_FUNC - to make it public. - - * kern/elf.c (grub_elf32_load): Get the physical address after the hook - function is called, so that it's possible to change it inside the hook. - (grub_elf64_load): Likewise. - (grub_elf_file): Don't close the file if elf header is not found. - (grub_elf_close): Close the file if grub_elf_file fails (The new - grub_elf_file won't close it). - (grub_elf32_size): Use NESTED_FUNC_ATTR for nested function calcsize. - (grub_elf64_size): Likewise. - - * kern/i386/loader.S (grub_unix_real_boot): New function. - - * loader/aout.c: New file. - - * loader/i386/bsd.c: New file. - - * loader/i386/bsd_normal.c: New file. - - * loader/i386/pc/multiboot.c (grub_multiboot): Handle a.out format. - - * loader/multiboot2.c (grub_multiboot2): Reset grub_errno so that it - can test other formats. - -2008-02-19 Robert Millan - - * partmap/gpt.c: Include `'. - (grub_gpt_partition_type_empty): Redefine with macro from - `'. - (gpt_partition_map_iterate): Adjust partition type comparison. - - Export `entry' as partmap-specific `part.data' struct. - (grub_gpt_header, grub_gpt_partentry): Move from here ... - - * include/grub/gpt_partition.h (grub_gpt_header) - (grub_gpt_partentry): ... to here (new file). - - * util/i386/pc/grub-setup.c: Include `'. - - (grub_gpt_partition_type_bios_boot): New const variable, defined - with macro from `'. - - (setup): Replace `first_start' with `embed_region', which keeps - track of the embed region (and is partmap-agnostic). - - Replace find_first_partition_start() with find_usable_region(), - which finds a usable region for embedding using partmap-specific - knowledge (supports PC/MSDOS and GPT). - - Fix all assumptions that the embed region start at sector 1, using - `embed_region.start' from now on. Similarly, use `embed_region.end' - rather than `first_start' to calculate available size. - - In grub_util_info() message, replace "into after the MBR" with an - indication of the specific sector our embed region starts at. - -2008-02-19 Robert Millan - - * DISTLIST: Replace `commands/ieee1275/halt.c' and - `commands/ieee1275/reboot.c' with `commands/halt.c' and - `commands/reboot.c'. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES, reboot_mod_SOURCES) - (halt_mod_SOURCES): Likewise. - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES, reboot_mod_SOURCES) - (halt_mod_SOURCES): Likewise. - -2008-02-17 Christian Franke - - * commands/cat.c (grub_cmd_cat): Add break on GRUB_TERM_ESC key. - -2008-02-17 Robert Millan - - * util/i386/pc/grub-setup.c (setup): In find_first_partition_start(), - set `first_start' to 0 for non-PC/MSDOS partition maps. - -2008-02-16 Robert Millan - - * util/i386/pc/grub-setup.c (setup): In find_first_partition_start(), - do not assume partition map is PC/MSDOS before performing checks that - are specific to that layout. - -2008-02-13 Robert Millan - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Remove - `commands/i386/pc/halt.c' and `commands/i386/pc/reboot.c'. - * kern/i386/linuxbios/init.c (grub_halt, grub_reboot): Remove stubs. - -2008-02-13 Yoshinori K. Okuji - - * configure.ac: Only a cosmetic change on the handling of - -fno-stack-protector. - -2008-02-12 Alexandre Boeglin - - * conf/i386-efi.rmk (grub_emu_SOURCES): Replace - commands/i386/pc/halt.c and reboot.c by commands/halt.c and - reboot.c. - (grub_install_SOURCES): Add halt.mod and reboot.mod. - (halt_mod_SOURCES): New variable. - (halt_mod_CFLAGS): Likewise. - (halt_mod_LDFLAGS): Likewise. - (reboot_mod_SOURCES): Likewise. - (reboot_mod_CFLAGS): Likewise. - (reboot_mod_LDFLAGS): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Replace - commands/ieee1275/halt.c and reboot.c by commands/halt.c and - reboot.c. - (halt_mod_SOURCES): Likewise. - (reboot_mod_SOURCES): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Replace - commands/i386/pc/reboot.c by commands/reboot.c. - (reboot_mod_SOURCES): Likewise. - - * commands/i386/pc/reboot.c: merge this file ... - - * commands/ieee1275/reboot.c: ... and this file ... - - * commands/reboot.c: ... to this file. - Add some precompiler directive to include the correct header for - each machine. - - * commands/ieee1275/halt.c: move this file ... - - * commands/halt.c: ... to here. - Add some precompiler directive to include the correct header for - each machine. - - * include/grub/efi/efi.h (grub_reboot): New function declaration. - (grub_halt): Likewise. - - * kern/efi/efi.c (grub_reboot): New function. - (grub_halt): Likewise. - -2008-02-12 Robert Millan - - * util/getroot.c (grub_guess_root_device): Inspect /dev/evms before - /dev (like it is done for /dev/mapper). This doesn't provide support - for EVMS, but at least it is now easy to identify the problem when it - arises. - -2008-02-11 Robert Millan - - * util/biosdisk.c (grub_util_biosdisk_open, linux_find_partition) - (grub_util_biosdisk_get_grub_dev): Check open() exit status by - comparing it with -1, not 0. - -2008-02-10 Robert Millan - - * conf/i386-efi.rmk (grub_emu_SOURCES): Add `disk/raid.c' and - `disk/lvm.c'. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_setup_SOURCES): Likewise. - - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Move `disk/raid.c' and - `disk/lvm.c' to the end of the list. - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - -2008-02-10 Robert Millan - - * kern/main.c (grub_load_normal_mode): Do not reset `grub_errno'. Call - grub_print_error() instead. This will let user know why we're entering - rescue mode. - Based on suggestions from Sam Morris. - -2008-02-10 Alexandre Boeglin - - * normal/arg.c (grub_arg_parse): If one of the args is "--", call add_arg() - on remaining N args, instead of "--" arg N times. - -2008-02-09 Vesa Jaaskelainen - - * font/manager.c (unknown_glyph): Added variable for unknown glyph. - (fill_with_default_glyph): Changed to use unknown_glyph for fill - pattern for unknown glyphs. - -2008-02-09 Robert Millan - - * configure.ac: Probe for `help2man'. - * Makefile.in (builddir): New variable. - (HELP2MAN): Likewise. Set to `true' when @HELP2MAN@ doesn't provide it, - or otherwise add a few flags/options to it. - (install-local): For every executable utility or script that is - installed, invoke $(HELP2MAN) to install a manpage based on --help - output. - - * util/i386/pc/grub-install.in: Move down `update-grub_lib' sourcing, so - that it doesn't prevent --help from working in build tree. - - * util/i386/pc/grub-mkrescue.in (usage): Replace `grub-devel@gnu.org' - with `bug-grub@gnu.org'. - * util/powerpc/ieee1275/grub-mkrescue.in (usage): Likewise. - * util/update-grub.in (usage): New function. - Implement proper argument check, with support for --help and --version - (as well as existing -y). - -2008-02-09 Christian Franke - - * commands/cat.c (grub_cmd_cat): Print '\r' as hex to - avoid overwriting previous output. - * kern/rescue.c (grub_rescue_cmd_cat): Likewise. - -2008-02-09 Robert Millan - - * normal/menu.c (run_menu): If timeout is set to zero, don't bother - drawing the menu. - -2008-02-09 Robert Millan - - * commands/sleep.c: New file. - * conf/common.rmk (pkglib_MODULES): Add `commands/sleep.c'. - (sleep_mod_SOURCES): New variable. - (sleep_mod_CFLAGS): Likewise. - (sleep_mod_LDFLAGS): Likewise. - -2008-02-09 Robert Millan - - * disk/raid.c (grub_raid_scan_device): Add a pair of sanity checks for - situations in which we can deduce the RAID size and the superblock - doesn't match it. - -2008-02-09 Robert Millan - - * disk/lvm.c [GRUB_UTIL] (grub_lvm_memberlist): New function. Construct - and return a grub_diskmemberlist_t composed of LVM physical volumes. - [GRUB_UTIL] (grub_lvm_dev): Add `memberlist' member. - - * disk/raid.c [GRUB_UTIL] (grub_raid_memberlist): New function. Construct - and return a grub_diskmemberlist_t composed of physical array members. - [GRUB_UTIL] (grub_raid_dev): Add `memberlist' member. - - * include/grub/disk.h [GRUB_UTIL] (grub_disk_memberlist): New struct - prototype. - [GRUB_UTIL] (struct grub_disk_dev): Add `memberlist' function pointer. - [GRUB_UTIL] (struct grub_disk_memberlist): New struct declaration. - [GRUB_UTIL] (grub_disk_memberlist_t): New typedef. - - * util/grub-probe.c (probe): Move partmap probing code from here ... - (probe_partmap): ... to here. - (probe): Use probe_partmap() once for the disk we're probing, and - additionally, when such disk contains a memberlist() struct member, - once for each disk that is contained in the structure returned by - memberlist(). - -2008-02-09 Robert Millan - - * util/grub-probe.c (main): When `verbosity > 1', set `debug' - environment variable to 'all' in order to obtain debug output from - non-util/ code. - * util/i386/pc/grub-setup.c (main): Likewise. - -2008-02-08 Robert Millan - - * disk/raid.c (grub_raid_scan_device): Check for - `array->device[sb.this_disk.number]' rather than for - `array->device[sb.this_disk.number]->name', since the latter is not - guaranteed to be accessible. - -2008-02-08 Robert Millan - - * disk/raid.c: Update copyright. - * fs/cpio.c: Likewise. - * include/grub/raid.h: Likewise. - * loader/i386/pc/multiboot.c: Likewise. - * util/hostfs.c: Likewise. - -2008-02-08 Robert Millan - - * include/grub/raid.h (struct grub_raid_array): Change type of `device' - to a grub_disk_t array. - * disk/raid.c (grub_raid_read): Replace `device[x].disk' accesses with - `device[x]'. - (grub_raid_scan_device): Replace `device[x].name' accesses with - `device[x]->name'. Simplify initialization of `array->device[x]'. - -2008-02-08 Robert Millan - - * disk/raid.c (grub_raid_open, grub_raid_scan_device): Add a few - grub_dprintf() calls. - * kern/disk.c (grub_disk_read): Include grub_errmsg in out of range - error message. - -2008-02-07 Christian Franke - - * util/hostfs.c (grub_hostfs_open): Use fseeko and ftello - instead of fseek and ftell to support large files. - (grub_hostfs_read): Likewise. - -2008-02-07 Robert Millan - - Patch from Jeroen Dekkers. - * disk/raid.c (grub_raid_scan_device): Reset `grub_errno' on disk - failure, since successfully reading all array members might not be - required. - -2008-02-06 Robert Millan - - * util/grub-probe.c (probe): Simplify partmap probing (with the - assumption that the first word up to the underscore equals to - the module name). - -2008-02-06 Christian Franke - - * fs/cpio.c (grub_cpio_find_file): Return GRUB_ERR_NONE - (and set *ofs = 0) instead of GRUB_ERR_FILE_NOT_FOUND on - last block of a cpio or tar stream. - Check for "TRAILER!!!" instead of any empty data - block to detect last block of a cpio stream. - (grub_cpio_dir): Fix constness of variable np. - (grub_cpio_open): Return GRUB_ERR_FILE_NOT_FOUND if - cpio or tar trailer is detected. This fixes a crash - on open of a non existing file. - -2008-02-05 Bean - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Get physical - address of entry. - (grub_multiboot_load_elf64): Likewise. - (grub_multiboot): Initialize mbi structure. - - * util/grub-fstest.c: Don't include unused header file script.h. - - * conf/common.rmk (grub-fstest.c_DEPENDENCIES): Move to the beginning - of file. - (grub_fstest_SOURCES): Likewise. - -2008-02-05 Robert Millan - - * include/grub/term.h (GRUB_TERM_LEFT, GRUB_TERM_RIGHT) - (GRUB_TERM_UP, GRUB_TERM_DOWN, GRUB_TERM_HOME, GRUB_TERM_END) - (GRUB_TERM_DC, GRUB_TERM_PPAGE, GRUB_TERM_NPAGE, GRUB_TERM_ESC) - (GRUB_TERM_TAB, GRUB_TERM_BACKSPACE): New macros. - - * kern/i386/pc/startup.S: Include `'. - (translation_table): Replace hardcoded values with macros - provided by `'. - - * term/i386/pc/at_keyboard.c: Include `'. - (keyboard_map): Correct/add a few values, with macros provided - by `'. - (keyboard_map_shift): Zero values that don't differ from their - `keyboard_map' equivalents. - (grub_console_checkkey): Optimize KEYBOARD_STATUS_CAPS_LOCK toggling. - Discard the second scan code that is always sent by Caps lock. - Only use `keyboard_map_shift' when it provides a non-zero value, - otherwise fallback to `keyboard_map'. - -2008-02-04 Bean - - * Makefile.in (enable_grub_fstest): New variable. - - * conf/common.rmk (grub_fstest_init.lst): New rule. - (grub_fstest_init.h): Likewise. - (grub_fstest_init.c): Likewise. - (util/grub-fstest.c_DEPENDENCIES): New variable. - (grub_fstest_SOURCES): Likewise. - - * configure.ac (enable_grub_fstest): Check for --enable-grub-fstest. - - * util/grub-fstest.c: New file. - -2008-02-03 Yoshinori K. Okuji - - Make grub-setup handle a separate root device. - - * util/i386/pc/grub-setup.c (setup): Always open the root device, - so that the root device can be compared with the destination - device. - When embedding the core image, if the root and destination devices - are different, set ROOT_DRIVE to ROOT_DEV->DISK->ID. Otherwise, to - 0xFF. - When not embedding, set ROOT_DRIVE to 0xFF. - -2008-02-03 Yoshinori K. Okuji - - Add support for having a grub directory in a different drive. This - is still only the data handling part. - - * kern/i386/pc/startup.S (multiboot_trampoline): Set %dh to 0xFF. - (codestart): Save %dh in GRUB_ROOT_DRIVE. - (grub_root_drive): New variable. - - * kern/i386/pc/init.c (make_install_device): Use GRUB_ROOT_DRIVE - instead of GRUB_BOOT_DRIVE to construct a device name. Set - GRUB_ROOT_DRIVE to GRUB_BOOT_DRIVE if it is 0xFF, otherwise use it - as it was. - - * include/grub/i386/pc/kernel.h (grub_root_drive): New prototype. - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_ROOT_DRIVE): New - macro. - (GRUB_BOOT_MACHINE_DRIVE_CHECK): Set to 0x4f. - - * boot/i386/pc/pxeboot.S (_start): Set %dh to 0xFF. For now, this - is bogus, because PXE booting does not specify any drive - correctly. - - * boot/i386/pc/lnxboot.S (reg_edx): Set the second byte to 0xFF. I - am not sure if this is really correct. - - * boot/i386/pc/cdboot.S: Set %dh to 0xFF, because the root drive - is always identical to the boot drive when booting from a CD. - - * boot/i386/pc/boot.S (MOV_MEM_TO_AL): Removed. Not needed any - longer. - (root_drive): New variable. - (real_start): Unconditionally set %dh to ROOT_DRIVE. - (setup_sectors): Push %dx right after popping it, because %dh will - be modified later. - (copy_buffer): Restore %dx. - -2008-02-03 Robert Millan - - * util/i386/pc/grub-mkrescue.in: Rewrite most of image generation to - use `cdboot.img' for cdrom images. - -2008-02-03 Robert Millan - - * util/grub.d/00_header.in: Issue scripting commands for GRUB to - only setup gfxterm when `font' command has succeeded. - -2008-02-03 Robert Millan - - * loader/multiboot_loader.c [GRUB_MACHINE_LINUXBIOS] - (grub_rescue_cmd_multiboot_loader) - (grub_rescue_cmd_module_loader): Enable multiboot1 calls. - -2008-02-03 Pavel Roskin - - * kern/i386/pc/startup.S (grub_chainloader_real_boot): Pop - %edx and %esi from stack only after grub_gate_a20() is called. - grub_gate_a20() clobbers %edx. - -2008-02-03 Yoshinori K. Okuji - - * configure.ac (AC_INIT): Bumped to 1.96. - - * DISTLIST: Added boot/i386/pc/cdboot.S, bus/pci.c, - commands/lspci.c,disk/memdisk.c, include/grub/pci.h, - include/grub/i386/pc/pci.h, video/readers/jpeg.c, and - video/readers/png.c. - -2008-02-03 Bean - - * conf/i386-pc.rmk (pkglib_IMAGES): Add cdboot.img. - (cdboot_img_SOURCES): New variable. - (cdboot_img_ASFLAGS): New variable. - (cdboot_img_LDFLAGS): New variable. - - * boot/i386/pc/cdboot.S: New file. - - * disk/i386/pc/biosdisk.c (cd_start): New variable. - (cd_count): Likewise. - (grub_biosdisk_get_drive): Add support for cd device. - (grub_biosdisk_call_hook): Likewise. - (grub_biosdisk_iterate): Likewise. - (grub_biosdisk_open): Likewise. - (GRUB_BIOSDISK_CDROM_RETRY_COUNT): New macro. - (grub_biosdisk_rw): Support reading from cd device. - (GRUB_MOD_INIT): Iterate cd devices. - - * include/grub/i386/pc/biosdisk.h (GRUB_BIOSDISK_FLAG_CDROM): New macro. - (GRUB_BIOSDISK_MACHINE_CDROM_START): Likewise. - (GRUB_BIOSDISK_MACHINE_CDROM_END): Likewise. - - * kern/i386/pc/init.c (make_install_device): Check for cd device. - -2008-02-02 Robert Millan - - * commands/read.c: New file. - * conf/common.rmk (pkglib_MODULES): Add `commands/read.c'. - (read_mod_SOURCES): New variable. - (read_mod_CFLAGS): Likewise. - (read_mod_LDFLAGS): Likewise. - -2008-02-02 Robert Millan - - * normal/main.c (grub_normal_execute): Check for `menu->size' when - determining whether menu has to be displayed. - -2008-02-02 Marco Gerards - - * bus/pci.c: New file. - - * include/grub/pci.h: Likewise. - - * include/grub/i386/pc/pci.h: Likewise. - - * commands/lspci.c: Likewise. - - * conf/i386-pc.rmk (pkglib_MODULES): Add `pci.mod' and - `lspci.mod'. - (pci_mod_SOURCES): New variable. - (pci_mod_CFLAGS): Likewise. - (pci_mod_LDFLAGS): Likewise. - (lspci_mod_SOURCES): Likewise. - (lspci_mod_CFLAGS): Likewise. - (lspci_mod_LDFLAGS): Likewise. - -2008-02-02 Bean - - * fs/ufs.c (INODE_BLKSZ): Fix incorrect value. - (grub_ufs_get_file_block): Fix indirect block calculation problem. - - * fs/xfs.c (grub_xfs_sblock): New member log2_dirblk. - (grub_xfs_btree_node): New structure. - (grub_xfs_btree_root): New structure. - (grub_xfs_inode): New members nblocks, extsize, nextents and btree. - (GRUB_XFS_EXTENT_OFFSET): Use exts instead of inode->data.extents. - (GRUB_XFS_EXTENT_BLOCK): Likewise. - (GRUB_XFS_EXTENT_SIZE): Likewise. - (grub_xfs_read_block): Support btree format type. - (grub_xfs_iterate_dir): Use NESTED_FUNC_ATTR in call_hook. - Use directory block as basic unit. - - * fs/fshelp.c (grub_fshelp_read_file): Bug fix for sparse block. - - * aclocal.m4 (grub_i386_CHECK_REGPARM_BUG): Define NESTED_FUNC_ATTR as - __attribute__ ((__regparm__ (1))). - -2008-02-01 Robert Millan - - Correct a mistake in previous commit. - - * conf/i386-pc.rmk (normal/execute.c_DEPENDENCIES): Move to the - top. - (normal/command.c_DEPENDENCIES): New variable. - -2008-02-01 Robert Millan - - * conf/i386-efi.rmk (normal/execute.c_DEPENDENCIES): Move to the - top. - (normal/command.c_DEPENDENCIES): New variable. - (grub-emu_DEPENDENCIES, normal_mod_DEPENDENCIES): Remove variables. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-linuxbios.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - (grub_emu_SOURCES): Add `fs/fshelp.c'. - - * genmk.rb: Add `$(#{src}_DEPENDENCIES)' in targets that require it. - -2008-02-01 Robert Millan - - * kern/disk.c (grub_disk_read, grub_disk_write): Add grub_dprintf() - call at beginning of function. - -2008-01-31 Pavel Roskin - - * util/powerpc/ieee1275/grub-mkrescue.in: New file. - * conf/powerpc-ieee1275.rmk (bin_SCRIPTS): New variable. - (grub_mkrescue_SOURCES): Likewise. - * DISTLIST: Add util/powerpc/ieee1275/grub-mkrescue.in. - -2008-01-30 Robert Millan - - * conf/i386-pc.rmk (sbin_UTILITIES): Remove `grub-probe'. - (util/grub-probe.c_DEPENDENCIES, grub_probe_SOURCES): Moved from here ... - * conf/common.rmk (util/grub-probe.c_DEPENDENCIES) - (grub_probe_SOURCES): ... to here. - - * conf/i386-efi.rmk (sbin_UTILITIES): Remove `grub-probe'. - (util/grub-probe.c_DEPENDENCIES, grub_probe_SOURCES): Remove. - * conf/i386-ieee1275.rmk: Likewise. - * conf/i386-linuxbios.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - -2008-01-30 Tristan Gingold - - * kern/rescue.c: Silently accept empty lines. - -2008-01-29 Bean - - * boot/i386/pc/lnxboot.S (data_start): Code cleanup. - (real_code_2): Code cleanup and change comment style. - (move_memory): Avoid using 32-bit address mode. - -2008-01-29 Bean - - * conf/i386-pc.rmk (pkglib_MODULES): Add `png.mod'. - (png_mod_SOURCES): New variable. - (png_mod_CFLAGS): Likewise. - (png_mod_LDFLAGS): Likewise. - - * video/readers/png.c: New file. - -2008-01-28 Robert Millan - - * include/grub/i386/linuxbios/kernel.h (GRUB_MOD_GAP): New macro. - * kern/powerpc/ieee1275/init.c (grub_arch_modules_addr): Remove - `ifndef GRUB_MOD_GAP' hack. - * util/elf/grub-mkimage.c (add_segments): Likewise. - -2008-01-27 Robert Millan - - * kern/powerpc/ieee1275/init.c (grub_arch_modules_addr): Skip - `GRUB_MOD_GAP' for platforms in which it's not defined. - * util/elf/grub-mkimage.c (add_segments): Likewise. - -2008-01-27 Robert Millan - - Get grub-emu to build again (including parallel builds). - - * conf/i386-pc.rmk (util/grub-emu.c_DEPENDENCIES): Remove variable. - Split into ... - (util/grub-emu.c_DEPENDENCIES): ... this, ... - (normal/execute.c_DEPENDENCIES): ... this, ... - (grub-emu_DEPENDENCIES): ... and this. - - * conf/i386-efi.rmk: Likewise. - * conf/i386-linuxbios.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - (grub_emu_SOURCES): Remove duplicated `kern/file.c'. - -2008-01-27 Robert Millan - - * NEWS: Add a few items. - -2008-01-27 Robert Millan - - Fix parallel builds with grub-emu. Based on earlier commit for - grub-probe and grub-setup. - - * conf/i386-pc.rmk (grub-emu_DEPENDENCIES): Renamed to ... - (util/grub-emu.c_DEPENDENCIES): ... this. - * conf/i386-efi.rmk (grub-emu_DEPENDENCIES): Renamed to ... - (util/grub-emu.c_DEPENDENCIES): ... this. - * conf/i386-linuxbios.rmk (grub-emu_DEPENDENCIES): Renamed to ... - (util/grub-emu.c_DEPENDENCIES): ... this. - * conf/i386-ieee1275.rmk (grub-emu_DEPENDENCIES): Renamed to ... - (util/grub-emu.c_DEPENDENCIES): ... this. - * conf/powerpc-ieee1275.rmk (grub-emu_DEPENDENCIES): Renamed to ... - (util/grub-emu.c_DEPENDENCIES): ... this. - -2008-01-27 Pavel Roskin - - * include/grub/powerpc/ieee1275/kernel.h: Introduce GRUB_MOD_GAP - to create a gap between _end and the modules added to the image - with grub-mkrescue. That fixes "CLAIM failed" on PowerMAC. - * kern/powerpc/ieee1275/init.c: Use GRUB_MOD_GAP. - * util/elf/grub-mkimage.c (add_segments): Likewise. - -2008-01-26 Pavel Roskin - - * kern/dl.c (grub_dl_load): Don't abort if prefix is not set, - just return an error. - -2008-01-26 Bean - - * fs/reiserfs.c (grub_fshelp_node): New member next_offset. - (grub_reiserfs_get_item): Save offset of the next item. - (grub_reiserfs_iterate_dir): Use next_offset to find next item. - -2008-01-25 Robert Millan - - * conf/i386-pc.rmk (grub_setup_SOURCES, grub_emu_SOURCES): Regroup to - make all filesystem sources appear together (possibly fixing omissions - while at it). - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-pc.rmk (grub_probe_SOURCES): Likewise. Additionally, - add `kern/file.c'. - * conf/i386-efi.rmk (grub_probe_SOURCES): Likewise. - * conf/i386-ieee1275.rmk (grub_probe_SOURCES): Likewise. - * conf/i386-linuxbios.rmk (grub_probe_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_probe_SOURCES): Likewise. - - * util/grub-probe.c: Include `' and `'. - (probe): Add a sanity check to make sure of our ability to read - requested files when probing for filesystem type. - - * genmk.rb: Update copyright year (2007). - - * include/grub/fs.h (grub_fat_init, grub_fat_fini, grub_ext2_init) - (grub_ext2_fini, grub_ufs_init, grub_ufs_fini, grub_minix_init) - (grub_minix_fini, grub_hfs_init, grub_hfs_fini, grub_jfs_init) - (grub_jfs_fini, grub_xfs_init, grub_xfs_fini, grub_affs_init) - (grub_affs_fini, grub_sfs_init, grub_sfs_fini, grub_iso9660_init) - : Remove function prototypes. - -2008-01-25 Robert Millan - - Revert my previous commits (based on wrong assumption of how grub_errno - works). - - * kern/disk.c (grub_disk_open): Stop resetting grub_errno. - * kern/file.c (grub_file_open): Likewise. - -2008-01-24 Pavel Roskin - - * include/grub/ieee1275/ieee1275.h: Introduce flag for firmwares - that hang if GRUB tries to setup colors. - * term/ieee1275/ofconsole.c (grub_ofconsole_init): Don't set - colors for firmwares that don't support it. - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_set_flag): - Recognize Open Hack'Ware, set flags to work around its - limitations. - -2008-01-24 Robert Millan - - * kern/file.c (grub_file_open): Do not account previous failures of - unrelated functions when grub_errno is checked for. - Reported by Oleg Strikov. - -2008-01-24 Bean - - * fs/ufs.c (GRUB_UFS_VOLNAME_LEN): New macro. - (grub_ufs_sblock): New member volume name. - (grub_ufs_find_file): Fix string copy bug. - (grub_ufs_label): Implement this function properly. - - * fs/hfs.c (grub_hfs_cnid_type): New enum. - (grub_hfs_iterate_records): Use the correct file number for extents - and catalog file. Fix problem in next index calculation. - (grub_hfs_find_node): Replace recursive function call with loop. - (grub_hfs_iterate_dir): Replace recursive function call with loop. - -2008-01-23 Robert Millan - - * include/grub/i386/ieee1275/loader.h: Include `', - `' and `'. - (grub_multiboot2_real_boot): New function prototype. - - * include/grub/i386/pc/memory.h: Include `'. - [!GRUB_MACHINE_IEEE1275] (grub_lower_mem, grub_upper_mem): Disable. - - * kern/i386/ieee1275/init.c (grub_os_area_addr) - (grub_os_area_size, grub_lower_mem, grub_upper_mem): Remove variables. - -2008-01-23 Robert Millan - - * kern/mm.c (grub_mm_init_region): Replace grub_dprintf() call with - #ifdef'ed out grub_printf(). - -2008-01-23 Robert Millan - - * term/i386/pc/at_keyboard.c (grub_keyboard_isr): #ifdef out - grub_dprintf calls, since they make "debug=all" mode unusable. - (grub_console_checkkey): Likewise. - -2008-01-23 Robert Millan - - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Add - `term/i386/pc/at_keyboard.c'. - (pkglib_MODULES): Add `serial.mod'. - (serial_mod_SOURCES): New variable. - (serial_mod_CFLAGS): Likewise. - (serial_mod_LDFLAGS): Likewise. - - * include/grub/i386/ieee1275/console.h: Add `'. Remove - `'. - (grub_keyboard_controller_init): New function prototype. - (grub_console_checkkey): Likewise. - (grub_console_getkey): Likewise. - - * kern/powerpc/ieee1275/init.c (grub_machine_init): Initialize AT - keyboard on i386. - - * term/ieee1275/ofconsole.c (grub_ofconsole_term): On i386, use - grub_ofconsole_checkkey() and grub_ofconsole_getkey() for input. - -2008-01-23 Robert Millan - - * kern/i386/pc/init.c (make_install_device): When memdisk image is - present, "(memdisk)/boot/grub" becomes the default prefix. - - * util/i386/pc/grub-mkrescue.in: Switch to a minimal core.img plus - a memdisk tarball with all the modules. Add --overlay=DIR option that - allows users to overlay additional files into the image. - -2008-01-23 Robert Millan - - * conf/i386-ieee1275.rmk (kernel_elf_SOURCES): Add `machine/loader.h' - and `machine/memory.h'. - (pkglib_MODULES): Add `multiboot.mod' and `_multiboot.mod'. - (_multiboot_mod_SOURCES): New variable. - (_multiboot_mod_CFLAGS): Likewise. - (_multiboot_mod_LDFLAGS): Likewise. - (multiboot_mod_SOURCES): Likewise. - (multiboot_mod_CFLAGS): Likewise. - (multiboot_mod_LDFLAGS): Likewise. - - * include/grub/i386/ieee1275/loader.h: New file. - - * include/grub/i386/ieee1275/machine.h: Likewise. - - * include/grub/i386/ieee1275/memory.h: Likewise. - - * include/grub/i386/pc/init.h (grub_os_area_addr): Remove (redundant) - variable declaration. - (grub_os_area_size): Likewise. - - * kern/i386/ieee1275/init.c (grub_os_area_addr, grub_os_area_size) - (grub_lower_mem, grub_upper_mem): New variables. - (grub_stop_floppy): New function (just to make - grub_multiboot2_real_boot() happy). - - * kern/i386/ieee1275/startup.S: Include `', - `', `' and `'. - (grub_stop): New function. - Include `"../realmode.S"' and `"../loader.S"'. - - * loader/multiboot_loader.c: Include `'. - Replace `__i386__' #ifdefs with `GRUB_MACHINE_PCBIOS'. - - * loader/powerpc/ieee1275/multiboot2.c (grub_mb2_arch_boot): On i386, - rely on grub_multiboot2_real_boot() for final boot. - -2008-01-22 Robert Millan - - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): When - `GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY' flag is set, skip any - device that doesn't look like an SD card. - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_flag): Add - `GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY' flag. - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_set_flag): Detect - OLPC laptop, and set `GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY' when - found. - -2008-01-22 Robert Millan - - * kern/powerpc/ieee1275/init.c (grub_claim_heap): Add sanity check to - avoid claiming over our own code. - -2008-01-22 Bean - - * conf/i386-pc.rmk (pkglib_MODULES): Add `jpeg.mod'. - (jpeg_mod_SOURCES): New variable. - (jpeg_mod_CFLAGS): Likewise. - (jpeg_mod_LDFLAGS): Likewise. - - * video/readers/jpeg.c : New file. - -2008-01-22 Bean - - * fs/cpio.c (grub_cpio_find_file): Return GRUB_ERR_FILE_NOT_FOUND when - there are no more items. - -2008-01-21 Robert Millan - - * kern/mm.c (grub_mm_init_region): Improve debug message. - -2008-01-21 Robert Millan - - * conf/i386-pc.rmk (GRUB_MEMORY_MACHINE_LINK_ADDR): New variable. - (kernel_img_LDFLAGS): Use `GRUB_MEMORY_MACHINE_LINK_ADDR' as link - address. - (grub_mkimage_CFLAGS): Propagate `GRUB_MEMORY_MACHINE_LINK_ADDR' as - a C macro. - * include/grub/i386/pc/memory.h (GRUB_MEMORY_MACHINE_UPPER): New macro. - Indicates start of upper memory. - * util/i386/pc/grub-mkimage.c: Include `'. - (generate_image): Abort when image size is big enough to corrupt - upper memory. - - * include/grub/i386/pc/vga.h: Include `'. - (GRUB_MEMORY_MACHINE_VGA_ADDR): Alias for `GRUB_MEMORY_MACHINE_UPPER'. - * term/i386/pc/vga.c (VGA_MEM): Use `GRUB_MEMORY_MACHINE_VGA_ADDR' - instead of hardcoding 0xA0000. - * video/i386/pc/vbe.c: Include `'. - (grub_vbe_set_video_mode): Use `GRUB_MEMORY_MACHINE_VGA_ADDR' - instead of hardcoding 0xA0000. - -2008-01-21 Robert Millan - - * disk/memdisk.c (memdisk_size): New variable. - (grub_memdisk_open): Replace grub_arch_memdisk_size() call with - `memdisk_size'. - (grub_memdisk_init): Initialize `memdisk_size'. Reallocate memdisk - image to dynamic memory. - (grub_memdisk_fini): Replace grub_arch_memdisk_size() call with - `memdisk_size'. Free memdisk block. - -2008-01-21 Robert Millan - - Fix detection of very small filesystems (like tar). - - * fs/reiserfs.c (grub_reiserfs_mount): When disk is too small to - contain a ReiserFS, abort with GRUB_ERR_BAD_FS rather than - GRUB_ERR_OUT_OF_RANGE (which made the upper layer think there's - a problem with this disk). - -2008-01-21 Robert Millan - - * disk/i386/pc/biosdisk.c (grub_biosdisk_iterate): Add debug message - on grub_biosdisk_rw_standard() error. - -2008-01-21 Robert Millan - - * include/grub/ieee1275/ieee1275.h: Add 2008 to Copyright line for - recent changes. - * kern/elf.c: Likewise. - * kern/ieee1275/ieee1275.c: Likewise. - * kern/powerpc/ieee1275/openfw.c: Likewise. - * term/ieee1275/ofconsole.c: Likewise. - -2008-01-21 Robert Millan - - * include/grub/i386/pc/kernel.h: Include `'. - - * include/grub/kernel.h (grub_arch_memdisk_addr) - (grub_arch_memdisk_size): Moved from here ... - - * include/grub/i386/pc/kernel.h (grub_arch_memdisk_addr) - (grub_arch_memdisk_size): ... to here. - -2008-01-21 Robert Millan - - Mostly based on bugfix from Bean. - - * kern/elf.c (grub_elf32_phdr_iterate): Use `NESTED_FUNC_ATTR' - attribute with hook() parameter. - (grub_elf32_load): Use `NESTED_FUNC_ATTR' with grub_elf32_load_segment() - declaration. - (grub_elf64_phdr_iterate): Use `NESTED_FUNC_ATTR' - attribute with hook() parameter. - (grub_elf64_load): Use `NESTED_FUNC_ATTR' with grub_elf64_load_segment() - declaration. - -2008-01-21 Robert Millan - - * conf/i386-pc.rmk (kernel_img_HEADERS): Add `machine/kernel.h'. - (pkglib_MODULES): Add `memdisk.mod'. - (memdisk_mod_SOURCES): New variable. - (memdisk_mod_CFLAGS): Likewise. - (memdisk_mod_LDFLAGS): Likewise. - - * disk/memdisk.c: New file. - - * include/grub/disk.h (grub_disk_dev_id): Add - `GRUB_DISK_DEVICE_MEMDISK_ID'. - - * include/grub/i386/pc/kernel.h - (GRUB_KERNEL_MACHINE_MEMDISK_IMAGE_SIZE): New macro. - (GRUB_KERNEL_MACHINE_PREFIX): Increment by 4. - (grub_kernel_image_size): New variable declaration. - (grub_total_module_size): Likewise. - (grub_memdisk_image_size): Likewise. - - * include/grub/i386/pc/memory.h - (GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR): New macro. - - * include/grub/kernel.h: Include `'. - (grub_arch_memdisk_addr): New variable declaration. - (grub_arch_memdisk_size): Likewise. - - * kern/i386/pc/init.c (grub_arch_memdisk_addr): New function. - (grub_arch_memdisk_size): Likewise. - - * kern/i386/pc/startup.S (grub_memdisk_image_size): New variable. - (codestart): Replace hardcoded `0x100000' with - `GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR' macro. - - * util/i386/pc/grub-mkimage.c: Include `'. - (generate_image): Add `memdisk_path' parameter. When `memdisk_path' is - not NULL, append the contents of the file it refers to, at the end of - the compressed kernel image. Initialize `grub_memdisk_image_size' - variable (at `GRUB_KERNEL_MACHINE_MEMDISK_IMAGE_SIZE' offset). - (options): Add "memdisk"|'m' option. - (main): Parse --memdisk|-m option, and pass user-provided path as - parameter to generate_image(). - -2008-01-20 Robert Millan - - * kern/sparc64/ieee1275/openfw.c (grub_devalias_iterate): Copy debug - grub_dprintf() calls from here ... - * kern/powerpc/ieee1275/openfw.c (grub_devalias_iterate): ... to here. - -2008-01-20 Robert Millan - - Fix detection of "real mode" when /options/real-mode? doesn't exist. - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_mmu): New variable - declaration. - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_mmu): New variable. - (grub_ieee1275_find_options): If `grub_ieee1275_mmu' is 0, set - `GRUB_IEEE1275_FLAG_REAL_MODE'. - (cmain): Initialize `grub_ieee1275_mmu' (using /chosen/mmu integer - property). - * kern/powerpc/ieee1275/openfw.c (grub_map): Rely on pre-initialized - `grub_ieee1275_mmu' rather than obtaining a handler on every call. - -2008-01-19 Robert Millan - - Get rid of confusing function (superseded by - `grub_ieee1275_get_integer_property') - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_decode_int_4): Remove - prototype. - * kern/ieee1275/ieee1275.c (grub_ieee1275_decode_int_4): Remove - function. - * term/ieee1275/ofconsole.c (grub_ofconsole_init): Avoid use of - grub_ieee1275_decode_int_4(), by obtaining integer properties directly - in native endianness from grub_ieee1275_get_integer_property(). - -2008-01-19 Robert Millan - - * kern/powerpc/ieee1275/openfw.c (grub_halt): Issue "power-off" - command after "shut-down", since implementations differ on which - the command for halt is. - -2008-01-19 Robert Millan - - * include/grub/i386/linuxbios/console.h: Add header protection. - (grub_keyboard_controller_init): New function prototype. - * term/i386/pc/at_keyboard.c (KEYBOARD_COMMAND_ISREADY): New macro. - (KEYBOARD_COMMAND_READ): Likewise. - (KEYBOARD_COMMAND_WRITE): Likewise. - (KEYBOARD_SCANCODE_SET1): Likewise. - (grub_keyboard_controller_write): New function. - (grub_keyboard_controller_read): Likewise. - (grub_keyboard_controller_init): Likewise. - - * term/i386/pc/console.c: Include `'. - (grub_console_init): On coreboot/LinuxBIOS, call - grub_keyboard_controller_init(). - -2008-01-19 Robert Millan - - PowerPC changes provided by Pavel Roskin. - - * kern/powerpc/ieee1275/cmain.c (cmain): Don't take any arguments. - * kern/powerpc/ieee1275/crt0.S: Store r5 in grub_ieee1275_entry_fn, - don't rely on cmain() doing it. - * kern/i386/ieee1275/startup.S (_start): Store %eax in - grub_ieee1275_entry_fn, don't rely on cmain() doing it. - -2008-01-16 Robert Millan - - * include/grub/i386/linuxbios/memory.h - (GRUB_MEMORY_MACHINE_LINUXBIOS_TABLE_ADDR): Remove macro. - * kern/i386/linuxbios/table.c (grub_linuxbios_table_iterate): Do not - receive `table_header' as argument. Instead, probe for it in the - known memory ranges where it can be present. - (grub_available_iterate): Do not pass a fixed `table_header' address - to grub_linuxbios_table_iterate(). - -2008-01-15 Robert Millan - - * configure.ac: Add `i386-ieee1275' to the list of supported targets. - * conf/i386-ieee1275.rmk: New file. - * include/grub/i386/ieee1275/console.h: Likewise. - * include/grub/i386/ieee1275/ieee1275.h: Likewise. - * include/grub/i386/ieee1275/kernel.h: Likewise. - * include/grub/i386/ieee1275/time.h: Likewise. - * kern/i386/ieee1275/init.c: Likewise. - * kern/i386/ieee1275/startup.S: Likewise. - -2008-01-15 Robert Millan - - * kern/misc.c (grub_vsprintf): Do not reset `longlongfmt' to zero - when pointers are 32-bit (but still do set it to one when they are - 64-bit). - -2008-01-15 Robert Millan - - * include/grub/ieee1275/ieee1275.h - (grub_ieee1275_get_integer_property): New function prototype. - - * kern/ieee1275/ieee1275.c: Include `'. - (grub_ieee1275_get_integer_property): New function. Wraps around - grub_ieee1275_get_property() to handle endianness. - - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_find_options): Replace - grub_ieee1275_get_property() with grub_ieee1275_get_integer_property() - where appropriate. - * kern/powerpc/ieee1275/openfw.c (grub_available_iterate): Likewise. - (grub_map): Likewise. - * kern/sparc64/ieee1275/openfw.c (grub_map): Likewise. - -2008-01-15 Bean - - * normal/execute.c (grub_script_exec_argument_to_string): Check for undefined variable. - (grub_script_execute_cmdline): Reset grub_errno. - - * normal/main.c (read_config_file): Reset grub_errno. - - * normal/parse.y (script_init): New. - (script): Move function and menuentry here. - (delimiter): New. - (command): Add delimiter at the end of command. - (commands): Adjust to match the new command. - (commandblock): Remove grub_script_lexer_record_start. - (menuentry): Add grub_script_lexer_record_start, use the new commands. - (if): Use the new commands. - - * conf/common.rmk (pkgdata_MODULES): Add echo.mod. - -2008-01-15 Robert Millan - - * normal/menu.c (run_menu): Move timeout message from here ... - (print_timeout): ... to here. - (run_menu): Use print_timeout() once during initial draw to print - the whole message, and again in every clock tick to update only - the number of seconds. - -2008-01-15 Robert Millan - - * kern/powerpc/ieee1275/openfw.c (grub_available_iterate): Obtain - actual size of `available' from grub_ieee1275_get_property(), and - restrict parsing to that bound. - -2008-01-15 Christian Franke - - * util/grub-emu.c: Replace by . - (argp_program_version): Remove variable. - (argp_program_bug_address): Likewise. - (options): Convert from struct argp_option to struct option. - (struct arguments): Remove. - (parse_opt): Remove. - (usage): New function. - (main): Replace struct args members by simple variables. - Replace argp_parse() by getopt_long(). - Add switch to evaluate options. - Add missing "(...)" around root_dev in prefix string. - -2008-01-14 Robert Millan - - * kern/powerpc/ieee1275/init.c (grub_exit): Reimplement as a wrapper - for grub_ieee1275_exit(), in order to improve portability. - -2008-01-14 Robert Millan - - * util/grub.d/10_linux.in (prefix): Define. - (exec_prefix): Likewise. Both definitions are later used by `libdir'. - -2008-01-13 Pavel Roskin - - * disk/ieee1275/ofdisk.c (grub_ofdisk_open): Don't use - grub_errno if no errors have been detected. - -2008-01-12 Robert Millan - - * include/grub/util/getroot.h (grub_dev_abstraction_types): New enum. - (grub_util_get_dev_abstraction): New function prototype. - - * util/getroot.c: Include `' - (grub_util_get_grub_dev): Move detection of abstraction type to ... - (grub_util_get_dev_abstraction): ... here (new function). - - * util/grub-probe.c: Convert PRINT_* to an enum. Add - `PRINT_ABSTRACTION'. - (probe): Probe for abstraction type when requested. - (main): Understand `--target=abstraction'. - - * util/i386/efi/grub-install.in: Add abstraction module to core - image when it is found to be necessary. - * util/i386/pc/grub-install.in: Likewise. - * util/powerpc/ieee1275/grub-install.in: Likewise. - - * util/update-grub_lib.in (font_path): Return system path without - converting to GRUB path. - * util/update-grub.in: Convert system path returned by font_path() - to a GRUB path. Use `grub-probe -t abstraction' to determine what - abstraction module is needed for loading fonts (if any). Export - that as `GRUB_PRELOAD_MODULES'. - * util/grub.d/00_header.in: Process `GRUB_PRELOAD_MODULES' (print - insmod commands). - -2008-01-12 Yoshinori K. Okuji - - Remove some unused code from reiserfs. - - * fs/reiserfs.c (struct grub_reiserfs_key) - [GRUB_REISERFS_KEYV2_BITFIELD]: Removed offset and type. - (struct grub_reiserfs_node_body): Removed. - (grub_reiserfs_get_key_v2_type) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_get_key_offset) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_set_key_offset) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_set_key_offset) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_set_key_type) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_iterate_dir) [GRUB_REISERFS_KEYV2_BITFIELD]: - Likewise. - (grub_reiserfs_open) [GRUB_REISERFS_KEYV2_BITFIELD]: Likewise. - (grub_reiserfs_read) [GRUB_REISERFS_KEYV2_BITFIELD]: Likewise. - (grub_reiserfs_dir) [GRUB_REISERFS_KEYV2_BITFIELD]: Likewise. - -2008-01-10 Robert Millan - - * util/update-grub_lib.in (grub_file_is_not_garbage): New function. - Determines if a file is garbage left by packaging systems, etc. - * util/update-grub.in: Use grub_file_is_not_garbage() as a condition - for processing /etc/grub.d scripts. - * util/grub.d/10_hurd.in: Fix `GRUB_DISTRIBUTOR' comparison. - * util/grub.d/10_linux.in: Likewise. Use grub_file_is_not_garbage() - as a condition for processing Linux images. - -2008-01-10 Pavel Roskin - - * include/grub/powerpc/libgcc.h (__ucmpdi2): New export. Needed - to compile reiserfs.c on PowerPC. - -2008-01-10 Robert Millan - - * kern/device.c (grub_device_iterate): Do not abort device iteration - when one of the devices cannot be opened. - * kern/disk.c (grub_disk_open): Do not account previous failures of - unrelated functions when grub_errno is checked for. - -2008-01-08 Robert Millan - - * loader/i386/pc/linux.c (grub_rescue_cmd_linux): For - `! grub_linux_is_bzimage', change order of address comparison to make - it more intuitive, and improve "too big zImage" error message. - -2008-01-08 Robert Millan - - * Makefile.in (uninstall): Handle `$(update-grub_SCRIPTS)' and - `$(update-grub_DATA)'. - (distcheck): Fix race condition when invoking `$(MAKE)' on multiple - targets. - -2008-01-07 Robert Millan - - * boot/i386/pc/boot.S (boot_drive_check): Add a comment indicating - which instruction is modified by grub-setup during installation - (since it wasn't obvious by only looking at this file). - -2008-01-07 Robert Millan - - * TODO: Rewrite. Just refer to the wiki and the BTS instead of - listing actual TODO items. - -2008-01-06 Yoshinori K. Okuji - - * fs/reiserfs.c (grub_reiserfs_get_key_v2_type): Handle endianness - correctly. - (grub_reiserfs_get_key_offset): Likewise. - (grub_reiserfs_set_key_offset): Likewise. - (grub_reiserfs_set_key_type): Likewise. - (grub_reiserfs_iterate_dir): Return 1 if found, otherwise 0. - - (GRUB_REISERFS_KEYV2_BITFIELD): Undefined. Probably it would be - better to remove the bitfield version completely. - -2008-01-06 Yoshinori K. Okuji - - * fs/reiserfs.c (grub_reiserfs_iterate_dir): ENTRY_ITEM must be - allocated from the heap, due to the fshelp implementation. - (grub_reiserfs_dir): Free NODE, due to the same reason. - -2008-01-06 Yoshinori K. Okuji - - Mostly from Vincent Pelletier: - - * fs/reiserfs.c: New file. - - * conf/common.rmk (pkglib_MODULES): Added reiserfs.mod. - (reiserfs_mod_SOURCES): New variable. - (reiserfs_mod_CFLAGS): Likewise. - (reiserfs_mod_LDFLAGS): Likewise. - - * DISTLIST: Added boot/i386/pc/lnxboot.S, commands/hexdump.c, - disk/ata.c, fs/cpio.c, fs/ntfscomp.c, fs/reiserfs.c, - include/grub/ntfs.h, include/grub/i386/pc/machine.h, and - normal/color.c. - -2008-01-06 Robert Millan - - * normal/color.c: Remove `'. - -2008-01-05 Jeroen Dekkers - - * include/grub/normal.h: Include . - -2008-01-05 Robert Millan - - * util/i386/pc/grub-setup.c (usage): Replace obsolete `(hd0,0)' in - usage example with `(hd0,1)'. - Reported by Samuel Thibault. - -2008-01-05 Robert Millan - - * kern/i386/loader.S (grub_linux_is_bzimage): New variable. - (grub_linux_boot_zimage): Rename to ... - (grub_linux_boot): ... this. - (grub_linux_boot_bzimage): Merge with `grub_linux_boot_zimage'. - (grub_linux_boot_zimage): Conditionalize zImage copy. - - * include/grub/i386/loader.h (grub_linux_is_bzimage): Add prototype. - (grub_linux_boot_bzimage): Remove prototype. - (grub_linux_boot_zimage): Rename to ... - (grub_linux_boot): ... this. - - * loader/i386/pc/linux.c (big_linux): Replace with `grub_linux_is_bzimage'. - (grub_linux_boot): Remove function. - -2008-01-05 Robert Millan - - * include/grub/normal.h (grub_env_write_color_normal): New prototype. - (grub_env_write_color_highlight): Likewise. - (grub_wait_after_message): Likewise. - - * normal/color.c: New file. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `normal/color.c'. - (normal_mod_DEPENDENCIES): Likewise. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Add `normal/color.c'. - (normal_mod_DEPENDENCIES): Likewise. - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Add `normal/color.c'. - (normal_mod_DEPENDENCIES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add `normal/color.c'. - (normal_mod_DEPENDENCIES): Likewise. - - * normal/menu_entry.c (run): Rely on grub_wait_after_message() - for waiting after a message is printed. - * normal/main.c (read_config_file): Likewise. - (grub_normal_init): Register grub_env_write_color_normal() and - grub_env_write_color_highlight() hooks. Mark `color_normal' and - `color_highlight' variables as global. - - * normal/menu.c (grub_wait_after_message): New function. - (grub_color_menu_normal): New variable. Replaces ... - (GRUB_COLOR_MENU_NORMAL): ... this macro. - (grub_color_menu_highlight): New variable. Replaces ... - (GRUB_COLOR_MENU_HIGHLIGHT): ... this macro. - (draw_border): Set color state to `GRUB_TERM_COLOR_NORMAL' instead of - `GRUB_TERM_COLOR_STANDARD'. - (print_message): Use `grub_setcolorstate' to reload colors. Rename - `normal_code' and `highlight_code' to `old_color_normal' and - `old_color_highlight', respectively. - (grub_menu_init_page): Update colors when drawing the menu, based on - `menu_color_normal' and `menu_color_highlight' variables. - (grub_menu_run): Rely on grub_wait_after_message() for waiting after - a message is printed. - -2008-01-05 Robert Millan - - * kern/env.c (grub_env_context_open): Propagate hooks for global - variables to new context. - - * kern/main.c (grub_set_root_dev): Export `root' variable. - -2008-01-05 Robert Millan - - * util/biosdisk.c (get_os_disk): Check for devfs-style IDE and SCSI - discs unconditionally, since udev and others have options to provide - them. - -2008-01-05 Robert Millan - - * normal/completion.c (iterate_dir): Skip `.' and `..' directories. - -2008-01-04 Christian Franke - - * kern/i386/pc/init.c (grub_machine_init): Fix evaluation - of eisa_mmap. - -2008-01-03 Pavel Roskin - - * kern/i386/linuxbios/init.c: Put "void" to all function - declarations with no arguments. - * kern/powerpc/ieee1275/init.c: Likewise. - * term/i386/pc/at_keyboard.c: Likewise. - * term/i386/pc/vga_text.c: Likewise. - * util/grub-mkdevicemap.c: Likewise. - -2008-01-02 Robert Millan - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf32): Improve error - message when loaded image is out of bounds. - (grub_multiboot_load_elf64): Likewise. - -2008-01-02 Pavel Roskin - - * util/grub.d/10_linux.in: Try version without ".old" when - looking for initrd. It's better to use initrd from the newer - kernel of the same version than no initrd at all. - -2008-01-01 Robert Millan - - * util/biosdisk.c (get_os_disk): Fix check for IDE or SCSI discs. - -2008-01-01 Vesa Jaaskelainen - - * include/grub/video.h: Added grub_video_unmap_color and - grub_video_get_active_render_target. - (grub_video_adapter): Added unmap_color and get_active_render_target. - - * video/video.c: Added grub_video_unmap_color and - grub_video_get_active_render_target. - (grub_video_get_info): Changed method to accept NULL pointer as an - argument to allow detection of active video adapter. - - * video/i386/pc/vbe.c: Renamed grub_video_vbe_unmap_color as - grub_video_vbe_unmap_color_int. - Added grub_video_vbe_unmap_color and - grub_video_vbe_get_active_render_target. - (grub_video_vbe_adapter): Added unmap_color and - get_active_render_target. - - * video/i386/pc/vbeblit.c: Replaced grub_video_vbe_unmap_color usage - with grub_video_vbe_unmap_color_int. - - * term/gfxterm.c (DEFAULT_STANDARD_COLOR): Added. - (DEFAULT_NORMAL_COLOR): Likewise. - (DEFAULT_HIGHLIGHT_COLOR) Likewise. - (DEFAULT_FG_COLOR): Removed. - (DEFAULT_BG_COLOR): Likewise. - (DEFAULT_CURSOR_COLOR): Changed value. - (grub_virtual_screen): Added standard_color_setting, - normal_color_setting, highlight_color_setting and term_color. - (grub_virtual_screen): Removed fg_color_setting and bg_color_setting. - (bitmap_width): Added. - (bitmap_height): Likewise. - (bitmap): Likewise. - (set_term_color): Likewise. - (grub_virtual_screen_setup): Changed to use new terminal coloring - settings. - (grub_gfxterm_init): Added init for bitmap. - (grub_gfxterm_fini): Added destroy for bitmap. - (redraw_screen_rect): Updated to use background bitmap and new - terminal coloring. - (scroll_up): Added optimization for case when there is no bitmap. - (grub_gfxterm_cls): Fixed to use correct background color. - (grub_virtual_screen_setcolorstate): Changed to use new terminal - coloring. - (grub_virtual_screen_setcolor): Likewise. - (grub_virtual_screen_getcolor): Added. - (grub_gfxterm_background_image_cmd): Likewise. - (grub_video_term): Added setcolor and getcolor. - (MOD_INIT): Added registration of background_image command. - (MOD_TERM): Added unregistration for background_image command. - -2007-12-30 Pavel Roskin - - * loader/multiboot_loader.c: Fix multiboot command - unregistration. Fix all typos in the word "multiboot". - -2007-12-29 Pavel Roskin - - * util/grub.d/10_linux.in: Refactor search for initrd. Add - support for initrd names used in Fedora. - -2007-12-26 Bean - - * conf/common.rmk (pkgdata_MODULES): Add cpio.mod. - (cpio_mod_SOURCES): New variable. - (cpio_mod_CFLAGS): Likewise. - (cpio_mod_LDFLAGS): Likewise. - - * fs/cpio.c: New file. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add cpio.c. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - -2007-12-25 Robert Millan - - * include/grub/term.h (struct grub_term): Add `getcolor' function. - (grub_getcolor): New function. - - * kern/term.c (grub_getcolor): New function. - * normal/menu.c (GRUB_COLOR_MENU_NORMAL): New macro. - (GRUB_COLOR_MENU_HIGHLIGHT): New macro. - (print_entry): Set normal and highlight colors to - `GRUB_COLOR_MENU_NORMAL' and `GRUB_COLOR_MENU_HIGHLIGHT', - respectively, before printing and restore them to old - values afterwards. - (grub_menu_init_page): Likewise. Fill an additional colored space - that would otherwise be left blank. - - * term/efi/console.c (grub_console_getcolor): New function. - (struct grub_console_term.getcolor): New variable. - * term/i386/pc/console.c (grub_console_getcolor): New function. - (struct grub_console_term.getcolor): New variable. - * term/ieee1275/ofconsole.c (grub_ofconsole_getcolor): New function. - (struct grub_console_term.getcolor): New variable. - - * term/i386/pc/serial.c (grub_serial_setcolor): Remove function. - (struct grub_console_term.setcolor): Remove variable. - * term/i386/pc/vesafb.c (grub_virtual_screen_setcolor): Remove function. - (struct grub_console_term.setcolor): Remove variable. - * term/i386/pc/vga.c (grub_vga_setcolor): Remove function. - (struct grub_console_term.setcolor): Remove variable. - * term/gfxterm.c (grub_virtual_screen_setcolor): Remove function. - (struct grub_console_term.setcolor): Remove variable. - -2007-12-25 Robert Millan - - * configure.ac: Search for possible unifont.hex locations, and - define UNIFONT_HEX if found. - - * Makefile.in (UNIFONT_HEX): Define variable. - (DATA): Rename to ... - (PKGLIB): ... this. Update all users. - (PKGDATA): New variable. - (pkgdata_IMAGES): Rename to ... - (pkglib_IMAGES): ... this. Update all users. - (pkgdata_MODULES): Rename to ... - (pkglib_MODULES): ... this. Update all users. - (pkgdata_PROGRAMS): Rename to ... - (pkglib_PROGRAMS): ... this. Update all users. - (pkgdata_DATA): Rename to ... - (pkglib_DATA): ... this. Update all users. - (CLEANFILES): Redefine to `$(pkglib_DATA) $(pkgdata_DATA)'. - (unicode.pff, ascii.pff): New rules. - (all-local): Add `$(PKGDATA)' dependency. - (install-local): Process `$(PKGDATA)'. - - * util/update-grub_lib.in (font_path): Search for *.pff files in - a few more locations, including `${pkgdata}'. - -2007-12-23 Robert Millan - - Patch from Bean : - * disk/loopback.c (grub_loopback_read): Add missing bit shift to - `size'. - -2007-12-21 Bean - - * conf/common.rmk (pkgdata_MODULES): Add ntfscomp.mod. - (ntfscomp_mod_SOURCES): New variable. - (ntfscomp_mod_CFLAGS): Likewise. - (ntfscomp_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/ntfscomp.c. - (grub_probe_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - - * conf/i386-efi.rmk (grub_probe_SOURCES): Add fs/ntfscomp.c. - (grub_emu_SOURCES): Likewise. - - * conf/i386-linuxbios.rmk (grub_probe_SOURCES): Add fs/ntfscomp.c. - (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_probe_SOURCES): Add fs/ntfscomp.c. - (grub_emu_SOURCES): Likewise. - - * fs/ntfs.c (grub_ntfscomp_func): New variable. - (read_run_list): Renamed to grub_ntfs_read_run_list. - (decomp_nextvcn): Moved to ntfscomp.c. - (decomp_getch): Likewise. - (decomp_get16): Likewise. - (decomp_block): Likewise. - (read_block): Likewise. - (read_data): Partially moved to ntfscomp.c. - (fixup): Change unsigned to grub_uint16_t. - (read_mft): Change unsigned long to grub_uint32_t. - (read_attr): Likewise. - (read_data): Likewise. - (read_run_data): Likewise. - (read_run_list): Likewise. - (read_mft): Likewise. - - * fs/ntfscomp.c: New file. - - * include/grub/ntfs.h: New file. - -2007-12-16 Robert Millan - - * util/grub-mkdevicemap.c (make_device_map): Iterate up to 20 for - IDE disk check, since Linux is known to support 20 IDE disks. - Reported by Colin Watson. - -2007-12-15 Bean - - * conf/i386-pc.rmk (pkgdata_IMAGES): Add lnxboot.img. - (lnxboot_img_SOURCES): New variable. - (lnxboot_img_ASFLAGS): Likewise. - (lnxboot_img_LDFLAGS): Likewise. - - * boot/i386/pc/lnxboot.S: New file. - -2007-11-24 Pavel Roskin - - * configure.ac: Test if '--build-id=none' is supported by the - linker. If yes, add it to TARGET_LDFLAGS. Build ID causes - objcopy to generate incorrect binary files (binutils - 2.17.50.0.18-1 as shipped by Fedora 8). - * aclocal.m4 (grub_PROG_OBJCOPY_ABSOLUTE): Use LDFLAGS when - linking, so that build ID doesn't break the test. - -2007-11-24 Pavel Roskin - - * include/grub/i386/time.h: use "void" in the argument list - of grub_cpu_idle(). - * include/grub/powerpc/time.h: Likewise. - * include/grub/sparc64/time.h: Likewise. - -2007-11-18 Christian Franke - - * util/console.c (grub_ncurses_getkey): Change curses KEY_* mapping, - now return control chars instead of GRUB_CONSOLE_KEY_* constants. - This fixes the problem that function keys did not work in grub-emu. - -2007-11-18 Christian Franke - - * disk/host.c (grub_host_open): Remove attribute unused from - name parameter. Add check for "host". This fixes the problem - that grub-emu does not find partitions. - -2007-11-18 Christian Franke - - * util/hostfs.c (is_dir): New function. - (grub_hostfs_dir): Handle missing dirent.d_type case. - (grub_hostfs_read): Add missing fseek(). - (grub_hostfs_label): Clear label pointer. This fixes a crash - of grub-emu on "ls (host)". - -2007-11-18 Christian Franke - - * include/grub/i386/pc/init.h (struct grub_machine_mmap_entry): - Add attribute packed, gcc 3.4.4 on Cygwin aligns this - to 64 bit boundary by default. - -2007-11-18 Bean - - * conf/common.rmk (pkgdata_MODULES): Add hexdump.mod. - (hexdump_mod_SOURCES): New variable. - (hexdump_mod_CFLAGS): Likewise. - (hexdump_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add command/hexdump.c. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Add command/hexdump.c. - - * conf/i386-linuxbios.rmk (grub_emu_SOURCES): Add command/hexdump.c. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add command/hexdump.c. - - * include/grub/hexdump.h: New file. - - * commands/hexdump.c: New file. - -2007-11-10 Robert Millan - - * commands/i386/pc/play.c (beep_off): Switch order of arguments - in grub_outb() calls. - (beep_on): Likewise. - -2007-11-10 Christian Franke - - * normal/menu.c (run_menu): Check for empty menu to avoid crash. - (grub_menu_run): Likewise. - -2007-11-10 Robert Millan - - * include/grub/i386/efi/machine.h: New file. - * include/grub/i386/linuxbios/machine.h: Likewise. - * include/grub/i386/pc/machine.h: Likewise. - * include/grub/powerpc/ieee1275/machine.h: Likewise. - * include/grub/sparc64/ieee1275/machine.h: Likewise. - - * term/i386/pc/serial.c: Include . - (serial_hw_io_addr): New variable. - (serial_hw_get_port): Obtain port address from `serial_hw_io_addr' - instead of `(unsigned short *) 0x400'. - -2007-11-10 Bean - - * fs/ntfs.c (read_block): Fix a bug caused by adjacent blocks. - -2007-11-10 Vesa Jaaskelainen - - * conf/i386-pc.rmk (pkgdata_MODULES): Added vga.mod. - (vga_mod_SOURCES): Added. - (vga_mod_CFLAGS): Likewise. - (vga_mod_LDFLAGS): Likewise. - - * term/i386/pc/vga.c (get_map_mask): Switch order of arguments in - grub_outb() calls. - (set_map_mask): Likewise. - (set_read_map): Likewise. - (set_read_address): Likewise. - (vga_font): Removed variable. - (get_vga_glyph): Removed function. - (invalidate_char): Likewise. - (write_char): Changed to use grub_font_get_glyph() for font - information. - (grub_vga_putchar): Likewise. - (grub_vga_getcharwidth): Likewise. - -2007-11-10 Vesa Jaaskelainen - - * conf/i386-pc.rmk (boot_img_LDFLAGS): Use COMMON_LDFLAGS for target - flags. - (pxeboot_img_LDFLAGS): Likewise. - (diskboot_img_LDFLAGS): Likewise. - (kernel_img_LDFLAGS): Likewise. - -2007-11-06 Robert Millan - - * term/i386/pc/serial.c (serial_hw_put): Switch order of arguments - in grub_outb() calls. - (serial_hw_init): Likewise. - -2007-11-05 Robert Millan - - * util/update-grub.in: Allow files in ${update_grub_dir} to contain - spaces. Skip non-regular files. - -2007-11-05 Robert Millan - - * kern/disk.c (grub_disk_firmware_fini) - (grub_disk_firmware_is_tainted): New variables. - - * include/grub/disk.h (grub_disk_firmware_fini) - (grub_disk_firmware_is_tainted): Likewise. - - * disk/i386/pc/biosdisk.c (GRUB_MOD_FINI(biosdisk)): Moved from here ... - (grub_disk_biosdisk_fini): ... to here. - (GRUB_MOD_FINI(biosdisk)): Implement using grub_disk_biosdisk_fini(). - (GRUB_MOD_INIT(biosdisk)): Abort when `grub_disk_firmware_is_tainted' - is set. Register grub_disk_biosdisk_fini() in - `grub_disk_firmware_fini'. - - * disk/ata.c: Remove `'. - (GRUB_MOD_INIT(ata)): Remove grub_biosdisk_fini() call. - Use `grub_disk_firmware_is_tainted' and `grub_disk_firmware_fini' - to finish existing firmware disk interface. - - * conf/i386-linuxbios.rmk (pkgdata_MODULES): Add `ata.mod'. - (ata_mod_SOURCES): New variable. - (ata_mod_CFLAGS): Likewise. - (ata_mod_LDFLAGS): Likewise. - -2007-11-05 Robert Millan - - * disk/ata.c: Remove `'. Include `'. - (grub_ata_wait): Reimplement using grub_millisleep(). - - * include/grub/misc.h (grub_div_roundup): Fix parenthesization. - * include/grub/i386/time.h (grub_cpu_idle): Disable `hlt' instruction. - -2007-11-03 Marco Gerards - - * term/i386/pc/vga_text.c: Include . - (CRTC_ADDR_PORT): New macro. - (CRTC_DATA_PORT): Likewise. - (CRTC_CURSOR): Likewise. - (CRTC_CURSOR_ADDR_HIGH): Likewise. - (CRTC_CURSOR_ADDR_LOW): Likewise. - (update_cursor): New function. - (grub_console_real_putchar): Call `update_cursor'. - (grub_console_gotoxy): Likewise. - (grub_console_cls): Set the default color when clearing the - screen. - (grub_console_setcursor): Implemented. - -2007-11-03 Marco Gerards - - * disk/ata.c (grub_ata_pio_read): Don't wait for the command to - become activate. - (grub_ata_pio_write): Likewise. - - (grub_atapi_identify): Wait after issuing an ATA command. - (grub_atapi_packet): Likewise. - (grub_ata_identify): Likewise. - (grub_ata_readwrite): Likewise. - -2007-11-03 Marco Gerards - - * disk/ata.c (grub_ata_pio_read): Detect and return the error code. - (grub_ata_pio_write): Likewise. - (grub_ata_readwrite): Use `grub_error', instead of - returning `grub_errno'. - -2007-11-03 Marco Gerards - - * disk/ata.c (grub_ata_readwrite): Call grub_ata_pio_read and - grub_ata_pio_write once for every single sector, instead of for - multiple sectors. - -2007-10-31 Robert Millan - - * configure.ac: Add `i386-linuxbios' to the list of supported targets. - - * conf/i386-linuxbios.rmk: New file. - - * kern/i386/pc/hardware.c: Likewise. - * term/i386/pc/at_keyboard.c: Likewise. - * term/i386/pc/vga_text.c: Likewise. - - * include/grub/i386/linuxbios/boot.h: Likewise. - * include/grub/i386/linuxbios/console.h: Likewise. - * include/grub/i386/linuxbios/init.h: Likewise. - * include/grub/i386/linuxbios/kernel.h: Likewise. - * include/grub/i386/linuxbios/loader.h: Likewise. - * include/grub/i386/linuxbios/memory.h: Likewise. - * include/grub/i386/linuxbios/serial.h: Likewise. - * include/grub/i386/linuxbios/time.h: Likewise. - - * kern/i386/linuxbios/init.c: Likewise. - * kern/i386/linuxbios/startup.S: Likewise. - * kern/i386/linuxbios/table.c: Likewise. - -2007-10-31 Marco Gerards - - * conf/i386-pc.rmk (pkgdata_MODULES): Add `ata.mod'. - (ata_mod_SOURCES): New variable. - (ata_mod_CFLAGS): Likewise. - (ata_mod_LDFLAGS): Likewise. - - * disk/ata.c: New file. - - * include/grub/disk.h (grub_disk_dev_id): Add - `GRUB_DISK_DEV_ATA_ID'. - -2007-10-31 Robert Millan - - * include/grub/i386/pc/init.h (grub_lower_mem): Moved from here ... - * include/grub/i386/pc/memory.h (grub_lower_mem): ... to here. - - * include/grub/i386/pc/init.h (grub_upper_mem): Moved from here ... - * include/grub/i386/pc/memory.h (grub_upper_mem): ... to here. - - * include/grub/i386/pc/memory.h: Include `' and - `'. - - * loader/i386/pc/multiboot.c: Include `'. - -2007-10-27 Robert Millan - - * include/grub/types.h (ULONG_MAX): Define macro. - -2007-10-22 Robert Millan - - * kern/i386/pc/startup.S: Remove `"kern/i386/realmode.S"'. Include - `"../realmode.S"'. - Remove `"kern/i386/loader.S"'. Include `"../loader.S"'. - -2007-10-22 Robert Millan - - * conf/i386-pc.rmk (kernel_img_SOURCES): Remove `disk/i386/pc/biosdisk.c'. - (pkgdata_MODULES): Add `biosdisk.mod'. - (biosdisk_mod_SOURCES, biosdisk_mod_CFLAGS, biosdisk_mod_LDFLAGS): New - variables. - - * disk/i386/pc/biosdisk.c: Include `'. - (grub_biosdisk_init): Replace with ... - (GRUB_MOD_INIT(biosdisk)): ... this. - (grub_biosdisk_fini): Replace with ... - (GRUB_MOD_FINI(biosdisk)): ... this. - - * kern/i386/pc/init.c: Remove `'. - (grub_machine_init): Remove call to grub_biosdisk_init(). - (grub_machine_fini): Remove call to grub_machine_fini(). - - * util/i386/pc/grub-install.in (modules): Add `biosdisk'. - -2007-10-22 Robert Millan - - * include/grub/time.h: New file. - * include/grub/i386/time.h: Likewise. - * include/grub/powerpc/time.h: Likewise. - * include/grub/sparc64/time.h: Likewise. - - * include/grub/i386/pc/time.h (KERNEL_TIME_HEADER): Rename all - instances to ... - (KERNEL_MACHINE_TIME_HEADER): ... this. - * include/grub/powerpc/ieee1275/time.h (KERNEL_TIME_HEADER): Rename all - instances to ... - (KERNEL_MACHINE_TIME_HEADER): ... this. - * include/grub/sparc64/ieee1275/time.h (KERNEL_TIME_HEADER): Rename all - instances to ... - (KERNEL_MACHINE_TIME_HEADER): ... this. - - * kern/i386/efi/init.c: Include `'. - (grub_millisleep): New function. - * kern/i386/pc/init.c: Include `'. - (grub_millisleep): New function. - * kern/powerpc/ieee1275/init.c: Include `'. - Remove `grub/machine/time.h' include. - (grub_millisleep): New function. - * kern/sparc64/ieee1275/init.c: Include `'. - Remove `grub/machine/time.h' include. - (grub_millisleep): New function. - - * include/grub/misc.h (grub_div_roundup): New function. - - * kern/misc.c: Include `'. - (grub_millisleep_generic): New function. - - * conf/i386-efi.rmk (kernel_mod_HEADERS): Remove `i386/efi/time.h'. - Add `time.h'. - * conf/i386-pc.rmk (kernel_img_HEADERS): Remove `machine/time.h'. - Add `time.h'. - * conf/powerpc-ieee1275.rmk (kernel_elf_HEADERS): Remove - `machine/time.h'. Add `time.h'. - * conf/sparc64-ieee1275.rmk (kernel_elf_HEADERS): Likewise. - -2007-10-21 Robert Millan - - * include/grub/misc.h (grub_max): New function. - -2007-10-21 Robert Millan - - * util/misc.c (grub_util_info): Call fflush() before returning. - -2007-10-20 Robert Millan - - * genmk.rb (Image): Copy `extra_flags' from here ... - (PModule): ... to here. Use it in `#{obj}: #{src}' rule. - - * commands/i386/cpuid.c (grub_cmd_cpuid): Add __attribute__ ((unused)) - to `argc' and `args' arguments. - -2007-10-17 Robert Millan - - * kern/i386/loader.S: New file. - - * kern/i386/pc/startup.S (grub_linux_prot_size): Moved from here ... - * kern/i386/loader.S (grub_linux_prot_size)... to here. - * kern/i386/pc/startup.S (grub_linux_tmp_addr): Moved from here ... - * kern/i386/loader.S (grub_linux_tmp_addr)... to here. - * kern/i386/pc/startup.S (grub_linux_real_addr): Moved from here ... - * kern/i386/loader.S (grub_linux_real_addr)... to here. - * kern/i386/pc/startup.S (grub_linux_boot_zimage): Moved from here ... - * kern/i386/loader.S (grub_linux_boot_zimage)... to here. - * kern/i386/pc/startup.S (grub_linux_boot_bzimage): Moved from here ... - * kern/i386/loader.S (grub_linux_boot_bzimage)... to here. - * kern/i386/pc/startup.S (grub_multiboot_real_boot): Moved from here ... - * kern/i386/loader.S (grub_multiboot_real_boot)... to here. - * kern/i386/pc/startup.S (grub_multiboot2_real_boot): Moved from here ... - * kern/i386/loader.S (grub_multiboot2_real_boot)... to here. - - * kern/i386/realmode.S: New file. - - * kern/i386/pc/startup.S (protstack): Moved from here ... - * kern/i386/realmode.S (protstack)... to here. - * kern/i386/pc/startup.S (gdt): Moved from here ... - * kern/i386/realmode.S (gdt)... to here. - * kern/i386/pc/startup.S (prot_to_real): Moved from here ... - * kern/i386/realmode.S (prot_to_real)... to here. - - * kern/i386/pc/startup.S: Include `kern/i386/loader.S' and - `kern/i386/realmode.S'. - -2007-10-17 Robert Millan - - * include/grub/i386/loader.h: New file. - - * include/grub/i386/pc/loader.h (grub_linux_prot_size) - (grub_linux_tmp_addr, grub_linux_real_addr, grub_os_area_addr) - (grub_os_area_size, grub_linux_boot_zimage, grub_linux_boot_bzimage) - (grub_multiboot_real_boot, grub_multiboot2_real_boot) - (grub_rescue_cmd_linux, grub_rescue_cmd_initrd): Moved from here ... - * include/grub/i386/loader.h (grub_linux_prot_size) - (grub_linux_tmp_addr, grub_linux_real_addr, grub_os_area_addr) - (grub_os_area_size, grub_linux_boot_zimage, grub_linux_boot_bzimage) - (grub_multiboot_real_boot, grub_multiboot2_real_boot) - (grub_rescue_cmd_linux, grub_rescue_cmd_initrd): ... to here. - - * include/grub/i386/pc/loader.h: Include `grub/cpu/loader.h'. - -2007-10-15 Robert Millan - - * normal/misc.c (grub_normal_print_device_info): Do not probe for - filesystem when dev->disk is unset. - Do probe for filesystem even when dev->disk->has_partitions is set. - In case a filesystem is found, always report it. - In case it isn't, if dev->disk->has_partitions is set, report that - a partition table was found instead of reporting that no filesystem - could be identified. - -2007-10-12 Robert Millan - - * conf/powerpc-ieee1275.rmk (grub_mkimage_SOURCES): Replace reference - to util/powerpc/ieee1275/grub-mkimage.c with util/elf/grub-mkimage.c. - - * include/grub/types.h (grub_host_to_target16): New macro. - (grub_host_to_target32): Likewise. - (grub_host_to_target64): Likewise. - (grub_target_to_host16): Likewise. - (grub_target_to_host32): Likewise. - (grub_target_to_host64): Likewise. - - * include/grub/powerpc/ieee1275/kernel.h (GRUB_IEEE1275_MOD_ALIGN): - Renamed from to ... - (GRUB_MOD_ALIGN): ...this. Update all users. - - * util/elf/grub-mkimage.c (load_note): Replace grub_cpu_to_be32 with - grub_host_to_target32. - Replace grub_be_to_cpu32 with grub_target_to_host32. - (load_modules): Likewise. - (add_segments): Replace grub_be_to_cpu16 with grub_target_to_host16. - Replace grub_be_to_cpu32 with grub_target_to_host32. - Replace grub_cpu_to_be16 with grub_host_to_target16. - Replace grub_cpu_to_be32 grub_host_to_target32. - -2007-10-12 Robert Millan - - * util/powerpc/ieee1275/grub-mkimage.c: Moved to ... - * util/elf/grub-mkimage.c: ... here. - - * DISTLIST: Add `util/elf/grub-mkimage.c'. Remove - `util/powerpc/ieee1275/grub-mkimage.c'. - -2007-10-07 Robert Millan - - * kern/powerpc/ieee1275/init.c: Rename HEAP_LIMIT to HEAP_MAX_ADDR, - and make it easier to figure out. - Add HEAP_MIN_SIZE and HEAP_MAX_ADDR definitions. - (grub_claim_heap): Use HEAP_MAX_ADDR rather than taking a parameter. - Do not avoid claiming a region above HEAP_MAX_ADDR if that would - leave us with less than HEAP_MIN_SIZE total heap. - Avoid our total amount of heap to surpass HEAP_MAX_SIZE. - -2007-10-03 Robert Millan - - * include/grub/i386/io.h: New file. - * commands/i386/pc/play.c (inb): Removed. - (outb): Removed. - Include grub/cpu/io.h. Replace inb() with grub_inb() and outb() - with grub_outb(). - * term/i386/pc/serial.c (inb): Removed. - (outb): Removed. - Include grub/cpu/io.h. Replace inb() with grub_inb() and outb() - with grub_outb(). - * term/i386/pc/vga.c (inb): Removed. - (outb): Removed. - Include grub/cpu/io.h. Replace inb() with grub_inb() and outb() - with grub_outb(). - -2007-10-02 Robert Millan - - * conf/i386-efi.rmk (grub_emu_SOURCES): Add util/hostfs.c. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - Reported by Marcin Kurek. - -2007-09-07 Robert Millan - - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_test_flag): Detect - SmartFirmware version updates (as released by Sven Luther), and avoid - setting GRUB_IEEE1275_FLAG_NO_PARTITION_0 or - GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS unless the running version is - known broken. - -2007-09-03 Yoshinori K. Okuji - - From Hitoshi Ozeki: - * kern/i386/pc/init.c (compact_mem_regions): Decrease NUM_REGIONS - when merging two regions. - -2007-09-03 Yoshinori K. Okuji - - * kern/rescue.c (grub_enter_rescue_mode): Free ARGS. - * normal/completion.c (grub_normal_do_completion): Likewise. - Reported by Hitoshi Ozeki. - -2007-09-03 Yoshinori K. Okuji - - Do not use devices at boot in chainloading. - - * loader/i386/pc/chainloader.c (boot_drive): New variable. - (boot_part_addr): Likewise. - (grub_chainloader_boot): Simply call grub_chainloader_real_boot - with BOOT_DRIVE and BOOT_PART_ADDR. - (grub_chainloader_cmd): Set BOOT_DRIVE and BOOT_PART_ADDR. - Reported by Hitoshi Ozeki . - -2007-08-29 Robert Millan - - Patch from Simon Peter : - * genmk.rb (Utility): Append $(#{src}_DEPENDENCIES) to #{obj} targets. - * conf/i386-pc.rmk: Replace grub-probe_DEPENDENCIES with - util/grub-probe.c_DEPENDENCIES. Replace grub-setup_DEPENDENCIES with - util/i386/pc/grub-setup.c_DEPENDENCIES. - * conf/i386-efi.rmk: Replace grub-probe_DEPENDENCIES with - util/grub-probe.c_DEPENDENCIES. - * conf/powerpc-ieee1275.rmk: Likewise. - -2007-08-28 Robert Millan - - * util/i386/get_disk_name.c: New. Implement grub_util_get_disk_name() - to tell grub-mkdevicemap how to name devices. - * util/ieee1275/get_disk_name.c: Likewise (using "ofpathname -a" - feature). - - * conf/i386-efi.rmk (grub_mkdevicemap_SOURCES): Add - util/i386/get_disk_name.c. - * conf/i386-pc.rmk (grub_mkdevicemap_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_mkdevicemap_SOURCES): Add - util/ieee1275/get_disk_name.c. - - * include/grub/util/misc.h: grub_util_get_disk_name() declaration. - - * DISTLIST: Add util/i386/get_disk_name.c and - util/ieee1275/get_disk_name.c. - - * util/grub-mkdevicemap.c: Replace device naming logic with - grub_util_get_disk_name() calls. - -2007-08-20 Robert Millan - - * normal/menu.c (run_menu): Refer to seconds as "s" not "seconds" - (so that it works for both plural and singular quantities). - -2007-08-05 Robert Millan - - * util/grub.d/10_linux.in (test_gt): Strip out vmlinu[xz]- prefix - so that [xz] isn't taken into account when determining order. - -2007-08-02 Marco Gerards - - * DISTLIST: Add `disk/host.c', `fs/ntfs.c', `include/multiboot.h', - `include/multiboot2.h', `include/grub/elfload.h', - `include/multiboot.h', `include/grub/multiboot.h', - `include/grub/multiboot_loader.h', `include/grub/multiboot2.h', - `include/grub/i386/pc/biosdisk.h', `include/grub/util/biosdisk.h', - `kern/elf.c', `loader/multiboot_loader.c', - `loader/multiboot_loader_normal.c', `loader/multiboot2.c', - `loader/i386/pc/multiboot2.c', - `loader/powerpc/ieee1275/multiboot2.c', `util/hostfs.c' and - `util/i386/pc/grub-mkrescue.in'. Remove - `include/grub/biosdisk.h', `include/grub/i386/pc/multiboot.h', - `include/grub/i386/pc/util/biosdisk.h' and - `include/grub/powerpc/ieee1275/multiboot.h'. - -2007-08-02 Bean - - * conf/common.rmk (pkgdata_MODULES): Add ntfs.mod. - (ntfs_mod_SOURCES): New variable. - (ntfs_mod_CFLAGS): Likewise. - (ntfs_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/ntfs.c. - (grub_probe_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - - * conf/i386-efi.rmk (grub_probe_SOURCES): Add fs/ntfs.c. - (grub_emu_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_probe_SOURCES): Add fs/ntfs.c. - (grub_emu_SOURCES): Likewise. - - * conf/misc.c (grub_utf16_to_utf8): Fix unicode conversion bug. - - * fs/ntfs.c: New file. - -2007-08-02 Bean - - * disk.h (grub_disk): Use NESTED_FUNC_ATTR. - - * file.h (grub_file): Likewise. - - * fshelp.h (grub_fshelp_read_file): Likewise. - - * util/i386/pc/grub-setup.c (setup): Likewise. - (save_first_sector): Likewise. - (save_blocklists): Likewise. - - * fs/affs.c (grub_affs_read_file): Likewise. - - * fs/ext2.c (grub_ext2_read_file): Likewise. - - * fs/fat.c (grub_fat_read_data): Likewise. - - * fs/fshelp.c (grub_fshelp_read_file): Likewise. - - * fs/hfs.c (grub_hfs_read_file): Likewise. - - * fs/hfsplus.c (grub_hfsplus_read_file): Likewise. - - * fs/jfs.c (grub_jfs_read_file): Likewise. - - * fs/minix.c (grub_minix_read_file): Likewise. - - * fs/sfs.c (grub_sfs_read_file): Likewise. - - * fs/ufs.c (grub_ufs_read_file): Likewise. - - * fs/xfs.c (grub_xfs_read_file): Likewise. - - * command/blocklist.c (read_blocklist): Likewise. - (print_blocklist): Likewise. - -2007-08-02 Marco Gerards - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `disk/host.c' and - `util/hostfs.c'. - - * disk/host.c: New file. - - * util/hostfs.c: Likewise. - - * fs/hfsplus.c (grub_hfsplus_mount): When reading out of disk, - return `GRUB_ERR_BAD_FS'. - * fs/sfs.c (grub_sfs_mount): Likewise. - * fs/xfs.c (grub_xfs_mount): Likewise. - - * include/grub/disk.h (enum grub_disk_dev_id): Add - `GRUB_DISK_DEVICE_HOST_ID'. - - * util/grub-emu.c (main): Initialize and de-initialize hostfs. - -2007-07-24 Jerone Young - - * conf/i386-pc.rmk: Add Multiboot loader and multiboot 2 to multiboot - modules for compilation. - * conf/powerpc-ieee1275.rmk: Likewise. - - * include/multiboot.h: Move multiboot definitions to one file. Rename - many definitions to not get grub specific. - * include/multiboot2.h: Create header with multiboot 2 definitions. - * include/grub/multiboot.h: Header for grub specific function - prototypes and definitions. - * include/grub/multiboot2.h: Likewise. - * include/grub/multiboot_loader.h: Likewise. - * include/grub/i386/pc/multiboot.h: Removed. - * include/grub/powerpc/ieee1275/multiboot.h: Removed. - - * loader/multiboot_loader.c: Created to act as a proxy for multiboot 1 - and 2 to allow for one multiboot and module commands. - * loader/multiboot2.c: Add multiboot2 functionality. - * loader/i386/pc/multiboot.c: Modify for new multiboot header location - and definition names. - * loader/i386/pc/multiboot2.c: Created to add i386 specific multiboot - 2 functions. - * loader/powerpc/ieee1275/multiboot2.c: Created to add powerpc - ieee1275 specific multiboot2 code. - - * kern/i386/pc/startup.S: Change headers and definition names for - multiboot. Add function grub_multiboot2_real_boot for multiboot 2. - -2007-07-22 Robert Millan - - * geninitheader.sh: Process file specified in first parameter rather - than hardcoding grub_modules_init.lst. - * geninit.sh: Likewise. Also, construct header name dynamically rather - than hardcoding grub_modules_init.h. - - * conf/common.rmk: Rename grub_modules_init.[ch] files associated with - grub-emu to grub_emu_init.[ch]. Add rules to build analogous - grub_probe_init.[ch] and grub_setup_init.[ch]. - - * conf/powerpc-ieee1275.rmk (grub_emu_DEPENDENCIES): Replace - grub_modules_init.h with grub_emu_init.h. - (grub_probe_DEPENDENCIES, grub_probe_SOURCES): Add new - grub_probe_init.[ch] files. - * conf/i386-efi.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - (grub_setup_DEPENDENCIES, grub_setup_SOURCES): Add new - grub_setup_init.[ch] files. - - * util/grub-emu.c: Replace grub_modules_init.h with grub_emu_init.h. - * util/grub-probe.c: Include grub_probe_init.h. Use grub_init_all() - to initialize modules rather than a list of hardcoded functions. - * util/i386/pc/grub-setup.c: Include grub_setup_init.h. Use - grub_init_all() to initialize modules rather than a list of hardcoded - functions. - -2007-07-22 Robert Millan - - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_find_options): Set - GRUB_IEEE1275_FLAG_NO_PARTITION_0 flag when running on SmartFirmware. - -2007-07-22 Robert Millan - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_flag): Add - GRUB_IEEE1275_FLAG_BROKEN_OUTPUT flag. - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_find_options): Set this - flag when running on SmartFirmware. - * term/ieee1275/ofconsole.c (grub_ofconsole_init): Avoid running - "output-device output" command when GRUB_IEEE1275_FLAG_BROKEN_OUTPUT - was set. - - * kern/powerpc/ieee1275/openfw.c (grub_ieee1275_encode_devname): - Increase partno when GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS flag is set, - rather than decreasing it. - - * util/i386/pc/grub-setup.c (setup): When embedding is required, but - there's not enough space to do it, fail in the same way as when it - can't be done because there are no partitions. - - * util/powerpc/ieee1275/grub-install.in: Improve error message shown - when nvsetenv failed. - -2007-07-22 Yoshinori K. Okuji - - * conf/i386-pc.rmk (CLEANFILES): Removed for grub-mkrescue, - because this rule is automatically generated. - (grub-mkrescue): Removed for the same reason as above. - -2007-07-22 Yoshinori K. Okuji - - Migrate to GNU General Public License Version 3. - - * COPYING: Replaced with the plain text version of GPLv3. - - * config.guess: Updated from gnulib. - * config.sub: Likewise. - - * geninit.sh: Output a GPLv3 copyright notice. - * geninitheader.sh: Likewise. - * genmodsrc.sh: Likewise. - * gensymlist.sh.in: Likewise. - - * boot/i386/pc/boot.S: Upgraded to GPLv3. - * boot/i386/pc/diskboot.S: Likewise. - * boot/i386/pc/pxeboot.S: Likewise. - * commands/blocklist.c: Likewise. - * commands/boot.c: Likewise. - * commands/cat.c: Likewise. - * commands/cmp.c: Likewise. - * commands/configfile.c: Likewise. - * commands/echo.c: Likewise. - * commands/help.c: Likewise. - * commands/ls.c: Likewise. - * commands/search.c: Likewise. - * commands/terminal.c: Likewise. - * commands/test.c: Likewise. - * commands/videotest.c: Likewise. - * commands/i386/cpuid.c: Likewise. - * commands/i386/pc/halt.c: Likewise. - * commands/i386/pc/play.c: Likewise. - * commands/i386/pc/reboot.c: Likewise. - * commands/i386/pc/vbeinfo.c: Likewise. - * commands/i386/pc/vbetest.c: Likewise. - * commands/ieee1275/halt.c: Likewise. - * commands/ieee1275/reboot.c: Likewise. - * commands/ieee1275/suspend.c: Likewise. - * disk/loopback.c: Likewise. - * disk/lvm.c: Likewise. - * disk/raid.c: Likewise. - * disk/efi/efidisk.c: Likewise. - * disk/i386/pc/biosdisk.c: Likewise. - * disk/ieee1275/ofdisk.c: Likewise. - * font/manager.c: Likewise. - * fs/affs.c: Likewise. - * fs/ext2.c: Likewise. - * fs/fat.c: Likewise. - * fs/fshelp.c: Likewise. - * fs/hfs.c: Likewise. - * fs/hfsplus.c: Likewise. - * fs/iso9660.c: Likewise. - * fs/jfs.c: Likewise. - * fs/minix.c: Likewise. - * fs/sfs.c: Likewise. - * fs/ufs.c: Likewise. - * fs/xfs.c: Likewise. - * hello/hello.c: Likewise. - * include/grub/acorn_filecore.h: Likewise. - * include/grub/arg.h: Likewise. - * include/grub/bitmap.h: Likewise. - * include/grub/boot.h: Likewise. - * include/grub/cache.h: Likewise. - * include/grub/device.h: Likewise. - * include/grub/disk.h: Likewise. - * include/grub/dl.h: Likewise. - * include/grub/elfload.h: Likewise. - * include/grub/env.h: Likewise. - * include/grub/err.h: Likewise. - * include/grub/file.h: Likewise. - * include/grub/font.h: Likewise. - * include/grub/fs.h: Likewise. - * include/grub/fshelp.h: Likewise. - * include/grub/gzio.h: Likewise. - * include/grub/hfs.h: Likewise. - * include/grub/kernel.h: Likewise. - * include/grub/loader.h: Likewise. - * include/grub/lvm.h: Likewise. - * include/grub/misc.h: Likewise. - * include/grub/mm.h: Likewise. - * include/grub/net.h: Likewise. - * include/grub/normal.h: Likewise. - * include/grub/parser.h: Likewise. - * include/grub/partition.h: Likewise. - * include/grub/pc_partition.h: Likewise. - * include/grub/raid.h: Likewise. - * include/grub/rescue.h: Likewise. - * include/grub/script.h: Likewise. - * include/grub/setjmp.h: Likewise. - * include/grub/symbol.h: Likewise. - * include/grub/term.h: Likewise. - * include/grub/terminfo.h: Likewise. - * include/grub/tparm.h: Likewise. - * include/grub/types.h: Likewise. - * include/grub/video.h: Likewise. - * include/grub/efi/api.h: Likewise. - * include/grub/efi/chainloader.h: Likewise. - * include/grub/efi/console.h: Likewise. - * include/grub/efi/console_control.h: Likewise. - * include/grub/efi/disk.h: Likewise. - * include/grub/efi/efi.h: Likewise. - * include/grub/efi/pe32.h: Likewise. - * include/grub/efi/time.h: Likewise. - * include/grub/i386/linux.h: Likewise. - * include/grub/i386/setjmp.h: Likewise. - * include/grub/i386/types.h: Likewise. - * include/grub/i386/efi/kernel.h: Likewise. - * include/grub/i386/efi/loader.h: Likewise. - * include/grub/i386/efi/time.h: Likewise. - * include/grub/i386/pc/biosdisk.h: Likewise. - * include/grub/i386/pc/boot.h: Likewise. - * include/grub/i386/pc/chainloader.h: Likewise. - * include/grub/i386/pc/console.h: Likewise. - * include/grub/i386/pc/init.h: Likewise. - * include/grub/i386/pc/kernel.h: Likewise. - * include/grub/i386/pc/loader.h: Likewise. - * include/grub/i386/pc/memory.h: Likewise. - * include/grub/i386/pc/multiboot.h: Likewise. - * include/grub/i386/pc/serial.h: Likewise. - * include/grub/i386/pc/time.h: Likewise. - * include/grub/i386/pc/vbe.h: Likewise. - * include/grub/i386/pc/vbeblit.h: Likewise. - * include/grub/i386/pc/vbefill.h: Likewise. - * include/grub/i386/pc/vbeutil.h: Likewise. - * include/grub/i386/pc/vga.h: Likewise. - * include/grub/ieee1275/ieee1275.h: Likewise. - * include/grub/ieee1275/ofdisk.h: Likewise. - * include/grub/powerpc/libgcc.h: Likewise. - * include/grub/powerpc/setjmp.h: Likewise. - * include/grub/powerpc/types.h: Likewise. - * include/grub/powerpc/ieee1275/biosdisk.h: Likewise. - * include/grub/powerpc/ieee1275/console.h: Likewise. - * include/grub/powerpc/ieee1275/ieee1275.h: Likewise. - * include/grub/powerpc/ieee1275/kernel.h: Likewise. - * include/grub/powerpc/ieee1275/loader.h: Likewise. - * include/grub/powerpc/ieee1275/multiboot.h: Likewise. - * include/grub/powerpc/ieee1275/time.h: Likewise. - * include/grub/powerpc/ieee1275/util/biosdisk.h: Likewise. - * include/grub/sparc64/libgcc.h: Likewise. - * include/grub/sparc64/setjmp.h: Likewise. - * include/grub/sparc64/types.h: Likewise. - * include/grub/sparc64/ieee1275/console.h: Likewise. - * include/grub/sparc64/ieee1275/ieee1275.h: Likewise. - * include/grub/sparc64/ieee1275/kernel.h: Likewise. - * include/grub/sparc64/ieee1275/time.h: Likewise. - * include/grub/util/biosdisk.h: Likewise. - * include/grub/util/getroot.h: Likewise. - * include/grub/util/lvm.h: Likewise. - * include/grub/util/misc.h: Likewise. - * include/grub/util/raid.h: Likewise. - * include/grub/util/resolve.h: Likewise. - * io/gzio.c: Likewise. - * kern/device.c: Likewise. - * kern/disk.c: Likewise. - * kern/dl.c: Likewise. - * kern/elf.c: Likewise. - * kern/env.c: Likewise. - * kern/err.c: Likewise. - * kern/file.c: Likewise. - * kern/fs.c: Likewise. - * kern/loader.c: Likewise. - * kern/main.c: Likewise. - * kern/misc.c: Likewise. - * kern/mm.c: Likewise. - * kern/parser.c: Likewise. - * kern/partition.c: Likewise. - * kern/rescue.c: Likewise. - * kern/term.c: Likewise. - * kern/efi/efi.c: Likewise. - * kern/efi/init.c: Likewise. - * kern/efi/mm.c: Likewise. - * kern/i386/dl.c: Likewise. - * kern/i386/efi/init.c: Likewise. - * kern/i386/efi/startup.S: Likewise. - * kern/i386/pc/init.c: Likewise. - * kern/i386/pc/lzo1x.S: Likewise. - * kern/i386/pc/startup.S: Likewise. - * kern/ieee1275/ieee1275.c: Likewise. - * kern/powerpc/cache.S: Likewise. - * kern/powerpc/dl.c: Likewise. - * kern/powerpc/ieee1275/cmain.c: Likewise. - * kern/powerpc/ieee1275/crt0.S: Likewise. - * kern/powerpc/ieee1275/init.c: Likewise. - * kern/powerpc/ieee1275/openfw.c: Likewise. - * kern/sparc64/cache.S: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/sparc64/ieee1275/init.c: Likewise. - * kern/sparc64/ieee1275/openfw.c: Likewise. - * loader/efi/chainloader.c: Likewise. - * loader/efi/chainloader_normal.c: Likewise. - * loader/i386/efi/linux.c: Likewise. - * loader/i386/efi/linux_normal.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/pc/chainloader_normal.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/i386/pc/linux_normal.c: Likewise. - * loader/i386/pc/multiboot.c: Likewise. - * loader/i386/pc/multiboot_normal.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Likewise. - * loader/powerpc/ieee1275/linux_normal.c: Likewise. - * normal/arg.c: Likewise. - * normal/cmdline.c: Likewise. - * normal/command.c: Likewise. - * normal/completion.c: Likewise. - * normal/execute.c: Likewise. - * normal/function.c: Likewise. - * normal/lexer.c: Likewise. - * normal/main.c: Likewise. - * normal/menu.c: Likewise. - * normal/menu_entry.c: Likewise. - * normal/misc.c: Likewise. - * normal/parser.y: Likewise. - * normal/script.c: Likewise. - * normal/i386/setjmp.S: Likewise. - * normal/powerpc/setjmp.S: Likewise. - * normal/sparc64/setjmp.S: Likewise. - * partmap/acorn.c: Likewise. - * partmap/amiga.c: Likewise. - * partmap/apple.c: Likewise. - * partmap/gpt.c: Likewise. - * partmap/pc.c: Likewise. - * partmap/sun.c: Likewise. - * term/gfxterm.c: Likewise. - * term/terminfo.c: Likewise. - * term/efi/console.c: Likewise. - * term/i386/pc/console.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * term/i386/pc/vga.c: Likewise. - * term/ieee1275/ofconsole.c: Likewise. - * util/biosdisk.c: Likewise. - * util/console.c: Likewise. - * util/genmoddep.c: Likewise. - * util/getroot.c: Likewise. - * util/grub-emu.c: Likewise. - * util/grub-mkdevicemap.c: Likewise. - * util/grub-probe.c: Likewise. - * util/lvm.c: Likewise. - * util/misc.c: Likewise. - * util/raid.c: Likewise. - * util/resolve.c: Likewise. - * util/update-grub.in: Likewise. - * util/update-grub_lib.in: Likewise. - * util/grub.d/00_header.in: Likewise. - * util/grub.d/10_hurd.in: Likewise. - * util/grub.d/10_linux.in: Likewise. - * util/i386/efi/grub-install.in: Likewise. - * util/i386/efi/grub-mkimage.c: Likewise. - * util/i386/pc/grub-install.in: Likewise. - * util/i386/pc/grub-mkimage.c: Likewise. - * util/i386/pc/grub-mkrescue.in: Likewise. - * util/i386/pc/grub-setup.c: Likewise. - * util/i386/pc/misc.c: Likewise. - * util/powerpc/ieee1275/grub-install.in: Likewise. - * util/powerpc/ieee1275/grub-mkimage.c: Likewise. - * util/powerpc/ieee1275/misc.c: Likewise. - * video/bitmap.c: Likewise. - * video/video.c: Likewise. - * video/i386/pc/vbe.c: Likewise. - * video/i386/pc/vbeblit.c: Likewise. - * video/i386/pc/vbefill.c: Likewise. - * video/i386/pc/vbeutil.c: Likewise. - * video/readers/tga.c: Likewise. - -2007-07-02 Robert Millan - - * conf/i386-efi.rmk: Replace obsolete reference to - util/i386/pc/biosdisk.c with util/biosdisk.c, and util/i386/pc/getroot.c - with util/getroot.c. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk: Likewise. - - * util/grub-emu.c (main): Fix unchecked pointer handling. - -2007-07-02 Robert Millan - - * util/i386/efi/grub-install.in: Allow `grub_probe --target=partmap' - invocation to fail, in order to support partition-less media. - - * util/i386/pc/grub-install.in: Likewise. - - * util/powerpc/ieee1275/grub-install.in: Use grub-probe to determine - which fs or partmap modules are needed (akin to its sister scripts). - - Also use grub-probe to get rid of unportable /proc/mounts check. - - Print the same informational message that the other scripts do, before - exiting. - -2007-06-23 Robert Millan - - * util/update-grub_lib.in (font_path): New function. Determine whether - a font file can be found and, if so, echo the GRUB path to it. - - * util/update-grub.in: Handle multiple terminals depending on user - input, platform availability and font file presence. Propagate - variables of our findings to /etc/grub.d/ children. - - * util/grub.d/00_header.in: Handle multiple terminals, based on - environment setup by update-grub. - -2007-06-23 Robert Millan - - * conf/i386-pc.rmk (pkgdata_MODULES): Add serial.mod. - -2007-06-21 Robert Millan - - * include/grub/i386/pc/kernel.h: Define GRUB_KERNEL_MACHINE_DATA_END to - indicate end of data section in kernel image. - * include/grub/i386/efi/kernel.h: Define GRUB_KERNEL_MACHINE_PREFIX and - GRUB_KERNEL_MACHINE_DATA_END. - - * kern/i386/pc/startup.S: Do not initialize grub_prefix, only reserve - space for it. - * kern/i386/efi/startup.S: Likewise. - - * util/i386/pc/grub-mkimage.c: Initialize grub_prefix to /boot/grub - during image generation. Implement --prefix option to override this - patch. - * util/i386/efi/grub-mkimage.c: Likewise. - - * util/update-grub_lib.in (convert_system_path_to_grub_path): Split - code to make path relative to its root into a separate function. - - * util/i386/pc/grub-install.in: Use newly provided - make_system_path_relative_to_its_root() to convert ${grubdir}, then - pass the result to grub-install --prefix. - -2007-06-13 Robert Millan - - * include/grub/util/misc.h: Define DEFAULT_DIRECTORY and - DEFAULT_DEVICE_MAP. - * util/grub-emu.c: Use above definitions from misc.h instead of - defining them. - * util/grub-mkdevicemap.c: Likewise. - * util/i386/pc/grub-setup.c: Likewise. - * util/grub-probe.c: Likewise. - (probe): Abort with grub_util_error() when either - grub_guess_root_device or grub_util_get_grub_dev fails. - -2007-06-12 Robert Millan - - * normal/command.c (grub_command_execute): Use NULL rather than 0 for - "pager" assignment. - * util/biosdisk.c (grub_util_biosdisk_get_grub_dev): Likewise for - "pcdata". - * util/grub-probe.c (probe): Likewise for "drive_name". - -2007-06-11 Robert Millan - - * util/i386/pc/grub-mkrescue.in: Pad both floppy images with zeroes, - not just the cdrom one. - -2007-06-11 Robert Millan - - * util/i386/pc/grub-mkrescue.in: Add "set -e". - Add --pkglibdir=DIR option to override pkglibdir. - Mention --image-type=TYPE in help output. - Fix --grub-mkimage (it was a no-op). - Abort gracefully when no parameter is given. - -2007-06-11 Robert Millan - - * util/i386/pc/grub-mkrescue.in: New file. - * conf/i386-pc.rmk: Add its build declarations. Put it in bin_SCRIPTS. - * Makefile.in: Handle bin_SCRIPTS. - -2007-06-10 Vesa Jaaskelainen - - * term/gfxterm.c (grub_gfxterm_init): Added support for specifying - list of video modes. - -2007-06-06 Robert Millan - - * util/update-grub_lib.in (convert_system_path_to_grub_path): Abort if - file doesn't exist, or if it is in a filesystem grub can't read. - - * util/update-grub.in: Set fallback for GRUB_FS check to "unknown". Do - not abort if GRUB_DRIVE could not be defined. Rearrange generated - header comment to fit in 80 columns when the variables are resolved. - - * util/grub.d/00_header.in: Only set root variable when GRUB_DRIVE - could be identified by update-grub. Remove redundant check for - unifont.pff existence (since convert_system_path_to_grub_path now - handles that). - -2007-06-04 Robert Millan - - * conf/i386-efi.rmk (grub_probe_SOURCES): Add partmap/apple.c. - - * conf/i386-pc.rmk (grub_probe_SOURCES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_probe_SOURCES): Add partmap/pc.c. - -2007-06-04 Robert Millan - - * conf/powerpc-ieee1275.rmk: Enable grub-mkdevicemap and grub-probe. - - * include/grub/partition.h: Declare grub_apple_partition_map_init and - grub_apple_partition_map_fini. - - * util/biosdisk.c - (grub_util_biosdisk_open): Replace BLKGETSIZE with BLKGETSIZE64 (needed - to access >2 TiB disks). - - Print disk->total_sectors with %llu instead of %lu, since this - variable is always 64-bit (prevents wrong disk size from being displayed - on either >2 TiB disk or big-endian CPU). - - (grub_util_biosdisk_get_grub_dev): Convert gpt_partition_map handling - into a generic case that supports all (sane) partition maps. - - Stop using grub_cpu_to_le32() on dos_part / bsd_part since it actually - breaks big-endian. - - * util/grub-probe.c: Call grub_apple_partition_map_init() before probe() - and grub_apple_partition_map_fini() after that. - -2007-06-01 Robert Millan - - * util/update-grub.in: Export GRUB_CMDLINE_LINUX. - - * util/grub.d/00_header.in: Only enable gfxterm when - convert_system_path_to_grub_path() succeeds. - -2007-05-20 Robert Millan - - * util/update-grub_lib.in: New file. - * DISTLIST: Add update-grub_lib.in. - * conf/common.rmk: Generate update-grub_lib and install it in - $(lib_DATA). - * Makefile.in: Add install routine for $(lib_DATA). - - * util/grub.d/00_header.in: Use convert_system_path_to_grub_path() - function provided by update-grub_lib to support arbitrary paths of - unifont.pff. - * util/update-grub.in: Use convert_system_path_to_grub_path() to - initialize GRUB_DRIVE_BOOT and GRUB_DRIVE_BOOT_GRUB variables. - -2007-05-19 Robert Millan - - * commands/i386/cpuid.c: New module. - * DISTLIST: Add it. - * conf/i386-efi.rmk: Enable cpuid.mod. - * conf/i386-pc.rmk: Likewise. - -2007-05-18 Jeroen Dekkers - - * kern/disk.c (grub_disk_read): Check return value of - grub_realloc(). - -2007-05-18 Jeroen Dekkers - - * util/getroot.c (grub_util_get_grub_dev): Support partitionable - arrays. - * disk/raid.c (grub_raid_open): Likewise. - -2007-05-17 Jeroen Dekkers - - * util/biosdisk.c (linux_find_partition): Allocate real_dev on the - stack instead of on the heap. - - * kern/disk.c (grub_disk_read): Make sure tmp_buf is big enough - before doing a read on it. - - * configure.ac: Only use -fno-stack-protector for the target - environment. - -2007-05-17 Jeroen Dekkers - - * video/i386/pc/vbe.c (grub_video_vbe_create_render_target): Add - __attribute_ ((unused)) to mode_type argument. - - * util/getroot.c (grub_guess_root_device): Fix #endif. - - * kern/misc.c (memcmp): Fix prototype. - - * include/grub/partition.h [GRUB_UTIL] - (grub_gpt_partition_map_init): Add prototype. - (grub_gpt_partition_map_fini): Likewise. - - * fs/jfs.c (struct grub_jfs_inode): Put __attribute__ ((packed) - at the right place. - - * fs/fat.c (grub_fat_mount): Replace ~0UL with ~0U. - (grub_fat_read_data): Likewise. - (grub_fat_find_dir): Likewise. - - * font/manager.c (find_glyph): Make table a const. - (grub_font_get_glyph): Remove bitmap from if statement. - -2007-05-16 Jeroen Dekkers - - * util/getroot.c (grub_guess_root_device): Remove RAID and LVM - code, first search for device in /dev/mapper, then in /dev. - (grub_util_get_grub_dev): New function. - * include/grub/util/getroot.h (grub_util_get_grub_dev): Add - prototype. - * util/grub-probe.c (probe): Remove check for RAID, call - grub_util_get_grub_dev() instead of - grub_util_biosdisk_get_grub_dev(). - * util/grub-emu.c (main): Call grub_util_get_grub_dev() instead of - grub_util_biosdisk_get_grub_dev(). - * util/i386/pc/grub-setup.c (main): Likewise. - -2007-05-16 Robert Millan - - * DISTLIST: Update for the latest changes. - * conf/i386-pc.rmk: Use the new paths for util/getroot.c, - util/grub-mkdevicemap.c, util/grub-probe.c and util/biosdisk.c. - * util/grub-emu.c: Replace grub/i386/pc/util/biosdisk.h with - grub/util/biosdisk.h. - * util/i386/pc/grub-setup.c: Replace grub/machine/util/biosdisk.h with - grub/util/biosdisk.h. - -2007-05-16 Robert Millan - - * util/grub.d/00_header.in: Set default gfxmode to `640x480'. - -2007-05-16 Robert Millan - - * util/i386/efi/grub-install.in: New. - * conf/i386-efi.rmk: Enable grub-mkdevicemap, grub-probe and the - newly added grub-install. - * util/biosdisk.c: Remove unnecessary grub/machine/biosdisk.h - include. - * util/getroot.c: Replace grub/i386/pc/util/biosdisk.h with - grub/util/biosdisk.h. - * util/grub-probe.c: Replace grub/machine/util/biosdisk.h with - grub/util/biosdisk.h. - -2007-05-16 Robert Millan - - * include/grub/i386/pc/util/biosdisk.h: Moved to ... - * include/grub/util/biosdisk.h: ... here. - * util/i386/pc/biosdisk.c: Moved to ... - * util/biosdisk.c: ... here. - * util/i386/pc/getroot.c: Moved to ... - * util/getroot.c: ... here. - * util/i386/pc/grub-mkdevicemap.c: Moved to ... - * util/grub-mkdevicemap.c: ... here. - * util/i386/pc/grub-probe.c: Moved to ... - * util/grub-probe.c: ... here. - -2007-05-15 Robert Millan - - * util/update-grub.in: Remove duplicated line in grub.cfg header - message. - -2007-05-13 Robert Millan - - * util/update-grub.in: Fix a few assumptions about the devices holding - /, /boot and /boot/grub being the same. - * util/grub.d/00_header.in: Likewise. - * util/grub.d/10_hurd.in: Likewise. - * util/grub.d/10_linux.in: Likewise. - - * util/grub.d/10_linux.in: Implement Linux image sorting with arbitrary - patterns. Use that to define the `.old' suffix as older than `'. - - * util/grub.d/00_header.in: Set default gfxmode to `800x600x16'. - - * util/update-grub.in: Add a reference to ${sysconfdir}/default/grub in - the grub.cfg header message. - -2007-05-11 Robert Millan - - * util/update-grub.in: Create device.map if it doesn't already exist, - before attempting to run grub-probe. - Check for grub-probe and grub-mkdevicemap with the same code - grub-install is using. - Remove test mode. - -2007-05-09 Jeroen Dekkers - - * Makefile.in: Add the datarootdir autoconf variable. - -2007-05-09 Robert Millan - - * util/i386/pc/grub-probe.c (probe): When detecting partition map, - fail gracefully if dev->disk->partition == NULL. - -2007-05-07 Robert Millan - - * util/i386/pc/grub-probe.c: Add `grub-probe -t partmap' parameter to - determine partition map module. - * util/i386/pc/grub-install.in: Use this feature to decide which - partition module to load, instead of hardcoding pc and gpt. - -2007-05-07 Robert Millan - - * Makefile.in: Fix assumption that $(srcdir) has a trailing slash when - source directory differs from build directory. - -2007-05-05 Robert Millan - - * util/powerpc/ieee1275/grub-install.in: Fix syntax error in pkglibdir - initialisation. - -2007-05-05 Robert Millan - - * util/update-grub.in: Create ${grub_prefix} if it doesn't exist. - -2007-05-05 Robert Millan - - * util/grub.d/10_linux.in: Allow the administrator to insert Linux - command-line arguments via ${GRUB_CMDLINE_LINUX}. - -2007-05-05 Robert Millan - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add partmap/gpt.c. - (grub_probe_SOURCES): Likewise. - * util/i386/pc/biosdisk.c (grub_util_biosdisk_get_grub_dev): Detect - GPT and initialize dos_part and bsd_part accordingly. - * util/i386/pc/grub-setup.c (setup): Ditto for install_dos_part and - install_bsd_part. - (main): Activate gpt module for use during partition identification, - and deactivate it afterwards. - * util/i386/pc/grub-install.in: Add gpt module to core.img. - * util/i386/pc/grub-probe.c (main): Activate gpt module for use during - partition identification, and deactivate it afterwards. - -2007-05-05 Robert Millan - - * term/i386/pc/console.c (grub_console_fini): Call - grub_term_set_current() before grub_term_unregister(). - -2007-05-04 Robert Millan - - * DISTLIST: Add util/update-grub.in, util/grub.d/00_header.in, - util/grub.d/10_hurd.in, util/grub.d/10_linux.in and util/grub.d/README. - * Makefile.in: Build update-grub_SCRIPTS. Install update-grub_SCRIPTS - and update-grub_DATA. - * conf/common.rmk: Build and install update-grub components. - * conf/common.mk: Regenerate. - * util/update-grub.in: New. Core of update-grub. - * util/grub.d/00_header.in: New. Generates grub.cfg header. - * util/grub.d/10_hurd.in: New. Generates boot entries for the Hurd. - * util/grub.d/10_linux.in: New. Generates boot entries for Linux. - * util/grub.d/README: New. Document grub.d directory layout. - -2007-05-01 Robert Millan - - * util/grub-emu.c: Move initialization functions - grub_util_biosdisk_init() and grub_init_all() before - grub_util_biosdisk_get_grub_dev(), which relies on them. - -2007-04-19 Robert Millan - - * util/powerpc/ieee1275/grub-install.in: Initialize ${bindir}, since - it is used later. - -2007-04-18 Jerone Young - - * kernel/elf.c: Add missing parenthesis for conditional statement - stanza. - -2007-04-10 Jerone Young - - * util/i386/pc/getroot.c: Update so that if root device is /dev/root , - continue on and look for device node with real device name. - -2007-04-10 Jerone Young - - * configure.ac: Add argument for autoconf to use transformation - ability. - * Makefile.in: Add autoconf package transformation code. - * util/i386/pc/grub-install.in: Likewise. - * util/powerpc/ieee1275/grub-install.in: Likewise. - -2007-03-19 Yoshinori K. Okuji - - * fs/ext2.c (EXT2_GOOD_OLD_REVISION): New macro. - (EXT2_GOOD_OLD_INODE_SIZE): Likewise. - (EXT2_REVISION): Likewise. - (EXT2_INODE_SIZE): Likewise. - (struct grub_ext2_block_group): Added a missing member - "used_dirs". - (grub_ext2_read_inode): Divide by the inode size in a superblock - instead of 128 to obtain INODES_PER_BLOCK. - Use the macro EXT2_INODE_SIZE instead of directly using - SBLOCK->INODE_SIZE. - -2007-03-18 Yoshinori K. Okuji - - * fs/ext2.c (grub_ext2_read_inode): Use the inode size in a - superblock instead of the structure size to compute an - offset. This fixes the problem that GRUB could not read a - filesystem when inode size is different from 128-byte. - -2007-03-05 Marco Gerards - - * normal/main.c (read_config_file): When "menu" is not set, create - an initial context. - -2007-02-21 Hollis Blanchard - - * kern/powerpc/ieee1275/init.c (HEAP_SIZE): Removed. - (HEAP_LIMIT): New macro. - (grub_claim_heap): Claim memory up to `heaplimit'. - -2007-02-21 Hollis Blanchard - - * conf/powerpc-ieee1275.rmk (kernel_elf_LDFLAGS): Link at 64KB. - * kern/powerpc/ieee1275/init.c (_end): Add declaration. - (_start): Likewise. - (grub_arch_modules_addr): Return address after `_end'. - * util/powerpc/ieee1275/grub-mkimage.c: Include grub/misc.h. - (load_modules): Use new parameter as `p_paddr' and `p_vaddr'. - (add_segments): Calculate `_end' from phdr size and location. - (ALIGN_UP): Moved to ... - * include/grub/misc.h: here. - * include/grub/powerpc/ieee1275/kernel.h (GRUB_IEEE1275_MOD_ALIGN): - New macro. - (GRUB_IEEE1275_MODULE_BASE): Removed. - -2007-02-20 Hollis Blanchard - - * kern/powerpc/ieee1275/openfw.c (grub_available_iterate): Correct - loop boundary. - -2007-02-20 Hollis Blanchard - - * include/grub/elfload.h (grub_elf32_load_hook_t): Return grub_err_t. - All users updated. - (grub_elf64_load_hook_t): Likewise. - * kern/elf.c: Call `grub_error_push' before `grub_error'. Improve - debug output. - -2007-02-20 Hollis Blanchard - - * kern/mm.c: Update copyright. - (grub_mm_debug): Correct syntax error. - (grub_mm_dump_free): New function. - (grub_debug_free): Call `grub_free'. - * include/grub/mm.h: Update copyright. - (grub_mm_dump_free): Add declaration. - -2007-02-12 Hollis Blanchard - - * include/grub/ieee1275/ieee1275.h: Update copyright. - * kern/powerpc/ieee1275/init.c: Likewise. - * kern/powerpc/ieee1275/openfw.c: Likewise. - - * loader/powerpc/ieee1275/linux.c: Likewise. - * include/grub/elfload.h: Likewise. - * kern/elf.c: Likewise. - (grub_elf32_load): Pass `base' and `size' parameters. Update all - callers. - (grub_elf64_load): Likewise. - (grub_elf32_load_segment): Move to a nested function. - (grub_elf64_load_segment): Likewise. - -2007-02-12 Hollis Blanchard - - * include/grub/ieee1275/ieee1275.h (grub_available_iterate): New - prototype. - * kern/powerpc/ieee1275/init.c (grub_heap_start): Removed. - (grub_heap_len): Likewise. - (HEAP_SIZE): New macro. - (grub_claim_heap): New function. - (grub_machine_init): Don't claim heap directly. Call - `grub_claim_heap'. - * kern/powerpc/ieee1275/openfw.c: Include alloca.h. - (grub_available_iterate): New function. - -2007-02-03 Thomas Schwinge - - * aclocal.m4 (grub_CHECK_STACK_PROTECTOR): New definition. - * configure.ac: Use it for testing the HOST and TARGET compilers. - -2006-12-13 Thomas Schwinge - - * Makefile.in (enable_grub_emu): New variable. - * configure.ac (--enable-grub-emu): New option. - Do the checks for (n)curses only if `--enable-grub-emu' is requested. - * conf/i386-efi.rmk (sbin_UTILITIES): Add `grub-emu' only if requested. - * conf/i386-pc.rmk: Likewise. - * conf/powerpc-ieee1275.rmk: Likewise. - * conf/sparc64-ieee1275.rmk (bin_UTILITIES): Likewise. - -2006-12-12 Marco Gerards - - * include/grub/err.h (grub_err_t): Add `GRUB_ERR_MENU'. - - * kern/env.c (grub_env_unset): Don't free the member `value' when - the type is GRUB_ENV_VAR_DATA, in this case it's a user defined - pointer. - - * normal/main.c (current_menu): Removed. - (free_menu): Unset the `menu' environment variable. - (grub_normal_menu_addentry): Make use of the environment variable - `menu', instead of using the global `current_menu'. Allocate - memory for the sourcecode of this entry. - (read_config_file): New argument `nested', changed all callers. - Only in the case of a new context, initialize a new menu. Set the - `menu' environment variable. - (grub_normal_execute): Don't set and unset the environment - variable `menu' here anymore. Only free the menu when leaving the - context. - - * util/i386/pc/biosdisk.c (linux_find_partition): Fixed a memory - leak. - -2006-12-11 Marco Gerards - - * normal/menu_entry.c (run): Fix off by one bug so the last line - is executed. Move the loader check to outside the loop. - -2006-12-08 Hollis Blanchard - - * kern/powerpc/ieee1275/cmain.c (cmain): Mark r3 and r4 as `UNUSED'. - -2006-11-25 Yoshinori K. Okuji - - * util/i386/pc/grub-mkimage.c (generate_image): Fix the offset of - the number of sectors. Reported by Andrey Shuvikov - . - -2006-11-11 Jeroen Dekkers - - * kern/disk.c (grub_disk_read): When there is a read error, always - try to read only the necessary data. - - * conf/i386-pc.rmk (grub_probe_SOURCES): Add disk/lvm.c and - disk/raid.c. - * include/grub/disk.h [GRUB_UTIL] (grub_raid_init): New - prototype. - [GRUB_UTIL] (grub_raid_fini): Likewise. - [GRUB_UTIL] (grub_lvm_init): Likewise. - [GRUB_UTIL] (grub_lvm_fini): Likewise. - * util/i386/pc/grub-probe.c (probe): Check whether DEVICE_NAME is - RAID device and copy DEVICE_NAME to DRIVE_NAME in that case. - (main): Call grub_raid_init(), grub_lvm_init(), grub_lvm_fini() - and grub_raid_fini(). - -2006-11-09 Jeroen Dekkers - - * include/grub/types.h (__unused): Rename to UNUSED. - * kern/elf.c (grub_elf32_size): Use UNUSED instead of __unused. - (grub_elf64_size): Likewise. - -2006-11-03 Hollis Blanchard - - * kern/elf.c (grub_elf_file): Call grub_file_seek. Call - grub_error_push and grub_error_pop in the error-handling path. - (grub_elf32_load_segment): Only call grub_file_read with non-zero - length. - -2006-11-03 Hollis Blanchard - - * conf/i386-efi.rmk (grub_emu_SOURCES): Add kern/elf.c. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (kernel_elf_SOURCES): Likewise. - * conf/i386-efi.rmk (kernel_mod_HEADERS): Add elfload.h and cache.h. - * conf/i386-pc.rmk (kernel_mod_HEADERS): Likewise. - * conf/powerpc-ieee1275.rmk (kernel_elf_HEADERS): Likewise. - * conf/sparc64-ieee1275.rmk (kernel_elf_HEADERS): Likewise. - * conf/common.rmk (pkgdata_MODULES): Add elf.mod. - (elf_mod_SOURCES): New variable. - (elf_mod_CFLAGS): Likewise. - (elf_mod_LDFLAGS): Likewise. - * include/grub/types.h (__unused): New macro. - * include/grub/elfload.h: New file. - * kern/elf.c: Likewise. - * loader/powerpc/ieee1275/linux.c: Include elfload.h. - (ELF32_LOADMASK): New macro. - (ELF64_LOADMASK): Likewise. - (vmlinux): Removed. - (grub_linux_load32): New function. - (grub_linux_load64): Likewise. - (grub_rescue_cmd_linux): Call grub_linux_load32 or grub_linux_load64. - Use grub_elf_t instead of grub_file_t. - -2006-11-02 Hollis Blanchard - - * kern/ieee1275/ieee1275.c (grub_ieee1275_set_color): Add - `catch_result' to struct set_color_args. - -2006-10-28 Yoshinori K. Okuji - - * normal/menu.c: Include grub/script.h. - * normal/menu_entry.c: Likewise. - * include/grub/normal.h: Do not include grub/script.h. - -2006-10-27 Hollis Blanchard - - * kern/disk.c (grub_disk_read): Correct debug printf formatting. - -2006-10-27 Hollis Blanchard - - * kern/disk.c (grub_disk_open): Print debug messages when opening a - disk. - (grub_disk_close): Print debug messages when closing a disk. - (grub_disk_read): Print debug messages when disk read fails. - * kern/fs.c (grub_fs_probe): Print debug messages when detecting - filesystem type. - * kern/partition.c: Include misc.h. - (grub_partition_iterate): Print debug messages when detecting - partition type. - -2006-10-27 Hollis Blanchard - - * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Return error if `status' - is negative. - * kern/ieee1275/ieee1275.c (IEEE1275_IHANDLE_INVALID): Change to 0. - -2006-10-26 Hollis Blanchard - - * kern/powerpc/ieee1275/openfw.c (grub_ieee1275_encode_devname): - Reverse GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS test. - -2006-10-25 Jeroen Dekkers - - * disk/lvm.c (grub_lvm_scan_device): Malloc sizeof(*lv) bytes - instead of sizeof(lv). Patch by Michael Guntsche. - -2006-10-18 Jeroen Dekkers - - * disk/lvm.c: Rename VGS to VG_LIST. - (grub_lvm_iterate): Change VGS->LV to VG-LV. - (grub_lvm_open): Likewise. - Thanks to Michael Guntsche for finding this bug. - -2006-10-15 Yoshinori K. Okuji - - * configure.ac (AC_INIT): Bumped to 1.95. - -2006-10-14 Robert Millan - - * util/i386/pc/getroot.c (grub_guess_root_device): Don't compare os_dev - with "/dev/.static/dev/md". - -2006-10-14 Yoshinori K. Okuji - - * util/i386/pc/grub-probe.c (probe): Print DEVICE_NAME instead of - DRIVE_NAME when grub_util_biosdisk_get_grub_dev fails. Open - DRIVE_NAME instead of DEVICE_NAME. Make sure that DEVICE_NAME and - DRIVE_NAME are always freed. - - * util/i386/pc/biosdisk.c (make_device_name): Add one into - DOS_PART, as a DOS partition is counted from one instead of zero - now. Reported by Robert Millan. - -2006-10-14 Robert Millan - - * util/i386/pc/getroot.c (grub_guess_root_device): Stop using - grub_util_biosdisk_get_grub_dev to convert system device to GRUB device. - * util/grub-emu.c (main): Use grub_util_biosdisk_get_grub_dev with the - string returned by grub_guess_root_device. - * util/i386/pc/grub-setup.c: Likewise. - * util/i386/pc/grub-probefs.c: Likewise. - - * util/i386/pc/grub-probefs.c: Rename to ... - * util/i386/pc/grub-probe.c: ... this. - * DISTLIST: Remove grub-probefs, add grub-probe. - * conf/i386-efi.rmk: Likewise. - * conf/i386-pc.rmk: Likewise. - * util/i386/pc/grub-install.in: Likewise. - - * util/i386/pc/grub-probe.c: Add --target=(fs|device|drive) option to - choose which information we want to print. - -2006-10-14 Yoshinori K. Okuji - - * DISTLIST: Added commands/echo.c, disk/lvm.c, disk/raid.c, - include/grub/bitmap.h, include/grub/lvm.h, include/grub/raid.h, - include/grub/i386/pc/vbeutil.h, include/grub/util/lvm.h, - include/grub/util/raid.h, util/lvm.c, util/raid.c, video/bitmap.c, - video/readers/tga.c and video/i386/pc/vbeutil.c. - -2006-10-14 Jeroen Dekkers - - Added support for RAID and LVM. - - * disk/lvm.c: New file. - * disk/raid.c: Likewise. - * include/grub/lvm.h: Likewise. - * include/grub/raid.h: Likewise. - * include/grub/util/lvm.h: Likewise. - * include/grub/util/raid.h: Likewise. - * util/lvm.c: Likewise. - * util/raid.c: Likewise. - - * include/grub/disk.h (grub_disk_dev_id): Add - GRUB_DISK_DEVICE_RAID_ID and GRUB_DISK_DEVICE_LVM_ID. - (grub_disk_get_size): New prototype. - * kern/disk.c (grub_disk_open): Check whether grub_partition_probe() - returns a partition. - (grub_disk_get_size): New function. - - * kern/i386/pc/init.c (make_install_device): Copy the prefix - verbatim if grub_install_dos_part is -2. - - * util/i386/pc/getroot.c (grub_guess_root_device): Support RAID - and LVM devices. - - * util/i386/pc/grub-setup.c (setup): New argument - MUST_EMBED. Force embedding of GRUB when the argument is - true. Close FILE before returning. - (main): Add support for RAID and LVM. - - * conf/common.rmk: Add RAID and LVM modules. - * conf/i386-pc.rmk (grub_setup_SOURCES): Add util/raid.c and - util/lvm.c. - (grub_emu_SOURCES): Add disk/raid.c and disk/lvm.c. - - * kern/misc.c (grub_strstr): New function. - * include/grub/misc.h (grub_strstr): New prototype. - -2006-10-10 Tristan Gingold - - * include/grub/efi/api.h (GRUB_EFI_ERROR_CODE): Long constant. - -2006-10-05 Tristan Gingold - - * kern/misc.c (grub_strtoull): Guess the base only if not - specified. - -2006-10-01 Hollis Blanchard - - * kern/powerpc/ieee1275/cmain.c (cmain): Remove incomplete Old World - PowerMac support. - -2006-10-01 Hollis Blanchard - - * disk/ieee1275/ofdisk.c (grub_ofdisk_iterate): Cast `size' to long. - - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_next_property): - Remove `flags' argument. All callers changed. - * kern/ieee1275/ieee1275.c (IEEE1275_PHANDLE_ROOT): Removed. - (IEEE1275_IHANDLE_INVALID): New variable. - (IEEE1275_CELL_INVALID): New variable. - (grub_ieee1275_finddevice, grub_ieee1275_get_property, - grub_ieee1275_get_property_length, grub_ieee1275_instance_to_package, - grub_ieee1275_package_to_path, grub_ieee1275_instance_to_path, - grub_ieee1275_peer, grub_ieee1275_child, grub_ieee1275_open, - grub_ieee1275_claim, grub_ieee1275_set_property): Error-check return - codes from Open Firmware. All callers updated. - (grub_ieee1275_next_property): Directly return Open Firmware return - code. - * kern/powerpc/ieee1275/cmain.c (grub_ieee1275_find_options): - Standardize error checking from `grub_ieee1275_get_property'. - * kern/powerpc/ieee1275/openfw.c (grub_devalias_iterate): Rename - `devalias' to `aliases'. Correct comments. Consolidate error paths. - -2006-10-01 Hollis Blanchard - - * kern/ieee1275/ieee1275.c (grub_ieee1275_instance_to_path): Rename - `instance_to_package_args' to `instance_to_path_args'. - - * kern/powerpc/ieee1275/init.c (grub_machine_init): Use - `grub_ieee1275_chosen'. - - * term/ieee1275/ofconsole.c (grub_ofconsole_init): Call - `grub_ieee1275_interpret'. - -2006-09-25 Hollis Blanchard - - * util/powerpc/ieee1275/grub-mkimage.c: Include config.h. - -2006-09-25 Hollis Blanchard - - * include/grub/powerpc/libgcc.h (__floatdisf): New prototype. - (__cmpdi): Likewise. - - * kern/powerpc/ieee1275/openfw.c (grub_devalias_iterate): Pass 0 as - `flags' to `grub_ieee1275_next_property'. Change `pathlen' to type - `grub_ssize_t'. - - * kern/powerpc/ieee1275/cmain.c: Include grub/misc.h. - - * loader/powerpc/ieee1275/linux.c (grub_linux_boot): Change `actual' - to type `grub_ssize_t'. - (grub_rescue_cmd_linux): Cast -1 to `grub_off_t'. - -2006-09-22 Marco Gerards - - * normal/script.c (grub_script_create_cmdmenu): Skip leading - newlines. - -2006-09-22 Marco Gerards - - * commands/echo.c: New file. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `commands/echo.c'. - - * conf/common.rmk (echo_mod_SOURCES): New variable. - (echo_mod_CFLAGS): Likewise. - (echo_mod_LDFLAGS): Likewise. - -2006-09-22 Marco Gerards - - * normal/main.c (get_line): Malloc memory instead of using - preallocated memory. Removed the arguments `cmdline' and - `max_len'. Updated all callers. - -2006-09-22 Marco Gerards - - * conf/i386-efi.rmk (grub_emu_DEPENDENCIES): New variable. - (normal_mod_DEPENDENCIES): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_DEPENDENCIES): Likewise. - (normal_mod_DEPENDENCIES): Likewise. - - * conf/sparc64-ieee1275.rmk (normal_mod_DEPENDENCIES): Likewise. - -2006-09-22 Johan Rydberg - - * genmk.rb: Add DEPENDENCIES variables to modules, utilities, and - programs. - * conf/i386-pc.rmk (grub_emu_DEPENDENCIES): Declare. - (normal_mod_DEPENDENCIES): Likewise. - * conf/i386-pc.mk: Regenerate. - * conf/i386-efi.mk: Likewise - * conf/common.mk: Likewise. - * conf/powerpc-ieee1275.mk: Likewise. - * conf/sparc64-ieee1275.mk: Likewise. - -2006-09-22 Robert Millan - - Sync with i386 version. - * conf/powerpc-ieee1275.rmk (bin_UTILITIES): Remove grub-emu, add grub-mkimage. - * conf/powerpc-ieee1275.rmk (sbin_UTILITIES): Remove grub-mkimage, add grub-emu. - -2006-09-21 Robert Millan - - Import from GRUB Legacy (lib/device.c): - * util/i386/pc/grub-mkdevicemap.c (get_i2o_disk_name): New function. - (init_device_map) [__linux__]: Add support for I2O devices. - -2006-09-14 Marco Gerards - - * conf/i386-pc.rmk (COMMON_LDFLAGS): Use `-m32' instead of - `-melf_i386'. - -2006-09-14 Robert Millan - - * util/i386/pc/grub-install.in: Skip menu.lst when removing - /boot/grub/*.lst. - - * util/i386/pc/getroot.c: Don't recurse into dotdirs (e.g. ".static"). - - * util/i386/pc/grub-mkdevicemap.c: Make sure the floppy device exists - before adding it to device.map. - -2006-08-15 Johan Rydberg - - * genmk.rb: Let GCC generate dependencies the first time it - compiles a file; using the -MD option. - * conf/common.mk: Regenerate. - * conf/i386-pc.mk: Likewise. - * conf/i386-efi.mk: Likewise. - * conf/powerpc-ieee1275.mk: Likewise. - * conf/sparc64-ieee1275.mk: Likewise. - -2006-08-04 Yoshinori K. Okuji - - Move the prototypes of grub_setjmp and grub_longjmp to - cpu/setjmp.h, so that each architecture may specify different - attributes. - - * include/grub/i386/setjmp.h (grub_setjmp): New prototype. - (grub_longjmp): Likewise. - * include/grub/powerpc/setjmp.h (grub_setjmp): Likewise.. - (grub_longjmp): Likewise. - * include/grub/sparc64/setjmp.h (grub_setjmp): Likewise.. - (grub_longjmp): Likewise. - - * include/grub/setjmp.h [!GRUB_UTIL] (grub_setjmp): Removed. - [!GRUB_UTIL] (grub_longjmp): Removed. - -2006-08-01 Pelletier Vincent - - * kern/ieee1275/ieee1275.c (grub_ieee1275_set_color): IEEE1275 - "color!" method does not return any value. - -2006-07-29 Vesa Jaaskelainen - - * include/grub/bitmap.h: New file. - - * include/grub/i386/pc/vbeutil.h: Likewise. - - * video/bitmap.c: Likewise. - - * video/readers/tga.c: Likewise. - - * video/i386/pc/vbeutil.c: Likewise. - - * commands/videotest.c: Code cleanup and updated to reflect to new - video API. - - * term/gfxterm.c: Likewise. - - * video/video.c: Likewise. - - * conf/i386-pc.rmk (pkgdata_MODULES): Added tga.mod and bitmap.mod. - (vbe_mod_SOURCES): Added video/i386/pc/vbeutil.c. - (bitmap_mod_SOURCES): New entry. - (bitmap_mod_CFLAGS): Likewise. - (bitmap_mod_LDFLAGS): Likewise. - (tga_mod_SOURCES): Likewise. - (tga_mod_CFLAGS): Likewise. - (tga_mod_LDFLAGS): Likewise. - - * include/grub/video.h (grub_video_blit_operators): New enum type. - (grub_video_render_target): Changed as forward declaration and moved - actual definition to be video driver specific. - (grub_video_adapter.blit_bitmap): Added blitting operator. - (grub_video_adapter.blit_render_target): Likewise. - (grub_video_blit_bitmap): Likewise. - (grub_video_blit_render_target): Likewise. - - * include/grub/i386/pc/vbe.h (grub_video_render_target): Added - driver specific render target definition. - (grub_video_vbe_map_rgba): Added driver internal helper. - (grub_video_vbe_unmap_color): Updated to use - grub_video_i386_vbeblit_info. - (grub_video_vbe_get_video_ptr): Likewise. - - * include/grub/i386/pc/vbeblit.h - (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8): Updated to use - grub_video_i386_vbeblit_info. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8A8): Likewise. - (grub_video_i386_vbeblit_R8G8B8A8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8): Likewise. - (grub_video_i386_vbeblit_index_index): Likewise. - (grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8): New blitter function. - (grub_video_i386_vbeblit_R8G8B8_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_index_R8G8B8X8): Likewise. - (grub_video_i386_vbeblit_blend): Added generic blitter for blend - operator. - (grub_video_i386_vbeblit_replace): Added generic blitter for replace - operator. - - * video/i386/pc/vbeblit.c: Updated to reflect changes on - include/grub/i386/pc/vbeblit.h. - - * include/grub/i386/pc/vbefill.h (grub_video_i386_vbefill_R8G8B8A8): - Updated to use grub_video_i386_vbeblit_info. - (grub_video_i386_vbefill_R8G8B8): Likewise. - (grub_video_i386_vbefill_index): Likewise. - (grub_video_i386_vbefill): Added generic filler. - - * video/i386/pc/vbefill.c: Updated to reflect changes on - include/grub/i386/pc/vbefill.h. - - * video/i386/pc/vbe.c (grub_video_vbe_get_video_ptr): Updated to use - grub_video_i386_vbeblit_info. - (grub_video_vbe_unmap_color): Likewise. - (grub_video_vbe_blit_glyph): Likewise. - (grub_video_vbe_scroll): Likewise. - (grub_video_vbe_draw_pixel): Removed function. - (grub_video_vbe_get_pixel): Likewise. - (grub_video_vbe_fill_rect): Moved all blitters to vbefill.c and - updated code to use it. - (common_blitter): Added common blitter for render target and bitmap. - (grub_video_vbe_blit_bitmap): Updated to use common_blitter. - (grub_video_vbe_blit_render_target): Likewise. - -2006-07-30 Johan Rydberg - - * kern/efi/efi.c (grub_efi_set_text_mode): Assume console already - is in text mode if there is no console control protocol instance - available. - -2006-07-29 Vesa Jaaskelainen - - * include/grub/video.h: Code cleanup. - - * include/grub/i386/pc/vbe.h: Likewise. - - * video/i386/pc/vbe.c: Likewise. - - * video/i386/pc/vbeblit.c: Likewise. - - * video/i386/pc/vbefill.c: Likewise. - - * video/video.c: Likewise. Also added more comments. - -2006-07-29 Vesa Jaaskelainen - - * disk/i386/pc/biosdisk.c (struct grub_biosdisk_drp): Moved to ... - (struct grub_biosdisk_dap): Likewise. - - * include/grub/i386/pc/biosdisk.h: ... to here. Also corrected - linkage settings for all functions. - -2006-07-12 Marco Gerards - - * configure.ac (--enable-mm-debug): Fix typo. - - * genkernsyms.sh.in: Use proper quoting for `CC'. - -2006-07-02 Jeroen Dekkers - - * conf/i386-pc.rmk (COMMON_ASFLAGS): Add "-m32". - (normal_mod_ASFLAGS): Remove "-m32". - -2006-06-14 Yoshinori K. Okuji - - * util/misc.c: Include config.h. - [!HAVE_MEMALIGN]: Do not include malloc.h. - (grub_memalign): Use posix_memalign, if present. Then, use - memalign, if present. Otherwise, emit an error. - - * util/grub-emu.c: Do not include malloc.h. - - * include/grub/util/misc.h: Include unistd.h. This is required for - FreeBSD, because off_t is defined in unistd.h. Reported by Harley - D. Eades III . - - * configure.ac (AC_GNU_SOURCE): Added. - (AC_CHECK_FUNCS): Check posix_memalign and memalign for the host - type. - -2006-06-09 Yoshinori K. Okuji - - * loader/i386/pc/linux.c (grub_rescue_cmd_initrd): Make sure that - ADDR_MAX does not exceed GRUB_LINUX_INITRD_MAX_ADDRESS. - -2006-06-07 Jeroen Dekkers - - * include/grub/types.h (grub_host_addr_t): Rename to - grub_target_addr_t. - (grub_host_off_t): Rename to grub_target_off_t. - (grub_host_size_t): Rename to grub_target_size_t. - (grub_host_ssize_t): Rename to grub_target_ssize_t. - Refer to GRUB_TARGET_SIZEOF_VOID_P to define those variables. - - * include/grub/kernel.h (struct grub_module_header): Change type - of OFFSET to grub_target_off_t and type of SIZE to grub_target_size_t. - (grub_module_info): Likewise. - -2006-06-05 Yoshinori K. Okuji - - * loader/i386/pc/linux.c (grub_rescue_cmd_initrd): The conditional - of checking LINUX_MEM_SIZE was reverse. Reported by Jesus - Velazquez . - -2006-06-05 Yoshinori K. Okuji - - Count partitions from 1 instead of 0 in the string representation - of partitions. Still use 0-based internally. - - * partmap/sun.c (grub_sun_is_valid): A cosmetic change. - (sun_partition_map_iterate): Use grub_partition_t instead of - struct grub_partition *. Cast DESC->START_CYLINDER to - grub_uint64_t after converting the endian. - (sun_partition_map_probe): Subtract 1 for PARTNUM. - (sun_partition_map_get_name): Add 1 to P->INDEX. - - * partmap/pc.c (grub_partition_parse): Subtract 1 for - PCDATA->DOS_PART. - (pc_partition_map_get_name): Add 1 into PCDATA->DOS_PART. - - * partmap/gpt.c (gpt_partition_map_iterate): Initialize PARTNO to - zero instead of one. - (gpt_partition_map_probe): Subtract 1 for PARTNUM. - (gpt_partition_map_get_name): Add 1 into P->INDEX. - - * partmap/apple.c (apple_partition_map_iterate): Change the type - of POS to unsigned. - (apple_partition_map_probe): Subtract 1 for PARTNUM. - (apple_partition_map_get_name): Add 1 into P->INDEX. - - * partmap/amiga.c (amiga_partition_map_iterate): Change the type - of POS to unsigned. - (amiga_partition_map_iterate): Cast NEXT to grub_off_t to - calculate the offset of a partition. - (amiga_partition_map_probe): Subtract 1 for PARTNUM. - (amiga_partition_map_get_name): Add 1 into P->INDEX. - - * partmap/acorn.c (acorn_partition_map_find): Change the type of - SECTOR to grub_disk_addr_t. - (acorn_partition_map_iterate): Likewise. - (acorn_partition_map_probe): Subtract 1 for PARTNUM. - Change the type of SECTOR to grub_disk_addr_t. Declare P on the - top. - (acorn_partition_map_get_name): Add 1 into P->INDEX. - - * kern/i386/pc/init.c (make_install_device): Add 1 into - GRUB_INSTALL_DOS_PART. - - * fs/iso9660.c (grub_iso9660_mount): Fixed a reversed - conditional. - -2006-06-04 Yoshinori K. Okuji - - Clean up the code to support 64-bit addressing in disks and - files. This change is not enough for filesystems yet. - - * util/i386/pc/grub-setup.c (struct boot_blocklist): Change the - type of "start" to grub_uint64_t. - (setup): Change the types of KERNEL_SECTOR and FIRST_SECTOR to - grub_disk_addr_t * and grub_disk_addr_t. Fix the format string in - save_first_sector and save_blocklists. Use grub_le_to_cpu64 to - convert addresses. - - * util/i386/pc/biosdisk.c (open_device): Change the type of SECTOR - to grub_disk_addr_t. - - * partmap/gpt.c (gpt_partition_map_iterate): Fix the format - string. - - * partmap/pc.c (pc_partition_map_iterate): Likewise. - - * partmap/amiga.c (amiga_partition_map_iterate): Cast RDSK.MAGIC - to char *. - - * normal/script.c (grub_script_parse): Remove unused MEMFREE. - - * normal/parser.y (YYLTYPE_IS_TRIVIAL): New macro. - - * normal/lexer.c (grub_script_yyerror): Specify unused to LEX. - - * loader/i386/pc/multiboot.c (grub_multiboot_load_elf64): Cast -1 - to grub_off_t, to detect an error from grub_file_seek. - (grub_multiboot_load_elf32): Likewise. - - * kern/misc.c (grub_strtoul): Use grub_strtoull. Return the - maximum unsigned long value when an overflow is detected. - (grub_strtoull): New function. - (grub_divmod64): Likewise. - (grub_lltoa): use grub_divmod64. - - * kern/fs.c (struct grub_fs_block): Change the type of "offset" to - grub_disk_addr_t. - (grub_fs_blocklist_open): Increase P if P is not NULL to advance - the pointer to next character. Use grub_strtoull instead of - grub_strtoul. - (grub_fs_blocklist_read): Change the types of SECTOR, OFFSET and - SIZE to grub_disk_addr_t, grub_off_t and grub_size_t, - respectively. - - * kern/file.c (grub_file_read): Prevent an overflow of LEN, as the - return value is signed. - (grub_file_seek): Change the type of OLD to grub_off_t. Do not - test if OFFSET is less than zero, as OFFSET is unsigned now. - - * kern/disk.c (struct grub_disk_cache): Change the type of - "sector" to grub_disk_addr_t. - (grub_disk_cache_get_index): Change the type of SECTOR to - grub_disk_addr_t. Calculate the hash with SECTOR casted to - unsigned after shifting. - (grub_disk_cache_invalidate): Change the type of SECTOR to - grub_disk_addr_t. - (grub_disk_cache_unlock): Likewise. - (grub_disk_cache_store): Likewise. - (grub_disk_check_range): Change the types of SECTOR, OFFSET, SIZE, - START and LEN to grub_disk_addr_t *, grub_off_t *, grub_size_t, - grub_disk_addr_t and grub_uint64_t, respectively. - (grub_disk_read): Use an unsigned variable REAL_OFFSET for the - body, as the value of OFFSET is tweaked by - grub_disk_check_range. Change the types of START_SECTOR, LEN and - POS to grub_disk_addr_t, grub_size_t and grub_size_t, - respectively. - (grub_disk_write): Use an unsigned variable REAL_OFFSET for the - body, as the value of OFFSET is tweaked by - grub_disk_check_range. Change the types of LEN and N to - grub_size_t. - - * io/gzio.c (struct grub_gzio): Change the types of "data_offset" - and "saved_offset" to grub_off_t. - (test_header): Cast BUF to char *. - (get_byte): Cast GZIO->DATA_OFFSET to grub_off_t. Cast GZIO->INBUF - to char *. - (grub_gzio_read): Change the types of OFFSET and SIZE to - grub_off_t and grub_size_t, respectively. - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_FORCE_LBA): - Removed. - (GRUB_BOOT_MACHINE_BOOT_DRIVE): Changed to 0x4c. - (GRUB_BOOT_MACHINE_KERNEL_ADDRESS): Changed to 0x40. - (GRUB_BOOT_MACHINE_KERNEL_SEGMENT): Changed to 0x42. - (GRUB_BOOT_MACHINE_DRIVE_CHECK): Changed to 0x4e. - (GRUB_BOOT_MACHINE_LIST_SIZE): Increased to 12. - - * include/grub/types.h (grub_off_t): Unconditionally set to - grub_uint64_t. - (grub_disk_addr_t): Changed to grub_uint64_t. - - * include/grub/partition.h (struct grub_partition): Change the - types of "start", "len" and "offset" to grub_disk_addr_t, - grub_uint64_t and grub_disk_addr_t, respectively. - (grub_partition_get_start): Return grub_disk_addr_t. - (grub_partition_get_len): Return grub_uint64_t. - - * include/grub/misc.h (grub_strtoull): New prototype. - (grub_divmod64): Likewise. - - * include/grub/fshelp.h (grub_fshelp_read_file): Change the types - of SECTOR, LEN and FILESIZE to grub_disk_addr_t, grub_size_t and - grub_off_t, respectively. - All callers and references changed. - - * include/grub/fs.h (struct grub_fs): Change the type of LEN to - grub_size_t in "read". - All callers and references changed. - - * include/grub/file.h (struct grub_file): Change the types of - "offset" and "size" to grub_off_t and grub_off_t, - respectively. Change the type of SECTOR to grub_disk_addr_t in - "read_hook". - (grub_file_read): Change the type of LEN to grub_size_t. - (grub_file_seek): Return grub_off_t. Change the type of OFFSET to - grub_off_t. - (grub_file_size): Return grub_off_t. - (grub_file_tell): Likewise. - All callers and references changed. - - * include/grub/disk.h (struct grub_disk_dev): Change the types of - SECTOR and SIZE to grub_disk_addr_t and grub_size_t in "read" and - "write". - (struct grub_disk): Change the type of "total_sectors" to - grub_uint64_t. Change the type of SECTOR to grub_disk_addr_t in - "read_hook". - (grub_disk_read): Change the types of SECTOR, OFFSET and SIZE to - grub_disk_addr_t, grub_off_t and grub_size_t, respectively. - (grub_disk_write): Likewise. - All callers and references changed. - - * fs/iso9660.c (grub_iso9660_susp_iterate): Cast parameters to - char * for grub_strncmp to silence gcc. - (grub_iso9660_mount): Likewise. - (grub_iso9660_mount): Likewise. - (grub_iso9660_read_symlink): Likewise. Also, remove the nonsense - return statement. - (grub_iso9660_iterate_dir): Likewise. - (grub_iso9660_label): Cast DATA->VOLDESC.VOLNAME to char *. - - * fs/hfs.c (grub_hfs_read_file): Change the types of SECTOR and - LEN to grub_disk_addr_t and grub_size_t, respectively. - - * fs/hfsplus.c (grub_hfsplus_read_file): Likewise. - - * fs/jfs.c (grub_jfs_read_file): Likewise. - - * fs/minix.c (grub_jfs_read_file): Likewise. - - * fs/sfs.c (grub_jfs_read_file): Likewise. - - * fs/ufs.c (grub_jfs_read_file): Likewise. - - * fs/xfs.c (grub_jfs_read_file): Likewise. - - * fs/fat.c (grub_fat_read_data): Change the types of SECTOR, LEN - and SIZE to grub_disk_addr_t, grub_size_t and grub_size_t, - respectively. - - * fs/ext2.c (grub_ext2_read_block): When an error happens, set - BLKNR to -1 instead of returning GRUB_ERRNO. - (grub_ext2_read_file): Change the types of SECTOR and - LEN to grub_disk_addr_t and grub_size_t, respectively. - - * fs/affs.c (grub_affs_read_file): Change the types of SECTOR and - LEN to grub_disk_addr_t and grub_size_t, respectively. - - * font/manager.c (grub_font_get_glyph): Cast BITMAP to char * for - grub_file_read. - - * disk/ieee1275/ofdisk.c (grub_ofdisk_read): Fix the format - string. Do not cast SECTOR explicitly. - - * disk/i386/pc/biosdisk.c (grub_biosdisk_open): Change the type of - TOTAL_SECTORS to grub_uint64_t. Do not mask DRP->TOTAL_SECTORS. - (grub_biosdisk_rw): Change the types of SECTOR and SIZE to - grub_disk_addr_t and grub_size_t, respectively. If the sector is - over 2TB and LBA mode is not supported, raise an error. - (get_safe_sectors): New function. - (grub_biosdisk_read): Use get_safe_sectors. - (grub_biosdisk_write): Likewise. - - * disk/efi/efidisk.c (grub_efidisk_read): Fix the format string. - (grub_efidisk_write): Likewise. - - * disk/loopback.c (delete_loopback): Cosmetic changes. - (grub_cmd_loopback): Likewise. Also, test NEWDEV->FILENAME - correctly. - (grub_loopback_open): Likewise. - (grub_loopback_read): Likewise. Also, change the type of POS to - grub_off_t, and fix the usage of grub_memset. - - * commands/i386/pc/play.c: Include grub/machine/time.h. - - * commands/ls.c (grub_ls_list_files): Use "llu" instead of "d" to - print FILE->SIZE. - - * commands/configfile.c: Include grub/env.h. - - * commands/cmp.c (grub_cmd_cmp): Do not use ERR, but use - GRUB_ERRNO directly instead. Change the type of POS to - grub_off_t. Follow the coding standard. - - * commands/blocklist.c: Include grub/partition.h. - (grub_cmd_blocklist): Return an error if the underlying device is - not a disk. Take the starting sector of a partition into account, - if a partition is used. - - * boot/i386/pc/diskboot.S (bootloop): Adapted to the new offset of - a length field. - (lba_mode): Support 64-bit addresses. - (chs_mode): Likewise. - (copy_buffer): Adapted to the new offsets of a length field and a - segment field. - (blocklist_default_start): Allocate 64-bit space. - - * boot/i386/pc/boot.S (force_lba): Removed. - (boot_drive): Moved to under KERNEL_SECTOR. - (kernel_sector): Moved to under KERNEL_SEGMENT. Allocate 64-bit - space. - (real_start): Set %si earlier. Remove code for FORCE_LBA, since it - is useless. - (lba_mode): Refactored to support a 64-bit address. More size - optimization. - (setup_sectors): Likewise. - -2006-06-04 Yoshinori K. Okuji - - * DISTLIST: Added include/grub/i386/linux.h. Removed - include/grub/i386/pc/linux.h - - * configure.ac (AC_INIT): Bumped to 1.94. - - * config.guess: Updated from gnulib. - * config.sub: Likewise. - * install-sh: Likewise. - * mkinstalldirs: Likewise. - -2006-06-02 Yoshinori K. Okuji - - * conf/common.rmk (grub_modules_init.lst): Depended on - grub_emu_SOURCES, excluding grub_emu_init.c, instead of - MODSRCFILES. - - * genmk.rb (PModule::rule): Reverted the previous change. - -2006-06-02 Yoshinori K. Okuji - - * conf/common.rmk (grub_modules_init.lst): Depends on - $(MODSRCFILES). Grep only the files in $(MODSRCFILES). Make sure - that the target does not exist before producing. - (grub_modules_init.h): Remove the target before generating. - (grub_emu_init.c): Likewise. - - * genmk.rb (PModule::rule): Add source files into MODSRCFILES. - -2006-05-31 Jeroen Dekkers - - * configure.ac: Don't set host_m32 for x86_64. Also reset LIBS - for the target-specific tests. Make sure that we also have the - up-to-date target variables for those tests. - -2006-05-31 Yoshinori K. Okuji - - * genmk.rb (Image::rule): Prefix CFLAGS or ASFLAGS with TARGET_. - (PModule::rule): Likewise. - -2006-05-31 Yoshinori K. Okuji - - * genmk.rb (Image::rule): Set FLAG to CFLAGS or ASFLAGS instead of - TARGET_CFLAGS or TARGET_ASFLAGS. There is no reason why - target-specific flags should be prefixed. - (PModule::rule): Likewise. - -2006-05-30 Yoshinori K. Okuji - - * configure.ac (CMP): Check if cmp is available explicitly. - -2006-05-29 Yoshinori K. Okuji - - * util/powerpc/ieee1275/grub-install.in (host_cpu): Removed. - (target_cpu): New variable. - (pkglibdir): Use target_cpu instead of host_cpu. - - * util/i386/pc/grub-install.in (host_cpu): Removed. - (target_cpu): New variable. - (pkglibdir): Use target_cpu instead of host_cpu. - - * util/genmoddep.c: Removed. - - * kern/efi/mm.c (filter_memory_map): Use GRUB_CPU_SIZEOF_VOID_P - instead of GRUB_HOST_SIZEOF_VOID_P. - * kern/dl.c: Likewise. - - * include/grub/i386/types.h (GRUB_HOST_SIZEOF_VOID_P): Renamed to - ... - (GRUB_TARGET_SIZEOF_VOID_P): ... this. - (GRUB_HOST_SIZEOF_LONG): Renamed to ... - (GRUB_TARGET_SIZEOF_LONG): ... this. - (GRUB_HOST_WORDS_BIGENDIAN): Renamed to ... - (GRUB_TARGET_WORDS_BIGENDIAN): ... this. - * include/grub/powerpc/types.h (GRUB_HOST_SIZEOF_VOID_P): Renamed - to ... - (GRUB_TARGET_SIZEOF_VOID_P): ... this. - (GRUB_HOST_SIZEOF_LONG): Renamed to ... - (GRUB_TARGET_SIZEOF_LONG): ... this. - (GRUB_HOST_WORDS_BIGENDIAN): Renamed to ... - (GRUB_TARGET_WORDS_BIGENDIAN): ... this. - * include/grub/sparc64/types.h (GRUB_HOST_SIZEOF_VOID_P): Renamed - to ... - (GRUB_TARGET_SIZEOF_VOID_P): ... this. - (GRUB_HOST_SIZEOF_LONG): Renamed to ... - (GRUB_TARGET_SIZEOF_LONG): ... this. - (GRUB_HOST_WORDS_BIGENDIAN): Renamed to ... - (GRUB_TARGET_WORDS_BIGENDIAN): ... this. - - * include/grub/types.h [!GRUB_UTIL] (GRUB_CPU_SIZEOF_VOID_P): Use - GRUB_TARGET_SIZEOF_VOID_P instead of GRUB_HOST_SIZEOF_VOID_P. - [!GRUB_UTIL] (GRUB_CPU_SIZEOF_LONG): Use GRUB_TARGET_SIZEOF_LONG - instead of GRUB_HOST_SIZEOF_LONG. - [!GRUB_UTIL]: Refer to GRUB_TARGET_WORDS_BIGENDIAN instead of - GRUB_HOST_WORDS_BIGENDIAN to define or undefine - GRUB_CPU_WORDS_BIGENDIAN. - Refer to SIZEOF_VOID_P instead of GRUB_HOST_SIZEOF_VOID_P to - define grub_host_addr_t, grub_host_off_t, grub_host_size_t and - grub_host_ssize_t. - - * conf/i386-efi.rmk (noinst_UTILITIES): Removed. - (genmoddep_SOURCES): Likewise. - * conf/i386-pc.rmk (noinst_UTILITIES): Likewise. - (genmoddep_SOURCES): Likewise. - * conf/conf/powerpc-ieee1275.rmk (noinst_UTILITIES): Likewise. - (genmoddep_SOURCES): Likewise. - * conf/conf/conf/sparc64-ieee1275.rmk (noinst_UTILITIES): - Likewise. - (genmoddep_SOURCES): Likewise. - - * genmoddep.awk: New file. - - * genmk.rb (Image::rule): Use TARGET_CC, TARGET_CPPFLAGS, - TARGET_CFLAGS, TARGET_ASFLAGS and TARGET_LDFLAGS instead of CC, - CPPFLAGS, CFLAGS, ASFLAGS and LDFLAGS, respectively. - (PModule::rule): Likewise. - (Program::rule): Likewise. - (Utility::rule): Use CC, CPPFLAGS, CFLAGS and LDFLAGS instead of - BUILD_CC, BUILD_CPPFLAGS, BUILD_CFLAGS and BUILD_LDFLAGS, - respectively. - - * configure.ac: Rewritten intensively to use host and target - instead of build and host, respectively. - - * Makefile.in (pkglibdir): Use target_cpu instead of host_cpu. - (host_cpu): Removed. - (target_cpu): New variable. - (CPPFLAGS): Added @CPPFLAGS@ and -DGRUB_LIBDIR=\"$(pkglibdir)\". - (BUILD_CC): Removed. - (BUILD_CFLAGS): Likewise. - (BUILD_CPPFLAGS): Likewise. - (TARGET_CC): New variable. - (TARGET_CFLAGS): Likewise. - (TARGET_CPPFLAGS): Likewise. - (TARGET_LDFLAGS): Likewise. - (AWK): Likewise. - (include): Use target_cpu instead of host_cpu. - (moddep.lst:): Use genmoddep.awk instead of genmoddep. - - * DISTLIST: Added genmoddep.awk. Removed util/genmoddep.c. - -2006-05-29 Vesa Jaaskelainen - - * include/grub/script.h (grub_script_cmdif): Renamed field 'bool' to - 'exec_to_evaluate'. Renamed field 'true' to 'exec_on_true'. Renamed - field 'false' to 'exec_on_false'. - (grub_script_create_cmdif): Renamed argument names to reflect above - changes. - - * normal/execute.c (grub_script_execute_cmdif): Likewise. - - * normal/script.c (grub_script_create_cmdif): Likewise. - -2006-05-28 Yoshinori K. Okuji - - * fs/hfsplus.c (grub_hfsplus_btree_recoffset): Moved to near the - top. - (grub_hfsplus_btree_recptr): Likewise. - (grub_hfsplus_find_block): Do not take RETRY any longer. Use - FILEBLOCK both to pass a block number and store next block - number. - (grub_hfsplus_read_block): Rewritten heavily to support an extent - overflow file correctly. Specify errors appropriately, because - fshelp expects that GRUB_ERRNO is set when fails. Reuse - grub_hfsplus_btree_recptr to get the pointer to a found key. - (grub_hfsplus_btree_search): Return 1 instead of 0 when no match - is found. - - * conf/i386-efi.rmk (pkgdata_MODULES): Added _linux.mod and - linux.mod. - (_linux_mod_SOURCES): New variable. - (_linux_mod_CFLAGS): Likewise. - (_linux_mod_LDFLAGS): Likewise. - (linux_mod_SOURCES): Likewise. - (linux_mod_CFLAGS): Likewise. - (linux_mod_LDFLAGS): Likewise. - - * DISTLIST: Added loader/i386/efi/linux.c, - loader/i386/efi/linux_normal.c and - include/grub/i386/efi/loader.h. - - * loader/i386/efi/linux.c: New file. - * loader/i386/efi/linux_normal.c: Likewise. - * include/grub/i386/efi/loader.h: Likewise. - -2006-05-27 Yoshinori K. Okuji - - * commands/blocklist.c: New file. - - * DISTLIST: Added commands/blocklist.c. - - * term/efi/console.c (grub_console_highlight_color): Use a lighter - color for the background, and a darker color for the foreground. - (grub_console_checkkey): Return READ_KEY. - (grub_console_cls): Set the background to - GRUB_EFI_BACKGROUND_BLACK temporarily to clean out the screen. - - * kern/efi/efi.c (grub_efi_exit_boot_services): New function. - - * include/grub/i386/linux.h (struct linux_kernel_params): Fixed - the size of "padding5", "hd0_drive_info" and "hd1_drive_info". - - * include/grub/efi/efi.h (grub_efi_exit_boot_services): New - prototype. - - * include/grub/efi/api.h (GRUB_EFI_TEXT_ATTR): Do not shift - BG. The spec is wrong again. - - * include/grub/normal.h [GRUB_UTIL] (grub_blocklist_init): New - prototype. - [GRUB_UTIL] (grub_blocklist_fini): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added - commands/blocklist.c. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * conf/common.rmk (pkgdata_MODULES): Added blocklist.mod. - (blocklist_mod_SOURCES): New variable. - (blocklist_mod_CFLAGS): Likewise. - (blocklist_mod_LDFLAGS): Likewise. - -2006-05-20 Yoshinori K. Okuji - - * boot/i386/pc/boot.S (real_start): Set %si earlier to eliminate - duplication. - (lba_mode): Use %eax more intensively to reduce the code size. - -2006-05-20 Marco Gerards - - * normal/lexer.c (grub_script_yylex): Don't filter out newlines. - - * normal/parser.y (commandblock): Defined as . A subroutine - for `menuentry'. - (script): Accept leading newlines. - (newlines): New rule to describe 0 or more newlines. - (commands): Accept `command' with trailing newline. Fixed the - order in which arguments were passed to `grub_script_add_cmd'. - Accept commands separated by newlines. - (function): Changed to accept newlines. - (menuentry) Rewritten. - - * normal/script.c (grub_script_create_cmdmenu): Add new entries in - front of the list, instead of to the end. - -2006-05-19 Yoshinori K. Okuji - - * util/i386/pc/grub-install.in (bindir): New variable. - (grub_mkimage): Use BINDIR instead of SBINDIR. Reported by Lee - Shaver . - -2006-05-14 Yoshinori K. Okuji - - * kern/i386/pc/startup.S: Include grub/cpu/linux.h instead of - grub/machine/linux.h - * loader/i386/pc/linux.c: Likewise. - - * include/grub/i386/pc/linux.h: Moved to ... - * include/grub/i386/linux.h: ... here. - - * include/grub/i386/linux.h (struct linux_kernel_params): New - struct. - -2006-05-09 Vesa Jaaskelainen - - * video/i386/pc/vbe.c (grub_video_vbe_fill_rect): Corrected bounds - checking. - (grub_video_vbe_blit_glyph): Likewise. - (grub_video_vbe_blit_bitmap): Likewise. - (grub_video_vbe_blit_render_target): Likewise. - -2006-05-09 Yoshinori K. Okuji - - * configure.ac (--with-platform): Properly quote the square - brackets. - -2006-05-08 Marco Gerards - - * conf/powerpc-ieee1275.rmk (grubof_HEADERS): Renamed from - this... - (kernel_elf_HEADERS): ...to this. Updated all users. - (grubof_symlist.c): Renamed from this... - (kernel_elf_symlist.c): ...to this. Updated all users. - (pkgdata_PROGRAMS): Changed `grubof' to `kernel.elf'. - (grubof_SOURCES): Renamed from this... - (kernel_elf_SOURCES): ...to this. - (grubof_HEADERS): Renamed from this... - (kernel_elf_HEADERS): ...to this. - (grubof_CFLAGS): Renamed from this... - (kernel_elf_CFLAGS): ...to this. - (grubof_ASFLAGS): Renamed from this... - (kernel_elf_ASFLAGS): ...to this. - (grubof_LDFLAGS): Renamed from this... - (kernel_elf_LDFLAGS): ...to this. - - * conf/sparc64-ieee1275.rmk (grubof_HEADERS): Renamed from - this... - (kernel_elf_HEADERS): ...to this. Updated all users. - (grubof_symlist.c): Renamed from this... - (kernel_elf_symlist.c): ...to this. Updated all users. - (pkgdata_PROGRAMS): Changed `grubof' to `kernel.elf'. - (grubof_SOURCES): Renamed from this... - (kernel_elf_SOURCES): ...to this. - (grubof_HEADERS): Renamed from this... - (kernel_elf_HEADERS): ...to this. - (grubof_CFLAGS): Renamed from this... - (kernel_elf_CFLAGS): ...to this. - (grubof_ASFLAGS): Renamed from this... - (kernel_elf_ASFLAGS): ...to this. - (grubof_LDFLAGS): Renamed from this... - (kernel_elf_LDFLAGS): ...to this. - - * util/powerpc/ieee1275/grub-mkimage.c (add_segments): Use - `kernel.elf' instead of `grubof'. - -2006-05-08 Yoshinori K. Okuji - - Add --with-platform to configure. Use pkglibdir instead of - pkgdatadir. This is reported by Roger Leigh. - - * util/powerpc/ieee1275/grub-install.in (datadir): Removed. - (host_vendor): Likewise. - (host_os): Likewise. - (pkgdatadir): Likewise. - (platform): New variable. - (pkglibdir): Likewise. - Use PKGLIBDIR instead of PKGDATADIR. - - * util/i386/pc/grub-install.in (datadir): Removed. - (host_vendor): Likewise. - (host_os): Likewise. - (pkgdatadir): Likewise. - (platform): New variable. - (pkglibdir): Likewise. - Use PKGLIBDIR instead of PKGDATADIR. - - * util/powerpc/ieee1275/grub-mkimage.c (usage): Use GRUB_LIBDIR - instead of GRUB_DATADIR. - (main): Likewise. - * util/i386/pc/grub-mkimage.c (usage): Likewise. - (main): Likewise. - * util/i386/efi/grub-mkimage.c (usage): Likewise. - (main): Likewise. - - * configure.ac (--with-platform): New option. - Use PLATFORM instead of HOST_VENDOR to specify a platform. - - * Makefile.in: Include a makefile based on PLATFORM instead of - HOST_VENDOR. - (pkgdatadir): Not appended by the machine type. - (pkglibdir): Appended by the machine type. - (host_vendor): Removed. - (platform): New variable. - (BUILD_CPPFLAGS): Specify GRUB_LIBDIR instead of GRUB_DATADIR. - (install-local): Use PKGLIBDIR instead of PKGDATADIR. - (uninstall): Likewise. - -2006-05-07 Yoshinori K. Okuji - - Use the environment context in the menu. Remove the commands - "default" and "timeout", and use variables instead. - - * normal/menu.c: Include grub/env.h. - (print_entry): Cast TITLE to silence gcc. - (get_timeout): New function. - (set_timeout): Likewise. - (get_entry_number): Likewise. - (run_menu): Use a default entry, a fallback entry and a timeout - in the environment variables "default", "fallback" and - "timeout". Also, tweak the default entry if it is not within the - current menu entries. - (grub_menu_run): Use a fallback entry in the environment variable - "fallback". - - * normal/main.c (read_config_file): Do not initialize - NEWMENU->DEFAULT_ENTRY, NEWMENU->FALLBACK_ENTRY or - NEWMENU->TIMEOUT. - (grub_normal_execute): Use a data slot to store the menu. - - * include/grub/normal.h (struct grub_menu): Removed default_entry, - fallback_entry and timeout. - (struct grub_menu_list): Removed. - (grub_menu_list_t): Likewise. - (struct grub_context): Likewise. - (grub_context_t): Likewise. - (grub_context_get): Likewise. - (grub_context_get_current_menu): Likewise. - (grub_context_push_menu): Likewise. - (grub_context_pop_menu): Likewise. - (grub_default_init): Likewise. - (grub_default_fini): Likewise. - (grub_timeout_init): Likewise. - (grub_timeout_fini): Likewise. - - * conf/sparc64-ieee1275.rmk (pkgdata_MODULES): Removed default.mod - and timeout.mod. - (normal_mod_SOURCES): Removed normal/context.c. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Removed - commands/default.c, commands/timeout.c and normal/context.c. - (normal_mod_SOURCES): Removed normal/context.c. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Removed commands/default.c, - commands/timeout.c and normal/context.c. - (normal_mod_SOURCES): Removed normal/context.c. - - * conf/i386-efi.rmk (grub_emu_SOURCES): Removed - commands/default.c, commands/timeout.c and normal/context.c. - (normal_mod_SOURCES): Removed normal/context.c. - - * conf/common.rmk (pkgdata_MODULES): Removed default.mod and - timeout.mod. - (default_mod_SOURCES): Removed. - (default_mod_CFLAGS): Likewise. - (default_mod_LDFLAGS): Likewise. - (timeout_mod_SOURCES): Removed. - (timeout_mod_CFLAGS): Likewise. - (timeout_mod_LDFLAGS): Likewise. - - * DISTLIST: Removed commands/default.c, commands/timeout.c and - normal/context.c. - - * commands/default.c: Removed. - * commands/timeout.c: Likewise. - * normal/context.c: Likewise. - -2006-05-07 Vesa Jaaskelainen - - * kern/i386/pc/startup.S (grub_exit): Added missing .code32 tag. - -2006-05-02 Yoshinori K. Okuji - - * kern/env.c (struct grub_env_context): Removed "sorted". Renamed - "next" to "prev" for readability. - (struct grub_env_sorted_var): New struct. - (grub_env_context): Renamed to ... - (initial_context): ... this. - (grub_env_var_context): Renamed to ... - (current_context): ... this. - (grub_env_find): Look only at CURRENT_CONTEXT. - (grub_env_context_open): Rewritten to copy exported variables from - previous context. - (grub_env_context_close): Rewritten according to the new - scheme. Also, add an assertion to prevent the initial context from - removed. - (grub_env_insert): Removed the code for the sorted list. - (grub_env_remove): Likewise. - (grub_env_export): Simply mark the variable with - GRUB_ENV_VAR_GLOBAL. - (grub_env_set): A cosmetic change for naming consistency. - (grub_env_get): Likewise. - (grub_env_unset): Likewise. - (grub_env_iterate): Rewritten to sort variables within this - function. - (grub_register_variable_hook): Fixed for naming consistency. Call - grub_env_find again, only if NAME is not found at the first time. - (mangle_data_slot_name): New function. - (grub_env_set_data_slot): Likewise. - (grub_env_get_data_slot): Likewise. - (grub_env_unset_data_slot): Likewise. - - * include/grub/env.h (grub_env_var_type): New enum. - (GRUB_ENV_VAR_LOCAL): New constant. - (GRUB_ENV_VAR_GLOBAL): Likewise. - (GRUB_ENV_VAR_DATA): Likewise. - (struct grub_env_var): Removed "sort_next" and "sort_prevp". Added - "type". - (grub_env_set): Replace VAR with NAME for consistency. - (grub_register_variable_hook): Likewise. - (grub_env_export): Specify the name of the argument. - (grub_env_set_data_slot): New prototype. - (grub_env_get_data_slot): Likewise. - (grub_env_unset_data_slot): Likewise. - -2006-04-30 Yoshinori K. Okuji - - Extend the loader so that GRUB can accept a loader which comes - back to GRUB when a loaded image exits. Also, this change adds - support for a chainloader on EFI. - - * term/efi/console.c: Include grub/misc.h. - (grub_console_checkkey): Display a scan code on the top for - debugging. This will be removed once the EFI port gets stable. - Correct the scan code mapping. - - * kern/efi/mm.c (sort_memory_map): Sort in a descending order to - allocate memory from larger regions, in order to reduce the number - of allocated regions. Otherwise, the MacOSX loader panics. - (filter_memory_map): Avoid less than 1MB for compatibility with - other loaders. - (add_memory_regions): Allocate from the tail of a region, if - possible, to avoid allocating a region near to 1MB, for the MacOSX - loader. - - * kern/efi/init.c (grub_efi_set_prefix): Specify - GRUB_EFI_IMAGE_HANDLE to grub_efi_get_loaded_image. - - * kern/efi/efi.c (grub_efi_get_loaded_image): Accept a new - argument IMAGE_HANDLE and specify it to get a loaded image. - (grub_arch_modules_addr): Specify GRUB_EFI_IMAGE_HANDLE to - grub_efi_get_loaded_image. - (grub_efi_get_filename): Divide the length by the size of - grub_efi_char16_t. - (grub_efi_get_device_path): New function. - (grub_efi_print_device_path): Print End Device Path nodes. Divide - the length by the size of grub_efi_char16_t for a file path device - path node. - - * kern/loader.c (grub_loader_noreturn): New variable. - (grub_loader_set): Accept a new argument NORETURN. Set - GRUB_LOADER_NORETURN to NORETURN. - All callers changed. - (grub_loader_boot): If GRUB_LOADER_NORETURN is false, do not call - grub_machine_fini. - - * include/grub/efi/efi.h (grub_efi_get_device_path): New - prototype. - (grub_efi_get_loaded_image): Take an argument to specify an image - handle. - - * include/grub/loader.h (grub_loader_set): Added one more argument - NORETURN. - - * disk/efi/efidisk.c (make_devices): Use grub_efi_get_device_path - instead of grub_efi_open_protocol. - (grub_efidisk_get_device_name): Likewise. - (grub_efidisk_close): Print a newline. - (grub_efidisk_get_device_handle): Fixed to use - GRUB_EFI_DEVICE_PATH_SUBTYPE instead of - GRUB_EFI_DEVICE_PATH_TYPE. - - * disk/efi/efidisk.c (device_path_guid): Moved to ... - * kern/efi/efi.c (device_path_guid): ... here. - - * conf/i386-efi.rmk (pkgdata_MODULES): Added _chain.mod and - chain.mod. - (kernel_mod_HEADERS): Added efi/disk.h. - (_chain_mod_SOURCES): New variable. - (_chain_mod_CFLAGS): Likewise. - (_chain_mod_LDFLAGS): Likewise. - (chain_mod_SOURCES): Likewise. - (chain_mod_CFLAGS): Likewise. - (chain_mod_LDFLAGS): Likewise. - - * DISTLIST: Added include/grub/efi/chainloader.h, - loader/efi/chainloader.c and loader/efi/chainloader_normal.c. - - * include/grub/efi/chainloader.h: New file. - * loader/efi/chainloader.c: Likewise. - * loader/efi/chainloader_normal.c: Likewise. - -2006-04-30 Marco Gerards - - * commands/configfile.c (grub_cmd_source): New function. - (GRUB_MOD_INIT): Register the commands `source' and `.'. - (GRUB_MOD_FINI): De-register the commands `source' and `.'. - -2006-04-30 Marco Gerards - - * normal/execute.c (grub_script_execute_cmd): Change the return - type to `grub_err_t'. Correctly return the error. - (grub_script_execute_cmdline): In case a command line is not a - command or a function, try to interpret it as an assignment. - -2006-04-30 Yoshinori K. Okuji - - * fs/hfsplus.c (grub_hfsplus_read_block): Fixed a memory leak. - (grub_hfsplus_iterate_dir): Reordered to skip unknown nodes. Also, - skip a node whose name is obviously invalid as UTF-16, - i.e. contains a NUL character. Stop the iteration when the last - directory entry is found. Instead of using the return value of - grub_hfsplus_btree_iterate_node, store the value in RET and use - it, because the iterator can be stopped by the last directory - entry. - -2006-04-30 Marco Gerards - - * include/grub/env.h (grub_env_export): New prototype. Reported - by Jan C. Kleinsorge . - -2006-04-30 Marco Gerards - - * fs/hfsplus.c (grub_hfsplus_iterate_dir): Correctly calculate the - size of the extents in a catalog file record. - -2006-04-29 Marco Gerards - - * commands/configfile.c (grub_cmd_configfile): Execute the - configfile within its own context. - - * include/grub/env.h (grub_env_context_open): New prototype. - (grub_env_context_close): Likewise. - - * kern/env.c (grub_env): Removed. - (grub_env_sorted): Likewise. - (grub_env_context): New variable. - (grub_env_var_context): Likewise. - (grub_env_find): Search both the active context and the global - context. - (grub_env_context_open): New function. - (grub_env_context_close): Likewise. - (grub_env_insert): Likewise. - (grub_env_remove): Likewise. - (grub_env_export): Likewise. - (grub_env_set): Changed to use helper functions to avoid code - duplication. - (grub_env_iterate): Rewritten so both the current context and the - global context are being used. - - * normal/command.c (export_command): New function. - (grub_command_init): Register the `export' function. - -2006-04-26 Yoshinori K. Okuji - - * util/i386/pc/grub-mkimage.c (compress_kernel): Cast arguments - explicitly to suppress gcc's warnings. - * fs/fat.c (grub_fat_find_dir): Likewise. - (grub_fat_label): Likewise. - * fs/xfs.c (grub_xfs_read_inode): Likewise. - (grub_xfs_mount): Likewise. - (grub_xfs_label): Likewise. - * fs/affs.c (grub_affs_mount): Likewise. - (grub_affs_label): Likewise. - (grub_affs_iterate_dir): Likewise. - * fs/sfs.c (grub_sfs_mount): Likewise. - (grub_sfs_iterate_dir): Likewise. - * fs/ufs.c (grub_ufs_lookup_symlink): Likewise. - * fs/hfs.c (grub_hfs_mount): Likewise. - (grub_hfs_cmp_catkeys): Likewise. - (grub_hfs_find_dir): Likewise. - (grub_hfs_dir): Likewise. - (grub_hfs_label): Likewise. - * fs/jfs.c (grub_jfs_mount): Likewise. - (grub_jfs_opendir): Likewise. - (grub_jfs_getent): Likewise. - (grub_jfs_lookup_symlink): Likewise. - (grub_jfs_label): Likewise. - * fs/hfsplus.c (grub_hfsplus_cmp_catkey): Likewise. - (grub_hfsplus_iterate_dir): Likewise. - (grub_hfsplus_btree_iterate_node): Made static. - - * util/grub-emu.c (prefix): New variable. - (grub_machine_set_prefix): New function. - (main): Do not set the environment variable "prefix" here. Only - set PREFIX, which is used later by grub_machine_set_prefix. - - * include/grub/video.h: Do not include grub/symbol.h. - (grub_video_register): Not exported. This symbol is not defined in - the kernel. - (grub_video_unregister): Likewise. - (grub_video_iterate): Likewise. - (grub_video_setup): Likewise. - (grub_video_restore): Likewise. - (grub_video_get_info): Likewise. - (grub_video_get_blit_format): Likewise. - (grub_video_set_palette): Likewise. - (grub_video_get_palette): Likewise. - (grub_video_set_viewport): Likewise. - (grub_video_get_viewport): Likewise. - (grub_video_map_color): Likewise. - (grub_video_map_rgb): Likewise. - (grub_video_map_rgba): Likewise. - (grub_video_fill_rect): Likewise. - (grub_video_blit_glyph): Likewise. - (grub_video_blit_bitmap): Likewise. - (grub_video_blit_render_target): Likewise. - (grub_video_scroll): Likewise. - (grub_video_swap_buffers): Likewise. - (grub_video_create_render_target): Likewise. - (grub_video_delete_render_target): Likewise. - (grub_video_set_active_render_target): Likewise. - - * include/grub/symbol.h [GRUB_SYMBOL_GENERATOR] (EXPORT_FUNC): - Undefined. - [GRUB_SYMBOL_GENERATOR] (EXPORT_VAR): Likewise. - - * conf/sparc64-ieee1275.rmk (grubof_symlist.c): Depended on - config.h. Use gensymlist.sh instead of $(srcdir)/gensymlist.sh. - (kernel_syms.lst): Depended on config.h. Use genkernsyms.sh - instead of $(srcdir)/genkernsyms.sh. - - * conf/powerpc-ieee1275.rmk (grubof_symlist.c): Depended on - config.h. Use gensymlist.sh instead of $(srcdir)/gensymlist.sh. - (kernel_syms.lst): Depended on config.h. Use genkernsyms.sh - instead of $(srcdir)/genkernsyms.sh. - - * conf/i386-pc.rmk (symlist.c): Depended on config.h. Use - gensymlist.sh instead of $(srcdir)/gensymlist.sh. - (kernel_syms.lst): Depended on config.h. Use genkernsyms.sh - instead of $(srcdir)/genkernsyms.sh. - - * conf/i386-efi.rmk (symlist.c): Depended on config.h. Use - gensymlist.sh instead of $(srcdir)/gensymlist.sh. - (kernel_syms.lst): Depended on config.h. Use genkernsyms.sh - instead of $(srcdir)/genkernsyms.sh. - - * configure.ac (AC_CONFIG_FILES): Added gensymlist.sh and - genkernsyms.sh. - - * Makefile.in (DISTCLEANFILES): Added gensymlist.sh and - genkernsyms.sh. - (gensymlist.sh): New target. - (genkernsyms.sh): Likewise. - - * DISTLIST: Removed genkernsyms.sh and gensymlist.sh. Added - genkernsyms.sh.in and gensymlist.sh.in. - - * genkernsyms.sh: Removed. - * gensymlist.sh: Likewise. - - * genkernsyms.sh.in: New file. - * gensymlist.sh.in: Likewise. - -2006-04-25 Hollis Blanchard - - * kern/powerpc/ieee1275/init.c (grub_machine_set_prefix): Do not - clobber "prefix", since we may have already set it manually. - -2006-04-25 Hollis Blanchard - - * kern/misc.c (abort): New alias for grub_abort. - -2006-04-25 Yoshinori K. Okuji - - A new machine-specific function "grub_machine_set_prefix" is - defined. This is called after loading modules, so that a prefix - initialization can use modules. Also, this change adds an - intensive debugging feature for the memory manager via the - configure option "--enable-mm-debug". - - * partmap/gpt.c (gpt_partition_map_iterate): Add one more into - PART.LEN. - - * kern/sparc64/ieee1275/init.c (abort): Removed. - (grub_stop): Likewise. - (grub_exit): New function. - (grub_set_prefix): Renamed to ... - (grub_machine_set_prefix): ... this. - (grub_machine_init): Do not call grub_set_prefix. - - * kern/powerpc/ieee1275/init.c (grub_set_prefix): Renamed to ... - (grub_machine_set_prefix): ... this. - (grub_machine_init): Do not call grub_set_prefix. - - * kern/i386/pc/init.c (grub_machine_set_prefix): New function. - (grub_machine_init): Do not set the prefix here. - - * kern/i386/efi/init.c (grub_machine_set_prefix): New function. - - * kern/efi/init.c: Include grub/mm.h. - (grub_efi_set_prefix): New function. - - * kern/efi/efi.c (grub_exit): Call grub_efi_fini. - (grub_efi_get_filename): New function. - (grub_print_device_path): Renamed to ... - (grub_efi_print_device_path): ... this. - - * kern/mm.c [MM_DEBUG] (grub_malloc): Undefined. - [MM_DEBUG] (grub_realloc): Likewise. - [MM_DEBUG] (grub_free): Likewise. - [MM_DEBUG] (grub_memalign): Likewise. - [MM_DEBUG] (grub_mm_debug): New variable. - [MM_DEBUG] (grub_debug_malloc): New function. - [MM_DEBUG] (grub_debug_free): New function. - [MM_DEBUG] (grub_debug_realloc): New function. - [MM_DEBUG] (grub_debug_memalign): New function. - - * kern/misc.c (grub_abort): Print a newline to distinguish - the message. - - * kern/main.c (grub_main): Call grub_machine_set_prefix and - grub_set_root_dev after loading modules. This is necessary when - setting a prefix depends on modules. - - * include/grub/efi/efi.h (grub_print_device_path): Renamed to ... - (grub_efi_print_device_path): ... this. - (grub_efi_get_filename): New prototype. - (grub_efi_set_prefix): Likewise. - - * include/grub/efi/disk.h: Include grub/efi/api.h, grub/symbol.h - and grub/disk.h. - (grub_efidisk_get_device_handle): New prototype. - (grub_efidisk_get_device_name): Likewise. - - * include/grub/mm.h: Include config.h. - (MM_DEBUG): Removed. - [MM_DEBUG && !GRUB_UTIL] (grub_mm_debug): New prototype. - [MM_DEBUG && !GRUB_UTIL] (grub_malloc): New macro. - [MM_DEBUG && !GRUB_UTIL] (grub_realloc): Likewise. - [MM_DEBUG && !GRUB_UTIL] (grub_memalign): Likewise. - [MM_DEBUG && !GRUB_UTIL] (grub_free): Likewise. - [MM_DEBUG && !GRUB_UTIL] (grub_debug_malloc): New prototype. - [MM_DEBUG && !GRUB_UTIL] (grub_debug_realloc): New prototype. - [MM_DEBUG && !GRUB_UTIL] (grub_debug_memalign): New prototype. - [MM_DEBUG && !GRUB_UTIL] (grub_debug_free): New prototype. - - * include/grub/kernel.h (grub_machine_set_prefix): New prototype. - - * disk/efi/efidisk.c: Include grub/partition.h. - (iterate_child_devices): New function. - (add_device): First, compare only last device path nodes, so that - devices are sorted by the types. - (grub_efidisk_get_device_handle): New function. - (grub_efidisk_get_device_name): Likewise. - - * configure.ac (--enable-mm-debug): New option to enable the - memory manager debugging feature. This makes the binary much - bigger, so is disabled by default. - -2006-04-23 Yoshinori K. Okuji - - Use grub_abort instead of grub_stop, and grub_exit must be - define in each architecture now. Also, this change adds support - for EFI disks. - - * util/i386/pc/grub-probefs.c: Include grub/term.h. - (grub_getkey): New function. - (grub_term_get_current): Likewise. - - * util/i386/pc/grub-setup.c: Include grub/term.h. - (grub_getkey): New function. - (grub_term_get_current): Likewise. - - * util/misc.c (grub_stop): Renamed to ... - (grub_exit): ... this. - - * kern/powerpc/ieee1275/init.c (abort): Renamed to ... - (grub_exit): ... this. - (grub_machine_init): Use grub_abort instead of abort. - (grub_stop): Removed. - - * kern/powerpc/ieee1275/cmain.c (cmain): Use grub_abort instead of - abort. - - * kern/i386/pc/startup.S (grub_exit): New function. - (cold_reboot): New label. - - * kern/efi/init.c: Include grub/efi/disk.h and grub/env.h. - (grub_efi_init): Call grub_efidisk_init. - (grub_efi_fini): Call grub_efidisk_fini. - - * kern/efi/efi.c: Include grub/mm.h. - (grub_efi_console_control_guid): Renamed to ... - (console_control_guid): ... this. - (grub_efi_loaded_image_guid): Renamed to ... - (loaded_image_guid): ... this. - (grub_efi_locate_handle): New function. - (grub_efi_open_protocol): Likewise. - (grub_efi_set_text_mode): Use CONSOLE_CONTROL_GUID instead of - GRUB_EFI_CONSOLE_CONTROL_GUID. - (grub_efi_exit): Removed. - (grub_stop): Likewise. - (grub_efi_get_loaded_image): Use grub_efi_open_protocol. - (grub_exit): New function. - (grub_print_device_path): Likewise. - - * kern/rescue.c (grub_rescue_cmd_exit): New function. - (grub_enter_rescue_mode): Register "exit". - - * kern/misc.c (grub_real_dprintf): A cosmetic change. - (grub_abort): New function. - - * kern/err.c (grub_fatal): Use grub_abort instead of grub_stop. - - * include/grub/sparc64/ieee1275/kernel.h (abort): Removed. - - * include/grub/powerpc/ieee1275/kernel.h (abort): Removed. - - * include/grub/efi/efi.h (grub_efi_exit): Removed. - (grub_print_device_path): New prototype. - (grub_efi_locate_handle): Likewise. - (grub_efi_open_protocol): Likewise. - - * include/grub/efi/disk.h (grub_efidisk_fini): New file. - * disk/efi/efidisk.c: Likewise. - - * DISTLIST: Added disk/efi/efidisk.c and include/grub/efi/disk.h. - - * include/grub/efi/console_control.h - (GRUB_EFI_CONSOLE_CONTROL_GUID): Use an array for the last 8 bytes. - - * include/grub/efi/api.h (GRUB_EFI_LOADED_IMAGE_GUID): Specify the - last 8 bytes as an array. - (GRUB_EFI_DISK_IO_GUID): New macro. - (GRUB_EFI_BLOCK_IO_GUID): Likewise. - (GRUB_EFI_DEVICE_PATH_GUID): Likewise. - (grub_efi_ipv6_address_t): Change the type to grub_uint16_t from - grub_uint8_t. - (struct grub_efi_guid): Use an array to specify the last 8 bytes. - (struct grub_efi_device_path): Rename the member "sub_type" to - "subtype". - (GRUB_EFI_DEVICE_PATH_TYPE): New macro. - (GRUB_EFI_DEVICE_PATH_SUBTYPE): Likewise. - (GRUB_EFI_DEVICE_PATH_LENGTH): Likewise. - (GRUB_EFI_END_DEVICE_PATH_TYPE): Likewise. - (GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE): Likewise. - (GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE): Likewise. - (GRUB_EFI_END_ENTIRE_DEVICE_PATH): Likewise. - (GRUB_EFI_NEXT_DEVICE_PATH): Likewise. - (GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE): Likewise. - (GRUB_EFI_PCI_DEVICE_PATH_SUBTYPE): Likewise. - (struct grub_efi_pci_device_path): New structure. - (grub_efi_pci_device_path_t): New type. - (GRUB_EFI_PCCARD_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_pccard_device_path): New structure. - (grub_efi_pccard_device_path_t): New type. - (GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_memory_mapped_device_path): New structure. - (grub_efi_memory_mapped_device_path_t): New type. - (GRUB_EFI_VENDOR_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_vendor_device_path): New structure. - (grub_efi_vendor_device_path_t): New type. - (GRUB_EFI_CONTROLLER_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_controller_device_path): New structure. - (grub_efi_controller_device_path_t): New type. - (GRUB_EFI_ACPI_DEVICE_PATH_TYPE): New macro. - (GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE): Likewise. - (struct grub_efi_acpi_device_path): New structure. - (grub_efi_acpi_device_path_t): New type. - (GRUB_EFI_EXPANDED_ACPI_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_expanded_acpi_device_path): New structure. - (grub_efi_expanded_acpi_device_path_t): New type. - (GRUB_EFI_EXPANDED_ACPI_HIDSTR): New macro. - (GRUB_EFI_EXPANDED_ACPI_UIDSTR): Likewise. - (GRUB_EFI_EXPANDED_ACPI_CIDSTR): Likewise. - (GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE): Likewise. - (GRUB_EFI_ATAPI_DEVICE_PATH_SUBTYPE): Likewise. - (struct grub_efi_atapi_device_path): New structure. - (grub_efi_atapi_device_path_t): New type. - (GRUB_EFI_FIBRE_CHANNEL_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_fibre_channel_device_path): New structure. - (grub_efi_fibre_channel_device_path_t): New type. - (GRUB_EFI_1394_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_1394_device_path): New structure. - (grub_efi_1394_device_path_t): New type. - (GRUB_EFI_USB_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_usb_device_path): New structure. - (grub_efi_usb_device_path_t): New type. - (GRUB_EFI_USB_CLASS_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_usb_class_device_path): New structure. - (grub_efi_usb_class_device_path_t): New type. - (GRUB_EFI_I2O_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_i2o_device_path): New structure. - (grub_efi_i2o_device_path_t): New type. - (GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_mac_address_device_path): New structure. - (grub_efi_mac_address_device_path_t): New type. - (GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_ipv4_device_path): New structure. - (grub_efi_ipv4_device_path_t): New type. - (GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_ipv6_device_path): New structure. - (grub_efi_ipv6_device_path_t): New type. - (GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_infiniband_device_path): New structure. - (grub_efi_infiniband_device_path_t): New type. - (GRUB_EFI_UART_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_uart_device_path): New structure. - (grub_efi_uart_device_path_t): New type. - (GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_vendor_messaging_device_path): New structure. - (grub_efi_vendor_messaging_device_path_t): New type. - (GRUB_EFI_MEDIA_DEVICE_PATH_TYPE): New macro. - (GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE): Likewise. - (struct grub_efi_hard_drive_device_path): New structure. - (grub_efi_hard_drive_device_path_t): New type. - (GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_cdrom_device_path): New structure. - (grub_efi_cdrom_device_path_t): New type. - (GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_vendor_media_device_path): New structure. - (grub_efi_vendor_media_device_path_t): New type. - (GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_file_path_device_path): New structure. - (grub_efi_file_path_device_path_t): New type. - (GRUB_EFI_PROTOCOL_DEVICE_PATH_SUBTYPE): New macro. - (struct grub_efi_protocol_device_path): New structure. - (grub_efi_protocol_device_path_t): New type. - (GRUB_EFI_BIOS_DEVICE_PATH_TYPE): New macro. - (GRUB_EFI_BIOS_DEVICE_PATH_SUBTYPE): Likewise. - (struct grub_efi_bios_device_path): New structure. - (grub_efi_bios_device_path_t): New type. - (struct grub_efi_disk_io): New structure. - (grub_efi_disk_io_t): New type. - (struct grub_efi_block_io_media): New structure. - (grub_efi_block_io_media_t): New type. - (struct grub_efi_block_io): New structure. - (grub_efi_block_io_t): New type. - - * include/grub/misc.h (grub_stop): Removed. - (grub_exit): New prototype. - (grub_abort): Likewise. - - * include/grub/disk.h (enum grub_disk_dev_id): Added - GRUB_DISK_DEVICE_EFIDISK_ID. - - * conf/i386-efi.rmk (kernel_mod_SOURCES): Added - disk/efi/efidisk.c. - (kernel_syms.lst): Remove the target if an error occurs. - -2006-04-22 Yoshinori K. Okuji - - * kern/misc.c (grub_lltoa): Rewritten the decimal conversion part, - as it was simply too buggy. - -2006-04-21 Yoshinori K. Okuji - - * kern/misc.c (grub_lltoa): New function. - (grub_vsprintf): Added support for the long long suffix, - i.e. "ll". - -2006-04-20 Hollis Blanchard - - * Makefile.in (LDFLAGS): Add variable. - (LD): Remove variable. - * configure.ac: Add -m32 to LDFLAGS. - * genmk.rb (PModule#rule): Use $(CC) instead of $(LD). - * conf/powerpc-ieee1275.rmk (COMMON_LDFLAGS): Add variable. - (grubof_LDFLAGS): Use $(COMMON_LDFLAGS). - (_linux_mod_LDFLAGS, linux_mod_LDFLAGS, normal_mod_LDFLAGS, - suspend_mod_LDFLAGS, reboot_mod_LDFLAGS, halt_mod_LDFLAGS): New - variables. - * conf/sparc64-ieee1275.rmk (COMMON_LDFLAGS): Add -nostdlib. - * conf/i386-pc.rmk (COMMON_LDFLAGS): Add -nostdlib. - * conf/i386-efi.rmk (COMMON_LDFLAGS): Add -nostdlib. - -2006-04-20 Vesa Jaaskelainen - - * term/gfxterm.c (grub_gfxterm_getcharwidth): Fixed character - length for unknown glyph. - -2006-04-20 Yoshinori K. Okuji - - Add support for pre-loaded modules into the EFI port. - - * util/i386/efi/grub-mkimage.c (make_mods_section): Rewritten - completely. Accept one more argument DIR. The caller has changed. - - * kern/i386/efi/init.c (grub_arch_modules_addr): Removed. - - * kern/efi/efi.c: Include grub/efi/pe32.h and grub/kernel.h. - (grub_efi_loaded_image_guid): New variable. - (grub_efi_get_loaded_image): New function. - (grub_arch_modules_addr): Likewise. - - * include/grub/efi/efi.h (grub_efi_get_loaded_image): New - prototype. - - * include/grub/efi/api.h (GRUB_EFI_LOADED_IMAGE_GUID): New macro. - (struct grub_efi_loaded_image): New structure. - (grub_efi_loaded_image_t): New type. - -2006-04-20 Yoshinori K. Okuji - - * loader/i386/pc/linux.c (grub_rescue_cmd_linux): Compare the file - size with GRUB_OS_AREA_SIZE as grub_size_t instead of - grub_ssize_t. Reported by Jeff Chua . - -2006-04-19 Roger Leigh - - * DISTLIST: Added `util/powerpc/ieee1275/grub-install.in'. - -2006-04-19 Yoshinori K. Okuji - - * DISTLIST: Added include/grub/efi/console.h, - include/grub/efi/time.h, include/grub/i386/efi/kernel.h, - kern/efi/init.c, kern/efi/mm.c, and term/efi/console.c. - - * include/grub/efi/console.h: New file. - * include/grub/efi/time.h: Likewise. - * include/grub/i386/efi/kernel.h: Likewise. - * kern/efi/init.c: Likewise. - * kern/efi/mm.c: Likewise. - * term/efi/console.c: Likewise. - - * kern/i386/efi/init.c: Do not include grub/machine/time.h. - (grub_stop): Removed. - (grub_get_rtc): Likewise. - (grub_machine_init): Simply call grub_efi_init. - (grub_machine_fini): Call grub_efi_fini. - - * kern/efi/efi.c: Include grub/machine/time.h and grub/term.h. - (grub_efi_output_string): Removed. - (grub_efi_stall): New function. - (grub_stop): Likewise. - (grub_get_rtc): Likewise. - - * include/grub/efi/efi.h (grub_efi_output_string): Removed. - (grub_efi_stall): New prototype. - (grub_efi_allocate_pages): Likewise. - (grub_efi_free_pages): Likewise. - (grub_efi_get_memory_map): Likewise. - (grub_efi_mm_init): Likewise. - (grub_efi_mm_fini): Likewise. - (grub_efi_init): Likewise. - (grub_efi_fini): Likewise. - - * include/grub/i386/efi/time.h: Do not include - grub/symbol.h. Include grub/efi/time.h. - (GRUB_TICKS_PER_SECOND): Removed. - (grub_get_rtc): Likewise. - - * include/grub/efi/api.h (struct grub_efi_memory_descriptor): - Added padding. The EFI spec is buggy. - (GRUB_EFI_BLACK): New macro. - (GRUB_EFI_BLUE): Likewise. - (GRUB_EFI_GREEN): Likewise. - (GRUB_EFI_CYAN): Likewise. - (GRUB_EFI_RED): Likewise. - (GRUB_EFI_MAGENTA): Likewise. - (GRUB_EFI_BROWN): Likewise. - (GRUB_EFI_LIGHTGRAY): Likewise. - (GRUB_EFI_BRIGHT): Likewise. - (GRUB_EFI_DARKGRAY): Likewise. - (GRUB_EFI_LIGHTBLUE): Likewise. - (GRUB_EFI_LIGHTGREEN): Likewise. - (GRUB_EFI_LIGHTCYAN): Likewise. - (GRUB_EFI_LIGHTRED): Likewise. - (GRUB_EFI_LIGHTMAGENTA): Likewise. - (GRUB_EFI_YELLOW): Likewise. - (GRUB_EFI_WHITE): Likewise. - (GRUB_EFI_BACKGROUND_BLACK): Likewise. - (GRUB_EFI_BACKGROUND_BLUE): Likewise. - (GRUB_EFI_BACKGROUND_GREEN): Likewise. - (GRUB_EFI_BACKGROUND_CYAN): Likewise. - (GRUB_EFI_BACKGROUND_RED): Likewise. - (GRUB_EFI_BACKGROUND_MAGENTA): Likewise. - (GRUB_EFI_BACKGROUND_BROWN): Likewise. - (GRUB_EFI_BACKGROUND_LIGHTGRAY): Likewise. - (GRUB_EFI_TEXT_ATTR): Likewise. - - * conf/i386-efi.rmk (kernel_mod_SOURCES): Added kern/efi/efi.c, - kern/efi/init.c, kern/efi/mm.c, and term/efi/console.c. - (kernel_mod_HEADERS): Added efi/time.h. - -2006-04-18 Yoshinori K. Okuji - - * DISTLIST: Added conf/i386-efi.mk, conf/i386-efi.rmk, - include/grub/efi/api.h, include/grub/efi/console_control.h, - include/grub/efi/efi.h, include/grub/efi/pe32.h, - include/grub/i386/efi/time.h, kern/efi/efi.c, - kern/i386/efi/init.c, kern/i386/efi/startup.S, - and util/i386/efi/grub-mkimage.c. - - * Makefile.in (RMKFILES): Added i386-efi.rmk. - - * genmk.rb (PModule#rule): Do not export symbols if - #{prefix}_EXPORTS is set to "no". - - * conf/i386-efi.mk: New file. - * conf/i386-efi.rmk: Likewise. - * include/grub/efi/api.h: Likewise. - * include/grub/efi/console_control.h: Likewise. - * include/grub/efi/efi.h: Likewise. - * include/grub/efi/pe32.h: Likewise. - * include/grub/i386/efi/time.h: Likewise. - * kern/efi/efi.c: Likewise. - * kern/i386/efi/init.c: Likewise. - * kern/i386/efi/startup.S: Likewise. - * util/i386/efi/grub-mkimage.c: Likewise. - -2006-04-17 Marco Gerards - - * include/grub/script.h: Include and - "grub_script.tab.h". - (struct grub_lexer_param): New struct. - (struct grub_parser_param): Likewise. - (grub_script_create_arglist): Pass the state in an argument. - (grub_script_add_arglist): Likewise. - (grub_script_create_cmdline): Likewise. - (grub_script_create_cmdblock): Likewise. - (grub_script_create_cmdif): Likewise. - (grub_script_create_cmdmenu): Likewise. - (grub_script_add_cmd): Likewise. - (grub_script_arg_add): Likewise. - (grub_script_lexer_ref): Likewise. - (grub_script_lexer_deref): Likewise. - (grub_script_lexer_record_start): Likewise. - (grub_script_lexer_record_stop): Likewise. - (grub_script_mem_record): Likewise. - (grub_script_mem_record_stop): Likewise. - (grub_script_malloc): Likewise. - (grub_script_yylex): Likewise. - (grub_script_yyparse): Likewise. - (grub_script_yyerror): Likewise. - (grub_script_yylex): Likewise. - (grub_script_lexer_init): Return the state. - - * normal/lexer.c (grub_script_lexer_state): Removed variable. - (grub_script_lexer_done): Likewise. - (grub_script_lexer_getline): Likewise. - (grub_script_lexer_refs): Likewise. - (script): Likewise. - (newscript): Likewise. - (record): Likewise. - (recording): Likewise. - (recordpos): Likewise. - (recordlen): Likewise. - (grub_script_lexer_init): Return the state instead of setting - global variables. - (grub_script_lexer_ref): Use the newly added argument for state - instead of globals. - (grub_script_lexer_deref): Likewise. - (grub_script_lexer_record_start): Likewise. - (grub_script_lexer_record_stop): Likewise. - (recordchar): Likewise. - (nextchar): Likewise. - (grub_script_yylex2): Likewise. - (grub_script_yylex): Likewise. - (grub_script_yyerror): Likewise. - - * normal/parser.y (func_mem): Removed variable. - (menu_entry): Likewise. - (err): Likewise. - (%lex-param): New parser option. - (%parse-param): Likewise. - (script): Always return the AST. - (argument): Pass the state around. - (arguments): Likewise. - (grubcmd): Likewise. - (commands): Likewise. - (function): Likewise. - (menuentry): Likewise. - (if_statement): Likewise. - (if): Likewise. - - * normal/script.c (grub_script_memused): Removed variable. - (grub_script_parsed): Likewise. - (grub_script_malloc): Added a state argument. Use that instead of - global variables. - (grub_script_mem_record): Likewise. - (grub_script_mem_record_stop): Likewise. - (grub_script_arg_add): Likewise. - (grub_script_add_arglist): Likewise. - (grub_script_create_cmdline): Likewise. - (grub_script_create_cmdif): Likewise. - (grub_script_create_cmdmenu): Likewise. - (grub_script_add_cmd): Likewise. - (grub_script_parse): Setup the state before calling the parser. - -2006-04-16 Marco Gerards - - * normal/command.c (grub_command_init): Remove the title command. - - * normal/lexer.c (grub_script_yylex): Renamed from this... - (grub_script_yylex2): ... to this. - (grub_script_yylex): New function. Temporary - introduced to filter some tokens. - (grub_script_yyerror): Print a newline. - - * normal/main.c (read_config_file): Output information about the - lines that contain errors. Wait for a key after all lines have - been processed. Don't return an empty menu. - - * normal/parser.y (func_mem): Don't initialize. - (menu_entry): Likewise. - (err): New variable. - (script): Don't return anything when an error was encountered. - (ws, returns): Removed rules. - (argument): Disabled concatenated variable support. - (arguments): Remove explicit separators. - (grubcmd): Likewise. - (function): Likewise. - (menuentry): Likewise. - (if): Likewise. - (commands): Likewise. Add error handling. - - * normal/script.c (grub_script_create_cmdline): If - `grub_script_parsed' is 0, assume the parser encountered an error. - -2006-04-02 Yoshinori K. Okuji - - * configure.ac: Add support for EFI. Fix the typo - BUILD_LDDFLAGS. Restore the LDFLAGS after testing. - -2006-04-01 Vesa Jaaskelainen - - * util/unifont2pff.rb: Removed unnecessary byte ordering. Now - foreign multibyte characters should be shown correctly. - -2006-04-01 Vesa Jaaskelainen - - * normal/main.c (grub_normal_menu_addentry): Fixed menu size - calculation. - (read_config_file): Made it to close file before returning. - -2006-03-31 Vesa Jaaskelainen - - * DISTLIST: Added include/grub/i386/pc/vbeblit.h, - include/grub/i386/pc/vbefill.h, video/i386/pc/vbeblit.c, - video/i386/pc/vbefill.c. - - * conf/i386-pc.rmk (vbe_mod_SOURCES): Added video/i386/pc/vbeblit.c, - video/i386/pc/vbefill.c. - - * include/grub/video.h (grub_video_blit_format): New enum. - (grub_video_mode_info): Added new member blit_format. - (grub_video_get_blit_format): New function prototype. - - * include/grub/i386/pc/vbe.h (grub_video_vbe_get_video_ptr): New - function prototype. - (grub_video_vbe_map_rgb): Likewise. - (grub_video_vbe_unmap_color): Likewise. - - * include/grub/i386/pc/vbeblit.h: New file. - - * include/grub/i386/pc/vbefill.h: New file. - - * video/video.c (grub_video_get_blit_format): New function. - (grub_video_vbe_get_video_ptr): Re-declared as non-static. - (grub_video_vbe_map_rgb): Likewise. - (grub_video_vbe_unmap_color): Likewise. - - * video/i386/pc/vbe.c (grub_video_vbe_fill_rect): Changed to use more - optimized fills. - (grub_video_vbe_blit_render_target): Changed to use more optimized - blits. - (grub_video_vbe_setup): Added detection for optimized settings. - (grub_video_vbe_create_render_target): Likewise. - - * video/i386/pc/vbeblit.c: New file. - - * video/i386/pc/vbefill.c: New file. - -2006-03-30 Vesa Jaaskelainen - - * font/manager.c (grub_font_get_glyph): Removed font fixup from - here... - - * util/unifont2pff.rb: ... and moved it to here. Improved argument - parsing to support both hex and dec ranges. If filename was missing - show usage information. - -2006-03-14 Vesa Jaaskelainen - - * DISTLIST: Added include/grub/video.h, term/gfxterm.c, - video/video.c, commands/videotest.c. Removed term/i386/pc/vesafb.c. - - * conf/i386-pc.rmk (pkgdata_MODULES): Added video.mod, - gfxterm.mod, videotest.mod. Removed vga.mod, vesafb.mod. - (video_mod_SOURCES): Added. - (video_mod_CFLAGS): Likewise. - (video_mod_LDFLAGS): Likewise. - (gfxterm_mod_SOURCES): Likewise. - (gfxterm_mod_CFLAGS): Likewise. - (gfxterm_mod_LDFLAGS): Likewise. - (videotest_mod_SOURCES): Likewise. - (videotest_mod_CFLAGS): Likewise. - (videotest_mod_LDFLAGS): Likewise. - (vesafb_mod_SOURCES): Removed. - (vesafb_mod_CFLAGS): Likewise. - (vesafb_mod_LDFLAGS): Likewise. - (vga_mod_SOURCES): Likewise. - (vga_mod_CFLAGS): Likewise. - (vga_mod_LDFLAGS): Likewise. - - * commands/videotest.c: New file. - - * font/manager.c (fill_with_default_glyph): Modified to use - grub_font_glyph. - (grub_font_get_glyph): Likewise. - (fontmanager): Renamed from this... - (font_manager): ... to this. - - * include/grub/font.h (grub_font_glyph): Added new structure. - (grub_font_get_glyph): Modified to use grub_font_glyph. - - * include/grub/misc.h (grub_abs): Added as inline function. - - * include/grub/video.h: New file. - - * include/grub/i386/pc/vbe.h (GRUB_VBE_STATUS_OK): New macro. - (GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL): Likewise. - (GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR): Likewise. - (grub_vbe_get_controller_info): Renamed from this... - (grub_vbe_bios_get_controller_info): ... to this. - (grub_vbe_get_mode_info): Renamed from this... - (grub_vbe_bios_get_mode_info): ... to this. - (grub_vbe_set_mode): Renamed from this... - (grub_vbe_bios_set_mode): ... to this. - (grub_vbe_get_mode): Renamed from this... - (grub_vbe_bios_get_mode): ... to this. - (grub_vbe_set_memory_window): Renamed from this... - (grub_vbe_bios_set_memory_window): ... to this. - (grub_vbe_get_memory_window): Renamed from this... - (grub_vbe_bios_get_memory_window): ... to this. - (grub_vbe_set_scanline_length): Renamed from this... - (grub_vbe_set_scanline_length): ... to this. - (grub_vbe_get_scanline_length): Renamed from this... - (grub_vbe_bios_get_scanline_length): ... to this. - (grub_vbe_set_display_start): Renamed from this... - (grub_vbe_bios_set_display_start): ... to this. - (grub_vbe_get_display_start): Renamed from this... - (grub_vbe_bios_get_display_start): ... to this. - (grub_vbe_set_palette_data): Renamed from this... - (grub_vbe_bios_set_palette_data): ... to this. - (grub_vbe_set_pixel_rgb): Removed. - (grub_vbe_set_pixel_index): Likewise. - - * kern/i386/pc/startup.S (grub_vbe_get_controller_info): Renamed - from this... - (grub_vbe_bios_get_controller_info): ... to this. - (grub_vbe_get_mode_info): Renamed from this... - (grub_vbe_bios_get_mode_info): ... to this. - (grub_vbe_set_mode): Renamed from this... - (grub_vbe_bios_set_mode): ... to this. - (grub_vbe_get_mode): Renamed from this... - (grub_vbe_bios_get_mode): ... to this. - (grub_vbe_set_memory_window): Renamed from this... - (grub_vbe_bios_set_memory_window): ... to this. - (grub_vbe_get_memory_window): Renamed from this... - (grub_vbe_bios_get_memory_window): ... to this. - (grub_vbe_set_scanline_length): Renamed from this... - (grub_vbe_set_scanline_length): ... to this. - (grub_vbe_get_scanline_length): Renamed from this... - (grub_vbe_bios_get_scanline_length): ... to this. - (grub_vbe_set_display_start): Renamed from this... - (grub_vbe_bios_set_display_start): ... to this. - (grub_vbe_get_display_start): Renamed from this... - (grub_vbe_bios_get_display_start): ... to this. - (grub_vbe_set_palette_data): Renamed from this... - (grub_vbe_bios_set_palette_data): ... to this. - (grub_vbe_bios_get_controller_info): Fixed problem with registers - getting corrupted after calling it. Added more pushes and pops. - (grub_vbe_bios_set_mode): Likewise. - (grub_vbe_bios_get_mode): Likewise. - (grub_vbe_bios_get_memory_window): Likewise. - (grub_vbe_bios_set_scanline_length): Likewise. - (grub_vbe_bios_get_scanline_length): Likewise. - (grub_vbe_bios_get_display_start): Likewise. - (grub_vbe_bios_set_palette_data): Likewise. - - * normal/cmdline.c (cl_set_pos): Refresh the screen. - (cl_insert): Likewise. - (cl_delete): Likewise. - - * term/gfxterm.c: New file. - - * term/i386/pc/vesafb.c: Removed file. - - * video/video.c: New file. - - * video/i386/pc/vbe.c (real2pm): Added new function. - (grub_video_vbe_draw_pixel): Likewise. - (grub_video_vbe_get_video_ptr): Likewise. - (grub_video_vbe_get_pixel): Likewise - (grub_video_vbe_init): Likewise. - (grub_video_vbe_fini): Likewise. - (grub_video_vbe_setup): Likewise. - (grub_video_vbe_get_info): Likewise. - (grub_video_vbe_set_palette): Likewise. - (grub_video_vbe_get_palette): Likewise. - (grub_video_vbe_set_viewport): Likewise. - (grub_video_vbe_get_viewport): Likewise. - (grub_video_vbe_map_color): Likewise. - (grub_video_vbe_map_rgb): Likewise. - (grub_video_vbe_map_rgba): Likewise. - (grub_video_vbe_unmap_color): Likewise. - (grub_video_vbe_fill_rect): Likewise. - (grub_video_vbe_blit_glyph): Likewise. - (grub_video_vbe_blit_bitmap): Likewise. - (grub_video_vbe_blit_render_target): Likewise. - (grub_video_vbe_scroll): Likewise. - (grub_video_vbe_swap_buffers): Likewise. - (grub_video_vbe_create_render_target): Likewise. - (grub_video_vbe_delete_render_target): Likewise. - (grub_video_vbe_set_active_render_target): Likewise. - (grub_vbe_set_pixel_rgb): Remove function. - (grub_vbe_set_pixel_index): Likewise. - (index_color_mode): Remove static variable. - (active_mode): Likewise. - (framebuffer): Likewise. - (bytes_per_scan_line): Likewise. - (grub_video_vbe_adapter): Added new static variable. - (framebuffer): Likewise. - (render_target): Likewise. - (initial_mode): Likewise. - (mode_in_use): Likewise. - (mode_list): Likewise. - -2006-03-10 Marco Gerards - - * configure.ac (AC_INIT): Bumped to 1.93. - - * DISTLIST: Added `include/grub/hfs.h'. - -2006-02-01 Yoshinori K. Okuji - - * boot/i386/pc/boot.S (general_error): Before looping, try INT - 18H, which might help the BIOS falling back to next boot media. - -2006-01-25 Yoshinori K. Okuji - - * util/i386/pc/grub-install.in: Escape a backslash. Reported by - Poe Chen . - -2006-01-17 Marco Gerards - - * include/grub/normal.h: Include . - (grub_command_list): Removed struct. - (grub_command_list_t): Removed type. - (grub_menu_entry): Remove members `num' and `command_list'. Add - members `commands' and `sourcecode'. - * include/grub/script.h: Add inclusion guards. - (grub_script_cmd_menuentry): New struct. - (grub_script_execute_menuentry): New prototype. - (grub_script_lexer_record_start): Likewise. - (grub_script_lexer_record_stop): Likewise. - * normal/execute.c (grub_script_execute_menuentry): New function. - * normal/lexer.c (record, recording, recordpos, recordlen): New - variables. - (grub_script_lexer_record_start): New function. - (grub_script_lexer_record_stop): Likewise. - (recordchar): Likewise. - (nextchar): Likewise. - (grub_script_yylex): Use `nextchar' to fetch new characters. Use - 2048 as the buffer size. Add the tokens `menuentry' and `@'. - * normal/main.c: Include and - (current_menu): New variable. - (free_menu): Mainly rewritten. - (grub_normal_menu_addentry): New function. - (read_config_file): Rewritten. - * normal/menu.c (run_menu_entry): Mainly rewritten. - * normal/menu_entry.c (make_screen): Rewritten the code to insert - the menu entry. - (run): Mainly rewritten. - * normal/parser.y (menu_entry): New variable. - (GRUB_PARSER_TOKEN_MENUENTRY): New token. - (menuentry): New rule. - (command): Add `menuentry'. - (if_statement): Allow additional returns before `fi'. - * normal/script.c (grub_script_create_cmdmenu): New function. - -2006-01-03 Marco Gerards - - * INSTALL: GNU Bison is required. - * configure.ac: Rewritten the test to detect Bison. - * Makefile.in (YACC): New variable. Reported by Xun Sun - . - -2006-01-03 Marco Gerards - - * fs/hfsplus.c (grub_hfsplus_read_block): Convert the offset of - the HFS+ filesystem to filesystem blocks. - (grub_hfsplus_iterate_dir): Cast the `fileinfo' assignment so a - GCC warning is silenced. - -2006-01-03 Marco Gerards - - * partmap/apple.c (apple_partition_map_iterate): Convert the data - read from disk from big endian to host byte order. - -2006-01-03 Hollis Blanchard - - * fs/hfs.c: Include . Added reference to the official - documentation. - (GRUB_HFS_EMBED_HFSPLUS_SIG): New macro. - (grub_hfs_mount): Grammar fix in error. Make sure this is not an - embedded HFS+ filesystem. - (GRUB_HFS_MAGIC, grub_hfs_extent, grub_hfs_datarecord_t) - (grub_hfs_sblock): Move from here... - * include/grub/hfs.h: To here... New file. - * fs/hfsplus.c: Include . Added reference to the official - documentation. - (GRUB_HFSPLUS_MAGIC, GRUB_HFSPLUSX_MAGIC, GRUB_HFSPLUS_SBLOCK): - New macros. - (grub_hfsplus_volheader): Change type of member `magic' to - `grub_uint16_t'. - (grub_hfsplus_data): Add new member `embedded_offset'. - (grub_hfsplus_read_block): Add the HFS+ wrapper offset to the - returned block. - (grub_hfsplus_mount): Read the HFS+ wrapper if it exists. - Calculate the offset. - -2005-12-25 Yoshinori K. Okuji - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_DRP_ADDR): - Removed. - (GRUB_BOOT_MACHINE_DRP_SIZE): Likewise. - -2005-12-25 Yoshinori K. Okuji - - * kern/env.c (grub_env_set): Check if ENV->VALUE instead of - ENV->NAME is NULL after allocating ENV->VALUE. - -2005-12-25 Marco Gerards - - * kern/env.c (grub_env_set): Rewritten the error handling code. - -2005-12-25 Yoshinori K. Okuji - - * geninit.sh: Made more robust, and more portable. - -2005-12-25 Marco Gerards - - Add support for Apple HFS+ filesystems. - - * fs/hfsplus.c: New file. - - * DISTLIST: Added `fs/hfsplus.c'. - - * conf/common.rmk (pkgdata_MODULES): Add `hfsplus.mod'. - (hfsplus_mod_SOURCES): New variable. - (hfsplus_mod_CFLAGS): Likewise. - (hfsplus_mod_LDFLAGS): Likewise. - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `fs/hfsplus.c'. - (grub_setup_SOURCES): Likewise. - (grub_mkdevicemap_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - - * fs/fshelp.c (grub_fshelp_log2blksize): New function. - - * include/grub/fshelp.h (grub_fshelp_log2blksize): new prototype. - -2005-12-25 Yoshinori K. Okuji - - * DISTLIST: Added geninitheader.sh, geninit.sh, commands/test.c, - commands/i386/pc/play.c, conf/common.mk, conf/common.rmk, - include/grub/parser.h, include/grub/script.h, kern/parser.c, - kern/sparc64/cache.S, normal/execute.c, normal/function.c, - normal/lexer.c, normal/parser.y, normal/script.c, and - partmap/gpt.c. - Removed kern/sparc64/cache.c. - - * conf/common.rmk (DISTCLEANFILES): Added grub_script.tab.c, - grub_script.tab.h, grub_modules_init.lst, grub_modules_init.h, - grub_emu_init.c. - - * configure.ac (AC_INIT): Bumped to 1.92. - -2005-12-24 Vesa Jaaskelainen - - * kern/err.c (grub_error_push): Added new function to support error - stacks. - (grub_error_pop): Likewise. - (grub_error_stack_items): New local variable to support error stacks. - (grub_error_stack_pos): Likewise. - (grub_error_stack_assert): Likewise. - (GRUB_ERROR_STACK_SIZE): Added new define to configure maximum error - stack depth. - (grub_print_error): Added support to print errors from error stack. - - * include/grub/err.h (grub_error_push): Added function prototype. - (grub_error_pop): Likewise. - -2005-12-09 Hollis Blanchard - - * configure.ac: Accept `powerpc64' as host_cpu. - (amd64): Rename to `biarch32'. - - * kern/powerpc/cache.S (grub_arch_sync_caches): Handle - non-cacheline-aligned addresses. - - * kern/dl.c (grub_dl_load_core): Add grub_dprintf messages. - (grub_dl_flush_cache): Likewise. Only call `grub_arch_sync_caches' - if `size' is non-zero. - -2005-12-03 Marco Gerards - - * conf/common.rmk (grub_modules_init.lst): Use `-printf "%P\n"' - and `cd' to make sure the filename is not prefixed with a - directory name. - (pkgdata_MODULES): Add `gpt.mod'. - (gpt_mod_SOURCES): New variable. - (gpt_mod_CFLAGS): Likewise. - (gpt_mod_LDFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `partmap/gpt.c'. - - * include/grub/pc_partition.h (GRUB_PC_PARTITION_TYPE_GPT_DISK): - New macro. - - * partmap/gpt.c: New file. - - * partmap/pc.c (pc_partition_map_iterate): Don't continue when a - GPT partition map is detected. - -2005-12-03 Vincent Pelletier - - * commands/i386/pc/play.c: New file. - * conf/i386-pc.rmk (pkgdata_MODULES): Added play.mod. - (play_mod_SOURCES, play_mod_CFLAGS, play_mod_LDFLAGS): New - macros. - -2005-11-27 Marco Gerards - - * include/grub/dl.h (GRUB_MOD_INIT): Use `__attribute__ - ((unused))' to silence gcc warning. - -2005-11-26 Hollis Blanchard - - * configure.ac: Correct `AC_PROG_YACC' test. - -2005-11-22 Hollis Blanchard - - * util/powerpc/ieee1275/grub-install.in: Run the mount point - check before installing files. - -2005-11-22 Mike Small - - * util/powerpc/ieee1275/grub-install.in (grubdir): Fixed partition - number regex so multidigit numbers are recognized correctly. - -2005-11-22 Mike Small - - * loader/powerpc/ieee1275/linux.c (grub_rescue_cmd_linux): Add a - debugging message before attempting to claim memory. - (grub_rescue_cmd_initrd): Add a claim debugging message and try - multiple addresses in case of failure. - -2005-11-22 Hollis Blanchard - - * term/tparm.c (get_space): Remove empty `if' statement. - - * fs/ufs.c (grub_ufs_find_file): Remove `grub_le_to_cpu32'. - - * kern/parser.c (check_varstate): Rename `state' to 's'. - -2005-11-22 Hollis Blanchard - - * partmap/acorn.c: Change `unsigned' to `unsigned int'. Move all - variable definitions to the beginning of each function. Sort stack - variables by size. - (find): Rename to `acorn_partition_map_find'. Cast `grub_disk_read' - `buf' argument to `char *'. - -2005-11-22 Hollis Blanchard - - * conf/powerpc-ieee1275.rmk: Include conf/common.mk. - (pkgdata_MODULES): Removed fshelp.mod, fat.mod, ext2.mod, ufs.mod, - minix.mod, hfs.mod, jfs.mod, xfs.mod, affs.mod, sfs.mod, - hello.mod, boot.mod, terminal.mod, ls.mod, cmp.mod, cat.mod, - help.mod, font.mod, terminfo.mod, amiga.mod, apple.mod, pc.mod, - sun.mod, acorn.mod, loopback.mod, default.mod, timeout.mod, - configfile.mod, search.mod, gzio.mod and test.mod. - (symlist.c, grub_script.tab.c, grub_script.tab.h, kernel_syms.lst) - (grub_modules_init.lst, grub_modules_init.h, grub_emu_init.c) - (fshelp_mod_SOURCES, fshelp_mod_CFLAGS, fshelp_mod_LDFLAGS) - (fat_mod_SOURCES, fat_mod_CFLAGS, fat_mod_LDFLAGS) - (ext2_mod_SOURCES, ext2_mod_CFLAGS, ext2_mod_LDFLAGS) - (ufs_mod_SOURCES, ufs_mod_CFLAGS, ufs_mod_LDFLAGS) - (minix_mod_SOURCES, minix_mod_CFLAGS, minix_mod_LDFLAGS) - (hfs_mod_SOURCES, hfs_mod_CFLAGS, hfs_mod_LDFLAGS, jfs_mod_SOURCES) - (jfs_mod_CFLAGS, jfs_mod_LDFLAGS, iso9660_mod_SOURCES) - (iso9660_mod_CFLAGS, iso9660_mod_LDFLAGS, xfs_mod_SOURCES) - (xfs_mod_CFLAGS, xfs_mod_LDFLAGS, affs_mod_SOURCES) - (affs_mod_CFLAGS, affs_mod_LDFLAGS, sfs_mod_SOURCES) - (sfs_mod_CFLAGS, sfs_mod_LDFLAGS, hello_mod_SOURCES) - (hello_mod_CFLAGS, hello_mod_LDFLAGS, boot_mod_SOURCES) - (boot_mod_CFLAGS, boot_mod_LDFLAGS, terminal_mod_SOURCES) - (terminal_mod_CFLAGS, terminal_mod_LDFLAGS, ls_mod_SOURCES) - (ls_mod_CFLAGS, ls_mod_LDFLAGS, cmp_mod_SOURCES, cmp_mod_CFLAGS) - (cmp_mod_LDFLAGS, cat_mod_SOURCES, cat_mod_CFLAGS, cat_mod_LDFLAGS) - (help_mod_SOURCES, help_mod_CFLAGS, help_mod_LDFLAGS) - (font_mod_SOURCES, font_mod_CFLAGS, font_mod_LDFLAGS) - (terminfo_mod_SOURCES, terminfo_mod_CFLAGS, terminfo_mod_LDFLAGS) - (amiga_mod_SOURCES, amiga_mod_CFLAGS, amiga_mod_LDFLAGS) - (apple_mod_SOURCES, apple_mod_CFLAGS, apple_mod_LDFLAG): Removed. - - * conf/common.mk (grub_modules_init.lst): Use `find' instead of - `grep --include'. - (pkgdata_MODULES): Add test.mod. - -2005-11-18 Timothy Baldwin - - * genmk.rb: Fixed list rules moved to Makefile.in. Recognise - appending to variables with "+=". - (PModule): Use full pathname to generate *.lst filenames. - - * Makefile.in: Fixed list rules moved from genmk.rb. - (.DELETE_ON_ERROR): New special target. - (RMKFILES): Add common.rmk and sparc64-ieee1275.rmk. - - * conf/i386-pc.rmk: Include conf/common.mk. - (pkgdata_MODULES): Removed fshelp.mod, fat.mod, ext2.mod, ufs.mod, - minix.mod, hfs.mod, jfs.mod, xfs.mod, affs.mod, sfs.mod, - hello.mod, boot.mod, terminal.mod, ls.mod, cmp.mod, cat.mod, - help.mod, font.mod, terminfo.mod, amiga.mod, apple.mod, pc.mod, - sun.mod, acorn.mod, loopback.mod, default.mod, timeout.mod, - configfile.mod, search.mod, gzio.mod and test.mod. - (symlist.c, grub_script.tab.c, grub_script.tab.h, kernel_syms.lst) - (grub_modules_init.lst, grub_modules_init.h, grub_emu_init.c) - (fshelp_mod_SOURCES, fshelp_mod_CFLAGS, fshelp_mod_LDFLAGS) - (fat_mod_SOURCES, fat_mod_CFLAGS, fat_mod_LDFLAGS) - (ext2_mod_SOURCES, ext2_mod_CFLAGS, ext2_mod_LDFLAGS) - (ufs_mod_SOURCES, ufs_mod_CFLAGS, ufs_mod_LDFLAGS) - (minix_mod_SOURCES, minix_mod_CFLAGS, minix_mod_LDFLAGS) - (hfs_mod_SOURCES, hfs_mod_CFLAGS, hfs_mod_LDFLAGS, jfs_mod_SOURCES) - (jfs_mod_CFLAGS, jfs_mod_LDFLAGS, iso9660_mod_SOURCES) - (iso9660_mod_CFLAGS, iso9660_mod_LDFLAGS, xfs_mod_SOURCES) - (xfs_mod_CFLAGS, xfs_mod_LDFLAGS, affs_mod_SOURCES) - (affs_mod_CFLAGS, affs_mod_LDFLAGS, sfs_mod_SOURCES) - (sfs_mod_CFLAGS, sfs_mod_LDFLAGS, hello_mod_SOURCES) - (hello_mod_CFLAGS, hello_mod_LDFLAGS, boot_mod_SOURCES) - (boot_mod_CFLAGS, boot_mod_LDFLAGS, terminal_mod_SOURCES) - (terminal_mod_CFLAGS, terminal_mod_LDFLAGS, ls_mod_SOURCES) - (ls_mod_CFLAGS, ls_mod_LDFLAGS, cmp_mod_SOURCES, cmp_mod_CFLAGS) - (cmp_mod_LDFLAGS, cat_mod_SOURCES, cat_mod_CFLAGS, cat_mod_LDFLAGS) - (help_mod_SOURCES, help_mod_CFLAGS, help_mod_LDFLAGS) - (font_mod_SOURCES, font_mod_CFLAGS, font_mod_LDFLAGS) - (terminfo_mod_SOURCES, terminfo_mod_CFLAGS, terminfo_mod_LDFLAGS) - (amiga_mod_SOURCES, amiga_mod_CFLAGS, amiga_mod_LDFLAGS) - (apple_mod_SOURCES, apple_mod_CFLAGS, apple_mod_LDFLAG): Move from - here... - * conf/common.rmk: ... to here. New file. - - * conf/common.mk: New file. - -2005-11-18 Yoshinori K. Okuji - - * conf/powerpc-ieee1275.rmk (grub_script.tab.h): Unified to ... - (grub_script.tab.c): ... here. - - * conf/sparc64-ieee1275.rmk (grub_script.tab.h): Unified to ... - (grub_script.tab.c): ... here. - - * conf/i386-pc.rmk (grub_script.tab.h): Unified to ... - (grub_script.tab.c): ... here. - - * normal/command.c (grub_command_find): Fixed a memory leak of - MODULE_NAME. Reported by Mike Small . - -2005-11-13 Timothy Baldwin - - * include/grub/symbol.h: (FUNCTION): Use double quotes instead of - "@" which marks the start of a comment on ARM. - (VARIABLE): Likewise. - -2005-11-13 Timothy Baldwin - - Add support for Linux/ADFS partition tables. - - * partmap/acorn.c: New file. - - * include/grub/acorn_filecore.h: Likewise. - - * DISTLIST: Added `partmap/acorn.c' and - `include/grub/acorn_filecore.h'. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `partmap/acorn.c'. - (pkgdata_MODULES): Add `acorn.mod'. - (acorn_mod_SOURCES): New variable. - (acorn_mod_CFLAGS): Likewise. - - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Add - `partmap/acorn.c'. - (pkgdata_MODULES): Add `acorn.mod'. - (acorn_mod_SOURCES): New variable. - (acorn_mod_CFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `partmap/acorn.c'. - (pkgdata_MODULES): Add `acorn.mod'. - (acorn_mod_SOURCES): New variable. - (acorn_mod_CFLAGS): Likewise. - (acorn_mod_LDFLAGS): Likewise. - - * include/types.h (grub_disk_addr_t): New typedef. - -2005-11-13 Marco Gerards - - * geninit.sh: New file. - - * geninitheader.sh: Likewise. - - * commands/boot.c (grub_boot_init, grub_boot_fini): Removed. - * commands/cat.c (grub_cat_init, grub_cat_fini): Likewise. - * commands/cmp.c (grub_cmp_init, grub_cmp_fini): Likewise. - * commands/configfile.c (grub_configfile_init) - (grub_configfile_fini): Likewise. - * commands/default.c (grub_default_init, grub_default_fini): - Likewise. - * commands/help.c (grub_help_init, grub_help_fini): Likewise. - * commands/ls.c (grub_ls_init, grub_ls_fini): Likewise. - * commands/search.c (grub_search_init, grub_search_fini): Likewise. - * commands/terminal.c (grub_terminal_init, grub_terminal_fini): - Likewise. - * commands/test.c (grub_test_init, grub_test_fini): Likewise. - * commands/timeout.c (grub_timeout_init, grub_timeout_fini): - Likewise. - * commands/i386/pc/halt.c (grub_halt_init, grub_halt_fini): Likewise. - * commands/ieee1275/halt.c (grub_halt_init, grub_halt_fini): - Likewise. - * commands/i386/pc/reboot.c (grub_reboot_init, grub_reboot_fini): - Likewise. - * commands/ieee1275/reboot.c (grub_reboot_init, grub_reboot_fini): - Likewise. - * disk/loopback.c (grub_loop_init, grub_loop_fini): Likewise. - * fs/affs.c (grub_affs_init, grub_affs_fini): Likewise. - * fs/ext2.c (grub_ext2_init, grub_ext2_fini): Likewise. - * fs/fat.c (grub_fat_init, grub_fat_fini): Likewise. - * fs/hfs.c (grub_hfs_init, grub_hfs_fini): Likewise. - * fs/iso9660.c (grub_iso9660_init, grub_iso9660_fini): Likewise. - * fs/jfs.c (grub_jfs_init, grub_jfs_fini): Likewise. - * fs/minix.c (grub_minix_init, grub_minix_fini): Likewise. - * fs/sfs.c (grub_sfs_init, grub_sfs_fini): Likewise. - * fs/ufs.c (grub_ufs_init, grub_ufs_fini): Likewise. - * fs/xfs.c (grub_xfs_init, grub_xfs_fini): Likewise. - * normal/main.c (grub_normal_init, grub_normal_fini): Likewise. - * partmap/amiga.c (grub_amiga_partition_map_init) - (grub_amiga_partition_map_fini): Likewise. - * partmap/apple.c (grub_apple_partition_map_init) - (grub_apple_partition_map_fini): Likewise. - * partmap/pc.c (grub_pc_partition_map_init) - (grub_pc_partition_map_fini): Likewise. - * partmap/sun.c (grub_sun_partition_map_init, - grub_sun_partition_map_fini): Likewise. - * term/terminfo.c (grub_terminal_init, grub_terminal_fini): - Likewise. - - * util/grub-emu.c: Include . - (main): Don't initialize and de-initialize any modules directly, - use `grub_init_all' and `grub_fini_all' instead. - - * term/i386/pc/vesafb.c (grub_vesafb_init): Renamed to - `grub_vesafb_mod_init'. - (grub_vesafb_fini): Renamed to `grub_vesafb_mod_fini'. Updated - all users. - * term/i386/pc/vga.c (grub_vga_init): Renamed to - `grub_vga_mod_init'. Updated all users. - (grub_vga_fini): Renamed to `grub_vga_mod_fini'. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `grub_emu_init.c'. - (grub_modules_init.lst, grub_modules_init.h, grub_emu_init.c): New - rules. - - * include/grub/dl.h (GRUB_MOD_INIT): Add argument `name'. - Generate a function to initialize the module in utilities. - Updated all callers. - (GRUB_MOD_FINI): Add argument `name'. Generate a function to - initialize the module in utilities. Updated all callers. - -2005-11-09 Hollis Blanchard - - * term/ieee1275/ofconsole.c (grub_ofconsole_cls): Use both the ANSI - escape sequence and a literal ^L to clear the screen. - - * commands/ieee1275/suspend.c (grub_cmd_suspend): Clear the screen - when returning from Open Firmware. - -2005-11-09 Hollis Blanchard - - * term/ieee1275/ofconsole.c (grub_ofconsole_width): New variable. - (grub_ofconsole_height): Likewise. - (grub_ofconsole_putchar): If `grub_curr_x' exceeds console width, - manually insert a '\n'. - (grub_ofconsole_getwh): Set and return `grub_ofconsole_width' and - `grub_ofconsole_height'. Return early if these are already set. - -2005-11-07 Vincent Pelletier - - * conf/sparc64-ieee1275.rmk (grub_emu_SOURCES): Add - `commands/test.c', `fs/affs.c', `fs/sfs.c', `fs/xfs.c', - `normal/execute.c', `normal/lexer.c', `io/gzio.c', - `kern/parser.c', `grub_script.tab.c', `normal/function.c' - and `normal/script.c'. - (normal_mod_SOURCES): `normal/execute.c', `normal/lexer.c', - `grub_script.tab.c', `normal/function.c' and `normal/script.c'. - (test_mod_SOURCES): New variable. - (test_mod_CFLAGS): Likewise. - (test_mod_LDFLAGS): Likewise. - (pkgdata_MODULES): Add `test.mod'. - (grub_script.tab.c): New rule. - (grub_script.tab.h): Likewise. - -2005-11-07 Marco Gerards - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `commands/test.c', `normal/execute.c', `normal/lexer.c', - `grub_script.tab.c', `normal/function.c' and `normal/script.c'. - (normal_mod_SOURCES): `normal/execute.c', `normal/lexer.c', - `grub_script.tab.c', `normal/function.c' and `normal/script.c'. - (test_mod_SOURCES): New variable. - (test_mod_CFLAGS): Likewise. - (pkgdata_MODULES): Add `test.mod'. - (grub_script.tab.c): New rule. - (grub_script.tab.h): Likewise. - -2005-11-06 Marco Gerards - - Add initial scripting support. - - * commands/test.c: New file. - * include/grub/script.h: Likewise. - * normal/execute.c: Likewise. - * normal/function.c: Likewise. - * normal/lexer.c: Likewise. - * normal/parser.y: Likewise. - * normal/script.c: Likewise. - - * configure.ac: Add `AC_PROG_YACC' test. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `commands/test.c', - `normal/execute.c', `normal/lexer.c', `grub_script.tab.c', - `normal/function.c' and `normal/script.c'. - (normal_mod_SOURCES): `normal/execute.c', `normal/lexer.c', - `grub_script.tab.c', `normal/function.c' and `normal/script.c'. - (test_mod_SOURCES, test_mod_CFLAGS, test_mod_LDFLAGS): New - variables. - (pkgdata_MODULES): Add `test.mod'. - (grub_script.tab.c): New rule. - (grub_script.tab.h): Likewise. - - * include/grub/err.h (grub_err_t): Add `GRUB_ERR_TEST_FAILURE'. - - * include/grub/normal.h (grub_test_init): New prototype. - (grub_test_fini): Likewise. - - * normal/command.c: Include . - (grub_command_execute): Rewritten. - - * util/grub-emu.c (main): Call `grub_test_init' and - `grub_test_fini'. - -2005-11-03 Hollis Blanchard - - * kern/powerpc/ieee1275/init.c (grub_get_rtc): Initialize `msecs' - to 0. - * term/ieee1275/ofconsole.c (grub_ofconsole_checkkey): Return -1 if - there are no pending characters. - -2005-11-03 Hollis Blanchard - - * kern/powerpc/ieee1275/openfw.c (grub_ieee1275_get_devname): Use - `grub_strndup' to drop device arguments. Replace unnecessary - `grub_strndup' with `grub_strdup'. - -2005-11-03 Hollis Blanchard - - * kern/term.c (grub_cls): Do not call grub_cur_term->cls() if the - `debug' environment variable has been set. - -2005-11-02 Hollis Blanchard - - * Makefile.in (install-local): Use $(DATA). - (uninstall): Likewise. - * conf/powerpc-ieee1275.rmk (bin_UTILITIES): Move grub-mkimage... - (sbin_UTILITIES): ... to here. - (sbin_SCRIPTS): New variable. - (grub_install_SOURCES): New variable. - * util/powerpc/ieee1275/grub-install.in: New file. - * util/powerpc/ieee1275/grub-mkimage.c (kernel_path): Remove - variable. - (add_segments): Call `grub_util_get_path'. - -2005-10-28 Yoshinori K. Okuji - - From Timothy Baldwin: - * commands/ls.c (grub_ls_list_files): Close FILE with - grub_file_close. - * kern/misc.c (grub_vsprintf): Terminate the string S with NUL. - -2005-10-24 Marco Gerards - - * include/grub/parser.h: New file. - - * kern/parser.c: Likewise. - - * conf/i386-pc.rmk (kernel_img_SOURCES): Add `kern/parser.c'. - (grub_setup_SOURCES): Likewise. - (grub_probefs_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - (kernel_img_HEADERS): Add `parser.h'. - - * conf/powerpc-ieee1275.rmk (grubof_HEADERS): Add `parser.h'. - (grub_emu_SOURCES): Add `kern/parser.c'. - (grubof_SOURCES): Likewise. - - * conf/sparc64-ieee1275.rmk (grubof_HEADERS): Add `parser.h'. - (grubof_SOURCES): Add `kern/parser.c'. - - * include/grub/misc.h (grub_split_cmdline): Removed prototype. - - * kern/misc.c (grub_split_cmdline): Removed function. - - * kern/rescue.c: Include . - (grub_enter_rescue_mode): Use `grub_parser_split_cmdline' instead - of `grub_split_cmdline'. - - * normal/command.c: Include . - (grub_command_execute): Use `grub_parser_split_cmdline' instead - of `grub_split_cmdline'. - - * normal/completion.c: Include . - (cmdline_state): New variable. - (iterate_dir): End the filename with a quote depending on the - command line state. - (get_state): new function. - (grub_normal_do_completion): Use `grub_parser_split_cmdline' to - split the arguments and determine the current argument. When the - argument string is not quoted, escape all spaces. - -2005-10-23 Vincent Pelletier - - * normal/sparc64/setjmp.S: New file. - -2005-10-23 Vincent Pelletier - - * include/grub/sparc64/libgcc.h: New file. - * conf/sparc64-ieee1275.rmk (COMMON_ASFLAGS): Remove -Av9. - (normal_mod_SOURCES): Use normal/sparc64/setjmp.S instead of - normal/sparc64/setjmp.c. - -2005-10-23 Vincent Pelletier - - * kern/sparc64/dl.c: Rewritten for SPARCV9 ELF. - * kern/sparc64/cache.S: New file. - * kern/sparc64/cache.c: Removed. - * conf/sparc64-ieee1275.rmk (COMMON_ASFLAGS): Add -Av9. - (COMMON_CFLAGS): Add -mno-app-regs. Remove -mcpu=v9 and - -mtune=ultrasparc. - (COMMON_LDFLAGS): Add -melf64_sparc. - (grubof_HEADERS): Add sparc64/libgcc.h and machine/kernel.h. - (grubof_SOURCES): Use cache.S instead of cache.c. - (grubof_LDFLAGS): Add -mno-app-regs. Replace "-Xlinker - --oformat -Xlinker elf64-sparc" by "-Bstatic,-melf64_sparc". - (pkgdata_MODULES): Uncomment. Leave linux.mod and _linux.mod - commented though. - (normal_mod_SOURCES): Add normal/completion.c and normal/misc.c. - (_linux_mod_SOURCES, _linux_mod_CFLAGS, linux_mod_SOURCES) - (linux_mod_CFLAGS): Commented out. - (_linux_mod_LDFLAGS, linux_mod_LDFLAGS): New macro, commented - out because module isn't built. - (fshelp_mod_LDFLAGS, fat_mod_LDFLAGS, ext2_mod_LDFLAGS) - (ufs_mod_LDFLAGS, minix_mod_LDFLAGS, hfs_mod_LDFLAGS) - (jfs_mod_LDFLAGS, iso9660_mod_LDFLAGS, normal_mod_LDFLAGS) - (hello_mod_LDFLAGS, boot_mod_LDFLAGS, terminal_mod_LDFLAGS) - (ls_mod_LDFLAGS, cmp_mod_LDFLAGS, cat_mod_LDFLAGS) - (font_mod_LDFLAGS, amiga_mod_LDFLAGS, apple_mod_LDFLAGS) - (pc_mod_LDFLAGS, sun_mod_LDFLAGS, loopback_mod_LDFLAGS) - (suspend_mod_LDFLAGS, reboot_mod_LDFLAGS, halt_mod_LDFLAGS) - (help_mod_LDFLAGS, default_mod_LDFLAGS, timeout_mod_LDFLAGS) - (configfile_mod_LDFLAGS, search_mod_LDFLAGS, xfs_mod_SOURCES) - (xfs_mod_CFLAGS, xfs_mod_LDFLAGS, affs_mod_SOURCES) - (affs_mod_CFLAGS, affs_mod_LDFLAGS, sfs_mod_SOURCES) - (sfs_mod_CFLAGS, sfs_mod_LDFLAGS, gzio_mod_SOURCES) - (gzio_mod_CFLAGS, gzio_mod_LDFLAGS): New macro. - -2005-10-20 Yoshinori K. Okuji - - * util/i386/pc/grub-probefs.c (main): Call grub_xfs_init and - grub_xfs_fini. Do not call grub_hfs_init or grub_hfs_fini any - longer, because HFS should not be used on PC. - -2005-10-20 Timothy Baldwin - - * io/gzio.c (grub_gzio_read): Use OFFSET instead of FILE->OFFSET - consistently within the loop. - -2005-10-15 Marco Gerards - - * fs/xfs.c (grub_xfs_iterate_dir): Detect an error if part of a - directory can not be read. - -2005-10-15 Yoshinori K. Okuji - - * configure.ac (AC_INIT): Increase the version number to 1.91. - - * DISTLIST: Added include/grub/terminfo.h, include/grub/tparm.h, - include/grub/i386/pc/serial.h, term/terminfo.c, term/tparm.c and - term/i386/pc/serial.c. - -2005-10-15 Yoshinori K. Okuji - - * kern/file.c (grub_file_seek): Seeking to an offset equal to a - file size must be permitted. - - * kern/i386/pc/startup.S (multiboot_trampoline): Fix a mistake - between %ah and %al. - -2005-10-15 Yoshinori K. Okuji - - * fs/xfs.c (grub_xfs_iterate_dir): Change the type of BLK to - grub_uint64_t. - Call the hook with a NUL-terminated filename. - (grub_xfs_mount): Use grub_be_to_cpu32 instead of - grub_cpu_to_be32. - - * kern/term.c (cursor_state): New variable. - (grub_term_set_current): Reset the cursor state on a new - terminal. - (grub_setcursor): Rewritten to use CURSOR_STATE. - (grub_getcursor): New function. - - * include/grub/term.h (grub_getcursor): New prototype. - - * io/gzio.c (test_header): Align BUF for accessing it as 32-bit - integers on ARM. Reported by Timothy Baldwin - . - -2005-10-11 Marco Gerards - - * fs/sfs.c (grub_sfs_open): Don't free `data->label' if it is not - allocated. - (grub_sfs_dir): Likewise. - -2005-10-09 Marco Gerards - - Add support for the SFS filesystem. - - * fs/sfs.c: New file. - - * DISTLIST: Added `fs/sfs.c'. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `fs/sfs.c'. - (grub_probefs_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add `sfs.mod'. - (sfs_mod_SOURCES): New variable. - (sfs_mod_CFLAGS): Likewise. - (sfs_mod_LDFLAGS): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add `fs/sfs.c'. - (pkgdata_MODULES): Add `sfs.mod'. - (sfs_mod_SOURCES): New variable. - (sfs_mod_CFLAGS): Likewise. - - * util/grub-emu.c (main): Call `grub_sfs_init' and - `grub_sfs_fini'. - - * include/grub/fs.h (grub_sfs_init): New prototype. - (grub_sfs_fini): Likewise. - -2005-10-07 Marco Gerards - - Add support for the AFFS filesystem. - - * fs/affs.c: New file. - - * DISTLIST: Added `fs/affs.c'. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `fs/affs.c'. - (grub_probefs_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add `affs.mod'. - (affs_mod_SOURCES): New variable. - (affs_mod_CFLAGS): Likewise. - (affs_mod_LDFLAGS): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add `fs/affs.c'. - (pkgdata_MODULES): Add `affs.mod'. - (affs_mod_SOURCES): New variable. - (affs_mod_CFLAGS): Likewise. - - * util/grub-emu.c (main): Call `grub_affs_init' and - `grub_affs_fini'. - - * include/grub/fs.h (grub_affs_init): New prototype. - (grub_affs_fini): Likewise. - -2005-10-01 Marco Gerards - - * fs/xfs.c (grub_xfs_iterate_dir): Add parentheses. - -2005-10-01 Marco Gerards - - * configure.ac: Accept `x86_64' as host_cpu. In that case add - `-m32' to CFLAGS. - - * genmk.rb (class PModule): Always use `$(#{prefix}_LDFLAGS)' when - linking. - - * conf/i386-pc.rmk (COMMON_CFLAGS): Add `-m32'. - (COMMON_LDFLAGS): New variable. - (kernel_img_LDFLAGS): Include `COMMON_FLAGS'. - (_chain_mod_LDFLAGS, fshelp_mod_LDFLAGS, fat_mod_LDFLAGS) - (ext2_mod_LDFLAGS, ufs_mod_LDFLAGS, minix_mod_LDFLAGS) - (hfs_mod_LDFLAGS, jfs_mod_LDFLAGS, iso9660_mod_LDFLAGS) - (xfs_mod_LDFLAGS, _linux_mod_LDFLAGS, linux_mod_LDFLAGS) - (normal_mod_LDFLAGS, hello_mod_LDFLAGS, boot_mod_LDFLAGS) - (terminal_mod_LDFLAGS, ls_mod_LDFLAGS, cmp_mod_LDFLAGS) - (cat_mod_LDFLAGS, help_mod_LDFLAGS, reboot_mod_LDFLAGS) - (halt_mod_LDFLAGS, vga_mod_LDFLAGS, font_mod_LDFLAGS) - (terminfo_mod_LDFLAGS, serial_mod_LDFLAGS, _multiboot_mod_LDFLAGS) - (multiboot_mod_LDFLAGS, amiga_mod_LDFLAGS, apple_mod_LDFLAGS) - (pc_mod_LDFLAGS, sun_mod_LDFLAGS, loopback_mod_LDFLAGS) - (default_mod_LDFLAGS, timeout_mod_LDFLAGS, configfile_mod_LDFLAGS) - (vbe_mod_LDFLAGS, vesafb_mod_LDFLAGS, vbeinfo_mod_LDFLAGS) - (vbetest_mod_LDFLAGS, search_mod_LDFLAGS, gzio_mod_LDFLAGS): New - variables. - (normal_mod_ASFLAGS): Add `-m32'. - - * include/grub/types.h (grub_host_addr_t, grub_host_off_t) - (grub_host_size_t, grub_host_ssize_t): New types. - (grub_addr_t, grub_off_t, grub_size_t, grub_ssize_t): Make type - dependent of `GRUB_CPU_SIZEOF_VOID_P' instead on - `GRUB_HOST_SIZEOF_VOID_P'. - - * include/grub/kernel.h (struct grub_module_header): Type of - member offset changed to `grub_host_off_t'. Type of member size - changed to `grub_host_size_t'. - (struct grub_module_info): Type of member offset changed to - `grub_host_off_t'. Type of member size changed to - `grub_host_size_t'. - -2005-09-29 Yoshinori K. Okuji - - Make GRUB's kernel compliant to Multiboot Specification. - - * kern/i386/pc/startup.S (multiboot_header): New label. - (multiboot_entry): Likewise. - (multiboot_trampoline): Likewise. - - * include/grub/i386/pc/kernel.h (GRUB_KERNEL_MACHINE_RAW_SIZE): - Increased to 0x4A0. - - * fs/xfs.c (grub_xfs_iterate_dir): Fix a syntax error. You may not - put parentheses after a question mark. - [!GRUB_UTIL] (my_mod): New variable. - - * util/grub-emu.c (main): Call grub_xfs_init and grub_xfs_fini. - -2005-09-28 Marco Gerards - - Adds support for the XFS filesystem. Btrees are not supported - yet. - - * fs/xfs.c: New file. - - * DISTLIST: Added `fs/xfs.c'. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `fs/xfs.c'. - (grub_probefs_SOURCES): Likewise. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add `xfs.mod'. - (xfs_mod_SOURCES): New variable. - (xfs_mod_CFLAGS): Likewise. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add `fs/xfs.c'. - (pkgdata_MODULES): Add `xfs.mod'. - (xfs_mod_SOURCES): New variable. - (xfs_mod_CFLAGS): Likewise. - - * util/grub-emu.c (main): Call `grub_xfs_init' and - `grub_xfs_fini'. - - * include/grub/fs.h (grub_xfs_init): New prototype. - (grub_xfs_fini): Likewise. - - -2005-09-18 Vesa Jaaskelainen - - * video/i386/pc/vbe.c (grub_vbe_set_video_mode): In indexed - color modes, allow greater than 16 colors to be configured as - a default palette. - -2005-09-03 Yoshinori K. Okuji - - * normal/completion.c (complete_arguments): Add the qualifier - const into OPTIONS. - - From Omniflux : - * include/grub/terminfo.h: New file. - * include/grub/tparm.h: Likewise. - * include/grub/i386/pc/serial.h: Likewise. - * term/terminfo.c: Likewise. - * term/tparm.c: Likewise. - * term/i386/pc/serial.c: Likewise. - * conf/i386-pc.rmk (pkgdata_MODULES): Added terminfo.mod and - serial.mod. - (terminfo_mod_SOURCES): New variable. - (terminfo_mod_CFLAGS): Likewise. - (serial_mod_SOURCES): Likewise. - (serial_mod_CFLAGS): Likewise. - -2005-08-31 Yoshinori K. Okuji - - * DISTLIST: Replaced boot/powerpc/ieee1275/crt0.S and - boot/powerpc/ieee1275/cmain.c with kern/powerpc/ieee1275/crt0.S - and kern/powerpc/ieee1275/cmain.c, respectively. - - * boot/powerpc/ieee1275/crt0.S: Moved to ... - * kern/powerpc/ieee1275/crt0.S: ... here. - - * boot/powerpc/ieee1275/cmain.c: Moved to ... - * kern/powerpc/ieee1275/cmain.c: ... here. - - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Use - kern/powerpc/ieee1275/crt0.S and kern/powerpc/ieee1275/cmain.c - instead of boot/powerpc/ieee1275/crt0.S and - boot/powerpc/ieee1275/cmain.c, respectively. - - * boot/i386/pc/boot.S (lba_mode): Do not store the total number of - sectors. It was not used anyway. - -2005-08-30 Hollis Blanchard - - * term/ieee1275/ofconsole.c (grub_ofconsole_getcharwidth): Fix - `unused parameter' warning. - -2005-08-30 Hollis Blanchard - - * term/ieee1275/ofconsole.c (grub_ofconsole_getcharwidth): New - function. - (grub_ofconsole_term): Specify grub_ofconsole_getcharwidth as - getcharwidth. - -2005-08-28 Marco Gerards - - * include/grub/normal.h (enum grub_completion_type): Added - `GRUB_COMPLETION_TYPE_ARGUMENT'. - - * normal/cmdline.c (print_completion): Handle - the `GRUB_COMPLETION_TYPE_ARGUMENT' type. - * normal/menu_entry.c (store_completion): Likewise. - - * normal/completion.c (complete_arguments): New function. - (grub_normal_do_completion): Call `complete_arguments' when the - current words start with a dash. - -2005-08-27 Marco Gerards - - * conf/powerpc-ieee1275.rmk (pkgdata_MODULES): Fix typo (use - `gzio.mod' instead of `io.mod'). - -2005-08-22 Yoshinori K. Okuji - - * gendistlist.sh (EXTRA_DISTFILES): Added genfslist.sh. - (DISTDIRS): Added io and video. - Rewrite the search routine to make an output consistently. - - * DISTLIST: Added conf/sparc64-ieee1275.mk, - conf/sparc64-ieee1275.rmk, include/grub/gzio.h, - include/grub/ieee1275/ieee1275.h, include/grub/ieee1275/ofdisk.h, - io/gzio.c, kern/sparc64/cache.c, kern/sparc64/dl.c, - kern/sparc64/ieee1275/init.c, kern/sparc64/ieee1275/openfw.c and - util/powerpc/ieee1275/misc.c. - - * include/grub/gzio.h: New file. - * io/gzio.c: Likewise. - - * kern/file.c (grub_file_close): Call grub_device_close only if - FILE->DEVICE is not NULL. - - * include/grub/mm.h [!NULL] (NULL): New macro. - - * include/grub/err.h (GRUB_ERR_BAD_GZIP_DATA): New constant. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Added io/gzip.c. - (pkgdata_MODULES): Added gzio.mod. - (gzio_mod_SOURCES): New variable. - (gzio_mod_CFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added io/gzip.c. - (pkgdata_MODULES): Added gzio.mod. - (gzio_mod_SOURCES): New variable. - (gzio_mod_CFLAGS): Likewise. - - * commands/cat.c: Include grub/gzio.h. - (grub_cmd_cat): Use grub_gzfile_open instead of - grub_file_open. - - * commands/cmp.c: Include grub/gzio.h. - (grub_cmd_cmp): Use grub_gzfile_open instead of - grub_file_open. - - * loader/i386/pc/multiboot.c: Include grub/gzio.h. - (grub_rescue_cmd_multiboot): Use grub_gzfile_open instead of - grub_file_open. - (grub_rescue_cmd_module): Likewise. - -2005-08-21 Vincent Pelletier - - * conf/sparc64-ieee1275.rmk (grubof_SOURCES): The first file must be - kern/sparc64/ieee1275/init.c because it contains _start. - * conf/sparc64-ieee1275.mk: Generated from conf/sparc64-ieee1275.rmk. - -2005-08-21 Vincent Pelletier - - * configure.ac: Add support for sparc64 host with ieee1275 - firmware. - * configure: Generated from configure.ac. - * disk/ieee1275/ofdisk.c (grub_ofdisk_open): Use grub_ssize_t - instead of int. - (grub_ofdisk_read): Likewise. - (grub_ofdisk_open): Use %p to print pointer values, and cast the - pointers as (void *) to remove a warning. - (grub_ofdisk_close): Likewise. - (grub_ofdisk_read): Likewise. - * kern/ieee1275/ieee1275.c (grub_ieee1275_exit): This never - returns, so make it return void to remove a warning. - * include/grub/ieee1275/ieee1275.h (grub_ieee1275_exit): - Corresponding prototype change. - * kern/mm.c (grub_mm_init_region): Use %p to print pointer - values, and cast the pointers as (void *) to remove a warning. - (grub_mm_dump): Likewise. - * conf/sparc64-ieee1275.mk: New file. - * conf/sparc64-ieee1275.rmk: Likewise. - * include/grub/sparc64/setjmp.h: Likewise. - * include/grub/sparc64/types.h: Likewise. - * include/grub/sparc64/ieee1275/console.h: Likewise. - * include/grub/sparc64/ieee1275/ieee1275.h: Likewise. - * include/grub/sparc64/ieee1275/kernel.h: Likewise. - * include/grub/sparc64/ieee1275/time.h: Likewise. - * kern/sparc64/cache.c: Likewise. - * kern/sparc64/dl.c: Likewise. - * kern/sparc64/ieee1275/init.c: Likewise. - * kern/sparc64/ieee1275/openfw.c: Likewise. - -2005-08-21 Yoshinori K. Okuji - - * util/console.c (grub_ncurses_putchar): If C is greater than - 0x7f, set C to a question mark. - (grub_ncurses_getcharwidth): New function. - (grub_ncurses_term): Specify grub_ncurses_getcharwidth as - getcharwidth. - - * normal/menu.c (print_entry): Made aware of Unicode. First, - convert TITLE to UCS-4, and predict the cursor position by - grub_getcharwidth. - - * include/grub/misc.h (grub_utf8_to_ucs4): Specify the qualifier - const to SRC. - * kern/misc.c (grub_utf16_to_utf8): Likewise. - -2005-08-20 Yoshinori K. Okuji - - * loader/powerpc/ieee1275/linux.c (grub_rescue_cmd_linux): Specify - the boot file by the option BOOT_IMAGE. Use grub_stpcpy instead of - grub_strcat. - - * loader/i386/pc/linux.c (grub_rescue_cmd_linux): Specify the boot - file by the option BOOT_IMAGE. Use grub_stpcpy instead of - grub_strcpy and grub_strlen. Take it into account that a space - character is inserted as a delimiter. - -2005-08-20 Yoshinori K. Okuji - - * partmap/pc.c (pc_partition_map_iterate): Include the value of an - invalid magic in the error. - - * commands/search.c: New file. - - * util/grub-emu.c (main): Call grub_search_init and - grub_search_fini. - - * kern/rescue.c (grub_rescue_print_disks): Removed. - (grub_rescue_print_devices): New function. - (grub_rescue_cmd_ls): Use grub_device_iterate with - grub_rescue_print_devices instead of grub_disk_dev_iterate with - grub_rescue_print_disks. - - * kern/partition.c (grub_partition_iterate): Return the result of - PARTMAP->ITERATE instead of GRUB_ERRNO. - - * kern/device.c: Include grub/partition.h. - (grub_device_iterate): New function. - - * include/grub/partition.h (grub_partition_iterate): Return int - instead of grub_err_t. - - * include/grub/normal.h [GRUB_UTIL] (grub_search_init): New - prototype. - [GRUB_UTIL] (grub_search_fini): Likewise. - - * include/grub/device.h (grub_device_iterate): New prototype. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Added - commands/search.c. - (pkgdata_MODULES): Added search.mod. - (search_mod_SOURCES): New variable. - (search_mod_CFLAGS): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added commands/search.c. - (pkgdata_MODULES): Added search.mod. - (search_mod_SOURCES): New variable. - (search_mod_CFLAGS): Likewise. - - * commands/ls.c (grub_ls_list_disks): Renamed to ... - (grub_ls_list_devices): ... this, and use grub_device_iterate. - All callers changed. - - * DISTLIST: Added commands/search.c. - -2005-08-20 Yoshinori K. Okuji - - * kern/term.c (grub_putchar): Use grub_utf8_to_ucs4 for the - conversion. - (grub_getcharwidth): New function. - - * kern/misc.c (grub_utf8_to_ucs4): New function. - - * include/grub/term.h (struct grub_term): Added a new member - "getcharwidth". - (grub_getcharwidth): New prototype. - - * include/grub/misc.h (grub_utf8_to_ucs4): New prototype. - - * term/i386/pc/console.c (map_char): New function. Segregated from - grub_console_putchar. - (grub_console_putchar): Use map_char. - (grub_console_getcharwidth): New function. - (grub_console_term): Specified grub_console_getcharwidth as - getcharwidth. - - * term/i386/pc/vga.c (grub_vga_getcharwidth): New function. - (grub_vga_term): Specified grub_vga_getcharwidth as getcharwidth. - - * term/i386/pc/vesafb.c (grub_virtual_screen_setup): Return - GRUB_ERRNO. - (grub_vesafb_init): Do not use RC. Instead, use GRUB_ERRNO. Rely - on grub_strtoul completely. - (write_char): Declare local variables in the beginning of the - function. - (grub_vesafb_getcharwidth): New function. - (grub_vesafb_term): Specified grub_vesafb_getcharwidth as - getcharwidth. - -2005-08-19 Yoshinori K. Okuji - - * DISTLIST: Replace commands/i386/pc/vbe_list_modes.c and - commands/i386/pc/vbe_test.c with commands/i386/pc/vbeinfo.c and - commands/i386/pc/vbetest.c. - - * video/i386/pc/vbe.c (grub_vbe_probe): If INFOBLOCK is not NULL, - call grub_vbe_get_controller_info again, because the returned - information is volatile. - (grub_vbe_set_video_mode): Mostly rewritten. - (grub_vbe_get_video_mode): Use grub_vbe_probe and use - grub_vbe_status_t correctly. - (grub_vbe_get_video_mode_info): Likewise. - (grub_vbe_set_pixel_rgb): Use a switch statement rather than - several if statements. - - * commands/i386/pc/vbe_list_modes.c: Renamed to ... - * commands/i386/pc/vbeinfo.c: ... this. - - * commands/i386/pc/vbe_test.c: Renamed to ... - * commands/i386/pc/vbetest.c: ... this. - - * commands/i386/pc/vbeinfo.c (grub_cmd_vbe_list_modes): Renamed to - ... - (grub_cmd_vbeinfo): ... this. Save video modes before - iterating. Skip a video mode, if it is not available, not enough - information is given or it is monochrome. Show the memory - model. Leave the interpretation of MODEVAR to grub_strtoul - completely. - (GRUB_MOD_INIT): Rename vbe_list_modes to vbeinfo. - (GRUB_MOD_FINI): Likewise. - - * commands/i386/pc/vbetest.c (grub_cmd_vbe_test): Renamed to ... - (grub_cmd_vbetest): ... this. Don't print unnecessarily. Use - grub_err_t instead of grub_uint32_t. Don't use SPTR. Remove a - duplicated grub_env_get. Leave the interpretation of MODEVAR to - grub_strtoul completely. - (real2pm): Removed. - (GRUB_MOD_INIT): Rename vbe_test to vbetest. - (GRUB_MOD_FINI): Likewise. - - * normal/misc.c: Include grub/mm.h. - - * conf/i386-pc.rmk (pkgdata_MODULES): Replaced vbe_test.mod and - vbe_list_modes with vbetest.mod and vbeinfo.mod. - (vbe_list_modes_mod_SOURCES): Removed. - (vbe_list_modes_mod_CFLAGS): Likewise. - (vbe_test_mod_SOURCES): Likewise. - (vbe_test_mod_CFLAGS): Likewise. - (vbeinfo_mod_SOURCES): New variable. - (vbeinfo_mod_CFLAGS): Likewise. - (vbetest_mod_SOURCES): Likewise. - (vbetest_mod_CFLAGS): Likewise. - -2005-08-18 Yoshinori K. Okuji - - * normal/misc.c: New file. - - * DISTLIST: Added normal/misc.c. - - * partmap/amiga.c (amiga_partition_map_iterate): Add an argument - DISK to HOOK. Call HOOK with DISK. - * partmap/apple.c (apple_partition_map_iterate): Likewise. - * partmap/pc.c (pc_partition_map_iterate): Likewise. - * partmap/sun.c (sun_partition_map_iterate): Likewise. - - * normal/menu_entry.c (struct screen): Added a new member - "completion_shown". - (completion_buffer): New global variable. - (make_screen): Set SCREEN->COMPLETION_SHOWN to zero. - (store_completion): New function. - (complete): Likewise. - (clear_completions): Likewise. - (grub_menu_entry_run): If SCREEN->COMPLETION_SHOWN is non-zero, - call clear_completions and reset SCREEN->COMPLETION_SHOWN. If C is - a tab, call complete. - - * normal/completion.c (disk_dev): Removed. - (print_simple_completion): Likewise. - (print_partition_completion): Likewise. - (print_func): New global variable. - (add_completion): Do not take the arguments WHAT or PRINT any - longer. Added a new argument TYPE. Instead of printing directly, - call PRINT_FUNC if not NULL. - All callers changed. - (complete_device): Use a local variable DEV instead of - DISK_DEV. Do not move CURRENT_WORD to the end of a device name. - (grub_normal_do_completion): Take a new argument HOOK. Do not - initialize DISK_DEV. Initialize PRINT_FUNC to HOOK. If RET is an - empty string, return NULL instead. - All callers changed. - - * normal/cmdline.c (print_completion): New function. - - * kern/partition.c (grub_partition_iterate): Add an argument DISK - to HOOK. - All callers changed. - - * kern/disk.c (grub_print_partinfo): Removed. - - * include/grub/partition.h (struct grub_partition_map): Add a new - argument DISK into HOOK of ITERATE. - (grub_partition_iterate): Add a new argument DISK to HOOK. - - * include/grub/normal.h (enum grub_completion_type): New enum. - (grub_completion_type_t): New type. - (GRUB_COMPLETION_TYPE_COMMAND): New constant. - (GRUB_COMPLETION_TYPE_DEVICE): Likewise. - (GRUB_COMPLETION_TYPE_PARTITION): Likewise. - (GRUB_COMPLETION_TYPE_FILE): Likewise. - (grub_normal_do_completion): Added a new argument HOOK. - (grub_normal_print_device_info): New prototype. - - * include/grub/disk.h (grub_print_partinfo): Removed. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added normal/misc.c. - (normal_mod_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * commands/ls.c (grub_ls_list_disks): Use - grub_normal_print_device_info instead of grub_print_partinfo. Free - PNAME. - (grub_ls_list_files): Use grub_normal_print_device_info instead of - duplicating the code. - -2005-08-16 Vesa Jaaskelainen - - * commands/i386/pc/vbe_list_modes.c: Update source formatting to - follow GCS more precisely. - * commands/i386/pc/vbe_test.c: Likewise. - * include/grub/i386/pc/vbe.h: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * video/i386/pc/vbe.c: Likewise. - -2005-08-16 Vesa Jaaskelainen - - * DISTLIST: Added term/i386/pc/vesafb.c - DISTLIST: Added video/i386/pc/vbe.c - DISTLIST: Added commands/i386/pc/vbe_list_modes.c. - DISTLIST: Added commands/i386/pc/vbe_test.c. - * commands/i386/pc/vbe_list_modes.c: New file. - * commands/i386/pc/vbe_test.c: Likewise. - * term/i386/pc/vesafb.c: Likewise. - * video/i386/pc/vbe.c: Likewise. - * include/grub/i386/pc/vbe.h (GRUB_VBE_DEFAULT_VIDEO_MODE): Added define. - (grub_vbe_probe) Added prototype. - (grub_vbe_set_video_mode) Likewise. - (grub_vbe_get_video_mode) Likewise. - (grub_vbe_get_video_mode_info) Likewise. - (grub_vbe_set_pixel_rgb) Likewise. - (grub_vbe_set_pixel_index) Likewise. - * conf/i386-pc.rmk (pkgdata_MODULES): Added vbe.mod. - (pkgdata_MODULES): Added vesafb.mod. - (pkgdata_MODULES): Added vbe_list_modes.mod. - (pkgdata_MODULES): Added vbe_test.mod. - (vbe_mod_SOURCES): Added. - (vbe_mod_CFLAGS): Likewise. - (vesafb_mod_SOURCES): Likewise. - (vesafb_mod_CFLAGS): Likewise. - (vbe_list_modes_mod_SOURCES): Likewise. - (vbe_list_modes_mod_CFLAGS): Likewise. - (vbe_test_mod_SOURCES): Likewise. - (vbe_test_mod_CFLAGS): Likewise. - -2005-08-14 Yoshinori K. Okuji - - * normal/command.c (grub_command_execute): If INTERACTIVE is - false and GRUB_COMMAND_FLAG_NO_ECHO is not specified, print - CMDLINE. Disable the pager if INTERACTIVE is true. - All callers are changed. - - * normal/main.c (grub_normal_execute): Read command.lst and fs.lst - before reading a config file. - * normal/main.c (read_config_file): Even if a command is not - found, register it if it is within an entry. - - * util/grub-emu.c: Include sys/types.h and unistd.h. - (options): Added --hold. - (struct arguments): Added a new member "hold". - (parse_opt): If KEY is 'H', set ARGS->HOLD to ARG or -1 if ARG is - missing. - (main): Initialize ARGS.HOLD to zero. Wait until ARGS.HOLD is - cleared by a debugger, if it is not zero. - - * include/grub/normal.h (grub_command_execute): Add an argument - INTERACTIVE. - -2005-08-14 Vesa Jaaskelainen - - * DISTLIST: Added include/grub/i386/pc/vbe.h. - -2005-08-13 Yoshinori K. Okuji - - * aclocal.m4 (grub_I386_CHECK_REGPARM_BUG): Replace the test - program with another one, because the old one didn't detect a bug - in gcc-3.4. Always use regparm 2, because the new test is still - not enough for gcc-4.0. Someone must investigate a simple test - case which detects a bug in gcc-4.0. - -2005-08-12 Yoshinori K. Okuji - - * DISTLIST: Added normal/completion.c. - - * normal/completion.c: New file. - - * term/i386/pc/console.c (grub_console_getwh): New function. - (grub_console_term): Assign grub_console_getwh to getwh. - - * normal/cmdline.c (grub_tab_complete): Removed. Now the same - function is defined in normal/completion.c as - grub_normal_do_completion. - (grub_cmdline_get): Use grub_normal_do_completion instead of - grub_tab_complete. - - * kern/partition.c (grub_partition_map_iterate): Return 1 if HOOK - returns non-zero, otherwise return 0. - (grub_partition_iterate): First, probe the partition map. Then, - call ITERATE only for this partition map. - - * kern/misc.c (grub_strncmp): Rewritten. - - * kern/disk.c (grub_disk_dev_iterate): Return 1 if P->ITERATE - returns non-zero. Otherwise return 0. - - * include/grub/partition.h (grub_partition_map_iterate): Return - int instead of void. - - * include/grub/normal.h (grub_normal_do_completion): New prototype. - - * include/grub/misc.h (grub_strncmp): Change the type of N to - grub_size_t. - - * include/grub/disk.h (grub_disk_dev_iterate): Return int instead - of void. - - * normal/menu.c (draw_border): Cast GRUB_TERM_BORDER_WIDTH to - unsigned explicitly before comparing it with I. - - * kern/main.c (grub_env_write_root): Add the attribute unused into - VAR. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Added - normal/completion.c. - (normal_mod_SOURCES): Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - - * normal/command.c (grub_iterate_commands): If ITERATE returns - non-zero, return one immediately. - -2005-08-09 Vesa Jaaskelainen - - * conf/i386-pc.rmk (kernel_img_HEADERS): Added machine/vbe.h. - * kern/i386/pc/startup.S: Updated Global Descriptor table's - descriptions. - (grub_vbe_get_controller_info): New function. - (grub_vbe_get_mode_info): Likewise. - (grub_vbe_set_mode): Likewise. - (grub_vbe_get_mode): Likewise. - (grub_vbe_set_memory_window): Likewise. - (grub_vbe_get_memory_window): Likewise. - (grub_vbe_set_scanline_length): Likewise. - (grub_vbe_get_scanline_length): Likewise. - (grub_vbe_set_display_start): Likewise. - (grub_vbe_get_display_start): Likewise. - (grub_vbe_set_palette_data): Likewise. - * include/grub/i386/pc/vbe.h: New file. - -2005-08-08 Hollis Blanchard - - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Replaced - kern/ieee1275/of.c with kern/ieee1275/ieee1275.c. - * DISTLIST: Likewise. - * kern/ieee1275/of.c: Moved to ... - * kern/ieee1275/ieee1275.c: ... here. - -2005-08-08 Hollis Blanchard - - * term/ieee1275/ofconsole.c: Include . - (grub_ofconsole_getwh): Cast -1 to type grub_ieee1275_ihandle_t. - Pass 0 as `end' parameter to grub_strtoul(). - -2005-08-08 Hollis Blanchard - - * include/grub/powerpc/ieee1275/console.h: Do not include - . Do not include . Remove ASM_FILE - ifdef. - (grub_console_cur_color): Remove i386-specific prototype. - (grub_console_real_putchar): Likewise. - (grub_console_checkkey): Likewise. - (grub_console_getkey): Likewise. - (grub_console_getxy): Likewise. - (grub_console_gotoxy): Likewise. - (grub_console_cls): Likewise. - (grub_console_setcursor): Likewise. - * kern/powerpc/ieee1275/init.c: Don't include . - Include . - * term/ieee1275/ofconsole.c: Likewise. - -2005-08-08 Yoshinori K. Okuji - - * Makefile.in (LIBLZO): New variable. - - * configure.ac: Check for LZO version 2. - - * util/i386/pc/grub-mkimage.c [HAVE_LZO_LZO1X_H]: Include - lzo/lzo1x.h instead of lzo1x.h. - - * conf/i386-pc.rmk (grub_mkimage_LDFLAGS): Use $(LIBLZO) instead - of -llzo. - - * util/i386/pc/grub-setup.c (main): Do not free PREFIX - twice. Reported by Vladimir Serbinenko . - - * partmap/pc.c (pc_partition_map_probe): Restore P->DATA after - copying the data from PARTITION to P. - -2005-08-07 Yoshinori K. Okuji - - * kern/rescue.c (grub_rescue_cmd_rmmod): If the reference count is - negative, unload the module. - - * util/i386/pc/grub-setup.c (setup): The name of the PC partition - map is "pc_partition_map" but not "pc". - (usage): Fix the description. The options are --boot-image and - --core-image but not --boot-file or --core-file. - (main): If not specified explicitly, make BOOT_FILE and CORE_FILE - based on DEFAULT_BOOT_FILE and DEFAULT_CORE_FILE with DIR or - DEFAULT_DIRECTORY. - - * util/i386/pc/grub-install.in: Do not specify --boot-file or - --core-file. Specify INSTALL_DEVICE as an argument. - - * util/console.c: Include config.h. - [HAVE_NCURSeS_CURSES_H]: Include ncurses/curses.h. - [HAVE_NCURSES_H]: Include ncurses.h. - [HAVE_CURSES_H]: Include curses.h. - [!A_NORMAL] (A_NORMAL): Defined as zero. - [!A_STANDOUT] (A_STANDOUT): Likewise. - - * conf/i386-pc.rmk (grub_emu_LDFLAGS): Use $(LIBCURSES) instead of - -lncurses. - * conf/powerpc-ieee1275.rmk (grub_emu_LDFLAGS): Likewise. - - * configure.ac: Check for curses libraries and headers. - - * Makefile.in (LIBCURSES): New variable. - - * genmk.rb (Script::rule): Set the executable bits. - - * util/i386/pc/biosdisk.c (grub_util_biosdisk_get_grub_dev): The - name of the PC partition map is "pc_partition_map" but not "pc". - -2005-08-07 Yoshinori K. Okuji - - * util/i386/pc/grub-install.in (grub_probefs): New variable. - (modules): Likewise. - (usage): Added descriptions for --modules and --grub-probefs. - Handle --modules and --grub-probefs. Save the arguments in MODULES - and GRUB_PROBEFS, respectively. - Auto-detect a filesystem module against GRUBDIR. If the result is - empty and modules are not specified explicitly, abort the - installation. Add the result to MODULES. - - * DISTLIST: Removed boot/powerpc/ieee1275/ieee1275.c, - disk/powerpc/ieee1275/ofdisk.c, - include/grub/powerpc/ieee1275/init.h and - term/powerpc/ieee1275/ofconsole.c. - Added disk/ieee1275/ofdisk.c, kern/ieee1275/of.c and - term/ieee1275/ofconsole.c. - - * include/grub/powerpc/ieee1275/console.h: Resurrected. - - * COPYING: Upgraded to the latest version. Only the address of the - FSF office has changed. - -2005-08-07 Yoshinori K. Okuji - - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Replaced - kern/ieee1275.c with kern/ieee1275/of.c. - - * kern/ieee1275.c: Moved to ... - * kern/ieee1275/of.c: ... here. - -2005-08-06 Yoshinori K. Okuji - - * conf/i386-pc.rmk (kernel_img_HEADERS): Reordered for - readability. - - * config.guess: Updated to the latest version from gnulib. - * config.sub: Likewise. - * install.sh: Likewise. - * mkinstalldirs: Likewise. - - * include/grub/console.h: Removed. This file is arch-specific. Do - not put this in include/grub. - - * include/grub/i386/pc/console.h: Resurrected. - - * util/console.c: Include grub/machine/console.h instead of - grub/console.h. - * util/grub-emu.c: Likewise. - -2005-08-04 Marco Gerards - - * kern/term.c (grub_putcode): Use `grub_getwh' instead of - hardcoded value. - - From Vincent Pelletier - * include/grub/term.h (GRUB_TERM_WIDTH, GRUB_TERM_HEIGHT): - Redefined to use grub_getwh. - (grub_term): New member named getwh. - (grub_getwh): New prototype. - * kern/term.c (grub_getwh): New function. - * term/i386/pc/console.c (grub_console_getwh): New function. - (grub_console_term): New member `getwh'. - * term/i386/pc/vga.c (grub_vga_getwh): New function. - (grub_vga_term): New member `getwh'. - * term/ieee1275/ofconsole.c (grub_ofconsole_readkey): Use - grub_ssize_t. - (grub_ofconsole_getw): New function. - (grub_ofconsole_init): Use grub_ssize_t and unsigned char. - (grub_ofconsole_term): New field named getwh and new initial - value. - -2005-08-03 Hollis Blanchard - - * include/grub/powerpc/ieee1275/ieee1275.h: Move ... - * include/grub/ieee1275/ieee1275.h: ... to here. All users updated. - Move `abort', `grub_reboot', and `grub_halt' prototypes ... - * include/grub/powerpc/ieee1275/kernel.h: ... to here. - * commands/ieee1275/halt.c: Include instead - of . - * commands/ieee1275/reboot.c: Likewise. - * boot/powerpc/ieee1275/ieee1275.c: Move ... - * kern/ieee1275.c: ... to here. All users updated. Change all - parameter structs to use new type `grub_ieee1275_cell_t'. - * term/powerpc/ieee1275/ofconsole.c: Move ... - * term/ieee1275/ofconsole.c: ... to here. All users updated. - * disk/powerpc/ieee1275/ofdisk.c: Move ... - * disk/ieee1275/ofdisk.c: ... to here. All users updated. - * boot/powerpc/ieee1275/cmain.c: Change `grub_ieee1275_entry_fn' type - to return int. - * include/grub/i386/pc/console.h: Move to include/grub/console.h. - Remove unused prototypes. All users updated. - * include/grub/powerpc/ieee1275/console.h: Removed. - * include/grub/powerpc/ieee1275/ieee1275.h: Define - `grub_ieee1275_cell_t'. - * kern/powerpc/ieee1275/openfw.c: Include . - Cast comparisons with -1 to the correct type. - * loader/powerpc/ieee1275/linux.c (kernel_entry_t): Change parameter - type to match `grub_ieee1275_entry_fn'. - -2005-08-01 Yoshinori K. Okuji - - * DISTLIST: Added util/i386/pc/grub-probefs.c. - - * conf/i386-pc.rmk (sbin_UTILITIES): Added grub-probefs. - (grub_setup_SOURCES): Removed partmap/amiga.c, partmap/apple.c and - partmap/sun.c. - (grub_probefs_SOURCES): New variable. - - * util/i386/pc/grub-probefs.c: New file. - - * util/i386/pc/grub-setup.c (main): Call - grub_pc_partition_map_init, grub_ufs_init, grub_minix_init, - grub_hfs_init and grub_jfs_init to initialize the system. Call - grub_ufs_fini, grub_minix_fini, grub_hfs_fini, grub_jfs_init and - grub_pc_partition_map_fini to finish the system. - -2005-07-31 Yoshinori K. Okuji - - * loader/i386/pc/multiboot.c (grub_multiboot_is_elf32): New - function. - (grub_multiboot_load_elf32): Likewise. - (grub_multiboot_is_elf64): Likewise. - (grub_multiboot_load_elf64): Likewise. - (grub_multiboot_load_elf): Likewise. - (grub_rescue_cmd_multiboot): Call grub_multiboot_load_elf to load - an ELF32 or ELF64 file. - This is based on a patch from Ruslan Nikolaev . - - From Serbinenko Vladimir : - * kern/disk.c (grub_print_partinfo): Check if FS->LABEL is not - NULL before calling FS->LABEL. - * fs/fat.c (grub_fat_dir): Initialize DIRNAME to NULL. - * commands/ls.c (grub_ls_list_files): Show labels, if possible. - (grub_ls_list_disks): Check if FS and FS->LABEL are not NULL - before calling FS->LABEL. - -2005-07-26 Yoshinori K. Okuji - - * util/i386/pc/grub-install.in (datadir): New variable. - (libdir): Removed. - (pkgdatadir): New variable. - (pkglibdir): Removed. - -2005-07-24 Yoshinori K. Okuji - - * DISTLIST: Added util/i386/pc/grub-install.in. - - * util/i386/pc/grub-install.in: New file. - - * conf/i386-pc.rmk (sbin_SCRIPTS): New variable. - (grub_install_SOURCES): Likewise. - - * genmk.rb: Added support for scripts. - (Script): New class. - (scripts): New variable. - - * Makefile.in (install-local): Install sbin_SCRIPTS by - INSTALL_SCRIPT. - (uninstall): Remove sbin_SCRIPTS. - - * util/i386/pc/grub-setup.c (main): If the argument is not a GRUB - device, try to get a GRUB device by - grub_util_biosdisk_get_grub_dev. - Free DEST_DEV. - - * util/i386/pc/grub-mkdevicemap.c (usage): Remove a duplicated - description for --device-map. - -2005-07-20 Yoshinori K. Okuji - - Change the semantics of variable hooks. They now return strings - instead of error values. - - * util/i386/pc/grub-setup.c: Include grub/env.h. - (setup): Use grub_device_set_root instead of grub_env_set. - - * kern/rescue.c (grub_rescue_cmd_root): Use grub_env_set and - grub_env_get instead of grub_device_set_root and - grub_device_get_root, respectively. - - * kern/main.c (grub_env_write_root): New function. - (grub_set_root_dev): Register grub_env_write_hook for "root". Use - grub_env_set instead of grub_device_set_root. - - * kern/env.c (HASHSZ): Reduced to 13, because GRUB does not need - many variables. - (grub_env_set): Set ENV->VALUE to the result of ENV->WRITE_HOOK - rather than calling ENV->WRITE_HOOK afterwards. - (grub_env_get): Return the result of ENV->READ_HOOK rather than - passing a pointer of a pointer. - (grub_register_variable_hook): Change the types of "read_hook" and - "write_hook" to grub_env_read_hook_t and grub_env_write_hook_t, - respectively. - Allocate the default empty string on the heap, because this string - may be freed later. - - * kern/device.c: Include grub/env.h. - (grub_device_set_root): Removed. - (grub_device_get_root): Likewise. - (grub_device_open): Use grub_env_get instead of - grub_device_get_root. - - * include/grub/env.h (grub_env_read_hook_t): New type. - (grub_env_write_hook_t): Likewise. - (grub_env_var): Change the types of "read_hook" and "write_hook" - to grub_env_read_hook_t and grub_env_write_hook_t, respectively. - (grub_register_variable_hook): Likewise. - - * include/grub/device.h (grub_device_set_root): Removed. - (grub_device_set_root): Likewise. - - * fs/fat.c (grub_fat_dir): Make a copy of PATH in DIRNAME, and - make sure that DIRNAME terminates with '/', so that - grub_fat_find_dir will fail if PATH is not a directory. - - * commands/ls.c (grub_ls_list_files): Remove the qualifier const - from DIRNAME. - Use the qualifier auto for print_files and print_files_long. - If FS->DIR sets GRUB_ERRNO to GRUB_ERR_BAD_FILE_TYPE, try DIRNAME - as a regular file. - Put a newline only if there is no error. - (grub_cmd_ls): Remove grub_ls_print_files, because this is not - used. - -2005-07-20 Yoshinori K. Okuji - - * kern/partition.c (grub_partition_probe): Initialize PART to - NULL. Otherwise, when no partition map is registered, this returns - a garbage. - -2005-07-19 Yoshinori K. Okuji - - * partmap/apple.c (apple_partition_map_iterate): Check if POS - equals GRUB_DISK_SECTOR_SIZE to see if the partition table is - valid. - -2005-07-18 Yoshinori K. Okuji - - * commands/ls.c (grub_ls_list_disks): Print the filesystem - information on each device, if it does not have partitions. Print - "Device" instead of "Disk", because this function is not specific - to disk devices. - - * normal/main.c (grub_rescue_cmd_normal): Make the variable CONFIG - static to ensure that it is put on the memory rather than a - register. - -2005-07-17 Yoshinori Okuji - - * commands/cat.c (GRUB_MOD_INIT): Use better documentation. - (grub_cat_init): Likewise. - * loader/i386/pc/chainloader_normal.c (GRUB_MOD_INIT): Likewise. - (options): Likewise. - * commands/configfile.c (GRUB_MOD_INIT): Likewise. - (grub_configfile_init): Likewise. - * font/manager.c (GRUB_MOD_INIT): Likewise. - * commands/help.c (GRUB_MOD_INIT): Likewise. - (grub_help_init): Likewise. - * normal/command.c (grub_command_init): Likewise. - * loader/i386/pc/linux_normal.c (GRUB_MOD_INIT): Likewise. - * disk/loopback.c (grub_loop_init): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/ls.c (grub_ls_init): Likewise. - (GRUB_MOD_INIT): Likewise. - (options): Likewise. - * commands/boot.c (grub_boot_init): Likewise. - (GRUB_MOD_INIT): Likewise. - * loader/i386/pc/multiboot_normal.c (GRUB_MOD_INIT): Likewise. - * commands/i386/pc/reboot.c (grub_reboot_init): Likewise. - (GRUB_MOD_INIT): Likewise. - * commands/cmp.c (grub_cmp_init): Likewise. - (GRUB_MOD_INIT): Likewise. - - * normal/arg.c: Use <> instead of "" to include header files. - (SHORT_ARG_HELP): New macro. - (SHORT_ARG_USAGE): Likewise. - (help_options): Specify SHORT_ARG_HELP and SHORT_ARG_USAGE instead - of 'h' and 'u' for help and usage, respectively. Use more GNU-like - descriptions. - (find_short): Check if C is 'h' or 'u' explicitly. - (grub_arg_show_help): Use space characters instead of tabs. Treat - SHORT_ARG_HELP and SHORT_ARG_USAGE exceptionally so that -h and -u - are shown with --help and --usage only if they are not used for - the command itself. - (parse_option): Use SHORT_ARG_HELP and SHORT_ARG_USAGE instead of - 'h' and 'u'. - - * include/grub/arg.h (struct grub_arg_option): Add the qualifier - const into "longarg". Change the type of "shortarg" to int. - -2005-07-17 Yoshinori Okuji - - * boot/i386/pc/boot.S (boot_drive_check): New label. - - * include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_DRIVE_CHECK): New - macro. - - * util/i386/pc/grub-setup.c (setup): Added a workaround for BIOSes - which do not pass a boot drive correctly. Copied from GRUB Legacy. - -2005-07-17 Yoshinori Okuji - - * kern/i386/pc/startup.S (gate_a20_try_system_control_port_a): - When turning off Gate A20, skip the check and return immediately, - because this is not fatal usually. - -2005-07-17 Yoshinori Okuji - - * conf/i386-pc.rmk (pxeboot_img_LDFLAGS): The text address should - be 0x7C00 instead of 0x8000. - - * boot/i386/pc/pxeboot.S: Rewritten. - - * kern/i386/pc/startup.S (gate_a20_try_bios): No need to specify - EXT_C. - (gate_a20_check_state): Read a byte from 0x108000. Invert the - result. - -2005-07-16 Yoshinori K. Okuji - - * kern/i386/pc/startup.S (grub_gate_a20): Rewritten for - robustness. This routine now supports a BIOS call and System - Control Port A to modify the gate A20. - - * include/grub/i386/pc/kernel.h (GRUB_KERNEL_MACHINE_RAW_SIZE): - Increased to 0x440. - -2005-07-12 Hollis Blanchard - - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_open): dprintf the - device path and resulting ihandle. - (grub_ofdisk_close): dprintf the ihandle being closed. - (grub_ofdisk_read): dprintf function parameters. - * kern/mm.c (grub_mm_init_region): Likewise. - * loader/powerpc/ieee1275/linux.c: Remove extra whitespace. - (grub_linux_boot): dprintf the Linux entry point, initrd address and - size, and boot arguments. - (grub_rescue_cmd_linux): dprintf each ELF segment's address and size - before loading into memory. - (grub_rescue_cmd_initrd): dprintf the initrd's address and size - before loading into memory. - -2005-07-12 Yoshinori K. Okuji - - * kern/mm.c: Added much documentation. - (GRUB_MM_ALIGN_LOG2): When GRUB_CPU_SIZEOF_VOID_P is - 8, set to 5 instead of 8. - -2005-07-10 Yoshinori Okuji - - * DISTLIST: Added util/i386/pc/grub-mkimage.c. - - * conf/i386-pc.rmk (sbin_UTILITIES): Added grub-mkdevicemap. - (grub_mkdevicemap_SOURCES): New variable. - - * util/i386/pc/grub-mkdevicemap.c: New file. Mostly copied from - lib/device.c of GRUB Legacy. - -2005-07-10 Yoshinori Okuji - - * commands/ls.c (grub_ls_list_files): Check if *PATH is NUL - instead of PATH is NULL. - -2005-07-09 Vincent Pelletier - - * commands/cmp.c (BUFFER_SIZE): New macro. - (grub_cmd_cmp): Close the right file at the right time. Compare - only data just read. Don't report files of different size as - identical. Dynamically allocate buffers. Move variable - declarations at the beginning of function. - -2005-07-09 Yoshinori Okuji - - * aclocal.m4 (grub_I386_CHECK_REGPARM_BUG): The return value was - reverse. - -2004-07-04 Vincent Pelletier - - * normal/cmdline.c (grub_cmdline_get): Don't fallback on ctrl-d - when backspace is pressed at beginning of line. - -2005-07-03 Yoshinori Okuji - - * DISTLIST: Added genfslist.sh. - - * normal/main.c (fs_module_list): New variable. - (autoload_fs_module): New function. - (read_fs_list): Likewise. - (grub_normal_execute): Call read_fs_list. - - * kern/fs.c (grub_fs_autoload_hook): New variable. - (grub_fs_probe): Added support for auto-loading. - - * include/grub/normal.h (struct grub_fs_module_list): New struct. - (grub_fs_module_list_t): New type. - - * include/grub/fs.h (grub_fs_autoload_hook_t): New type. - (grub_fs_autoload_hook): New prototype. - - * genfslist.sh: New file. - - * genmk.rb: Added a rule to generate a filesystem list. - -2005-06-30 Marco Gerards - - * configure.ac: Fix the test for cross-compiling. - - * genmk.rb (Program): Use `$(CC)' instead of `$(BUILD_CC)'. Don't - define GRUB_UTIL anymore. - - * util/powerpc/ieee1275/grub-mkimage.c (load_note): Endian fixes - so this function works on other systems than just big endian. - (load_modules): Likewise. - (add_segments): Likewise. - -2005-06-23 Hollis Blanchard - - * kern/misc.c (grub_vsprintf): Add `longfmt'. If format string - contains `l' modifier, get a long from va_arg(). - -2005-06-23 Yoshinori K. Okuji - - * kern/mm.c (grub_free): If the next free block which is being - merged is the first free block, set the first block to the block - being freed. - Reported by Vincent Guffens . - -2005-05-08 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (cmain): Initialize - `grub_ieee1275_chosen'. - -2005-05-08 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (module_info): Remove definition. - (grub_ieee1275_chosen): New variable. - (cmain): Initialize and use `grub_ieee1275_chosen' instead of - `chosen'. - * boot/powerpc/ieee1275/crt0.S (init_stack): Remove stack space. - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_get_property): - Rename first argument to `phandle' for consistency. - (grub_ieee1275_get_property_length): Likewise. - (grub_ieee1275_next_property): Likewise. Change type of first argument - to grub_ieee1275_phandle_t. - * include/grub/powerpc/ieee1275/ieee1275.h (grub_ieee1275_entry_fn): - Move export next to declaration. - (grub_ieee1275_chosen): New variable. - * include/grub/powerpc/ieee1275/kernel.h (GRUB_IEEE1275_MODULE_BASE): - Correct cosmetic typo. - * kern/powerpc/ieee1275/init.c (grub_set_prefix): Use - `grub_ieee1275_chosen'. - * kern/powerpc/ieee1275/openfw.c (grub_map): Likewise. - * loader/powerpc/ieee1275/linux.c (grub_linux_boot): Likewise. - (grub_rescue_cmd_linux): Set `initrd_addr' to 0. - * term/powerpc/ieee1275/ofconsole.c (grub_ofconsole_refresh): Use - `grub_ieee1275_chosen'. - -2005-05-10 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (cmain): Remove code to parse - /chosen/bootargs. - * kern/powerpc/ieee1275/init.c (grub_machine_init): Parse - /chosen/bootargs as "variable=value" pairs. - -2005-05-08 Vincent Pelletier - - * include/grub/misc.h (grub_dprintf): New macro. - (grub_real_dprintf): New prototype. - (grub_strword): Likewise. - (grub_iswordseparator): Likewise. - * kern/misc.c (grub_real_dprintf): New function. - (grub_strword): Likewise. - (grub_iswordseparator): Likewise. - -2005-04-30 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c: Don't include grub/machine/init.h. - (roundup): Remove macro. - (grub_ieee1275_flags): Make static. - (grub_ieee1275_realmode): Remove. - (grub_ieee1275_test_flag): New function. - (grub_ieee1275_set_flag): Likewise. - (find_options): Rename to `grub_ieee1275_find_options'; update - callers. Set GRUB_IEEE1275_FLAG_REAL_MODE and - GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS. - (cmain): New prototype. - (cmain): Use `grub_ieee1275_set_flag' instead of accessing - `grub_ieee1275_flags' directly. - * conf/powerpc-ieee1275.rmk (grubof_HEADERS): Remove - machine/biosdisk.h. - * disk/powerpc/ieee1275/ofdisk.c: Include grub/machine/ofdisk.h. - Don't include grub/machine/init.h. - (grub_ofdisk_open): Call `grub_ieee1275_test_flag'. - * include/grub/powerpc/ieee1275/ieee1275.h (grub_ieee1275_flags): - Remove prototype. - (grub_ieee1275_realmode): Likewise. - (grub_ieee1275_flag): New enum. - (grub_ieee1275_test_flag): New prototype. - (grub_ieee1275_set_flag): New prototype. - * include/grub/powerpc/ieee1275/init.h: Remove file. - * include/grub/powerpc/ieee1275/ofdisk.h: New file. - * kern/powerpc/ieee1275/init.c: Don't include grub/machine/init.h. - Include grub/machine/console.h. Include grub/machine/ofdisk.h. - (grub_machine_fini): Don't call `grub_ieee1275_release'. Remove - comment. - * kern/powerpc/ieee1275/openfw.c (grub_claimmap): Call - `grub_ieee1275_test_flag'. - (grub_ieee1275_encode_devname): Likewise. - -2005-04-21 Hollis Blanchard - - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_encode_devname): New prototype. - (grub_ieee1275_get_filename): Likewise. - * kern/powerpc/ieee1275/init.c (grub_translate_ieee175_path): New - function. - (grub_set_prefix): Likewise. - (grub_machine_init): Call grub_set_prefix. - * kern/powerpc/ieee1275/openfw.c: Fix typos. - (grub_parse_type): New enum. - (grub_ieee1275_get_devargs): New function. - (grub_ieee1275_get_devname): Likewise. - (grub_ieee1275_parse_args): Likewise. - (grub_ieee1275_get_filename): Likewise. - (grub_ieee1275_encode_devname): Likewise. - -2005-03-30 Marco Gerards - - * kern/powerpc/ieee1275/init.c (grub_machine_fini): Don't call - `grub_loader_unset'. - -2005-03-26 Hollis Blanchard - - * commands/ieee1275/halt.c (grub_cmd_halt): Call grub_halt - instead of grub_ieee1275_interpret. - (grub_halt_init): New function. - (grub_halt_fini): Likewise. - (GRUB_MOD_INIT): Correct message grammar. - * commands/ieee1275/reboot.c (grub_cmd_reboot): Call grub_reboot - instead of grub_ieee1275_interpret. - (grub_reboot_init): New function. - (grub_reboot_fini): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Replace - commands/i386/pc/halt.c, commands/i386/pc/reboot.c, and - util/i386/pc/misc.c with commands/ieee1275/halt.c, - commands/ieee1275/reboot.c, and util/powerpc/ieee1275/misc.c. - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_fini): New - function. - * include/grub/powerpc/ieee1275/console.h (grub_console_fini): - Add prototype. - * include/grub/powerpc/ieee1275/ieee1275.h (grub_reboot): Add - prototype. - (grub_halt): Likewise. - * include/grub/powerpc/ieee1275/init.h: Remove inaccurate comment. - (cmain): Remove __attribute__((unused)). - * kern/powerpc/ieee1275/init.c (grub_heap_start): New variable. - (grub_heap_len): Likewise. - (grub_machine_fini): New function. - * kern/powerpc/ieee1275/openfw.c (grub_reboot): New function. - (grub_halt): Likewise. - * term/powerpc/ieee1275/ofconsole.c (grub_console_fini): New - function. - * util/powerpc/ieee1275/misc.c: New file. - -2005-03-19 Yoshinori K. Okuji - - * DISTLIST: New file. - * gendistlist.sh: Likewise. - - * Makefile.in (COMMON_DISTFILES): Removed. - (BOOT_DISTFILES): Likewise. - (CONF_DISTFILES): Likewise. - (DISK_DISTFILES): Likewise. - (FS_DISTFILES): Likewise. - (INCLUDE_DISTFILES): Likewise. - (KERN_DISTFILES): Likewise. - (LOADER_DISTFILES): Likewise. - (TERM_DISTFILES): Likewise. - (UTIL_DISTFILES): Likewise. - (DISTFILES): Likewise. - (uninstall): Uninstall files in $(pkgdata_DATA). - (DISTLIST): New target. - (distdir): Use the contents of the file DISTLIST to get a list of - distributed files. - -2005-03-18 Yoshinori K. Okuji - - * fs/fat.c (grub_fat_mount): Ignore the 3rd bit of a media - descriptor. This is ported from GRUB Legacy. - - * gencmdlist.sh: Added an extra semicolon to make it work with - old sed versions. Reported by Robert Bihlmeyer - . - -2005-03-08 Yoshinori Okuji - - Automatic loading of commands is supported. - - * normal/main.c (read_command_list): New function. - (grub_normal_execute): Call read_command_list. - - * normal/command.c (grub_register_command): Return zero or CMD. - Allocate CMD->NAME from the heap. - Initialize CMD->MODULE_NAME to zero. - Find the same name as well. If the same command is found and it is - a dummy command, overwrite members. If it is not a dummy command, - return zero. - (grub_unregister_command): Free Q->NAME and Q->MODULE_NAME. - (grub_command_find): If a dummy command is found, load a module - and retry to find a command only once. - - * normal/cmdline.c (grub_tab_complete): Call grub_command_find to - make sure that each command is loaded. - - * include/grub/normal.h (GRUB_COMMAND_FLAG_NOT_LOADED): New - macro. - (struct grub_command): Remove const from the member `name'. - Add a new member `module_name'. - (grub_register_command): Return grub_command_t. - - * commands/help.c (grub_cmd_help): Call grub_command_find to make - sure that each command is loaded. - - * genmk.rb (PModule::rule): Specify a module name without the - suffix ".mod" to gencmdlist.sh. - -2005-03-02 Yoshinori K. Okuji - - * gencmdlist.sh: New file. - - * genmk.rb (PModule::rule): Generate a rule for a command list. - Clean command.lst. - Generate command.lst from $(COMMANDFILES). - - * Makefile.in (COMMON_DISTFILES): Added gencmdlist.sh. - (DATA): Added $(pkgdata_DATA). - (install-local): Install files in $(pkgdata_DATA). - -2005-03-02 Yoshinori K. Okuji - - * term/i386/pc/vga.c (debug_command): Removed. - (GRUB_MOD_INIT): Do not register the command "debug". - - From Hollis Blanchard: - * commands/configfile.c: New file. - * conf/i386-pc.rmk (grub_emu_SOURCES): Added - commands/configfile.c. - (pkgdata_MODULES): Added configfile.mod. - (configfile_mod_SOURCES): New variable. - (configfile_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Added - commands/configfile.c. - (pkgdata_MODULES): Added configfile.mod. - (configfile_mod_SOURCES): New variable. - (configfile_mod_CFLAGS): Likewise. - * util/grub-emu.c (main): Call grub_configfile_init and - grub_configfile_fini. - * include/grub/normal.h [GRUB_UTIL] (grub_configfile_init): New - prototype. - [GRUB_UTIL] (grub_configfile_fini): Likewise. - -2005-02-27 Yoshinori K. Okuji - - * normal/arg.c (grub_arg_show_help): Do not show the bug report - address. - - * commands/help.c (grub_cmd_help): Do not print newlines after - the last command in print_command_help. - -2005-02-27 Yoshinori K. Okuji - - * commands/default.h: New file. - * commands/timeout.h: Likewise. - * normal/context.c: Likewise. - - * util/misc.c: Do not include sys/times.h. - Include sys/time.h and grub/machine/time.h. - (grub_get_rtc): Rewritten with gettimeofday. - - * util/grub-emu.c (main): Call grub_default_init and - grub_timeout_init before grub_normal_init, and call - grub_timeout_fini and grub_default_fini after grub_main. - - * util/console.c (grub_ncurses_checkkey): Return the read - character or -1. - - * normal/menu.c (run_menu): Set MENU->TIMEOUT to -1 once it - timeouts. - - * normal/main.c (read_config_file): Push MENU. If this fails, - print an error and wait for a user input. - Print an error only if GRUB_ERRNO is not GRUB_ERR_NONE. - If a menu is empty or an error occurs, pop MENU. - (grub_normal_execute): Pop and free MENU after grub_menu_run - returns. - - * kern/loader.c (grub_loader_boot): Call grub_machine_fini. - - * include/grub/powerpc/ieee1275/time.h [GRUB_UTIL]: Do not - include time.h. - [GRUB_UTIL] (GRUB_TICKS_PER_SECOND): Use the same definition as - without GRUB_UTIL. - * include/grub/i386/pc/time.h [GRUB_UTIL]: Do not include - time.h. - [GRUB_UTIL] (GRUB_TICKS_PER_SECOND): Use the same definition as - without GRUB_UTIL. - - * include/grub/normal.h (struct grub_menu_list): New struct. - (grub_menu_list_t): New type. - (struct grub_context): New struct. - (grub_context_t): New type. - (grub_register_command): Got rid of EXPORT_FUNC. - (grub_unregister_command): Likewise. - (grub_context_get): New prototype. - (grub_context_get_current_menu): Likewise. - (grub_context_push_menu): Likewise. - (grub_context_pop_menu): Likewise. - [GRUB_UTIL] (grub_default_init): Likewise. - [GRUB_UTIL] (grub_default_fini): Likewise. - [GRUB_UTIL] (grub_timeout_init): Likewise. - [GRUB_UTIL] (grub_timeout_fini): Likewise. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added commands/default.c, - commands/timeout.c and normal/context.c. - (pkgdata_MODULES): Added default.mod and timeout.mod. - (normal_mod_SOURCES): Added normal/context.c. - (default_mod_SOURCES): New variable. - (default_mod_CFLAGS): Likewise. - (timeout_mod_SOURCES): Likewise. - (timeout_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Copied from - conf/i386-pc.rmk. - (pkgdata_MODULES): Added default.mod and timeout.mod. - (normal_mod_SOURCES): Added normal/context.c. - (default_mod_SOURCES): New variable. - (default_mod_CFLAGS): Likewise. - (timeout_mod_SOURCES): Likewise. - (timeout_mod_CFLAGS): Likewise. - - * Makefile.in (all-local): Added $(MKFILES). - -2005-02-21 Vincent Pelletier - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add `partmap/sun.c'. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add `sun.mod'. - (sun_mod_SOURCES, sun_mod_CFLAGS): New variables. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `partmap/sun.c'. - (pkgdata_MODULES): Add `sun.mod'. - (sun_mod_SOURCES, sun_mod_CFLAGS): New variables. - * include/grub/partition.h (grub_sun_partition_map_init): New - prototype. - (grub_sun_partition_map_fini): Likewise. - * partmap/sun.c: New file. - * util/grub-emu.c (main): Initialize and de-initialize the sun - partitionmap support. - -2005-02-19 Yoshinori K. Okuji - - This implements an Emacs-like menu entry editor. - - * normal/menu_entry.c: New file. - - * util/console.c (grub_ncurses_putchar): Translate some Unicode - characters to ASCII. - (saved_char): New variable. - (grub_ncurses_checkkey): Rewritten completely. - (grub_ncurses_getkey): Likewise. - (grub_ncurses_init): Call raw instead of cbreak. - - * normal/menu.c (print_entry): Do not put a space. - (init_page): Renamed to ... - (grub_menu_init_page): ... this. All callers changed. - (edit_menu_entry): Removed. - (run_menu): Call grub_menu_entry_run instead of edit_menu_entry. - - * normal/cmdline.c (grub_cmdline_run): Call grub_setcursor. - - * kern/misc.c (grub_vprintf): Call grub_refresh. - - * normal/menu.c (DISP_LEFT): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_LEFT): ... this. - * normal/menu.c (DISP_UP): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_UP): ... this. - * normal/menu.c (DISP_RIGHT): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_RIGHT): ... this. - * normal/menu.c (DISP_DOWN): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_DOWN): ... this. - * normal/menu.c (DISP_HLINE): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_HLINE): ... this. - * normal/menu.c (DISP_VLINE): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_VLINE): ... this. - * normal/menu.c (DISP_UL): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_UL): ... this. - * normal/menu.c (DISP_UR): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_UR): ... this. - * normal/menu.c (DISP_LL): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_LL): ... this. - * normal/menu.c (DISP_LR): Renamed to ... - * include/grub/term.h (GRUB_TERM_DISP_LR): ... this. - * normal/menu.c (TERM_WIDTH): Renamed to ... - * include/grub/term.h (GRUB_TERM_WIDTH): ... this. - * normal/menu.c (TERM_HEIGHT): Renamed to ... - * include/grub/term.h (GRUB_TERM_HEIGHT): ... this. - * normal/menu.c (TERM_INFO_HEIGHT): Renamed to ... - * include/grub/term.h (GRUB_TERM_INFO_HEIGHT): ... this. - * normal/menu.c (TERM_MARGIN): Renamed to ... - * include/grub/term.h (GRUB_TERM_MARGIN): ... this. - * normal/menu.c (TERM_SCROLL_WIDTH): Renamed to ... - * include/grub/term.h (GRUB_TERM_SCROLL_WIDTH): ... this. - * normal/menu.c (TERM_TOP_BORDER_Y): Renamed to ... - * include/grub/term.h (GRUB_TERM_TOP_BORDER_Y): ... this. - * normal/menu.c (TERM_LEFT_BORDER_X): Renamed to ... - * include/grub/term.h (GRUB_TERM_LEFT_BORDER_X): ... this. - * normal/menu.c (TERM_BORDER_WIDTH): Renamed to ... - * include/grub/term.h (GRUB_TERM_BORDER_WIDTH): ... this. - * normal/menu.c (TERM_MESSAGE_HEIGHT): Renamed to ... - * include/grub/term.h (GRUB_TERM_MESSAGE_HEIGHT): ... this. - * normal/menu.c (TERM_BORDER_HEIGHT): Renamed to ... - * include/grub/term.h (GRUB_TERM_BORDER_HEIGHT): ... this. - * normal/menu.c (TERM_NUM_ENTRIES): Renamed to ... - * include/grub/term.h (GRUB_TERM_NUM_ENTRIES): ... this. - * normal/menu.c (TERM_FIRST_ENTRY_Y): Renamed to ... - * include/grub/term.h (GRUB_TERM_FIRST_ENTRY_Y): ... this. - * normal/menu.c (TERM_ENTRY_WIDTH): Renamed to ... - * include/grub/term.h (GRUB_TERM_ENTRY_WIDTH): ... this. - * normal/menu.c (TERM_CURSOR_X): Renamed to ... - * include/grub/term.h (GRUB_TERM_CURSOR_X): ... this. - All callers changed. - - * include/grub/normal.h: New prototype. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added - normal/menu_entry.c. - (normal_mod_SOURCES): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. - (normal_mod_SOURCES): Likewise. - -2005-02-15 Yoshinori K. Okuji - - * include/grub/normal.h (grub_halt_init): New prototype. - (grub_halt_fini): Likewise. - (grub_reboot_init): Likewise. - (grub_reboot_fini): Likewise. - - * util/grub-emu.c: Include signal.h. - (main_env): New global variable. - (grub_machine_init): Ignore SIGINT. Otherwise grub-emu cannot - catch C-c. - (grub_machine_fini): New function. - (main): Call grub_halt_init and grub_reboot_init before - grub_main, and grub_reboot_fini and grub_halt_fini after it. - Call setjmp with MAIN_ENV to go back afterwards. - Call grub_machine_fini right before return. - - * include/grub/util/misc.h: Include setjmp.h. - (main_env): New prototype. - - * include/grub/kernel.h (grub_machine_fini): New prototype. - * include/grub/i386/pc/biosdisk.h (grub_biosdisk_fini): Likewise. - * include/grub/i386/pc/console.h (grub_console_fini): Likewise. - - * disk/i386/pc/biosdisk.c (grub_biosdisk_fini): New function. - * kern/i386/pc/init.c (grub_machine_fini): Likewise. - * term/i386/pc/console.c (grub_console_fini): Likewise. - - * util/i386/pc/misc.c: New file. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Added - util/i386/pc/misc.c, commands/i386/pc/halt.c and - commands/i386/pc/reboot.c. - -2005-02-14 Guillem Jover - - * include/grub/dl.h (grub_dl_check_header): New prototype. - (grub_arch_dl_check_header): Change return type to grub_err_t, - remove size parameter and export function. Update all callers. - * kern/dl.c (grub_dl_check_header): New function. - (grub_dl_load_core): Use `grub_dl_check_header' instead of - `grub_arch_dl_check_header'. Check ELF type. Check if sections - are inside the core. - * kern/i386/dl.c (grub_arch_dl_check_header): Remove arch - independent ELF header checks. - * kern/powerpc/dl.c (grub_arch_dl_check_header): Likewise. - * loader/i386/pc/multiboot.c (grub_rescue_cmd_multiboot): Use - `grub_dl_check_header' instead of explicit checks. Check for the - ELF type. - * loader/powerpc/ieee1275/linux.c (grub_rescue_cmd_linux): Use - `grub_dl_check_header' instead of explicit checks. Remove arch - specific ELF header checks. - - * util/grub-emu.c (grub_arch_dl_check_header): Remove the - argument SIZE. - -2005-02-13 Hollis Blanchard - - * conf/powerpc-ieee1275.rmk (pkgdata_MODULES): Add ls.mod. - * include/grub/powerpc/libgcc.h (__mulsf3): New prototype. - -2005-02-12 Hollis Blanchard - - * kern/partition.c (grub_partition_probe): Clear `grub_errno' and - return 0 if `grub_errno' is GRUB_ERR_BAD_PART_TABLE. - (part_map_iterate): Clear `grub_errno' and return 0 if - `partmap->iterate' returns GRUB_ERR_BAD_PART_TABLE. - * partmap/amiga.c (amiga_partition_map_iterate): Return - GRUB_ERR_BAD_PART_TABLE if no partition map magic is found. - * partmap/apple.c (apple_partition_map_iterate): Likewise. - -2005-02-01 Guillem Jover - - * loader/i386/pc/multiboot_normal.c (GRUB_MOD_INIT): Fix module - help info. - -2005-01-31 Marco Gerards - - * include/grub/powerpc/ieee1275/loader.h (grub_load_linux): - Removed prototype. - (grub_rescue_cmd_linux): New prototype. - (grub_rescue_cmd_initrd): Likewise. - * powerpc/ieee1275/linux.c (grub_linux_boot): Remove struct - `bi_rec'. - (grub_linux_release_mem): Release the memory for the initrd. - (grub_load_linux): Renamed from this... - (grub_rescue_cmd_linux): ...To this. Changed all callers. - Changed `entry' not to be static. Loop over memory regions to - find another one when the default fails. - (grub_rescue_cmd_initrd): New function. - (grub_linux_init): Remove function. - (grub_linux_fini): Likewise. - (GRUB_MOD_INIT): Register `initrd'. - (GRUB_MOD_FINI): Unregister `initrd'. - * powerpc/ieee1275/linux_normal.c (grub_linux_normal_init): - Function removed. - (grub_linux_normal_fini): Likewise. - (GRUB_MOD_INIT): Register `initrd'. - (GRUB_MOD_FINI): Unregister `initrd'. - -2005-01-31 Marco Gerards - - * commands/help.c: New file. - * normal/arg.c (show_help): Renamed to... - (grub_arg_show_help): ... this. - * commands/i386/pc/halt.c: New file. - * commands/i386/pc/reboot.c: Likewise. - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `commands/help.c'. - (pkgdata_MODULES): Add `reboot.mod', `halt.mod' and `help.mod'. - (help_mod_SOURCES, help_mod_CFLAGS, reboot_mod_SOURCES) - (reboot_mod_CFLAGS, halt_mod_SOURCES, halt_mod_CFLAGS): New - variables. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `commands/help.c'. - (pkgdata_MODULES): Add `help.mod'. - (help_mod_SOURCES, help_mod_CFLAGS): New variables. - * grub/i386/pc/init.h (grub_reboot): New prototype. - (grub_halt): Likewise. - * include/grub/normal.h (grub_arg_show_help): New prototype. - (grub_help_init): Likewise. - (grub_help_fini): Likewise. - * util/grub-emu.c (main): Initialize and deinitialize the help - command. - - * normal/cmdline.c (grub_cmdline_get): Doc fix. - - * normal/command.c (grub_command_init): Fixed the description of - the `set' and `unset' commands. - -2005-01-31 Marco Gerards - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_interpret): New - function. - * commands/ieee1275/halt.c: New file. - * commands/ieee1275/reboot.c: Likewise. - * commands/ieee1275/suspend.c (grub_cmd_suspend): Use - `__attribute__ ((unused))'. Some GCS related fixed. - (grub_suspend_init) [GRUB_UTIL]: Function removed. - (grub_suspend_fini): Likewise. - * conf/powerpc-ieee1275.rmk (pkgdata_MODULES): Add `reboot.mod' - and `halt.mod'. - (reboot_mod_SOURCES, reboot_mod_CFLAGS, halt_mod_SOURCES) - (halt_mod_CFLAGS): New variables. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_interpret): New prototype. - -2005-01-29 Yoshinori K. Okuji - - * include/grub/misc.h (memmove): New prototype. - (memcpy): Likewise. - -2005-01-22 Hollis Blanchard - - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_open): Don't initialize - `devpath' to 0. Use `name' instead of `devpath' with `grub_strndup'. - -2005-01-22 Marco Gerards - - * kern/misc.c (grub_strndup): Function rewritten. - -2005-01-22 Vincent Pelletier - - * normal/menu.c (TERM_WIDTH): Macro redefined. - (TERM_TOP_BORDER_Y): Likewise. - (draw_border): Replaced while-loop by a for-loop. Make the number - of lines consistent with the number of lines displayed in - print_entries. Added a margin below the rectangle. - (print_entry): Make the entry fit in the rectangle. - (print_entries): Display the scroll arrows next to the right - border. - -2005-01-21 Marco Gerards - - * fs/minix.c (grub_minix_find_file): Reserve more space for - `fpath' so the \0 can be stored. Use `grub_strcpy' instead of - `grub_strncpy' to copy `path' into it. - -2005-01-21 Marco Gerards - - Add the loopback device, a device via which files can be accessed - as devices. - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add `disk/loopback.c'. - (pkgdata_MODULES): Add loopback.mod. - (loopback_mod_SOURCES): New variable. - (loopback_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add - `disk/loopback.c'. - (pkgdata_MODULES): Add loopback.mod. - (loopback_mod_SOURCES): New variable. - (loopback_mod_CFLAGS): Likewise. - * disk/loopback.c: new file. - * include/grub/normal.h (grub_loop_init): New prototype. - (grub_loop_fini): New prototype. - * util/grub-emu.c (main): Initialize and de-initialize loopback - support. - * include/grub/disk.h (grub_disk_dev_id): Add - `GRUB_DISK_DEVICE_LOOPBACK_ID'. - -2005-01-20 Hollis Blanchard - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_enter): New - function. - * conf/powerpc-ieee1275.rmk (pkgdata_MODULES): Add suspend.mod. - (suspend_mod_SOURCES): New variable. - (suspend_mod_CFLAGS): Likewise. - * include/grub/powerpc/ieee1275/ieee1275.h (grub_ieee1275_enter): - New prototype. - * commands/ieee1275/suspend.c: New file. - -2005-01-20 Timothy Baldwin - - * include/grub/dl.h (GRUB_MOD_INIT): Changed `__attribute__ - ((unused))' to `__attribute__ ((used))'. - (GRUB_MOD_FINI): Likewise. - * kern/dl.c (grub_dl_load_file): Fix null pointer dereference. - * genmk.rb (PModule): Assign space to common symbols when linking - modules. - -2005-01-20 Marco Gerards - - * include/grub/mm.h (grub_mm_init_region): Change the type of the - `unsigned' arguments to `grub_size_t'. - (grub_malloc): Likewise. - (grub_realloc): Likewise. - (grub_memalign): Likewise. - * kern/i386/dl.c (grub_arch_dl_check_header): Likewise. - * kern/powerpc/dl.c (grub_arch_dl_check_header): Likewise. - * util/misc.c (grub_malloc): Likewise. - (grub_realloc): Likewise. - * kern/mm.c (get_header_from_pointer): Change the casts to - `unsigned' into a cast to `grub_size_t'. - - * fs/fshelp.c (grub_fshelp_find_file): The `oldnode' should always - point to `currnode' when `currnode' is changed. - - * util/grub-emu.c (main): Initialize `progname'. Reported by Nico - Schottelius . - -2005-01-09 Hollis Blanchard - - * util/powerpc/ieee1275/grub-mkimage.c: Include . - (note_path): Remove variable. - (GRUB_IEEE1275_NOTE_NAME): New macro. - (GRUB_IEEE1275_NOTE_TYPE): Likewise. - (grub_ieee1275_note_hdr): New structure. - (grub_ieee1275_note_desc): Likewise. - (grub_ieee1275_note): Likewise. - (load_note): Remove `dir' argument. All callers updated. Remove - `note_img' and `path'. Do not load a file from `note_path'. - Initialize a struct grub_ieee1275_note and write that to `out'. - Use GRUB_IEEE1275_MODULE_BASE instead of MODULE_BASE. - -2005-01-05 Marco Gerards - - * util/misc.c (grub_util_read_image): Revert last change. It - called `grub_util_read_at', which seeks from the beginning of the - file. - -2005-01-04 Hollis Blanchard - - * TODO: Add note about endianness in grub-mkimage. - * boot/powerpc/ieee1275/crt0.S (note): Remove unused .note - section. - * conf/powerpc-ieee1275.rmk (bin_UTILITIES): Add grub-mkimage. - (grub_mkimage_SOURCES): New target. - * include/grub/kernel.h (grub_start_addr): Remove variable. - (grub_end_addr): Likewise. - (grub_total_module_size): Likewise. - (grub_kernel_image_size): Likewise. - (GRUB_MODULE_MAGIC): New constant. - (grub_module_info): New structure. - (grub_arch_modules_addr): New prototype. - (grub_get_end_addr): Remove prototype. - * include/grub/i386/pc/kernel.h (grub_end_addr): New prototype. - * include/grub/powerpc/ieee1275/kernel.h: New file. - * include/grub/util/misc.h (grub_util_get_fp_size): New - prototype. - (grub_util_read_at): Likewise. - (grub_util_write_image_at): Likewise. - * kern/main.c (grub_get_end_addr): Remove function. - (grub_load_modules): Call grub_arch_modules_addr instead of using - grub_end_addr. Look for a grub_module_info struct in memory. Use - the grub_module_info fields instead of calling grub_get_end_addr - as loop conditions. Move grub_add_unused_region code here. - (grub_add_unused_region): Remove function. - * kern/i386/pc/init.c: Include grub/cache.h. - (grub_machine_init): Remove call to grub_get_end_addr. Remove - one call to add_mem_region. - (grub_arch_modules_addr): New function. - * kern/powerpc/ieee1275/init.c (grub_end_addr): Remove variable. - (grub_total_module_size): Likewise. - Include grub/machine/kernel.h. - (grub_arch_modules_addr): New function. - * util/grub-emu.c (grub_end_addr): Remove variable. - (grub_total_module_size): Likewise. - (grub_arch_modules_addr): New function. - * util/misc.c: Include unistd.h. - (grub_util_get_fp_size): New function. - (grub_util_read_at): Likewise. - (grub_util_write_image_at): Likewise. - (grub_util_read_image): Call grub_util_read_at. - (grub_util_write_image): Call grub_util_write_image_at. - * util/i386/pc/grub-mkimage.c (generate_image): Allocate - additional memory in kernel_img for a struct grub_module_info. - Fill in that grub_module_info. - * util/powerpc/ieee1275/grub-mkimage.c: New file. - -2005-01-03 Hollis Blanchard - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_milliseconds): - New function. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_milliseconds): New prototype. - * include/grub/powerpc/ieee1275/time.h (GRUB_TICKS_PER_SECOND): - Change to 1000. - * kern/powerpc/ieee1275/init.c (grub_get_rtc): Call - grub_ieee1275_milliseconds. - -2005-01-03 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (grub_ieee1275_realmode): New - variable. - (find_options): New function. - (cmain): Call find_options. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_realmode): New extern variable. - * kern/powerpc/ieee1275/openfw.c (grub_claimmap): Only call - grub_map if grub_ieee1275_realmode is false. - -2004-12-29 Marco Gerards - - * normal/cmdline.c (grub_cmdline_get): Redone logic so no empty - lines are inserted and make it work like readline. Reported by - Vincent Pelletier . - -2004-12-28 Marco Gerards - - * boot/powerpc/ieee1275/crt0.S (_start): Don't set up the stack. - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCE): Remove - `kern/powerpc/cache.S'. - -2004-12-27 Marco Gerards - - * genmk.rb: Handle the `Program' class in the main loop. Written - by Johan Rydberg . - (Program): New class. - (programs): New variable. - * boot/powerpc/ieee1275/cmain.c: Include - instead of "grub/machine/ieee1275.h". Include - instead of "grub/kernel.h". Include . - (help_arch): Function removed. - * conf/powerpc-ieee1275.rmk (grubof_HEADERS): Add - `powerpc/libgcc.h' and `loader.h'. - (pkgdata_PROGRAMS): New variable. - (sbin_UTILITIES): Variable removed. - (grub_emu_SOURCES): Added kern/powerpc/cache.S. - (grubof_SOURCES): Variable re-defined so it only includes the - core functionality. - (grubof_CFLAGS): Remove `-DGRUBOF'. - (pkgdata_MODULES, fshelp_mod_SOURCES, fshelp_mod_CFLAGS, - (fat_mod_SOURCES, fat_mod_CFLAGS, ext2_mod_SOURCES) - (ext2_mod_CFLAGS, ufs_mod_SOURCES, ufs_mod_CFLAGS) - (minix_mod_SOURCES, minix_mod_CFLAGS, hfs_mod_SOURCES) - (hfs_mod_CFLAGS, jfs_mod_SOURCES, jfs_mod_CFLAGS) - (iso9660_mod_SOURCES, iso9660_mod_CFLAGS, _linux_mod_SOURCES) - (_linux_mod_CFLAGS, linux_mod_SOURCES, linux_mod_CFLAGS) - (normal_mod_SOURCES, normal_mod_CFLAGS, normal_mod_ASFLAGS) - (hello_mod_SOURCES, hello_mod_CFLAGS, boot_mod_SOURCES) - (boot_mod_CFLAGS, terminal_mod_SOURCES, terminal_mod_CFLAGS) - (ls_mod_SOURCES, ls_mod_CFLAGS, cmp_mod_SOURCES, cmp_mod_CFLAGS) - (cat_mod_SOURCES, cat_mod_CFLAGS, font_mod_SOURCES) - (font_mod_CFLAGS, amiga_mod_SOURCES, amiga_mod_CFLAGS) - (apple_mod_SOURCES, apple_mod_CFLAGS, pc_mod_SOURCES) - (pc_mod_CFLAGS): New variables. - * disk/powerpc/ieee1275/ofdisk.c: Include . - (grub_ofdisk_iterate): Add a prototype for `dev_iterate'. - * include/grub/dl.h (grub_arch_dl_sync_caches): New prototype. - * include/grub/loader.h (grub_os_area_addr, grub_os_area_size): - Moved from here... - * include/grub/i386/pc/init.h (grub_os_area_addr) - (rub_os_area_size): ... to here. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_entry_fn): Export symbol. - * include/grub/powerpc/ieee1275/init.h: New file. - * include/grub/powerpc/libgcc.h: Likewise. - * include/grub/cache.h: Likewise. - * kern/powerpc/cache.S: Likewise. Written by Hollis Blanchard - . - * kern/dl.c: Include . - (grub_dl_flush_cache): New function. - (grub_dl_load_core): Call `grub_dl_flush_cache' to flush the cache - for this module. - * kern/powerpc/ieee1275/init.c (grub_ofdisk_init) - (grub_console_init): Removed prototypes. - (grub_machine_init): Don't initialize the modules anymore. - * kern/powerpc/ieee1275/openfw.c (grub_map): Make the function - static. - * include/grub/powerpc/types.h (GRUB_HOST_WORDS_LITTLEENDIAN): - Macro undef removed. - (GRUB_HOST_WORDS_BIGENDIAN): New macro. - * kern/powerpc/dl.c (grub_arch_dl_relocate_symbols): Add - relocation `R_PPC_REL32'. Return an error when the relocation is - unknown. - * Makefile.in (DATA): Add `$(pkgdata_PROGRAMS)'. - * kern/i386/pc/init.c (grub_arch_sync_caches): New function. - * util/misc.c (grub_arch_sync_caches): Likewise. - -2004-12-19 Marco Gerards - - * conf/powerpc-ieee1275.rmk (MOSTLYCLEANFILES): Remove - `symlist.c', add `grubof_symlist.c'. - (symlist.c): Variable removed. - (grubof_HEADERS): Variable added. - (grubof_symlist.c): New target. - (kernel_syms.lst): Use `grubof_HEADERS' instead of - `kernel_img_HEADERS'. - (grubof_SOURCES): Add `kern/powerpc/dl.c' and `grubof_symlist.c'. - * kern/powerpc/dl.c: New file. - * kern/powerpc/ieee1275/init.c (grub_arch_dl_check_header): - Function removed. - (grub_arch_dl_relocate_symbols): Likewise. - (grub_register_exported_symbols): Likewise. - -2004-12-13 Marco Gerards - - * fs/ext2.c (grub_ext2_open): Don't use data after freeing it. - (grub_ext2_dir): Likewise. Don't return in case of an error, jump - to fail instead. Reported by Vincent Pelletier - . - - * fs/fshelp.c (grub_fshelp_find_file): Don't free `oldnode' when - it is not allocated. Reported by Vincent Pelletier - . - - * normal/cmdline.c (grub_tab_complete): Add a blank line to the - output so the output looks better. - -2004-12-04 Marco Gerards - - Modulize the partition map support and add support for the amiga - partition map. - - * commands/ls.c: Include instead of - . - * kern/disk.c: Likewise. - * kern/rescue.c: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * normal/cmdline.c: Likewise. - * kern/powerpc/ieee1275/init.c: Likewise. - (grub_machine_init): Call `grub_pc_partition_map_init', - `grub_amiga_partition_map_init' and - `grub_apple_partition_map_init'. - * conf/i386-pc.rmk (kernel_img_SOURCES): Remove - `disk/i386/pc/partition.c'. Add `kern/partition.c'. - (kernel_img_HEADERS): Remove `machine/partition.h'. Add - `partition.h' and `pc_partition.h'. - (grub_setup_SOURCES): Remove - `disk/i386/pc/partition.c'. Add `kern/partition.c', - `partmap/amiga.c', `partmap/apple.c' and `partmap/pc.c'. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add `amiga.mod', `apple.mod' and `pc.mod'. - (amiga_mod_SOURCES, amiga_mod_CFLAGS, apple_mod_SOURCES) - (apple_mod_CFLAGS, pc_mod_SOURCES, pc_mod_CFLAGS): New variables. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Remove - `disk/powerpc/ieee1275/partition.c'. Add `kern/partition.c', - `partmap/amiga.c', `partmap/apple.c' and `partmap/pc.c'. - (grubof_SOURCES): Likewise. - * disk/i386/pc/partition.c: File removed. - * disk/powerpc/ieee1275/partition.c: Likewise. - * include/grub/powerpc/ieee1275/partition.h: Likewise. - * include/grub/i386/pc/partition.h: Likewise. - * kern/partition.c: New file. - * partmap/amiga.c: Likewise. - * partmap/apple.c: Likewise. - * partmap/pc.c: Likewise. - * include/grub/partition.h: Likewise.. - * include/grub/pc_partition.h: Likewise. - * util/grub-emu.c: Include instead of - . - (main): Call `grub_pc_partition_map_init', - `grub_amiga_partition_map_init' and - `grub_apple_partition_map_init' and deinitialize afterwards. - * util/i386/pc/biosdisk.c: Include `#include - ' and `include ' instead of - `'. - * util/i386/pc/grub-setup.c: Likewise. - * util/i386/pc/biosdisk.c: Likewise. - (grub_util_biosdisk_get_grub_dev): Only access the PC specific - partition information in case of a PC partition. - * util/i386/pc/grub-setup.c: Include `#include - ' and `include ' instead of - `'. - (setup): Only access the PC specific partition information in case - of a PC partition. - -2004-11-17 Hollis Blanchard - - * kern/powerpc/ieee1275/init.c (grub_setjmp): Remove function. - (grub_longjmp): Likewise. - * include/grub/powerpc/setjmp.h (grub_jmp_buf): Set array size to - 20. - * normal/powerpc/setjmp.S: New file. - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Add - `normal/powerpc/setjmp.S'. - (grubof_CFLAGS): Add `-DGRUBOF'. - * include/grub/setjmp.h [GRUB_UTIL]: Changed condition to - [GRUB_UTIL && !GRUBOF]. - -2004-11-16 Marco Gerards - - * kern/powerpc/ieee1275/openfw.c (grub_devalias_iterate): Skip any - property named `name'. Correctly handle the error returned by - `grub_ieee1275_finddevice' if a device can not be opened. - -2004-11-02 Hollis Blanchard - - * term/powerpc/ieee1275/ofconsole.c (grub_ofconsole_readkey): Test - `actual' for negativity. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Remove - kern/fshelp.c. - -2004-11-01 Marco Gerards - - * term/i386/pc/vga.c (VGA_HEIGHT): Changed to 350. - (PAGE_OFFSET): New macro. - (CRTC_ADDR_PORT): Likewise. - (CRTC_DATA_PORT): Likewise. - (START_ADDR_HIGH_REGISTER): Likewise. - (START_ADDR_LOW_REGISTER): Likewise. - (GRAPHICS_ADDR_PORT): Likewise. - (GRAPHICS_DATA_PORT): Likewise. - (READ_MAP_REGISTER): Likewise. - (INPUT_STATUS1_REGISTER): Likewise. - (INPUT_STATUS1_VERTR_BIT): Likewise. - (page): New variable. - (wait_vretrace): New function. - (set_read_map): Likewise. - (set_start_address): Likewise. - (grub_vga_init): Use mode 0x10 instead of mode 0x12. Switch to - the right page. - (check_vga_mem): Take the page into account. - (write_char): Likewise. - (write_cursor): Likewise. - (scroll_up): Likewise. Copy the page to the page that is not - shown and switch between both pages. - (grub_vga_putchar): Fix off by one error. - (grub_vga_cls): Wait for the vertical retrace. Take the page into - account. - -2004-11-01 Marco Gerards - - Add support for iso9660 (including rockridge). - - * conf/i386-pc.rmk (grub_emu_SOURCES): Add fs/iso9660.c. - (iso9660_mod_SOURCES): New variable. - (iso9660_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add fs/iso9660.c. - * include/grub/fs.h (grub_iso9660_init): New prototype. - * util/grub-emu.c (main): Call `grub_iso9660_init'. - * fs/iso9660.c: New file. - - * include/grub/misc.h (grub_strncat): New prototype. - * kern/misc.c (grub_strncat): New function. - - * fs/hfs.c (grub_hfs_mount): Translate the error - `GRUB_ERR_OUT_OF_RANGE' to `GRUB_ERR_BAD_FS'. - * fs/jfs.c (grub_jfs_mount): Likewise. - * fs/ufs.c (grub_ufs_mount): Likewise. - -2004-10-28 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (cmain): Remove asm statements - which initialized BAT registers. - * boot/powerpc/ieee1275/ieee1275.c (IEEE1275_CALL_ENTRY_FN, - grub_ieee1275_common_hdr, INIT_IEEE1275_COMMON): - Move from here... - * include/grub/powerpc/ieee1275/ieee1275.h (IEEE1275_CALL_ENTRY_FN, - grub_ieee1275_common_hdr, INIT_IEEE1275_COMMON): - ... to here. - * kern/powerpc/ieee1275/openfw.c (grub_map): New function. - (grub_mapclaim): Likewise. - * loader/powerpc/ieee1275/linux.c (grub_load_linux): Use - grub_mapclaim instead of grub_ieee1275_claim. Assign linux_addr by - hand. - -2004-10-19 Hollis Blanchard - - * conf/powerpc-ieee1275.rmk (COMMON_ASFLAGS): Remove -fno-builtin. - (COMMON_CFLAGS): Remove -fno-builtin and -D__ASSEMBLY__. Add - -ffreestanding and -msoft-float. - -2004-10-15 Hollis Blanchard - - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_open): Do not - append ":0" to devpath if the GRUB_IEEE1275_NO_PARTITION_0 flag is - set in grub_ieee1275_flags. - -2004-10-14 Hollis Blanchard - - * include/grub/powerpc/ieee1275/ieee1275.h (abort): Add function - prototype. - * kern/powerpc/ieee1275/init.c (grub_machine_init): Call - grub_console_init first. - Change the memory range used for grub_ieee1275_claim and - grub_mm_init_region. - Print an error message if the claim fails. - Include . - -2004-10-13 Hollis Blanchard - - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_iterate): - Call grub_children_iterate for device nodes of type `scsi', - `ide', or `ata'. - (grub_ofdisk_open): Remove manual device alias resolution. - Fix memory leak when device cannot be opened. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_children_iterate): New prototype. - * kern/powerpc/ieee1275/openfw.c (grub_children_iterate): - New function. - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_get_property): - Return -1 if args.size was -1. - -2004-10-11 Hollis Blanchard - - * boot/powerpc/ieee1275/cmain.c (grub_ieee1275_flags): New global. - (cmain): Accept 3 parameters. Test for 0xdeadbeef, indicating Old - World Macintosh. If Old Wold, set flag in grub_ieee1275_flags; claim - Open Firmware's memory for it; claim memory from _start to _end. - * boot/powerpc/ieee1275/crt0.S (__bss_start): New extern. - (_end): New extern. - (_start): Zero BSS from __bss_start to _end. - * include/grub/powerpc/ieee1275/ieee1275.h (grub_ieee1275_flags): - New extern. - (GRUB_IEEE1275_NO_PARTITION_0): New #define. - -2004-10-11 Hollis Blanchard - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_claim): Return - -1 if args.base was -1. - -2004-10-08 Hollis Blanchard - - * term/powerpc/ieee1275/ieee1275.c (grub_ofconsole_cls): Use an ANSI - escape sequence instead of a literal ^L. Also call - grub_ofconsole_gotoxy. - -2004-10-03 Hollis Blanchard - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_claim): change - void * arguments to grub_addr_t. All callers updated. Also make - the `result' argument optional. - (grub_ieee1275_release): change void * arguments to grub_addr_t. - All callers updated. - -2004-09-22 Hollis Blanchard - - * commands/ls.c (grub_ls_list_files): Use the string following the - initial ')', if present, as the filesystem path. - * kern/rescue.c (grub_rescue_cmd_ls): Likewise. - - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): List crt0.S first. - -2004-09-18 Yoshinori K. Okuji - - Make the source code of the menu interface more readable. - - * normal/menu.c: Include grub/mm.h. - (TERM_WIDTH): New macro. - (TERM_HEIGHT): Likewise. - (TERM_INFO_HEIGHT): Likewise. - (TERM_MARGIN): Likewise. - (TERM_SCROLL_WIDTH): Likewise. - (TERM_TOP_BORDER_Y): Likewise. - (TERM_LEFT_BORDER_X): Likewise. - (TERM_BORDER_WIDTH): Likewise. - (TERM_MESSAGE_HEIGHT): Likewise. - (TERM_BORDER_HEIGHT): Likewise. - (TERM_NUM_ENTRIES): Likewise. - (TERM_FIRST_ENTRY_Y): Likewise. - (TERM_ENTRY_WIDTH): Likewise. - (TERM_CURSOR_X): Likewise. - (draw_border): Use macros instead of magic numbers. - (print_entry): Likewise. - (print_entries): Likewise. - (run_menu): Likewise. Also, handle the key 'e'. - (run_menu_entry): Ignore empty command lines. - (print_message): Added a new argument EDIT. If EDIT is true, - print a different message. - (init_page): Likewise. - (edit_menu_entry): New function. Not implemented yet. - -2004-09-17 Marco Gerards - - Add `linux.mod' and `multiboot.mod' so linux and multiboot kernels - can be loaded from normal mode. - - * conf/i386-pc.rmk (pkgdata_MODULES): Add `linux.mod' and - `multiboot.mod'. - (linux_mod_SOURCES, linux_mod_CFLAGS, multiboot_mod_SOURCES) - (multiboot_mod_CFLAGS): New variables. - * loader/i386/pc/linux_normal.c: New file. - * loader/i386/pc/multiboot_normal.c: Likewise. - - * loader/i386/pc/linux.c (grub_rescue_cmd_initrd): Don't use the - attribute `unused'. - - * fs/ext2.c (grub_ext2_iterate_dir): Fix typos in inode type. Use - `fdiro' to read the mode information from instead of `diro'. - - * fs/fshelp.c (grub_fshelp_find_file): Set type to foundtype after - looking up a symlink. - - * include/grub/normal.h (GRUB_COMMAND_FLAG_NO_ARG_PARSE): New - macro. - * normal/command.c (grub_command_execute): Don't parse the - arguments when `GRUB_COMMAND_FLAG_NO_ARG_PARSE' is set in the - flags of the command. - - * normal/menu.c (grub_menu_run): Fix typo. - -2004-09-14 Hollis Blanchard - - * kern/powerpc/ieee1275/init.c (abort): Trap into Open Firmware. - - * term/powerpc/ieee1275/ofconsole.c (grub_ofconsole_gotoxy): Use - `y + 1' instead of `y - 1'. - - * conf/powerpc-ieee1275.rmk (grubof_LDFLAGS): Add `-N' and `-S'. - -2004-09-14 Yoshinori K. Okuji - - From Hollis Blanchard : - * kern/misc.c (memmove): New alias for grub_memmove. - (memcmp): New alias for grub_memcmp. - (memset): New alias for grub_memset. - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_get_property): - Change "int handle" to "grub_ieee1275_phandle_t handle". - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_get_property): Likewise. - -2004-09-12 Tomas Ebenlendr - - Added normal mode command `chainloader' as module chain.mod, which - depends on normal.mod and _chain.mod. - - * conf/i386-pc.rmk (pkgdata_MODULES): Add `chain.mod'. - (chain_mod_SOURCES, chain_mod_CFLAGS): Variables added. - * include/grub/i386/pc/loader.h (grub_rescue_cmd_chainloader): - Deleted prototype. - * loader/i386/pc/chainloader.c (grub_rescue_cmd_chainloader): All - but arguments parsing moved to ... - (grub_chainloader_cmd): ... here. New function. - * include/grub/i386/pc/chainloader.h: New file. - * loader/i386/pc/chainloader_normal.c: Likewise. - -2004-09-11 Marco Gerards - - * conf/i386-pc.rmk (kernel_img_SOURCES): Added kern/fshelp.c. - (grub_mkimage_LDFLAGS): Likewise. - (grub_emu_SOURCES): Likewise. - (kernel_img_HEADERS): Added fshelp.h. - * fs/ext2.c: Include . - (FILETYPE_REG): New macro. - (FILETYPE_INO_REG): Likewise. - (grub_ext_sblock): Renamed to `grub_ext2_sblock'. - Changed all users. - (ext2_block_group): Renamed to `grub_ext2_block_group'. Changed - all users. - (grub_fshelp_node): New struct. - (grub_ext2_data): Added member `diropen'. Changed member `inode' - to a pointer. - (grub_ext2_get_file_block): Removed function. - (grub_ext2_read_block): New function. - (grub_ext2_read_file): Replaced parameter `data' by `node'. - This function was written. - (grub_ext2_mount): Read the root inode. Create a diropen struct. - (grub_ext2_find_file): Removed function. - (grub_ext2_read_symlink): New function. - (grub_ext2_iterate_dir): Likewise. - (grub_ext2_open): Rewritten. - (grub_ext2_dir): Rewritten. - * include/grub/fshelp.h: New file. - * fs/fshelp.c: Likewise. - -2004-09-10 Yoshinori K. Okuji - - * normal/menu.c: Include grub/loader.h and grub/machine/time.h. - (print_message): Add a missing newline. - (run_menu): Added timeout support. - (run_menu_entry): New local function. - (grub_menu_run): Added support for booting. - - * kern/loader.c (grub_loader_is_loaded): New function. - - * include/grub/powerpc/ieee1275/time.h: Include grub/symbol.h. - (grub_get_rtc): Exported. - - * include/grub/i386/pc/time.h: Include grub/symbol.h. - (grub_get_rtc): Exported. - - * include/grub/normal.h (struct grub_command_list): Remove - constant from the member `command'. - - * include/grub/loader.h (grub_loader_is_loaded): Declared. - - * include/grub/err.h (GRUB_ERR_INVALID_COMMAND): New constant. - - * conf/i386-pc.rmk (kernel_img_HEADERS): Added machine/time.h. - -2004-08-28 Marco Gerards - - Add support for the JFS filesystem. - - * fs/jfs.c: New file. - * include/grub/fs.h (grub_jfs_init): New prototype. - (grub_jfs_fini): New prototype. - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/jfs.c. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add jfs.mod. - (jfs_mod_SOURCES): New variable. - (jfs_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add fs.jfs.c. - (grubof_SOURCES): Likewise. - * util/grub-emu.c (main): Initialize and deinitialize JFS support. - - * fs/fat.c (grub_fat_find_dir): Convert the filename little - endian to the host endian. - (grub_fat_utf16_to_utf8): Move function from there... - * kern/misc.c (grub_utf16_to_utf8): ...to here. Do not convert - the endianness of the source string anymore. - * include/grub/misc.h (grub_utf16_to_utf8): New prototype. - -2004-08-24 Marco Gerards - - * commands/boot.c (grub_boot_init) [GRUB_UTIL]: Make conditional. - (grub_boot_fini) [GRUB_UTIL]: Likewise. - (GRUB_MOD_INIT) [!GRUB_UTIL]: Likewise. - (GRUB_MOD_FINI) [!GRUB_UTIL]: Likewise. - - * fs/hfs.c (grub_hfs_find_node): Add a prototype for `node_found'. - (grub_hfs_iterate_dir): Make the function static. Add prototypes - for `node_found' and `it_dir'. - (grub_hfs_dir): Add prototype for `dir_hook'. - - * fs/minix.c (grub_minix_get_file_block): Add prototype for - `grub_get_indir'. Rename `indir' in two blocks to `indir16' - and `indir32' to silence a gcc warning. - - * include/grub/fs.h (grub_hfs_init): New prototype. - (grub_hfs_fini): Likewise. - - -2004-08-21 Yoshinori K. Okuji - - Each disk device has its own id now. This is useful to make use - of multiple disk devices. - - * include/grub/disk.h (grub_disk_dev_id): New enum. - (GRUB_DISK_DEVICE_BIOSDISK_ID): New constant. - (GRUB_DISK_DEVICE_OFDISK_ID): Likewise. - - * disk/i386/pc/biosdisk.c (grub_biosdisk_dev): Specify - GRUB_DISK_DEVICE_BIOSDISK_ID as an id. - - * disk/powerpc/ieee1275/ofdisk.c (grub_ofdisk_dev): Specify - GRUB_DISK_DEVICE_OFDISK_ID as an id. - - * util/i386/pc/biosdisk.c (grub_util_biosdisk_dev): Specify - GRUB_DISK_DEVICE_BIOSDISK_ID as an id. - - * include/grub/disk.h (struct grub_disk_dev): Added a new member - "id" which is used by the cache manager. - - * normal/main.c (grub_normal_init_page): Use "GNU GRUB" instead - of just "GRUB". - -2004-08-18 Marco Gerards - - * fs/hfs.c: New file. - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/hfs.c. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add hfs.mod. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add fs/hfs.c. - (grubof_SOURCES): Likewise. - * util/grub-emu.c (main): Initialize and deinitialize HFS support. - - * include/grub/misc.h (grub_strncasecmp): Add prototype. - * kern/misc.c (grub_strncasecmp): Add function. - -2004-08-14 Marco Gerards - - * include/grub/arg.h (GRUB_ARG_OPTION_OPTIONAL): Surround macro - with parentheses. - - * fs/ext2.c (FILETYPE_UNKNOWN): New macro. - (grub_ext2_dir): In case the directory entry type is unknown, read - it from the inode. - -2004-08-02 Peter Bruin - - * loader/powerpc/ieee1275/linux.c (grub_linux_init): Pass - grub_load_linux instead of grub_rescue_cmd_linux as second - argument of grub_rescue_register_command. - - * Makefile.in (RMKFILES): Add conf/powerpc-ieee1275.rmk. - -2004-07-27 Marco Gerards - - * boot/powerpc/ieee1275/ieee1275.c (grub_ieee1275_release): New - function. - * commands/boot.c: Remove the check for `GRUB_UTIL'. - * conf/powerpc-ieee1275.rmk (grubof_SOURCES): Add - `loader/powerpc/ieee1275/linux.c', - `loader/powerpc/ieee1275/linux_normal.c' and `commands/boot.c'. - * include/grub/powerpc/ieee1275/ieee1275.h - (grub_ieee1275_release): New prototype. - * include/grub/powerpc/ieee1275/loader.h: Rewritten. - * kern/powerpc/ieee1275/init.c (grub_machine_init): Initialize - normal, boot, linux and linux_normal. - * loader/powerpc/ieee1275/linux.c: New file. - * loader/powerpc/ieee1275/linux_normal.c: Likewise. - -2004-07-12 Marco Gerards - - * normal/arg.c (grub_arg_parse): Correct error handling after - reallocating the argumentlist (check if `argl' is not null instead - of checking if `args' is not null). - * kern/mm.c (grub_realloc): Return the same pointer when using the - same region, instead of returning the header address. - -2004-07-11 Marco Gerards - - * disk/powerpc/ieee1275/partition.c (grub_partition_iterate): Skip - one block instead of two when looking for the initial partition. - (grub_partition_probe): Initialize the local variable `p' with 0. - Use base 10 for the grub_strtoul call. - * kern/misc.c (grub_strncpy): Fix off by one bug. Eliminated the - need for one local variable. - (grub_strtoul): Don't add the new value to `num', instead of that - just assign it. - -2004-07-11 Marco Gerards - - * conf/i386-pc.rmk (pkgdata_IMAGE): Add pxeboot.img. - (pxeboot_img_SOURCES): New variable. - (pxeboot_img_ASFLAGS): Likewise. - (pxeboot_img_LDFLAGS): Likewise. - * boot/i386/pc/pxeboot.S: New file. Based on pxeloader.S from - GRUB Legacy and boot.S. Adopted for GRUB 2 by lode leroy - . - -2004-06-27 Tomas Ebenlendr - - * kern/rescue.c (grub_enter_rescue_mode): Don't continue when - there was no input. - -2004-06-27 Tomas Ebenlendr - - * normal/cmdline.c (grub_set_history): Fix off by one bug. Fixed - the history buffer logic. - -2004-06-27 Tomas Ebenlendr - - * fs/ext2.c (FILETYPE_INO_MASK, FILETYPE_INO_DIRECTORY) - (FILETYPE_INO_SYMLINK): New macros. - (grub_ext2_find_file): Check if the node is a directory using the - inode stat information instead of using the filetype in the - dirent. Exclude the first character of an absolute symlink. - (grub_ext2_dir): Mask out the filetype part of the mode member of - the inode. - -2004-05-24 Marco Gerards - - Add support for UFS version 1 and 2. Add support for the minix - filesystem version 1 and 2, both the variants with 14 and 30 long - filenames. - - * conf/i386-pc.rmk (grub_setup_SOURCES): Add fs/ufs.c and - fs/minix.c. - (grub_emu_SOURCES): Likewise. - (pkgdata_MODULES): Add ufs.mod and minix.mod. - (ufs_mod_SOURCES): New variable. - (ufs_mod_CFLAGS): Likewise. - (minix_mod_SOURCES): Likewise. - (minix_mod_CFLAGS): Likewise. - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add fs/ufs.c and - fs/minix.c. - (grubof_SOURCES): Likewise. - * fs/ufs.c: New file. - * fs/minix.c: New file. - * include/grub/fs.h (grub_ufs_init): New prototype. - (grub_ufs_fini): Likewise. - (grub_minix_init): Likewise. - (grub_minix_fini): Likewise. - * util/grub-emu.c (main): Initialize and deinitialize UFS and - minix fs. - -2004-04-30 Jeroen Dekkers - - * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Add normal/arg.c, - commands/ls.c, commands/terminal.c, commands/boot.c, - commands/cmp.c and commands/cat.c. - (grubof_LDFLAGS): Add -nostdlib -static-libgcc -lgcc. - - * kern/powerpc/ieee1275/init.c: Include "grub/env.h" instead of - "env.h" - -2004-04-04 Yoshinori K. Okuji - - All symbols prefixed with PUPA_ and pupa_ are renamed to GRUB_ - and grub_, respectively. Because the conversion is trivial and - mechanical, I omit the details here. Please refer to the CVS - if you need more information. - -2004-04-04 Yoshinori K. Okuji - - * include/pupa: Renamed to ... - * include/grub: ... this. - * util/i386/pc/pupa-mkimage.c: Renamed to ... - * util/i386/pc/grub-mkimage.c: ... this. - * util/i386/pc/pupa-setup.c: Renamed to ... - * util/i386/pc/grub-setup.c: ... this. - * util/pupa-emu.c: Renamed to ... - * util/grub-emu.c: ... this. - -2004-03-29 Marco Gerards - - Add support for the newworld apple macintosh (PPC). This has been - tested on the powerbook 2000 only. It only adds support for - generic ieee1275 functions, console and disk support. This should - be easy to port to other architectures with support for Open - Firmware. - - * configure.ac: Accept the powerpc as host_cpu. In the case of - the powerpc cpu set the host_vendor to ieee1275. Make sure the i386 - specific tests are only executed while building for the i386. - Inverse test for crosscompile. - * genmk.rb (Utility): Allow assembler files. - * normal/cmdline.c (pupa_tab_complete): Reset pupa_errno. - * conf/powerpc-ieee1275.rmk: New file. - * disk/powerpc/ieee1275/ofdisk.c: Likewise. - * disk/powerpc/ieee1275/partition.c: Likewise. - * include/pupa/powerpc/ieee1275/biosdisk.h: Likewise. - * include/pupa/powerpc/ieee1275/console.h: Likewise. - * include/pupa/powerpc/ieee1275/partition.h: Likewise. - * include/pupa/powerpc/ieee1275/time.h: Likewise. - * include/pupa/powerpc/ieee1275/util/biosdisk.h: Likewise. - * include/pupa/powerpc/ieee1275/multiboot.h: Likewise. - * include/pupa/powerpc/ieee1275/loader.h - * include/pupa/powerpc/setjmp.h: Likewise. - * include/pupa/powerpc/types.h: Likewise. - * kern/powerpc/ieee1275/init.c: Likewise. - * kern/powerpc/ieee1275/openfw.c: Likewise. - * term/powerpc/ieee1275/ofconsole.c: Likewise. - - These files were written by Johan Rydberg - (jrydberg@night.trouble.net) and I only modified them slightly. - - * boot/powerpc/ieee1275/cmain.c: New file. - * boot/powerpc/ieee1275/crt0.S: Likewise. - * boot/powerpc/ieee1275/ieee1275.c: Likewise. - * include/pupa/powerpc/ieee1275/ieee1275.h: Likewise. - -2004-03-14 Jeroen Dekkers - - * Makefile.in: Update copyright. - * genmodsrc.sh: Likewise. - * gensymlist.sh: Likewise. - * term/i386/pc/vga.c: Indent correctly. - - * util/i386/pc/pupa-mkimage.c (usage): Use PACKAGE_BUGREPORT as - bugreporting address. - * util/i386/pc/pupa-setup.c (usage): Likewise, - (main): Call pupa_ext2_init and pupa_ext2_fini. - - * fs/fat.c (log2): Renamed to ... - (fat_log2): ... this. - All callers changed. - * kern/misc.c (memcpy): Alias to pupa_memmove. - * loader/i386/pc/multiboot.c (pupa_rescue_cmd_multiboot): Fix - lvalue cast. - * util/console.c (pupa_ncurses_fini): Return 0. - - * util/i386/pc/biosdisk.c (pupa_util_biosdisk_open)[__linux__]: - Move fail label here. - [__GNU__]: Don't warn when using stat. - (open_device)[!__linux__]: Check if FD < 0 instead of !FD. - (pupa_util_biosdisk_get_pupa_dev)[__GNU__]: Change type of N to - long int. Use strtol instead of strtoul. - -2004-03-14 Marco Gerards - - * commands/boot.c: New file. - * commands/cat.c: Likewise. - * commands/cmp.c: Likewise. - * commands/ls.c: Likewise. - * commands/terminal.c: Likewise. - * normal/command.c: Include and . - (pupa_register_command): Changed interface to match the new - argument parser. - (pupa_command_execute): Changed (almost rewritten) so it uses - pupa_split_command. Added support for setting variables using the - syntax `foo=bar'. - (rescue_command): Changed to work with the new argument parser. - (terminal_command): Moved from here to commands/terminal.c. - (set_command): New function. - (unset_command): New function. - (insmod_command): New function. - (rmmod_command): New function. - (lsmod_command): New function. - (pupa_command_init): Don't initialize the command terminal - anymore. Initialize the commands set, unset, insmod, rmmod and - lsmod. - * conf/i386-pc.rmk (kernel_img_SOURCES): Add kern/env.c. - (kernel_img_HEADERS): Add arg.h and env.h. - (pupa_mkimage_LDFLAGS): Add kern/env.c. - (pupa_emu_SOURCES): Add kern/env.c, commands/ls.c, - commands/terminal.c commands/boot.c commands/cmp.c commands/cat.c, - normal/arg.c. - (pkgdata_MODULES): Add ls.mod, boot.mod, cmp.mod, cat.mod and - terminal.mod. - (normal_mod_SOURCES): Add normal/arg.c and normal/arg.c. - (boot_mod_SOURCES): New variable. - (terminal_mod_SOURCES): Likewise. - (ls_mod_SOURCES): Likewise. - (cmp_mod_SOURCES): Likewise. - (cat_mod_SOURCES): Likewise. - - * normal/arg.c: New file. - * kern/env.c: Likewise. - * include/pupa/arg.h: Likewise. - * include/pupa/env.h: Likewise. - * font/manager.c (font_command): Changed to match argument parsing - interface changes. - (PUPA_MOD_INIT): Likewise. - * hello/hello.c (pupa_cmd_hello): Likewise. - (PUPA_MOD_INIT): Likewise. - * include/pupa/disk.h: Include . - (pupa_print_partinfo): New prototype. - * include/pupa/dl.h (pupa_dl_set_prefix): Prototype removed. - (pupa_dl_get_prefix): Likewise. - * include/pupa/misc.h: Include . - (pupa_isgraph): New prototype. - (pupa_isdigit): Likewise. - (pupa_split_cmdline): Likewise. - * include/pupa/normal.h: Include . - (pupa_command): Changed the prototype of the member `func' to - match the argument parsing interface. Added member `options'. - (pupa_register_command): Updated to match function. - (pupa_arg_parse): New prototype. - (pupa_hello_init) [PUPA_UTIL]: New prototype. - (pupa_hello_fini) [PUPA_UTIL]: Likewise. - (pupa_ls_init) [PUPA_UTIL]: Likewise. - (pupa_ls_fini) [PUPA_UTIL]: Likewise. - (pupa_cat_init) [PUPA_UTIL]: Likewise. - (pupa_cat_fini) [PUPA_UTIL]: Likewise. - (pupa_boot_init) [PUPA_UTIL]: Likewise. - (pupa_boot_fini) [PUPA_UTIL]: Likewise. - (pupa_cmp_init) [PUPA_UTIL]: Likewise. - (pupa_cmp_fini) [PUPA_UTIL]: Likewise. - (pupa_terminal_init) [PUPA_UTIL]: Likewise. - (pupa_terminal_fini) [PUPA_UTIL]: Likewise. - * kern/disk.c: Include . - (pupa_print_partinfo): New function. - * kern/dl.c: Include . - (pupa_dl_dir): Variable removed. - (pupa_dl_load): Use the environment variable `prefix' instead of - the variable pupa_dl_dir. - (pupa_dl_set_prefix): Function removed. - (pupa_dl_get_prefix): Likewise. - * kern/i386/pc/init.c: Include . - (pupa_machine_init): Use the environment variable `prefix' instead of - using pupa_dl_set_prefix to set the prefix. - * kern/main.c: Include . - (pupa_set_root_dev): Use the environment variable `prefix' instead of - using pupa_dl_get_prefix to get the prefix. - * kern/misc.c: Include . - (pupa_isdigit): New function. - (pupa_isgraph): Likewise. - (pupa_ftoa): Likewise. - (pupa_vsprintf): Added support for printing values of the type - `double'. Make it possible to format variable output when using - formatting like `%1.2%f'. - (pupa_split_cmdline): New function. - * kern/rescue.c: Include . - (next_word): Removed function. - (pupa_rescue_cmd_prefix): Likewise. - (pupa_rescue_cmd_set): New function. - (pupa_rescue_cmd_unset): New function. - (pupa_enter_rescue_mode): Use the `pupa_split_cmdline' function to - split the command line instead of splitting it here. Added - support for setting variables using the syntax `foo=bar'. Don't - initialize the prefix command anymore. Initialized the set and - unset commands. - * normal/cmdline.c: Include . - (pupa_tab_complete): Added prototypes for print_simple_completion, - print_partition_completion, add_completion, iterate_commands, - iterate_dev, iterate_part and iterate_dir. Moved code to print - partition information from here to kern/disk.c. - (pupa_cmdline_run): Don't check if the function exists anymore. - * normal/main.c: Include . - (pupa_rescue_cmd_normal): Use the environment variable `prefix' - instead of using pupa_dl_get_prefix to get the prefix. - * term/i386/pc/vga.c: Include . - (check_vga_mem): Cast pointers to `void *' to silence a gcc - warning. - (pupa_vga_putchar) [! DEBUG_VGA]: Removed for this case. - (pupa_vga_setcolor): Declare unused variables with `__attribute__ - ((unused))' to silence a gcc warning. - (pupa_vga_setcolor): Likewise. - (debug_command): Changed to match argument parsing - interface changes. - * util/pupa-emu.c: Include . - (options): Added 0's for unused fields to silence a gcc warning. - (argp): Likewise. - (main): Use the environment variable `prefix' instead of using - pupa_dl_set_prefix to set the prefix. Initialize the commands ls, - boot, cmp, cat and terminal. Finish the commands boot, cmp, cat - and terminal. - - * util/i386/pc/getroot.c: Include . - * util/misc.c: Include . - (pupa_malloc): Rewritten so errors are correctly reported. - (pupa_realloc): Likewise. - (pupa_memalign): Likewise. - (pupa_mm_init_region): Declare unused variables with - `__attribute__ ((unused))' to silence a gcc warning. - * normal/i386/setjmp.S: Remove tab at the end of the file to - silence a gcc warning. - * loader/i386/pc/linux.c (pupa_rescue_cmd_initrd): Declare unused - variables with `__attribute__ ((unused))' to silence a gcc - warning. - * loader/i386/pc/multiboot.c (pupa_multiboot_unload): Make the - local variable i unsigned to silence a gcc warning. - - * kern/term.c: Include . - (pupa_more_lines): New variable. - (pupa_more): Likewise. - (pupa_putcode): When the pager is active pause at the end of every - screen. - (pupa_set_more): New function. - * include/pupa/term.h (pupa_set_more): New prototype. - - -2004-03-07 Yoshinori K. Okuji - - Now this project is GRUB 2 rather than PUPA. The location of - the CVS repository was moved to GRUB's. - - * configure.ac: Use bug-grub as the reporting address. - Use GRUB instead of PUPA. - Change the version number to 1.90. - -2004-02-24 Yoshinori K. Okuji - - * genkernsyms.sh: Updated copyright information. - * genmk.rb: Likewise. - * genmodsrc.sh: Likewise. - * gensymlist.sh: Likewise. - * boot/i386/pc/boot.S: Likewise. - * boot/i386/pc/diskboot.S: Likewise. - * disk/i386/pc/biosdisk.c: Likewise. - * disk/i386/pc/partition.c: Likewise. - * font/manager.c: Likewise. - * fs/ext2.c: Likewise. - * fs/fat.c: Likewise. - * include/pupa/boot.h: Likewise. - * include/pupa/device.h: Likewise. - * include/pupa/disk.h: Likewise. - * include/pupa/dl.h: Likewise. - * include/pupa/elf.h: Likewise. - * include/pupa/err.h: Likewise. - * include/pupa/file.h: Likewise. - * include/pupa/font.h: Likewise. - * include/pupa/fs.h: Likewise. - * include/pupa/kernel.h: Likewise. - * include/pupa/loader.h: Likewise. - * include/pupa/misc.h: Likewise. - * include/pupa/mm.h: Likewise. - * include/pupa/net.h: Likewise. - * include/pupa/normal.h: Likewise. - * include/pupa/rescue.h: Likewise. - * include/pupa/setjmp.h: Likewise. - * include/pupa/symbol.h: Likewise. - * include/pupa/term.h: Likewise. - * include/pupa/types.h: Likewise. - * include/pupa/i386/setjmp.h: Likewise. - * include/pupa/i386/types.h: Likewise. - * include/pupa/i386/pc/biosdisk.h: Likewise. - * include/pupa/i386/pc/boot.h: Likewise. - * include/pupa/i386/pc/console.h: Likewise. - * include/pupa/i386/pc/init.h: Likewise. - * include/pupa/i386/pc/kernel.h: Likewise. - * include/pupa/i386/pc/linux.h: Likewise. - * include/pupa/i386/pc/loader.h: Likewise. - * include/pupa/i386/pc/memory.h: Likewise. - * include/pupa/i386/pc/multiboot.h: Likewise. - * include/pupa/i386/pc/partition.h: Likewise. - * include/pupa/i386/pc/time.h: Likewise. - * include/pupa/i386/pc/vga.h: Likewise. - * include/pupa/i386/pc/util/biosdisk.h: Likewise. - * include/pupa/util/getroot.h: Likewise. - * include/pupa/util/misc.h: Likewise. - * include/pupa/util/resolve.h: Likewise. - * kern/device.c: Likewise. - * kern/disk.c: Likewise. - * kern/dl.c: Likewise. - * kern/err.c: Likewise. - * kern/file.c: Likewise. - * kern/fs.c: Likewise. - * kern/loader.c: Likewise. - * kern/main.c: Likewise. - * kern/misc.c: Likewise. - * kern/mm.c: Likewise. - * kern/rescue.c: Likewise. - * kern/term.c: Likewise. - * kern/i386/dl.c: Likewise. - * kern/i386/pc/init.c: Likewise. - * kern/i386/pc/lzo1x.S: Likewise. - * kern/i386/pc/startup.S: Likewise. - * loader/i386/pc/chainloader.c: Likewise. - * loader/i386/pc/linux.c: Likewise. - * loader/i386/pc/multiboot.c: Likewise. - * normal/cmdline.c: Likewise. - * normal/command.c: Likewise. - * normal/main.c: Likewise. - * normal/menu.c: Likewise. - * normal/i386/setjmp.S: Likewise. - * term/i386/pc/console.c: Likewise. - * term/i386/pc/vga.c: Likewise. - * util/console.c: Likewise. - * util/genmoddep.c: Likewise. - * util/misc.c: Likewise. - * util/pupa-emu.c: Likewise. - * util/resolve.c: Likewise. - * util/unifont2pff.rb: Likewise. - * util/i386/pc/biosdisk.c: Likewise. - * util/i386/pc/getroot.c: Likewise. - * util/i386/pc/pupa-mkimage.c: Likewise. - * util/i386/pc/pupa-setup.c: Likewise. - -2004-02-15 Jeroen Dekkers - - * fs/ext2.c (pupa_ext2_read_file): Correct the value of BLOCKEND - when it is EXT2_BLOCK_SIZE (data). New argument READ_HOOK, all - callers changed. Set DATA->DISK->READ_HOOK to READ_HOOK before - reading and reset it after reading. - (pupa_ext2_close): Return PUPA_ERR_NONE. - - * include/pupa/i386/pc/linux.h (PUPA_LINUX_INITRD_MAX_ADDRESS): - Correct value. - (struct linux_kernel_header): Add kernel_version and - initrd_addr_max. - * loader/i386/pc/linux.c (pupa_rescue_cmd_linux): Check whether - pupa_file_read succeeds. - (pupa_rescue_cmd_initrd): Implement. - -2003-12-03 Marco Gerards - - * fs/ext2.c (pupa_ext2_label): New function. - (pupa_ext2_fs): Added label. - * fs/fat.c (pupa_fat_label): New function. - (pupa_fat_fs): Added label. - * include/pupa/fs.h (struct pupa_fs): Added prototype label. - - * kern/misc.c (pupa_strndup): New function. - * include/pupa/misc.h (pupa_strndup): New prototype. - - * include/pupa/normal.h: Include . - (pupa_set_history): New prototype. - (pupa_iterate_commands): New prototype. - * normal/cmdline.c: Include , - , . - (hist_size): New variable. - (hist_lines): Likewise. - (hist_end): Likewise. - (hist_used): Likewise. - (pupa_set_history): New function. - (pupa_history_get): Likewise. - (pupa_history_add): Likewise. - (pupa_history_replace): Likewise. - (pupa_tab_complete): Likewise. - (pupa_cmdline_run): Added tab completion and history buffer. Tab - completion shows partitionnames while completing partitions, this - feature was suggested by Jeff Bailey. - * normal/command.c (pupa_iterate_commands): New function. - * normal/main.c (PUPA_DEFAULT_HISTORY_SIZE): New macro. - (pupa_normal_init): Initialize history buffer. - (PUPA_MOD_INIT): Likewise. - (pupa_normal_fini): Free the history buffer. - (PUPA_MOD_FINI): Likewise. - - * util/console.c (pupa_ncurses_getkey): Accept 127 as backspace - key. - - * aclocal.m4 (pupa_I386_CHECK_REGPARM_BUG): New DEFUN. - * configure.ac [i386]: Check for regparam bug. - (NESTED_FUNC_ATTR) [! i386]: Defined. - -2003-11-17 Marco Gerards - - * conf/i386-pc.rmk (sbin_UTILITIES): Added pupa-emu. - (pupa_setup_SOURCES): Added util/i386/pc/getroot.c. - (pupa_emu_SOURCES): New variable. - (pupa_emu_LDFLAGS): Likewise. - * include/pupa/fs.h (pupa_ext2_init) [PUPA_UTIL]: New prototype. - (pupa_ext2_fini) [PUPA_UTIL]: Likewise. - * include/pupa/normal.h (pupa_normal_init) [PUPA_UTIL]: Likewise. - (pupa_normal_fini) [PUPA_UTIL]: Likewise. - * include/pupa/setjmp.h [PUPA_UTIL]: Include . - (pupa_jmp_buf): New typedef. - (pupa_setjmp) [PUPA_UTIL]: New macro. - (pupa_longjmp) [PUPA_UTIL]: Likewise. - * include/pupa/term.h (struct pupa_term): New member `refresh'. - (pupa_refresh): New prototype. - * include/pupa/util/getroot.h: New file. - * kern/misc.c (pupa_vsprintf): Refresh the screen after updating - it. - * kern/rescue.c (pupa_rescue_get_command_line): Likewise. - (pupa_rescue_cmd_cat): Likewise. - (pupa_rescue_cmd_ls): Likewise. - (pupa_rescue_cmd_testload): Likewise. - (pupa_rescue_cmd_lsmod): Likewise. - * normal/cmdline.c (pupa_cmdline_get): Likewise. - * normal/menu.c (run_menu): Likewise. - * kern/term.c (pupa_cls): Likewise. - (pupa_refresh): New function. - * normal/normal.c (pupa_normal_init) [PUPA_UTIL]: New function. - (pupa_normal_fini) [PUPA_UTIL]: Likewise. - * util/console.c: New file. - - * util/i386/pc/getroot.c: New file. - * util/i386/pc/pupa-setup.c: Include . - (pupa_putchar): New function. - (pupa_refresh): Likewise. - (xgetcwd): Function moved to ... - (strip_extra_slashes): Likewise. - (get_prefix): Likewise. - * util/i386/pc/getroot.c: ... here. - (find_root_device): Function moved and renamed to... - * util/i386/pc/getroot.c (pupa_find_root_device): ... here. - Changed all callers. - * util/i386/pc/pupa-setup.c (guess_root_device): Function moved - and renamed to... - * util/i386/pc/getroot.c (pupa_guess_root_device): ... here. - Changed all callers. - * util/misc.c (pupa_memalign): New function. - (pupa_mm_init_region): Likewise. - (pupa_register_exported_symbols): Likewise. - (pupa_putchar): Function removed. - * util/pupa-emu.c: New file. - -2003-11-16 Jeroen Dekkers - - * conf/i386-pc.rmk (pkgdata_MODULES): Add _multiboot.mod. - (_multiboot_mod_SOURCES): New variable. - (_multiboot_mod_CFLAGS): Likewise. - * loader/i386/pc/multiboot.c: New file. - * include/pupa/i386/pc/multiboot.h: Likewise. - * kern/i386/pc/startup.S: Include pupa/machine/multiboot.h. - (pupa_multiboot_real_boot): New function. - * include/pupa/i386/pc/loader.h: Include pupa/machine/multiboot.h. - (pupa_multiboot_real_boot): New prototype. - (pupa_rescue_cmd_multiboot): Likewise - (pupa_rescue_cmd_module): Likewise. - - * kern/loader.c (pupa_loader_set): Continue when - pupa_loader_unload_func() fails. - (pupa_loader_unset): New function. - * include/pupa/loader.h (pupa_loader_unset): New prototype. - - * kern/misc.c (pupa_stpcpy): New function. - * include/pupa/misc.h (pupa_stpcpy): New prototype. - -2003-11-12 Marco Gerards - - * disk/i386/pc/biosdisk.c (pupa_biosdisk_open): Correctly check - for available extensions. - - * include/pupa/i386/pc/time.h: New file. - * kern/disk.c: Include . - (PUPA_CACHE_TIMEOUT): New macro. - (pupa_last_time): New variable. - (pupa_disk_open): Flush the cache when there was a timeout. - (pupa_disk_close): Reset the timer. - * kern/i386/pc/startup.S (pupa_get_rtc): Renamed from - pupa_currticks. - * util/misc.c: Include - (pupa_get_rtc): New function. - -2003-11-09 Jeroen Dekkers - - * fs/ext2.c (struct pupa_ext2_inode): Declare struct datablocks - as blocks. - (pupa_ext2_get_file_block): Use blocks member. - - * fs/ext2.c (pupa_ext2_read_file): Only set skipfirst for the - first block. Return -1 instead of pupa_errno on error. - -2003-10-27 Marco Gerards - - * README: In the pupa-mkimage example use _chain instead of chain - and ext2 instead of fat. - * TODO: Replace ext2fs with jfs as an example. Add an item for - adding journal playback for ext2fs. - * conf/i386-pc.rmk (pupa_setup_SOURCES): Added fs/ext2.c. - (pkgdata_MODULES): Added ext2.mod. - (ext2_mod_SOURCES): New variable. - (ext2_mod_CFLAGS): Likewise. - * include/pupa/err.h (pupa_err_t): Added PUPA_ERR_SYMLINK_LOOP. - * include/pupa/misc.h (pupa_strncpy): New prototype. - (pupa_strcat): Likewise. - (pupa_strncmp): Likewise. - * kern/misc.c (pupa_strcat): Enable function. - (pupa_strncpy): New function. - (pupa_strncmp): Likewise. - * fs/ext2.c: New file. - - * kern/disk.c (pupa_disk_read): Set pupa_errno to PUPA_ERR_NONE - when the read failed before retrying. - * util/i386/pc/biosdisk.c (_LARGEFILE_SOURCE): Removed. - (_FILE_OFFSET_BITS): Likewise. - * configure.ac: Added AC_SYS_LARGEFILE. - -2003-09-25 Yoshinori K. Okuji - - * genmk.rb (PModule#rule): Make sure to get only symbol names - from the output of nm. - Reported by Robert Millan . - -2003-09-25 Yoshinori K. Okuji - - I forgot to check in these changes for a long time. This adds - incomplete support for VGA console, and this is still very - buggy. Also, a lot of consideration is required for I18N, - UNICODE, and VGA font issues. Therefore, assume that this is - such that "better than nothing". - - * font/manager.c: New file. - * include/pupa/font.h: Likewise. - * include/pupa/i386/pc/vga.h: Likewise. - * term/i386/pc/vga.c: Likewise. - * util/unifont2pff.rb: Likewise. - - * conf/i386-pc.rmk (kernel_img_HEADERS): Added machine/vga.h. - (pkgdata_MODULES): Added vga.mod and font.mod. - (vga_mod_SOURCES): New variables. - (vga_mod_CFLAGS): Likewise. - (font_mod_SOURCES): Likewise. - (font_mod_CFLAGS): Likewise. - - * include/pupa/err.h (PUPA_ERR_BAD_FONT): New constant. - - * include/pupa/term.h: Include pupa/err.h. - (struct pupa_term): Added init and fini. - Changed the argument of putchar to pupa_uint32_t. - - * include/pupa/i386/pc/console.h: Include pupa/symbol.h. - (pupa_console_real_putchar): New prototype. - (pupa_console_putchar): Removed. - (pupa_console_checkkey): Exported. - (pupa_console_getkey): Likewise. - - * kern/misc.c (pupa_vsprintf): Add support for UNICODE - characters. - - * kern/term.c (pupa_term_set_current): Rewritten. - (pupa_putchar): Likewise. - (pupa_putcode): New function. - - * kern/i386/pc/startup.S (pupa_console_putchar): Renamed to ... - (pupa_console_real_putchar): ... this. - (pupa_vga_set_mode): New function. - (pupa_vga_get_font): Likewise. - - * normal/command.c: Include pupa/term.h. - (terminal_command): New function. - (pupa_command_init): Register the command "terminal". - - * normal/menu.c (DISP_LEFT): Changed to a UNICODE value. - (DISP_UP): Likewise. - (DISP_RIGHT): Likewise. - (DISP_DOWN): Likewise. - (DISP_HLINE): Likewise. - (DISP_VLINE): Likewise. - (DISP_UL): Likewise. - (DISP_UR): Likewise. - (DISP_LL): Likewise. - (DISP_LR): Likewise. - - * term/i386/pc/console.c (pupa_console_putchar): New function. - -2003-02-08 NIIBE Yutaka - - * util/resolve.c (pupa_util_resolve_dependencies): BUG - FIX. Reverse the path_list. - - * include/pupa/normal.h: Export pupa_register_command and - pupa_unregister_command. - - * hello/hello.c (pupa_cmd_hello): New module. - * conf/i386-pc.rmk: Added hello.mod. - -2003-01-31 Yoshinori K. Okuji - - * kern/i386/pc/lzo1x.S: New file. - - * util/i386/pc/pupa-mkimage.c: Include lzo1x.h. - (compress_kernel): New variable. - (generate_image): Heavily modified to support compressing a - large part of the core image. - - * util/misc.c (pupa_util_read_image): Fix a file descriptor - leak. - (pupa_util_load_image): New function. - - * kern/i386/pc/startup.S: Include pupa/machine/kernel.h. - (pupa_compressed_size): New variable. - (codestart): Enable Gate A20 here. - Decompress the compressed part of the core image. - Rearrange the code to put functions and variables which are - required for initialization in the non-compressed part. - Include lzo1x.S. - - * kern/i386/pc/init.c (pupa_machine_init): Don't enable Gate A20 - here. - - * include/pupa/util/misc.h (pupa_util_write_image): Declared. - - * include/pupa/i386/pc/kernel.h - (PUPA_KERNEL_MACHINE_COMPRESSED_SIZE): New macro. - (PUPA_KERNEL_MACHINE_INSTALL_DOS_PART): Increased by 4. - (PUPA_KERNEL_MACHINE_INSTALL_BSD_PART): Likewise. - (PUPA_KERNEL_MACHINE_PREFIX): Likewise. - (PUPA_KERNEL_MACHINE_RAW_SIZE): New macro. - - * conf/i386-pc.rmk (pupa_mkimage_LDFLAGS): New variable. - - * genmk.rb (Image#rule): Put LDFLAGS at the end of a line. - (Utility#rule): Likewise. - - * configure.ac: Check if LZO is available. - -2003-01-20 Yoshinori K. Okuji - - * include/pupa/normal.h: New file. - * include/pupa/setjmp.h: Likewise. - * include/pupa/i386/setjmp.h: Likewise. - * normal/cmdline.c: Likewise. - * normal/command.c: Likewise. - * normal/main.c: Likewise. - * normal/menu.c: Likewise. - * normal/i386/setjmp.S: Likewise. - - * loader/i386/pc/linux.c (pupa_rescue_cmd_linux): Made global. - (pupa_rescue_cmd_initrd): Likewise. - - * loader/i386/pc/chainloader.c (pupa_rescue_cmd_chainloader): - Likewise. - - * kern/i386/pc/startup.S (translation_table): New variable. - (translate_keycode): New function. - (pupa_console_getkey): Call translate_keycode. - - * kern/rescue.c (attempt_normal_mode): New function. - (pupa_enter_rescue_mode): Attempt to execute the normal mode. If - it failed, print a message. - - * kern/mm.c (pupa_real_malloc): Print more information when a - free magic is broken. - (pupa_free): If the first free header is not free actually, set - it to P. - - * kern/main.c (pupa_load_normal_mode): Just load the module - "normal". - (pupa_main): Don't print the message - "Entering into rescue mode..." here. - - * include/pupa/i386/pc/loader.h (pupa_rescue_cmd_initrd): - Declared. - (pupa_rescue_cmd_initrd): Likewise. - (pupa_rescue_cmd_initrd): Likewise. - - * include/pupa/symbol.h (FUNCTION): Specify the type. - (VARIABLE): Likewise. - - * include/pupa/err.h (pupa_err_t): Added - PUPA_ERR_UNKNOWN_COMMAND. - - * include/pupa/dl.h (pupa_dl_set_prefix): Exported. - (pupa_dl_get_prefix): Likewise. - - * conf/i386-pc.rmk (pkgdata_MODULES): Added normal.mod. - Added _chain.mod and _linux.mod instead of chain.mod and - linux.mod. - (chain_mod_SOURCES): Renamed to ... - (_chain_mod_SOURCES): ... this. - (chain_mod_CFLAGS): Renamed to ... - (_chain_mod_CFLAGS): ... this. - (linux_mod_SOURCES): Renamed to ... - (_linux_mod_SOURCES): ... this. - (linux_mod_CFLAGS): Renamed to ... - (_linux_mod_CFLAGS): ... this. - (normal_mod_SOURCES): New variable. - (normal_mod_CFLAGS): Likewise. - (normal_mod_ASFLAGS): Likewise. - -2003-01-18 Yoshinori K. Okuji - - * kern/rescue.c (pupa_rescue_cmd_rmmod): Call pupa_dl_unload, if - possible. - - * kern/dl.c (pupa_dl_ref): Refer depending modules - recursively. - (pupa_dl_unref): Unrefer depending modules recursively. - Don't call pupa_dl_unload implicitly, because PUPA can crash if - a module is unloaded before one depending on that module is - unloaded. - (pupa_dl_unload): Unload depending modules explicitly, - if possible. - -2003-01-17 Yoshinori K. Okuji - - * include/pupa/i386/pc/linux.h: New file. - * loader/i386/pc/linux.c: Likewise. - - * loader/i386/pc/chainloader.c (pupa_chainloader_boot_sector): - Removed. - (pupa_chainloader_unload): Return PUPA_ERR_NONE. - (pupa_rescue_cmd_chainloader): Read the image to 0x7C00 instead - of PUPA_CHAINLOADER_BOOT_SECTOR. - - * kern/i386/pc/startup.S: Include pupa/machine/linux.h. - (pupa_linux_prot_size): New variable. - (pupa_linux_tmp_addr): Likewise. - (pupa_linux_real_addr): Likewise. - (pupa_linux_boot_zimage): New function. - (pupa_linux_boot_bzimage): Likewise. - - * kern/i386/pc/init.c (struct mem_region): New structure. - (MAX_REGIONS): New macro. - (mem_regions): New variable. - (num_regions): Likewise. - (pupa_os_area_addr): Likewise. - (pupa_os_area_size): Likewise. - (pupa_lower_mem): Likewise. - (pupa_upper_mem): Likewise. - (add_mem_region): New function. - (compact_mem_regions): Likewise. - (pupa_machine_init): Set PUPA_LOWER_MEM and PUPA_UPPER_MEM to - the size of the conventional memory and that of so-called upper - memory (before the first memory hole). - Instead of adding each found region to free memory, use - add_mem_region and add them after removing overlaps. - Also, add only 1/4 of the upper memory to free memory. The rest - is used for loading OS images. Maybe this is ad hoc, but this - makes it much easier to relocate OS images when booting. - - * kern/rescue.c (pupa_rescue_cmd_module): Removed. - (pupa_enter_rescue_mode): Don't register initrd and module. - - * kern/mm.c: Include pupa/dl.h. - - * kern/main.c: Include pupa/file.h and pupa/device.h. - - * kern/loader.c (pupa_loader_load_module_func): Removed. - (pupa_loader_load_module): Likewise. - - * kern/dl.c (pupa_dl_load): Use the suffix ``.mod'' instead of - ``.o''. - - * include/pupa/i386/pc/loader.h (pupa_linux_prot_size): Declared. - (pupa_linux_tmp_addr): Likewise. - (pupa_linux_real_addr): Likewise. - (pupa_linux_boot_zimage): Likewise. - (pupa_linux_boot_bzimage): Likewise. - - * include/pupa/i386/pc/init.h (pupa_lower_mem): Declared. - (pupa_upper_mem): Likewise. - (pupa_gate_a20): Don't export, because turning off Gate A20 in a - module is too dangerous. - - * include/pupa/loader.h (pupa_os_area_addr): Declared. - (pupa_os_area_size): Likewise. - (pupa_loader_set): Remove the first argument. Loader doesn't - manage modules or initrd any longer. - (pupa_loader_load_module): Removed. - - * conf/i386-pc.rmk (pkgdata_MODULES): Added linux.mod. - (linux_mod_SOURCES): New variable. - (linux_mod_CFLAGS): Likewise. - -2003-01-07 Yoshinori K. Okuji - - * util/i386/pc/pupa-setup.c (setup): Convert the endianness of - the length of a blocklist correctly. - - * util/i386/pc/biosdisk.c (pupa_util_biosdisk_open) [__linux__]: - Use ioctl only if the OS file is a block device. - (pupa_util_biosdisk_open): Don't use ST.ST_BLOCKS, because it is - not very useful for normal files. - - * kern/main.c (pupa_set_root_dev): New function. - (pupa_load_normal_mode): Likewise. - (pupa_main): Call those above. - - * include/pupa/types.h (pupa_swap_bytes16): Cast the result to - pupa_uint16_t. - - * include/pupa/kernel.h (pupa_enter_normal_mode): Removed. - -2003-01-06 Yoshinori K. Okuji - - * util/i386/pc/pupa-setup.c: Include pupa/machine/kernel.h. - (setup): Configure the installed partition information and the - dl prefix. - - * loader/i386/pc/chainloader.c (my_mod): New variable. - (pupa_chainloader_unload): New function. - (pupa_rescue_cmd_chainloader): Refer itself. - (PUPA_MOD_INIT): Save its own module in MY_MOD. - - * kern/i386/pc/startup.S (install_partition): Removed. - (version_string): Likewise. - (config_file): Likewise. - (pupa_install_dos_part): New variable. - (pupa_install_bsd_part): Likewise. - (pupa_prefix): Likewise. - (pupa_chainloader_real_boot): Call pupa_dl_unload_all. - - * kern/i386/pc/init.c: Include pupa/machine/kernel.h, pupa/dl.h - and pupa/misc.h. - (make_install_device): New function. - (pupa_machine_init): Set the dl prefix. - - * kern/rescue.c: Include pupa/rescue.h and pupa/dl.h. - (buf): Renamed to ... - (linebuf): ... this. - (pupa_rescue_cmd_prefix): New function. - (pupa_rescue_cmd_insmod): Likewise. - (pupa_rescue_cmd_rmmod): Likewise. - (pupa_rescue_cmd_lsmod): Likewise. - (pupa_enter_rescue_mode): Register new commands: prefix, insmod, - rmmod and lsmod. - - * kern/mm.c (pupa_memalign): If failed even after invalidating - disk caches, unload unneeded modules and retry. - - * kern/misc.c (pupa_memmove): New function. - (pupa_memcpy): Removed. - (pupa_strcpy): New function. - (pupa_itoa): Made static. - - * kern/dl.c (pupa_dl_iterate): New function. - (pupa_dl_ref): Likewise. - (pupa_dl_unref): Likewise. - (pupa_dl_unload): Return if succeeded or not. - (pupa_dl_unload_unneeded): New function. - (pupa_dl_unload_all): Likewise. - (pupa_dl_init): Renamed to ... - (pupa_dl_set_prefix): ... this. - (pupa_dl_get_prefix): New function. - - * include/pupa/i386/pc/kernel.h: Include pupa/types.h. - (PUPA_KERNEL_MACHINE_INSTALL_DOS_PART): New macro. - (PUPA_KERNEL_MACHINE_INSTALL_BSD_PART): Likewise. - (PUPA_KERNEL_MACHINE_PREFIX): Likewise. - (pupa_install_dos_part): Declared. - (pupa_install_bsd_part): Likewise. - (pupa_prefix): Likewise. - (pupa_boot_drive): Likewise. - - * include/pupa/types.h: Fix a typo. - - * include/pupa/misc.h (pupa_memcpy): New macro. Just an alias to - pupa_memmove. - (pupa_memmove): Declared. - (pupa_strcpy): Likewise. - - * include/pupa/dl.h (PUPA_MOD_INIT): Change the prototype. Now - pupa_mod_init takes one argument, its own module. - (pupa_dl_unload_unneeded): Declared. - (pupa_dl_unload_all): Likewise. - (pupa_dl_ref): Likewise. - (pupa_dl_unref): Likewise. - (pupa_dl_iterate): Likewise. - (pupa_dl_init): Renamed to ... - (pupa_dl_set_prefix): ... this. - (pupa_dl_get_prefix): Declared. - - * fs/fat.c [!PUPA_UTIL] (my_mod): New variable. - (pupa_fat_dir) [!PUPA_UTIL]: Prevent the fat module from being - unloaded. - (pupa_fat_open) [!PUPA_UTIL]: Refer itself if succeeded. - (pupa_fat_close) [!PUPA_UTIL]: Unrefer itself. - - * configure.ac (tmp_CFLAGS): Added -Wshadow, -Wpointer-arith, - -Wmissing-prototypes, -Wundef and -Wstrict-prototypes. - -2003-01-03 Yoshinori K. Okuji - - * util/i386/pc/pupa-setup.c (setup): Define the internal - function find_first_partition_start at the top level, because GCC - 3.0.x cannot compile internal functions in deeper scopes - correctly. - (find_root_device): Use lstat instead of stat. - Don't follow symbolic links. - Fix the path-constructing code. - - * util/i386/pc/biosdisk.c [__linux__] (BLKFLSBUF): New macro. - (pupa_util_biosdisk_open) [__linux__]: Get the size of a device - by a BLKGETSIZE ioctl first, because block devices don't fill - the member st_mode of the structure stat on Linux. - [__linux__] (linux_find_partition): Use a temporary buffer - REAL_DEV for the working space. Copy it to DEV before returning. - (open_device) [__linux__]: Call ioctl with BLKFLSBUF to make the - buffer cache consistent. - (get_os_disk) [__linux__]: Use the length 5 instead of 4 for - strncmp. The previous value was merely wrong. - (pupa_util_biosdisk_get_pupa_dev): Use stat instead of lstat. - - * fs/fat.c (pupa_fat_read_data): Shift 4 instead of 12 when the - FAT size is 12. The previous value was merely wrong. - - * kern/main.c (pupa_main): Don't split the starting message from - newlines. - - * kern/term.c (pupa_putchar): Put CR after LF instead of before - LF, because BIOS goes crazy about character attributes in this - case. - -2003-01-03 Yoshinori K. Okuji - - * include/i386/pc/util/biosdisk.h: New file. - * util/i386/pc/biosdisk.c: Likewise. - * util/i386/pc/pupa-setup.c: Likewise. - - * Makefile.in (INCLUDE_DISTFILES): Added - include/pupa/i386/pc/util/biosdisk.h. - (UTIL_DISTFILES): Added biosdisk.c and pupa-setup.c under the - directory util/i386/pc. - (install-local): Added a rule for sbin_UTILITIES. - (uninstall): Likewise. - - * util/i386/pc/pupa-mkimage.c (usage): Fix a typo in the doc. - - * util/misc.c (xrealloc): New function. - (pupa_malloc): Likewise. - (pupa_free): Likewise. - (pupa_realloc): Likewise. - (pupa_stop): Likewise. - (pupa_putchar): Likewise. - - * kern/disk.c (pupa_disk_read): Prevent L from underflowing. - - * include/pupa/util/misc.h (xrealloc): Declared. - - * include/pupa/i386/pc/boot.h (PUPA_BOOT_MACHINE_BPB_START): New - macro. - (PUPA_BOOT_MACHINE_BPBEND): Renamed to ... - (PUPA_BOOT_MACHINE_BPB_END): ... this. - - * include/pupa/fs.h [PUPA_UTIL] (pupa_fat_init): Declared. - [PUPA_UTIL] (pupa_fat_fini): Likewise. - - * fs/fat.c [PUPA_UTIL] (pupa_fat_init): Defined. Maybe a better - way should be implemented. - [PUPA_UTIL] (pupa_fat_fini): Likewise. - - * disk/i386/pc/biosdisk.c (pupa_biosdisk_call_hook): Increase - the size of NAME for safety. - (pupa_biosdisk_iterate): Search hard disks to 0x90 instead of - 0x88. - - * conf/i386-pc.rmk (sbin_UTILITIES): New variable. - (pupa_setup_SOURCES): Likewise. - - * genmk.rb (Utility#rule): Add $(BUILD_CFLAGS) into the rules. - -2002-12-28 Yoshinori K. Okuji - - * kern/i386/pc/startup.S (push_get_mmap_entry): Revert to a - bunch of pushl's from pusha, because this destroys the return - value. - -2002-12-28 Yoshinori K. Okuji - - Use -mrtd and -mregparm=3 to reduce the generated code sizes. - This means that any missing prototypes could be fatal. Also, you - must take care when writing assembly code. See the comments at - the beginning of startup.S, for more details. - - * kern/i386/pc/startup.S (pupa_halt): Modified for the new - compilation mechanism. - (pupa_chainloader_real_boot): Likewise. - (pupa_biosdisk_rw_int13_extensions): Likewise. - (pupa_biosdisk_rw_standard): Likewise. - (pupa_biosdisk_check_int13_extensions): Likewise. - (pupa_biosdisk_get_diskinfo_int13_extensions): Likewise. - (pupa_biosdisk_get_diskinfo_standard): Likewise. - (pupa_get_memsize): Likewise. - (pupa_get_mmap_entry): Likewise. - (pupa_console_putchar): Likewise. - (pupa_console_setcursor): Likewise. - (pupa_getrtsecs): Use pushl instead of push. - - * kern/i386/pc/init.c (pupa_machine_init): Use the scratch - memory instead of the stack for a mmap entry, because some - BIOSes may ignore the maximum size and overflow. - - * conf/i386-pc.rmk (COMMON_CFLAGS): Added -mrtd and -mregparm=3. - - * genmk.rb (PModule#rule): Compile automatically generated - sources with module-specific CFLAGS as well as other sources. - -2002-12-27 Yoshinori K. Okuji - - * configure.ac: Check ld. - Replace CFLAGS and CPPFLAGS with BUILD_CFLAGS and BUILD_CPPFLAGS - respectively, before checking endianness and sizes. - - * Makefile.in (LD): New variable. - -2002-12-27 Yoshinori K. Okuji - - * Makefile.in (BUILD_CC): CC -> BUILD_CC. - -2002-12-27 Yoshinori K. Okuji - - * Changelog: New file. - diff --git a/INSTALL b/INSTALL index 0dd408bcc..724584c57 100644 --- a/INSTALL +++ b/INSTALL @@ -4,6 +4,10 @@ This is the GRUB. Welcome. This file contains instructions for compiling and installing the GRUB. +Where this document refers to packages names, they are named according to the +Debian 11 package repositories. These packages can be found by searching +https://packages.debian.org/. + The Requirements ================ @@ -11,24 +15,95 @@ GRUB depends on some software packages installed into your system. If you don't have any of them, please obtain and install them before configuring the GRUB. -* GCC 4.1.3 or later +* GCC 5.1.0 or later + Experimental support for clang 8.0.0 or later (results in much bigger binaries) + for i386, x86_64, arm (including thumb), arm64, mips(el), powerpc, sparc64 * GNU Make * GNU Bison 2.3 or later -* GNU gettext 0.17 or later +* GNU gettext * GNU binutils 2.9.1.0.23 or later +* Flex 2.5.35 or later +* pkg-config +* GNU patch * Other standard GNU/Unix tools +* a libc with large file support (e.g. glibc 2.1 or later) + +On GNU/Linux, you also need: + +* libdevmapper 1.02.34 or later (recommended) + +For optional grub-emu features, you need: + +* SDL (recommended) +* libpciaccess (optional) + +To build GRUB's graphical terminal (gfxterm), you need: + +* FreeType 2.1.5 or later +* GNU Unifont + +To build grub-mkfont the unicode fonts are required (xfonts-unifont package +on Debian). If you use a development snapshot or want to hack on GRUB you may need the following. -* Ruby 1.6 or later -* Python 2.5.2 or later -* Autoconf 2.60 or later -* Automake 1.10.1 or later +* Python 3 (NOTE: python 2.6 should still work, but it's not tested) +* Autoconf 2.64 or later +* Automake 1.14 or later + +Your distro may package cross-compiling toolchains such as the following +incomplete list on Debian: gcc-aarch64-linux-gnu, gcc-arm-linux-gnueabihf, +gcc-mips-linux-gnu, gcc-mipsel-linux-gnu, gcc-powerpc64-linux-gnu, +gcc-riscv64-linux-gnu, gcc-sparc64-linux-gnu, mingw-w64 and mingw-w64-tools. + +More cross compiling toolchains can be found at the following trusted sites: + +* https://mirrors.kernel.org/pub/tools/crosstool/ +* https://toolchains.bootlin.com/ Prerequisites for make-check: -* qemu, specifically the binary 'qemu-system-i386' +* qemu, specifically the binary "qemu-system-ARCH" where ARCH is the + architecture GRUB has been built for; the "qemu-system" package on Debian + will install all needed qemu architectures +* OVMF, for EFI platforms (packages ovmf, ovmf-ia32, qemu-efi-arm, and + qemu-efi-aarch64) +* OpenBIOS, for ieee1275 platforms (packages openbios-ppc and openbios-sparc) +* xorriso 1.2.9 or later, for grub-mkrescue and grub-shell +* wamerican, for grub-fs-tester +* mtools, FAT tools for EFI platforms +* xfonts-unifont, for the functional tests +* swtpm-tools and tpm2-tools, for TPM2 key protector tests + +* If running a Linux kernel the following modules must be loaded: + - fuse, loop + - btrfs, erofs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2, + reiserfs, udf, xfs + - On newer kernels, the exfat kernel modules may be used instead of the + exfat FUSE filesystem +* The following are Debian named packages required mostly for the full + suite of filesystem testing (but some are needed by other tests as well): + - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfatprogs, exfat-fuse, + f2fs-tools, genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, + reiserfsprogs, squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse + - exfat-fuse, if not using the exfat kernel module + - gzip, lzop, xz-utils + - attr, cpio, g++, gawk, parted, recode, tar, util-linux + +Note that `make check' will run and many tests may complete successfully +with only a subset of these prerequisites. However, some tests may be +skipped or fail due to missing prerequisites. + +To build the documentation you'll need: +* texinfo, for the info and html documentation +* texlive, for building the dvi and pdf documentation (optional) + +To use the gdb_grub GDB script you'll need: +* readelf (binutils package) +* objdump (binutils package) +* GNU Debugger > 7, built with python support (gdb package) +* Python >= 3.5 (python3 package) Configuring the GRUB ==================== @@ -59,9 +134,17 @@ Building the GRUB The simplest way to compile this package is: - 1. `cd' to the directory containing the package's source code. If - you don't use a release tarball you have to type `./autogen.sh'. - Type `./configure' to configure the package for your system. + 1. `cd' to the directory containing the package's source code. + + 2. Skip this and following step if you use release tarball and proceed to + step 4. If you want translations type `./linguas.sh'. + + 3. Type `./bootstrap'. + + The autogen.sh (called by bootstrap) uses python. By default autodetect + it, but it can be overridden by setting the PYTHON variable. + + 4. Type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. @@ -69,15 +152,19 @@ The simplest way to compile this package is: Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. - 2. Type `make' to compile the package. + 6. Type `make' to compile the package. - 3. Optionally, type `make check' to run any self-tests that come with - the package. + 7. Optionally, type `make check' to run any self-tests that come with + the package. Note that many of the tests require root privileges in + order to run. - 4. Type `make install' to install the programs and any data files and + 8. Type `make install' to install the programs and any data files and documentation. - 5. You can remove the program binaries and object files from the + 9. Type `make html' or `make pdf' to generate the html or pdf + documentation. Note, these are not built by default. + + 10. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is @@ -86,6 +173,108 @@ The simplest way to compile this package is: all sorts of other programs in order to regenerate files that came with the distribution. +Cross-compiling the GRUB +======================== + +GRUB defines 3 platforms: + + - "Build" is the one which build systems runs on. + - "Host" is where you execute GRUB utils. + - "Target" is where GRUB itself runs. + +For grub-emu host and target must be the same but may differ from build. + +If build and host are different make check isn't available. + +If build and host are different man pages are not generated. + +As an example imagine you have a build system running on FreeBSD on sparc +which prepares packages for developers running amd64 GNU/Linux laptop and +they need to make images for ARM board running U-boot. In this case: + +build=sparc64-freebsd +host=amd64-linux-gnu +target=arm-uboot + +For this example the configure line might look like (more details below) +(some options are optional and included here for completeness but some rarely +used options are omitted): + + ./configure --build=sparc64-freebsd --host=x86_64-linux-gnu \ + --target=arm-linux-gnueabihf --with-platform=efi \ + BUILD_CC=gcc BUILD_PKG_CONFIG=pkg-config \ + HOST_CC=x86_64-linux-gnu-gcc HOST_CFLAGS='-g -O2' \ + PKG_CONFIG=x86_64-linux-gnu-pkg-config TARGET_CC=arm-linux-gnueabihf-gcc \ + TARGET_CFLAGS='-Os -march=armv8.3-a' TARGET_CCASFLAGS='-march=armv8.3-a' \ + TARGET_OBJCOPY=arm-linux-gnueabihf-objcopy \ + TARGET_STRIP=arm-linux-gnueabihf-strip TARGET_NM=arm-linux-gnueabihf-nm \ + TARGET_RANLIB=arm-linux-gnueabihf-ranlib LEX=flex + +Note, that the autoconf 2.65 manual states that when using the --host argument +to configure, the --build argument should be specified as well. Not sending +--build, enters a compatibility mode that will be removed in the future. + +Normally, for building a GRUB on amd64 with tools to run on amd64 to +generate images to run on ARM, using your Linux distribution's +packaged cross compiler, the following would suffice: + + ./configure --target=arm-linux-gnueabihf --with-platform=efi + +You need to use following options to specify tools and platforms. For minimum +version look at prerequisites. All tools not mentioned in this section under +corresponding platform are not needed for the platform in question. + + - For build + 1. --build= to autoconf name of build. + 2. BUILD_CC= to gcc able to compile for build. This is used, for + example, to compile build-gentrigtables which is then run to + generate sin and cos tables. + 3. BUILD_CFLAGS= for C options for build. + 4. BUILD_CPPFLAGS= for C preprocessor options for build. + 5. BUILD_LDFLAGS= for linker options for build. + 6. BUILD_PKG_CONFIG= for pkg-config for build (optional). + + - For host + 1. --host= to autoconf name of host. + 2. CC= for gcc able to compile for host. + 3. CFLAGS= for C options for host. + 4. HOST_CC= for gcc able to compile for host. + 5. HOST_CFLAGS= for C options for host. + 6. HOST_CPPFLAGS= for C preprocessor options for host. + 7. HOST_LDFLAGS= for linker options for host. + 8. PKG_CONFIG= for pkg-config for host (optional). + 9. Libdevmapper if any must be in standard linker folders (-ldevmapper) (optional). + 10. Libfuse if any must be in standard linker folders (-lfuse) (optional). + 11. Libzfs if any must be in standard linker folders (-lzfs) (optional). + 12. Liblzma if any must be in standard linker folders (-llzma) (optional). + Note: The HOST_* variables override not prefixed variables. + + - For target + 1. --target= to autoconf cpu name of target. + 2. --with-platform to choose firmware. + 3. TARGET_CC= for gcc able to compile for target. + 4. TARGET_CFLAGS= for C options for target. + 5. TARGET_CPPFLAGS= for C preprocessor options for target. + 6. TARGET_CCASFLAGS= for assembler options for target. + 7. TARGET_LDFLAGS= for linker options for target. + 8. TARGET_OBJCOPY= for objcopy for target. + 9. TARGET_STRIP= for strip for target. + 10. TARGET_NM= for nm for target. + 11. TARGET_RANLIB= for ranlib for target. + Note: If the TARGET_* variables are not specified then they will default + to be the same as the host variables. If host variables are not + specified then the TARGET_* variables will default to be the same + as not prefixed variables. + + - Additionally for emu, for host and target. + 1. SDL is looked for in standard linker directories (-lSDL) (optional) + 2. libpciaccess is looked for in standard linker directories (-lpciaccess) (optional) + + - Platform-agnostic tools and data. + 1. make is the tool you execute after ./configure. + 2. Bison is specified in YACC= variable + 3. Flex is specified in LEX= variable + 4. GNU unifont and Djvu sans are looked for in standard directories. Compiling For Multiple Architectures ==================================== diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 000000000..45e870c78 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,35 @@ +List of current GRUB maintainers and some basic information about the project +============================================================================= + +Here is the list of current GRUB maintainers: + - Daniel Kiper and , + - Alex Burmashev , + - Vladimir 'phcoder' Serbinenko . + +The maintainers drive and overlook the GRUB development. + +If you found a security vulnerability in the GRUB please check the SECURITY +file to get more information how to properly report this kind of bugs to +the maintainers. + +The GRUB development happens on the grub-devel mailing list [1]. The latest +GRUB source code is available at Savannah git repository [2]. + +Users can ask for help on the help-grub mailing list [3]. + + +List of past GRUB maintainers and people who strongly contributed to the project +================================================================================ + +Here is the list, sorted alphabetically, of past GRUB maintainers and people who +strongly contributed to the project: + - Andrei Borzenkov, + - Bryan Ford, + - Erich Stefan Boleyn, + - Gordon Matzigkeit, + - Yoshinori K. Okuji. + + +[1] https://lists.gnu.org/mailman/listinfo/grub-devel +[2] https://git.savannah.gnu.org/gitweb/?p=grub.git&view=view+git+repository +[3] https://lists.gnu.org/mailman/listinfo/help-grub diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 000000000..43635d5ff --- /dev/null +++ b/Makefile.am @@ -0,0 +1,494 @@ +AUTOMAKE_OPTIONS = subdir-objects -Wno-portability + +DEPDIR = .deps-util +SUBDIRS = grub-core/lib/gnulib . +if COND_real_platform +SUBDIRS += grub-core +endif +SUBDIRS += po docs util/bash-completion.d + +include $(top_srcdir)/conf/Makefile.common +include $(top_srcdir)/conf/Makefile.extra-dist + +AM_CFLAGS = $(HOST_CFLAGS) +AM_LDFLAGS = $(HOST_LDFLAGS) +AM_CPPFLAGS = $(HOST_CPPFLAGS) $(CPPFLAGS_DEFAULT) +AM_CCASFLAGS = $(HOST_CCASFLAGS) $(CCASFLAGS_DEFAULT) + +ACLOCAL_AMFLAGS = -I m4 + +CFLAGS_PROGRAM += $(CFLAGS_GNULIB) +LDFLAGS_PROGRAM += $(LDFLAGS_GNULIB) +CPPFLAGS_PROGRAM += $(CPPFLAGS_GNULIB) +CCASFLAGS_PROGRAM += $(CCASFLAGS_GNULIB) + +include $(srcdir)/Makefile.util.am + +check_SCRIPTS = $(check_SCRIPTS_native) $(check_SCRIPTS_nonnative) +check_PROGRAMS = $(check_PROGRAMS_native) $(check_PROGRAMS_nonnative) +TESTS = $(check_SCRIPTS) $(check_PROGRAMS) + +check-native: + $(MAKE) TESTS="$(check_PROGRAMS_native) $(check_SCRIPTS_native)" check +check-nonnative: + $(MAKE) TESTS="$(check_PROGRAMS_nonnative) $(check_SCRIPTS_nonnative)" check + +# XXX Use Automake's LEX & YACC support +grub_script.tab.h: $(top_srcdir)/grub-core/script/parser.y + $(YACC) -d -p grub_script_yy -b grub_script $(top_srcdir)/grub-core/script/parser.y +grub_script.tab.c: grub_script.tab.h +CLEANFILES += grub_script.tab.c grub_script.tab.h + +# For the lexer. +grub_script.yy.h: $(top_srcdir)/grub-core/script/yylex.l + $(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(top_srcdir)/grub-core/script/yylex.l +grub_script.yy.c: grub_script.yy.h +CLEANFILES += grub_script.yy.c grub_script.yy.h + +# For libgrub.a +libgrub.pp: config-util.h grub_script.tab.h grub_script.yy.h $(libgrubmods_a_SOURCES) $(libgrubkern_a_SOURCES) + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libgrubmods_a_CPPFLAGS) $(libgrubkern_a_CPPFLAGS) $(CPPFLAGS) \ + -D'GRUB_MOD_INIT(x)=@MARKER@x@' $^ > $@ || (rm -f $@; exit 1) +CLEANFILES += libgrub.pp + +libgrub_a_init.lst: libgrub.pp + cat $< | grep '^@MARKER@' | sed 's/@MARKER@\(.*\)@/\1/g' | sort -u > $@ || (rm -f $@; exit 1) +CLEANFILES += libgrub_a_init.lst + +libgrub_a_init.c: libgrub_a_init.lst $(top_srcdir)/geninit.sh + sh $(top_srcdir)/geninit.sh `cat $<` > $@ || (rm -f $@; exit 1) +CLEANFILES += libgrub_a_init.c + +# For grub-fstest +grub_fstest.pp: config-util.h $(grub_fstest_SOURCES) + $(CPP) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(grub_fstest_CPPFLAGS) $(CPPFLAGS) \ + -D'GRUB_MOD_INIT(x)=@MARKER@x@' $^ > $@ || (rm -f $@; exit 1) +CLEANFILES += grub_fstest.pp + +grub_fstest_init.lst: libgrub.pp grub_fstest.pp + cat $^ | grep '^@MARKER@' | sed 's/@MARKER@\(.*\)@/\1/g' | sort -u > $@ || (rm -f $@; exit 1) +CLEANFILES += grub_fstest_init.lst + +grub_fstest_init.c: grub_fstest_init.lst $(top_srcdir)/geninit.sh + sh $(top_srcdir)/geninit.sh `cat $<` > $@ || (rm -f $@; exit 1) +CLEANFILES += grub_fstest_init.c + +if COND_HAVE_FONT_SOURCE +pkgdata_DATA += unicode.pf2 ascii.pf2 euro.pf2 ascii.h widthspec.h +endif + +starfield_theme_files = $(srcdir)/themes/starfield/blob_w.png $(srcdir)/themes/starfield/boot_menu_c.png $(srcdir)/themes/starfield/boot_menu_e.png $(srcdir)/themes/starfield/boot_menu_ne.png $(srcdir)/themes/starfield/boot_menu_n.png $(srcdir)/themes/starfield/boot_menu_nw.png $(srcdir)/themes/starfield/boot_menu_se.png $(srcdir)/themes/starfield/boot_menu_s.png $(srcdir)/themes/starfield/boot_menu_sw.png $(srcdir)/themes/starfield/boot_menu_w.png $(srcdir)/themes/starfield/slider_c.png $(srcdir)/themes/starfield/slider_n.png $(srcdir)/themes/starfield/slider_s.png $(srcdir)/themes/starfield/starfield.png $(srcdir)/themes/starfield/terminal_box_c.png $(srcdir)/themes/starfield/terminal_box_e.png $(srcdir)/themes/starfield/terminal_box_ne.png $(srcdir)/themes/starfield/terminal_box_n.png $(srcdir)/themes/starfield/terminal_box_nw.png $(srcdir)/themes/starfield/terminal_box_se.png $(srcdir)/themes/starfield/terminal_box_s.png $(srcdir)/themes/starfield/terminal_box_sw.png $(srcdir)/themes/starfield/terminal_box_w.png $(srcdir)/themes/starfield/theme.txt $(srcdir)/themes/starfield/README $(srcdir)/themes/starfield/COPYING.CC-BY-SA-3.0 + +build-grub-mkfont$(BUILD_EXEEXT): util/grub-mkfont.c grub-core/unidata.c grub-core/kern/emu/misc.c util/misc.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-mkfont\" $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) +CLEANFILES += build-grub-mkfont$(BUILD_EXEEXT) + +garbage-gen$(BUILD_EXEEXT): util/garbage-gen.c + $(BUILD_CC) -o $@ $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) $^ +CLEANFILES += garbage-gen$(BUILD_EXEEXT) +EXTRA_DIST += util/garbage-gen.c + +build-grub-gen-asciih$(BUILD_EXEEXT): util/grub-gen-asciih.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) -Wall -Werror +CLEANFILES += build-grub-gen-asciih$(BUILD_EXEEXT) + +build-grub-gen-widthspec$(BUILD_EXEEXT): util/grub-gen-widthspec.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_MKFONT=1 -DGRUB_BUILD=1 -DGRUB_UTIL=1 $^ $(BUILD_FREETYPE_CFLAGS) $(BUILD_FREETYPE_LIBS) -Wall -Werror +CLEANFILES += build-grub-gen-widthspec$(BUILD_EXEEXT) + +if COND_STARFIELD +starfield_DATA = dejavu_10.pf2 dejavu_12.pf2 dejavu_bold_14.pf2 dejavu_14.pf2 dejavu_16.pf2 $(starfield_theme_files) +dejavu_10.pf2: $(DJVU_FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -s 10 -o $@ $(DJVU_FONT_SOURCE) +CLEANFILES += dejavu_10.pf2 +dejavu_12.pf2: $(DJVU_FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -s 12 -o $@ $(DJVU_FONT_SOURCE) +CLEANFILES += dejavu_12.pf2 +dejavu_14.pf2: $(DJVU_FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -s 14 -o $@ $(DJVU_FONT_SOURCE) +CLEANFILES += dejavu_14.pf2 +dejavu_bold_14.pf2: $(DJVU_FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -b -s 14 -o $@ $(DJVU_FONT_SOURCE) +CLEANFILES += dejavu_bold_14.pf2 +dejavu_16.pf2: $(DJVU_FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -s 16 -o $@ $(DJVU_FONT_SOURCE) +CLEANFILES += dejavu_16.pf2 +else +starfield_DATA = +endif + +EXTRA_DIST += $(starfield_theme_files) +EXTRA_DIST += $(srcdir)/themes/starfield/src/slider_s.xcf $(srcdir)/themes/starfield/src/slider_n.xcf $(srcdir)/themes/starfield/src/slider_c.xcf $(srcdir)/themes/starfield/src/blob_nw.xcf $(srcdir)/themes/starfield/src/bootmenu/center.xcf $(srcdir)/themes/starfield/src/bootmenu/corner.xcf $(srcdir)/themes/starfield/src/bootmenu/side.xcf $(srcdir)/themes/starfield/src/terminalbox/side.xcf $(srcdir)/themes/starfield/src/terminalbox/corner.xcf $(srcdir)/themes/starfield/src/terminalbox/center.xcf + +unicode.pf2: $(FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -o $@ $(FONT_SOURCE) || (rm -f $@; exit 1) +CLEANFILES += unicode.pf2 + +# Arrows and lines are needed to draw the menu, so always include them +UNICODE_ARROWS=0x2190-0x2193 +UNICODE_LINES=0x2501-0x251B + +ascii.pf2: $(FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -o $@ $(FONT_SOURCE) -r 0x0-0x7f,$(UNICODE_ARROWS),$(UNICODE_LINES) || (rm -f $@; exit 1) +CLEANFILES += ascii.pf2 + +euro.pf2: $(FONT_SOURCE) build-grub-mkfont$(BUILD_EXEEXT) + ./build-grub-mkfont$(BUILD_EXEEXT) -o $@ $(FONT_SOURCE) -r 0x0-0x4ff,0x1e00-0x1fff,$(UNICODE_ARROWS),$(UNICODE_LINES) || (rm -f $@; exit 1) +CLEANFILES += euro.pf2 + +ascii.h: $(FONT_SOURCE) build-grub-gen-asciih$(BUILD_EXEEXT) + ./build-grub-gen-asciih$(BUILD_EXEEXT) $(FONT_SOURCE) $@ || (rm -f $@; exit 1) +CLEANFILES += ascii.h + +widthspec.h: $(FONT_SOURCE) build-grub-gen-widthspec$(BUILD_EXEEXT) + ./build-grub-gen-widthspec$(BUILD_EXEEXT) $(FONT_SOURCE) $@ || (rm -f $@; exit 1) +CLEANFILES += widthspec.h + +# Install config.h into platformdir +nodist_platform_HEADERS = config.h + +pkgdata_DATA += grub-mkconfig_lib + + +if COND_real_platform + +if COND_i386_coreboot +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_i386_multiboot +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_i386_ieee1275 +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_i386_qemu +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_i386_pc +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_i386_efi +QEMU32=qemu-system-i386 +MINIMUM_CPU_LINUX=pentium2 +endif + +if COND_x86_64_efi +QEMU32=qemu-system-x86_64 +MINIMUM_CPU_LINUX=core2duo +endif + +linux.init.x86_64: $(srcdir)/grub-core/tests/boot/linux.init-x86_64.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -static -m64 -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux.init.i386: $(srcdir)/grub-core/tests/boot/linux.init-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -static -m32 -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux.init.mips: $(srcdir)/grub-core/tests/boot/linux.init-mips.S + $(TARGET_CC) -o $@ $< -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux.init.ppc: $(srcdir)/grub-core/tests/boot/linux.init-ppc.S + $(TARGET_CC) -o $@ $< -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux.init.mipsel: $(srcdir)/grub-core/tests/boot/linux.init-mips.S + $(TARGET_CC) -o $@ $< -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux.init.loongson: $(srcdir)/grub-core/tests/boot/linux.init-mips.S + $(TARGET_CC) -o $@ $< -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -DREBOOT=1 + +multiboot.elf: $(srcdir)/grub-core/tests/boot/kernel-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -static -ffreestanding -nostdlib -nostdinc -DTARGET_MULTIBOOT=1 -Wl,--build-id=none -Wl,-N -Wl,-Ttext,0x100000 -m32 -I$(srcdir)/include + +kfreebsd.elf: $(srcdir)/grub-core/tests/boot/kernel-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -static -ffreestanding -nostdlib -nostdinc -Wl,--build-id=none -Wl,-N -Wl,-Ttext,0x100000 -m32 -I$(srcdir)/include + +kfreebsd.aout: kfreebsd.elf + $(TARGET_OBJCOPY) -O a.out-i386-linux $< $@ -j .text + +pc-chainloader.elf: $(srcdir)/grub-core/tests/boot/kernel-8086.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -static -DTARGET_CHAINLOADER=1 -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -ffreestanding -nostdlib -nostdinc -Wl,--build-id=none -Wl,-N -Wl,-Ttext,0x7c00 -m32 + +pc-chainloader.bin: pc-chainloader.elf + $(TARGET_OBJCOPY) -O binary --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .reginfo -R .rel.dyn -R .note.gnu.gold-version $< $@; + +ntldr.elf: $(srcdir)/grub-core/tests/boot/kernel-8086.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -DTARGET_NTLDR=1 -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -static -ffreestanding -nostdlib -nostdinc -Wl,--build-id=none -Wl,-N -Wl,-Ttext,0 -m32 + +ntldr.bin: ntldr.elf + $(TARGET_OBJCOPY) -O binary --strip-unneeded -j .text $< $@; + +multiboot2.elf: $(srcdir)/grub-core/tests/boot/kernel-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -static -o $@ $< -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" -ffreestanding -nostdlib -nostdinc -Wl,--build-id=none -Wl,-N -Wl,-Ttext,0x100000 -m32 -I$(srcdir)/include -DTARGET_MULTIBOOT2=1 + +kfreebsd.init.x86_64: $(srcdir)/grub-core/tests/boot/kfreebsd.init-x86_64.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m64 -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" && freebsd-brandelf -t FreeBSD $@ + +kfreebsd.init.i386: $(srcdir)/grub-core/tests/boot/kfreebsd.init-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m32 -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" && freebsd-brandelf -t FreeBSD $@ + +knetbsd.init.i386: $(srcdir)/grub-core/tests/boot/kbsd.init-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m32 -static -nostdlib -nostdinc -DTARGET_NETBSD=1 -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +kopenbsd.init.i386: $(srcdir)/grub-core/tests/boot/kbsd.init-i386.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m32 -static -nostdlib -nostdinc -DTARGET_OPENBSD=1 -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +knetbsd.init.x86_64: $(srcdir)/grub-core/tests/boot/kbsd.init-x86_64.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m64 -DTARGET_NETBSD=1 -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +kopenbsd.init.x86_64: $(srcdir)/grub-core/tests/boot/kbsd.init-x86_64.S $(srcdir)/grub-core/tests/boot/qemu-shutdown-x86.S + $(TARGET_CC) -o $@ $< -m64 -DTARGET_OPENBSD=1 -static -nostdlib -nostdinc -DSUCCESSFUL_BOOT_STRING=\"$(SUCCESSFUL_BOOT_STRING)\" + +linux-initramfs.mips: linux.init.mips Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +linux-initramfs.ppc: linux.init.ppc Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +linux-initramfs.mipsel: linux.init.mipsel Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +linux-initramfs.loongson: linux.init.loongson Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +linux-initramfs.i386: linux.init.i386 Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +linux-initramfs.x86_64: linux.init.x86_64 Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && cp $< $$TDIR/init && (cd $$TDIR && echo ./init | cpio -R 0:0 --quiet --dereference -o -H newc) | gzip > $@ && rm -rf $$TDIR + +kfreebsd-mfsroot.i386.img: kfreebsd.init.i386 Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -t ffs -s 30m -f 1000 -o minfree=0,version=1 $@ $$TDIR && rm -rf $$TDIR + +knetbsd.image.i386: knetbsd.init.i386 $(srcdir)/grub-core/tests/boot/kbsd.spec.txt + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -F $(srcdir)/grub-core/tests/boot/kbsd.spec.txt -t ffs -s 64k -f 10 -o minfree=0,version=1 $@ $$TDIR && rm -rf $$TDIR + +kopenbsd.image.i386: kopenbsd.init.i386 $(srcdir)/grub-core/tests/boot/kopenbsdlabel.txt + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -F $(srcdir)/grub-core/tests/boot/kbsd.spec.txt -t ffs -s 128k -f 10 -o minfree=0,version=1 $@ $$TDIR && bsdlabel -f -R $@ $(srcdir)/grub-core/tests/boot/kopenbsdlabel.txt && rm -rf $$TDIR || rm -f $@ + +kopenbsd.image.x86_64: kopenbsd.init.x86_64 $(srcdir)/grub-core/tests/boot/kopenbsdlabel.txt + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -F $(srcdir)/grub-core/tests/boot/kbsd.spec.txt -t ffs -s 128k -f 10 -o minfree=0,version=1 $@ $$TDIR && bsdlabel -f -R $@ $(srcdir)/grub-core/tests/boot/kopenbsdlabel.txt && rm -rf $$TDIR || rm -f $@ + +knetbsd.miniroot-image.i386.img: knetbsd.image.i386 $(GRUB_PAYLOADS_DIR)/knetbsd.miniroot.i386 + $(TARGET_OBJCOPY) --add-section=miniroot=$< $(GRUB_PAYLOADS_DIR)/knetbsd.miniroot.i386 $@ + +kfreebsd-mfsroot.x86_64.img: kfreebsd.init.x86_64 Makefile + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -t ffs -s 30m -f 1000 -o minfree=0,version=1 $@ $$TDIR && rm -rf $$TDIR + +knetbsd.image.x86_64: knetbsd.init.x86_64 $(srcdir)/grub-core/tests/boot/kbsd.spec.txt + TDIR=`mktemp -d "$${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` && mkdir $$TDIR/dev && mkdir $$TDIR/sbin && cp $< $$TDIR/sbin/init && makefs -F $(srcdir)/grub-core/tests/boot/kbsd.spec.txt -t ffs -s 64k -f 10 -o minfree=0,version=1 $@ $$TDIR && rm -rf $$TDIR + +knetbsd.miniroot-image.x86_64.img: knetbsd.image.x86_64 $(GRUB_PAYLOADS_DIR)/knetbsd.miniroot.x86_64 + $(TARGET_OBJCOPY) --add-section=miniroot=$< $(GRUB_PAYLOADS_DIR)/knetbsd.miniroot.x86_64 $@ + +CLEANFILES += linux.init.i386 kfreebsd.init.i386 linux.init.x86_64 linux-initramfs.i386 linux-initramfs.x86_64 + +kfreebsd-mfsroot.i386.gz: kfreebsd-mfsroot.i386.img + gzip < $< > $@ + +bootcheck-kfreebsd-i386: kfreebsd-mfsroot.i386.gz $(GRUB_PAYLOADS_DIR)/kfreebsd.i386 $(GRUB_PAYLOADS_DIR)/kfreebsd_env.i386 $(srcdir)/grub-core/tests/boot/kfreebsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/mfsroot.gz=kfreebsd-mfsroot.i386.gz --files=/kfreebsd=$(GRUB_PAYLOADS_DIR)/kfreebsd.i386 --files=/kfreebsd_env=$(GRUB_PAYLOADS_DIR)/kfreebsd_env.i386 $(srcdir)/grub-core/tests/boot/kfreebsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +kfreebsd-mfsroot.x86_64.gz: kfreebsd-mfsroot.x86_64.img + gzip < $< > $@ + +bootcheck-kfreebsd-x86_64: kfreebsd-mfsroot.x86_64.gz $(GRUB_PAYLOADS_DIR)/kfreebsd.x86_64 $(GRUB_PAYLOADS_DIR)/kfreebsd_env.x86_64 $(srcdir)/grub-core/tests/boot/kfreebsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=qemu-system-x86_64 --files=/mfsroot.gz=kfreebsd-mfsroot.x86_64.gz --files=/kfreebsd=$(GRUB_PAYLOADS_DIR)/kfreebsd.x86_64 --files=/kfreebsd_env=$(GRUB_PAYLOADS_DIR)/kfreebsd_env.x86_64 $(srcdir)/grub-core/tests/boot/kfreebsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +knetbsd.miniroot-image.i386.gz: knetbsd.miniroot-image.i386.img + gzip < $< > $@ + +bootcheck-knetbsd-i386: knetbsd.miniroot-image.i386.gz $(GRUB_PAYLOADS_DIR)/knetbsd.i386 $(srcdir)/grub-core/tests/boot/knetbsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/miniroot.gz=knetbsd.miniroot-image.i386.gz --files=/knetbsd=$(GRUB_PAYLOADS_DIR)/knetbsd.i386 $(srcdir)/grub-core/tests/boot/knetbsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-kopenbsd-i386: kopenbsd.image.i386 $(GRUB_PAYLOADS_DIR)/kopenbsd.i386 $(srcdir)/grub-core/tests/boot/kopenbsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/ramdisk=kopenbsd.image.i386 --files=/kopenbsd=$(GRUB_PAYLOADS_DIR)/kopenbsd.i386 $(srcdir)/grub-core/tests/boot/kopenbsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-kopenbsd-x86_64: kopenbsd.image.x86_64 $(GRUB_PAYLOADS_DIR)/kopenbsd.x86_64 $(srcdir)/grub-core/tests/boot/kopenbsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=qemu-system-x86_64 --files=/ramdisk=kopenbsd.image.x86_64 --files=/kopenbsd=$(GRUB_PAYLOADS_DIR)/kopenbsd.x86_64 $(srcdir)/grub-core/tests/boot/kopenbsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +knetbsd.miniroot-image.x86_64.gz: knetbsd.miniroot-image.x86_64.img + gzip < $< > $@ + +bootcheck-knetbsd-x86_64: knetbsd.miniroot-image.x86_64.gz $(GRUB_PAYLOADS_DIR)/knetbsd.x86_64 $(srcdir)/grub-core/tests/boot/knetbsd.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=qemu-system-x86_64 --files=/miniroot.gz=knetbsd.miniroot-image.x86_64.gz --files=/knetbsd=$(GRUB_PAYLOADS_DIR)/knetbsd.x86_64 $(srcdir)/grub-core/tests/boot/knetbsd.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-i386: linux-initramfs.i386 $(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/initrd=linux-initramfs.i386 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/grub-core/tests/boot/linux.cfg --qemu-opts="-cpu $(MINIMUM_CPU_LINUX)" | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-x86_64: linux-initramfs.x86_64 $(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=qemu-system-x86_64 --files=/initrd=linux-initramfs.x86_64 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/grub-core/tests/boot/linux.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-mips: linux-initramfs.mips $(GRUB_PAYLOADS_DIR)/linux.mips $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --files=/initrd=linux-initramfs.mips --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.mips $(srcdir)/grub-core/tests/boot/linux.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-ppc: linux-initramfs.ppc $(GRUB_PAYLOADS_DIR)/linux.ppc $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --files=/initrd=linux-initramfs.ppc --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.ppc $(srcdir)/grub-core/tests/boot/linux-ppc.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-mipsel: linux-initramfs.mipsel $(GRUB_PAYLOADS_DIR)/linux.mipsel $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --files=/initrd=linux-initramfs.mipsel --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.mipsel $(srcdir)/grub-core/tests/boot/linux.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux-loongson: linux-initramfs.loongson $(GRUB_PAYLOADS_DIR)/linux.loongson $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --files=/initrd=linux-initramfs.loongson --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.loongson $(srcdir)/grub-core/tests/boot/linux.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux16-i386: linux-initramfs.i386 $(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/initrd=linux-initramfs.i386 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.i386 $(srcdir)/grub-core/tests/boot/linux16.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-linux16-x86_64: linux-initramfs.x86_64 $(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/grub-core/tests/boot/linux.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=qemu-system-x86_64 --files=/initrd=linux-initramfs.x86_64 --files=/linux=$(GRUB_PAYLOADS_DIR)/linux.x86_64 $(srcdir)/grub-core/tests/boot/linux16.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-multiboot: multiboot.elf $(srcdir)/grub-core/tests/boot/multiboot.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/multiboot.elf=multiboot.elf $(srcdir)/grub-core/tests/boot/multiboot.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-multiboot2: multiboot2.elf $(srcdir)/grub-core/tests/boot/multiboot2.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/multiboot2.elf=multiboot2.elf $(srcdir)/grub-core/tests/boot/multiboot2.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-kfreebsd-aout: kfreebsd.aout $(srcdir)/grub-core/tests/boot/kfreebsd-aout.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/kfreebsd.aout=kfreebsd.aout $(srcdir)/grub-core/tests/boot/kfreebsd-aout.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-pc-chainloader: pc-chainloader.bin $(srcdir)/grub-core/tests/boot/pc-chainloader.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/pc-chainloader.bin=pc-chainloader.bin $(srcdir)/grub-core/tests/boot/pc-chainloader.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +bootcheck-ntldr: ntldr.bin $(srcdir)/grub-core/tests/boot/ntldr.cfg grub-shell + ./grub-shell --timeout=$(BOOTCHECK_TIMEOUT) --qemu=$(QEMU32) --files=/ntldr.bin=ntldr.bin $(srcdir)/grub-core/tests/boot/ntldr.cfg | grep $(SUCCESSFUL_BOOT_STRING) > /dev/null + +if COND_i386_efi +# NetBSD has no support for finding ACPI on EFI +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-kopenbsd-i386 bootcheck-kopenbsd-x86_64 bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 bootcheck-kfreebsd-x86_64 bootcheck-kfreebsd-i386 +endif + +if COND_x86_64_efi +# NetBSD has no support for finding ACPI on EFI +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-kopenbsd-i386 bootcheck-kopenbsd-x86_64 bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 bootcheck-kfreebsd-x86_64 bootcheck-kfreebsd-i386 +endif + +if COND_i386_multiboot +# *BSD requires ACPI +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 +endif + + +if COND_i386_qemu +# *BSD requires ACPI +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 +endif + +if COND_i386_coreboot +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-kopenbsd-i386 bootcheck-kopenbsd-x86_64 bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 bootcheck-knetbsd-x86_64 bootcheck-kfreebsd-x86_64 bootcheck-kfreebsd-i386 +endif + +if COND_i386_ieee1275 +# *BSD requires ACPI +#legacy protocol (linux16) makes early BIOS calls. +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 +endif + +if COND_i386_pc +#pc chainloader by definition is only for i386-pc +#ntldr and bootmgr require BIOS. +#legacy protocol (linux16) makes early BIOS calls. +# 32-bit NetBSD crashes early on non-BIOS +BOOTCHECKS = bootcheck-kfreebsd-aout bootcheck-kopenbsd-i386 bootcheck-kopenbsd-x86_64 bootcheck-multiboot bootcheck-multiboot2 bootcheck-linux-i386 bootcheck-linux-x86_64 bootcheck-knetbsd-x86_64 bootcheck-kfreebsd-x86_64 bootcheck-kfreebsd-i386 bootcheck-pc-chainloader bootcheck-ntldr bootcheck-linux16-i386 bootcheck-linux16-x86_64 bootcheck-knetbsd-i386 +endif + +if COND_mips_loongson +BOOTCHECKS = bootcheck-linux-loongson +endif + +if COND_mipsel +if COND_mips_qemu_mips +BOOTCHECKS = bootcheck-linux-mipsel +endif +endif +if COND_mipseb +if COND_mips_qemu_mips +BOOTCHECKS = bootcheck-linux-mips +endif +endif + +if COND_powerpc_ieee1275 +BOOTCHECKS = bootcheck-linux-ppc +endif + +.PHONY: bootcheck-linux-i386 bootcheck-linux-x86_64 \ + bootcheck-kfreebsd-i386 bootcheck-kfreebsd-x86_64 \ + bootcheck-knetbsd-i386 bootcheck-knetbsd-x86_64 \ + bootcheck-linux-mips FORCE + +# Randomly generated +SUCCESSFUL_BOOT_STRING=3e49994fd5d82b7c9298d672d774080d +# tianocore cd access is very slow +BOOTCHECK_TIMEOUT=180 + +bootcheck: $(BOOTCHECKS) + +if COND_i386_coreboot +FS_PAYLOAD_MODULES ?= $(shell cat grub-core/fs.lst) +default_payload.elf: grub-mkstandalone grub-mkimage FORCE + test -f $@ && rm $@ || true + pkgdatadir=. ./grub-mkstandalone --grub-mkimage=./grub-mkimage -O i386-coreboot -o $@ --modules='ahci pata ehci uhci ohci usb_keyboard usbms part_msdos ext2 fat at_keyboard part_gpt usbserial_usbdebug cbfs' --install-modules='ls linux search configfile normal cbtime cbls memrw iorw minicmd lsmmap lspci halt reboot hexdump pcidump regexp setpci lsacpi chain test serial multiboot cbmemc linux16 gzio echo help syslinuxcfg xnu $(FS_PAYLOAD_MODULES) password_pbkdf2 $(EXTRA_PAYLOAD_MODULES)' --fonts= --themes= --locales= -d grub-core/ /boot/grub/grub.cfg=$(srcdir)/coreboot.cfg +endif + +endif + +EXTRA_DIST += grub-core/tests/boot/kbsd.init-i386.S grub-core/tests/boot/kbsd.init-x86_64.S grub-core/tests/boot/kbsd.spec.txt grub-core/tests/boot/kernel-8086.S grub-core/tests/boot/kernel-i386.S grub-core/tests/boot/kfreebsd-aout.cfg grub-core/tests/boot/kfreebsd.cfg grub-core/tests/boot/kfreebsd.init-i386.S grub-core/tests/boot/kfreebsd.init-x86_64.S grub-core/tests/boot/knetbsd.cfg grub-core/tests/boot/kopenbsd.cfg grub-core/tests/boot/kopenbsdlabel.txt grub-core/tests/boot/linux16.cfg grub-core/tests/boot/linux.cfg grub-core/tests/boot/linux.init-i386.S grub-core/tests/boot/linux.init-mips.S grub-core/tests/boot/linux.init-ppc.S grub-core/tests/boot/linux.init-x86_64.S grub-core/tests/boot/linux-ppc.cfg grub-core/tests/boot/multiboot2.cfg grub-core/tests/boot/multiboot.cfg grub-core/tests/boot/ntldr.cfg grub-core/tests/boot/pc-chainloader.cfg grub-core/tests/boot/qemu-shutdown-x86.S + +windowsdir=$(top_builddir)/$(PACKAGE)-$(VERSION)-for-windows +windowsdir: $(PROGRAMS) $(starfield_DATA) $(platform_DATA) + test -d $(windowsdir) && rm -rf $(windowsdir) || true + test -d $(windowsdir) || mkdir $(windowsdir) + $(MAKE) -C po $(AM_MAKEFLAGS) windowsdir + $(MAKE) -C grub-core $(AM_MAKEFLAGS) windowsdir + test -d $(windowsdir)/themes || mkdir $(windowsdir)/themes + test -d $(windowsdir)/themes/starfield || mkdir $(windowsdir)/themes/starfield + for x in $(PROGRAMS); do \ + if [ x$(STRIP) != x ]; then $(STRIP) $$x -o $(windowsdir)/$$x; \ + else cp -fp $$x $(windowsdir)/$$x; fi; \ + done + for x in $(pkgdata_DATA); do \ + cp -fp $$x $(windowsdir)/$$x; \ + done + for x in $(starfield_DATA); do \ + cp -fp $$x $(windowsdir)/themes/starfield/$$(basename $$x); \ + done + for x in $(GRUB_WINDOWS_EXTRA_DIST); do \ + cp -fp $$x $(windowsdir); \ + done + +windowszip=$(top_builddir)/$(PACKAGE)-$(VERSION)-for-windows.zip +windowszip: windowsdir + test -f $(windowszip) && rm $(windowszip) || true + zip -r $(windowszip) $(windowsdir) + rm -rf $(windowsdir) + +EXTRA_DIST += linguas.sh + +changelog_start_date = 2015-01-23 +gitlog_to_changelog = $(top_srcdir)/build-aux/gitlog-to-changelog + +ChangeLog: FORCE + if test -d $(top_srcdir)/.git; then \ + $(gitlog_to_changelog) --srcdir=$(top_srcdir) --since=$(changelog_start_date) > '$@.tmp'; \ + rm -f '$@'; mv '$@.tmp' '$@'; \ + else \ + touch $@; \ + fi + +syslinux_test: $(top_builddir)/config.status tests/syslinux/ubuntu10.04_grub.cfg + +# Mimic simplify_filename from grub-core/lib/syslinux_parse.c, so that we +# can predict its behaviour in tests. We have to pre-substitute this before +# calling config.status, as config.status offers no reliable way to hook in +# a command between setting ac_abs_top_srcdir and emitting output files. +tests/syslinux/ubuntu10.04_grub.cfg: $(top_builddir)/config.status tests/syslinux/ubuntu10.04_grub.cfg.in + simplified_abs_top_srcdir=`echo "$(abs_top_srcdir)" | sed 's,//,/,g; s,/\./,/,g; :loop; s,/[^/][^/]*/\.\.\(/\|$$\),\1,; t loop'`; \ + sed "s,@simplified_abs_top_srcdir@,$$simplified_abs_top_srcdir,g" $(srcdir)/tests/syslinux/ubuntu10.04_grub.cfg.in | $(top_builddir)/config.status --file=$@:- +CLEANFILES += tests/syslinux/ubuntu10.04_grub.cfg diff --git a/Makefile.in b/Makefile.in deleted file mode 100644 index 47584cdde..000000000 --- a/Makefile.in +++ /dev/null @@ -1,544 +0,0 @@ -# -*- makefile -*- -# -# Copyright (C) 1994,1995,1996,1997,1998,1999,2000,2001,2002,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. -# -# This Makefile.in is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -### The configure script will replace these variables. - -SHELL = /bin/sh - -@SET_MAKE@ - -transform = @program_transform_name@ - -srcdir = @srcdir@ -builddir = @builddir@ -top_srcdir = @top_srcdir@ -VPATH = @srcdir@ -prefix = @prefix@ -exec_prefix = @exec_prefix@ - -bindir = @bindir@ -sbindir = @sbindir@ -libexecdir = @libexecdir@ -datarootdir = @datarootdir@ -datadir = @datadir@ -sysconfdir = @sysconfdir@ -sharedstatedir = @sharedstatedir@ -localstatedir = @localstatedir@ -libdir = @libdir@ -infodir = @infodir@ -mandir = @mandir@ -includedir = @includedir@ -pkgdatadir = $(datadir)/`echo @PACKAGE_TARNAME@ | sed '$(transform)'` -pkglibdir = $(libdir)/`echo @PACKAGE_TARNAME@/$(target_cpu)-$(platform) | sed '$(transform)'` - -# Internationalization library. -LIBINTL = @LIBINTL@ - -XGETTEXT = @XGETTEXT@ -MSGMERGE = @MSGMERGE@ -MSGFMT = @MSGFMT@ - -LINGUAS = $(shell for i in $(srcdir)/po/*.po ; do \ - if test -e $$i ; then echo $$i ; fi ; \ - done | sed -e "s,.*/po/\(.*\)\.po$$,\1,") - -PACKAGE = @PACKAGE@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_TARNAME = @PACKAGE_TARNAME@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -PACKAGE_STRING = @PACKAGE_STRING@ -PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ - -host_os = @host_os@ -host_kernel = @host_kernel@ -host_cpu = @host_cpu@ - -target_cpu = @target_cpu@ -platform = @platform@ - -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_DATA = @INSTALL_DATA@ -INSTALL_SCRIPT = @INSTALL_SCRIPT@ -INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ -MKDIR_P = @MKDIR_P@ - -mkinstalldirs = $(srcdir)/mkinstalldirs - -LIBS = @LIBS@ $(LIBINTL) - -CC = @CC@ -CFLAGS = @CFLAGS@ -ASFLAGS = @ASFLAGS@ -LDFLAGS = @LDFLAGS@ $(LIBS) -CPPFLAGS = @CPPFLAGS@ -I$(builddir) -I$(builddir)/include -I$(srcdir)/gnulib -I$(srcdir)/include -Wall -W \ - -DGRUB_LIBDIR=\"$(pkglibdir)\" -DLOCALEDIR=\"$(localedir)\" -TARGET_CC = @TARGET_CC@ -TARGET_CFLAGS = @TARGET_CFLAGS@ -TARGET_ASFLAGS = @TARGET_ASFLAGS@ -TARGET_MODULE_FORMAT = @TARGET_MODULE_FORMAT@ -TARGET_APPLE_CC = @TARGET_APPLE_CC@ -OBJCONV = @OBJCONV@ -TARGET_CPPFLAGS = @TARGET_CPPFLAGS@ -I$(srcdir)/include -I$(builddir) -I$(builddir)/include \ - -Wall -W -TARGET_LDFLAGS = @TARGET_LDFLAGS@ -TARGET_IMG_LDSCRIPT = @TARGET_IMG_LDSCRIPT@ -TARGET_IMG_LDFLAGS = @TARGET_IMG_LDFLAGS@ -TARGET_IMG_CFLAGS = @TARGET_IMG_CFLAGS@ -TARGET_OBJ2ELF = @TARGET_OBJ2ELF@ -EXEEXT = @EXEEXT@ -OBJCOPY = @OBJCOPY@ -STRIP = @STRIP@ -NM = @NM@ -RUBY = @RUBY@ -MAKEINFO = @MAKEINFO@ -ifeq (, $(MAKEINFO)) -MAKEINFO = true -endif -HELP2MAN = @HELP2MAN@ -ifeq (, $(HELP2MAN)) -HELP2MAN = true -else -HELP2MAN := LANG=C $(HELP2MAN) --no-info --source=FSF -endif -AWK = @AWK@ -LIBCURSES = @LIBCURSES@ -LIBUSB = @LIBUSB@ -LIBSDL = @LIBSDL@ -LIBPCIACCESS = @LIBPCIACCESS@ -YACC = @YACC@ -FONT_SOURCE = @FONT_SOURCE@ - -# Options. -enable_grub_emu_usb = @enable_grub_emu_usb@ -enable_grub_emu_sdl = @enable_grub_emu_sdl@ -enable_grub_emu_pci = @enable_grub_emu_pci@ -enable_grub_fstest = @enable_grub_fstest@ -enable_grub_pe2elf = @enable_grub_pe2elf@ -enable_grub_mkfont = @enable_grub_mkfont@ -freetype_cflags = @freetype_cflags@ -freetype_libs = @freetype_libs@ -enable_efiemu = @enable_efiemu@ - -### General variables. - -RMKFILES = $(wildcard conf/*.rmk) - -MKFILES = $(patsubst %.rmk,%.mk,$(RMKFILES)) - -PKGLIB = $(pkglib_IMAGES) $(pkglib_MODULES) $(pkglib_PROGRAMS) \ - $(pkglib_DATA) $(pkglib_BUILDDIR) -PKGDATA = $(pkgdata_DATA) -PROGRAMS = $(bin_UTILITIES) $(sbin_UTILITIES) -SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS) $(grub-mkconfig_SCRIPTS) \ - $(lib_SCRIPTS) -INFOS = $(info_INFOS) - -CLEANFILES = -MOSTLYCLEANFILES = -DISTCLEANFILES = config.status config.cache config.log config.h \ - Makefile stamp-h stamp-h1 include/grub/cpu include/grub/machine \ - gensymlist.sh genkernsyms.sh build_env.mk \ - docs/grub.info docs/version.texi docs/stamp-vti - -MAINTAINER_CLEANFILES = $(srcdir)/configure $(addprefix $(srcdir)/,$(MKFILES)) \ - $(srcdir)/DISTLIST $(srcdir)/config.h.in $(srcdir)/stamp-h.in $(INFOS) - -# The default target. -all: all-local - -### Include an arch-specific Makefile. -$(addprefix $(srcdir)/,$(MKFILES)): %.mk: %.rmk genmk.rb - if test "x$(RUBY)" = x; then \ - touch $@; \ - else \ - $(RUBY) $(srcdir)/genmk.rb < $< > $@; \ - fi - -ifeq ($(platform), emu) -include $(srcdir)/conf/any-emu.mk -else -include $(srcdir)/conf/$(target_cpu)-$(platform).mk -# For tests. -include $(srcdir)/conf/tests.mk -# For external modules. --include $(wildcard $(GRUB_CONTRIB)/*/conf/common.mk) -endif - -### General targets. - -CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA) po/*.mo -pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst crypto.lst terminal.lst -moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk - cat $(DEFSYMFILES) /dev/null \ - | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \ - || (rm -f $@; exit 1) - -command.lst: $(COMMANDFILES) - cat $^ /dev/null | sort > $@ - -fs.lst: $(FSFILES) - cat $^ /dev/null | sort > $@ - -partmap.lst: $(PARTMAPFILES) - cat $^ /dev/null | sort > $@ - -handler.lst: $(HANDLERFILES) - cat $^ /dev/null | sort > $@ - -terminal.lst: $(TERMINALFILES) - cat $^ /dev/null | sort > $@ - -parttool.lst: $(PARTTOOLFILES) - cat $^ /dev/null | sort | uniq > $@ - -video.lst: $(VIDEOFILES) - cat $^ /dev/null | sort | uniq > $@ - -crypto.lst: lib/libgcrypt-grub/cipher/crypto.lst - cp $^ $@ - -ifneq (true, $(MAKEINFO)) -info_INFOS += docs/grub.info -endif - -MOSTLYCLEANFILES += vti.tmp -MAINTAINER_CLEANFILES += docs/stamp-vti docs/version.texi -docs/version.texi: docs/stamp-vti -docs/stamp-vti: docs/grub.texi configure.ac - $(MKDIR_P) docs - (set `$(SHELL) $(srcdir)/docs/mdate-sh $<`; \ - echo "@set UPDATED $$1 $$2 $$3"; \ - echo "@set UPDATED-MONTH $$2 $$3"; \ - echo "@set EDITION $(PACKAGE_VERSION)"; \ - echo "@set VERSION $(PACKAGE_VERSION)") > vti.tmp - @cmp -s vti.tmp $(builddir)/docs/version.texi \ - || (echo "Updating $(builddir)/docs/version.texi"; \ - cp vti.tmp $(builddir)/docs/version.texi) - -@rm -f vti.tmp - @cp $(builddir)/docs/version.texi $@ - -# Use --force until such time as the documentation is cleaned up. -docs/grub.info: docs/grub.texi docs/version.texi docs/fdl.texi - $(MKDIR_P) docs - -$(MAKEINFO) -P $(builddir)/docs --no-split --force $< -o $@ - -ifeq (, $(FONT_SOURCE)) -else - -ifeq ($(enable_grub_mkfont),yes) - -pkgdata_DATA += unicode.pf2 ascii.pf2 ascii.h -CLEANFILES += ascii.bitmaps - -# Arrows and lines are needed to draw the menu, so we always include them -UNICODE_ARROWS=0x2190-0x2193 -UNICODE_LINES=0x2501-0x251B - -unicode.pf2: $(FONT_SOURCE) grub-mkfont - $(builddir)/grub-mkfont -o $@ $(FONT_SOURCE) - -ascii.pf2: $(FONT_SOURCE) grub-mkfont - $(builddir)/grub-mkfont -o $@ $(FONT_SOURCE) -r 0x0-0x7f,$(UNICODE_ARROWS),$(UNICODE_LINES) - -ascii.bitmaps: $(FONT_SOURCE) grub-mkfont - $(builddir)/grub-mkfont --ascii-bitmaps -o $@ $(FONT_SOURCE) - -ascii.h: ascii.bitmaps grub-bin2h - $(builddir)/grub-bin2h ascii_bitmaps < $< > $@ - -TARGET_CFLAGS += -DUSE_ASCII_FAILBACK=1 -endif -endif - -# Used for building modules externally -pkglib_BUILDDIR += build_env.mk -build_env.mk: Makefile - (\ - echo "TARGET_CC=$(TARGET_CC)" ; \ - echo "TARGET_CFLAGS=$(TARGET_CFLAGS)" ; \ - echo "TARGET_ASFLAGS=$(TARGET_ASFLAGS)" ; \ - echo "TARGET_CPPFLAGS=$(TARGET_CPPFLAGS) -I$(pkglibdir) -I$(includedir)" ; \ - echo "STRIP=$(STRIP)" ; \ - echo "OBJCONV=$(OBJCONV)" ; \ - echo "TARGET_MODULE_FORMAT=$(TARGET_MODULE_FORMAT)" ; \ - echo "TARGET_APPLE_CC=$(TARGET_APPLE_CC)" ; \ - echo "COMMON_ASFLAGS=$(COMMON_ASFLAGS)" ; \ - echo "COMMON_CFLAGS=$(COMMON_CFLAGS)" ; \ - echo "COMMON_LDFLAGS=$(COMMON_LDFLAGS)"\ - ) > $@ -pkglib_BUILDDIR += config.h grub_script.tab.h - -all-local: $(PROGRAMS) $(PKGLIB) $(PKGDATA) $(SCRIPTS) $(INFOS) $(MKFILES) $(foreach lang, $(LINGUAS), po/$(lang).mo) - -install: install-local - -install-local: all - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(pkglibdir) - rm -f $(DESTDIR)$(pkglibdir)/* - @list='$(PKGLIB)'; \ - for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(pkglibdir)/$$dest; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) - @list='$(PKGDATA)'; \ - for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(pkgdatadir)/$$dest; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 - @list='$(bin_UTILITIES)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_PROGRAM) $$dir$$file $(DESTDIR)$(bindir)/$$dest; \ - $(HELP2MAN) --section=1 -o $(DESTDIR)$(mandir)/man1/$$dest.1 $(builddir)/$$file; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man8 - @list='$(sbin_UTILITIES)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_PROGRAM) $$dir$$file $(DESTDIR)$(sbindir)/$$dest; \ - $(HELP2MAN) --section=8 -o $(DESTDIR)$(mandir)/man8/$$dest.8 $(builddir)/$$file; \ - done - @list='$(bin_SCRIPTS)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_SCRIPT) $$dir$$file $(DESTDIR)$(bindir)/$$dest; \ - $(HELP2MAN) --section=1 -o $(DESTDIR)$(mandir)/man1/$$dest.1 $(builddir)/$$file; \ - done - @list='$(sbin_SCRIPTS)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_SCRIPT) $$dir$$file $(DESTDIR)$(sbindir)/$$dest; \ - $(HELP2MAN) --section=8 -o $(DESTDIR)$(mandir)/man8/$$dest.8 $(builddir)/$$file; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(sysconfdir)/grub.d - @list='$(grub-mkconfig_SCRIPTS)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_SCRIPT) $$dir$$file $(DESTDIR)$(sysconfdir)/grub.d/$$dest; \ - done - @list='$(grub-mkconfig_DATA)'; for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(sysconfdir)/grub.d/$$dest; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(libdir)/grub - @list='$(lib_SCRIPTS)'; \ - for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(libdir)/grub/$$dest; \ - done - @langs='$(LINGUAS)'; \ - for lang in $$langs; do \ - $(SHELL) $(mkinstalldirs) $(DESTDIR)/$(datadir)/locale/$$lang/LC_MESSAGES; \ - file="po/$$lang.mo"; \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)/$(datadir)/locale/$$lang/LC_MESSAGES/$(PACKAGE).mo; \ - done - $(SHELL) $(mkinstalldirs) $(DESTDIR)$(infodir) - @list='$(info_INFOS)'; \ - for file in $$list; do \ - if test -f "$$file"; then dir=; else dir="$(srcdir)/"; fi; \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - $(INSTALL_DATA) $$dir$$file $(DESTDIR)$(infodir); \ - if (install-info --version && \ - install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ - install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$$dest" || :; \ - fi; \ - done - -install-strip: - $(MAKE) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" install - -uninstall: - @list='$(PKGLIB)'; \ - for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - rm -f $(DESTDIR)$(pkglibdir)/$$dest; \ - done - @list='$(PKGDATA)'; \ - for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - rm -f $(DESTDIR)$(pkgdatadir)/$$dest; \ - done - @list='$(bin_UTILITIES) $(bin_SCRIPTS)'; for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - rm -f $(DESTDIR)$(bindir)/$$dest; \ - rm -f $(DESTDIR)$(mandir)/man1/$$dest.1; \ - done - @list='$(sbin_UTILITIES) $(sbin_SCRIPTS)'; for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - rm -f $(DESTDIR)$(sbindir)/$$dest; \ - rm -f $(DESTDIR)$(mandir)/man8/$$dest.8; \ - done - @list='$(grub-mkconfig_SCRIPTS) $(grub-mkconfig_DATA)'; for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,' | sed '$(transform)'`"; \ - rm -f $(DESTDIR)$(sysconfdir)/grub.d/$$dest; \ - done - @list='$(lib_SCRIPTS)'; \ - for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - echo rm -f $(DESTDIR)$(libdir)/$$dest; \ - rm -f $(DESTDIR)$(libdir)/grub/$$dest; \ - done - @list='$(info_INFOS)'; \ - for file in $$list; do \ - dest="`echo $$file | sed 's,.*/,,'`"; \ - if (install-info --version && \ - install-info --version 2>&1 | sed 1q | grep -i -v debian) >/dev/null 2>&1; then \ - if install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$$dest"; then \ - :; \ - else \ - test ! -f "$(DESTDIR)$(infodir)/$$dest" || exit 1; \ - fi; \ - fi; \ - rm -f $(DESTDIR)$(infodir)/$$dest; \ - done - -clean: $(CLEAN_IMAGE_TARGETS) $(CLEAN_MODULE_TARGETS) $(CLEAN_UTILITY_TARGETS) - -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) - -mostlyclean: clean $(MOSTLYCLEAN_IMAGE_TARGETS) $(MOSTLYCLEAN_MODULE_TARGETS) $(MOSTLYCLEAN_UTILITY_TARGETS) - -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) - -distclean: mostlyclean - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) - -rm -rf $(srcdir)/autom4te.cache - -maintainer-clean: distclean - -test -z "$(MAINTAINER_CLEANFILES)" || rm -f $(MAINTAINER_CLEANFILES) - -info: - -dvi: - -distdir=$(PACKAGE_TARNAME)-$(PACKAGE_VERSION) - -DISTLIST: gendistlist.sh - $(SHELL) $(srcdir)/gendistlist.sh > $(srcdir)/DISTLIST - -distdir: DISTLIST - -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) - $(SHELL) $(mkinstalldirs) $(distdir) - for i in `cat $(srcdir)/DISTLIST`; do \ - dir=`echo "$$i" | sed 's:/[^/]*$$::'`; \ - if test -d $(srcdir)/$$dir; then \ - $(SHELL) $(mkinstalldirs) $(distdir)/$$dir; \ - fi; \ - cp -p $(srcdir)/$$i $(distdir)/$$i || exit 1; \ - done - chmod -R a+r $(distdir) - -GZIP_ENV = --best - -dist: distdir - tar chof - $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz - -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) - -distcheck: dist - -chmod -R a+w $(distdir) >/dev/null 2>&1; rm -rf $(distdir) - GZIP=$(GZIP_ENV) gzip -cd $(distdir).tar.gz | tar xf - - chmod -R a-w $(distdir) - chmod a+w $(distdir) - mkdir $(distdir)/=build - mkdir $(distdir)/=inst - chmod a-w $(distdir) - dc_instdir=`CDPATH=: && cd $(distdir)/=inst && pwd` \ - && cd $(distdir)/=build \ - && $(SHELL) ../configure --srcdir=.. --prefix=$$dc_instdir \ - && $(MAKE) all dvi check install && $(MAKE) uninstall \ - && (test `find $$dc_instdir -type f -print | wc -l` -le 1 \ - || (echo "Error: files left after uninstall" 1>&2; \ - exit 1)) \ - && $(MAKE) dist && $(MAKE) distclean \ - && rm -f $(distdir).tar.gz \ - && (test `find . -type f -print | wc -l` -eq 0 \ - || (echo "Error: files left after distclean" 1>&2; \ - exit 1)) - -chmod -R a+w $(distdir) > /dev/null 2>&1; rm -rf $(distdir) - @echo "$(distdir).tar.gz is ready for distribution" | \ - sed 'h;s/./=/g;p;x;p;x' - -check: all $(UNIT_TESTS) $(FUNCTIONAL_TESTS) $(SCRIPTED_TESTS) - @list="$(UNIT_TESTS)"; \ - set -e; \ - for file in $$list; do \ - $(builddir)/$$file; \ - done - @list="$(FUNCTIONAL_TESTS)"; \ - set -e; \ - for file in $$list; do \ - mod=`basename $$file .mod`; \ - echo "insmod functional_test; insmod $$mod; functional_test" \ - | $(builddir)/grub-shell; \ - done - @list="$(SCRIPTED_TESTS)"; \ - set -e; \ - for file in $$list; do \ - $(builddir)/$$file; \ - done - -.SUFFIX: -.SUFFIX: .c .o .S .d - -# Regenerate configure and Makefile automatically. -$(srcdir)/aclocal.m4: configure.ac acinclude.m4 - cd $(srcdir) && aclocal - -$(srcdir)/configure: configure.ac aclocal.m4 - cd $(srcdir) && autoconf - -$(srcdir)/config.h.in: stamp-h.in -$(srcdir)/stamp-h.in: configure.ac aclocal.m4 - cd $(srcdir) && autoheader - echo timestamp > $(srcdir)/stamp-h.in - -config.h: stamp-h -stamp-h: config.h.in config.status - $(SHELL) ./config.status - -Makefile: Makefile.in config.status - $(SHELL) ./config.status - -config.status: configure - $(SHELL) ./config.status --recheck - -gensymlist.sh: gensymlist.sh.in config.status - $(SHELL) ./config.status - -genkernsyms.sh: genkernsyms.sh.in config.status - $(SHELL) ./config.status - -$(srcdir)/po/$(PACKAGE).pot: po/POTFILES po/POTFILES-shell - cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o $@ -f $< --keyword=_ --keyword=N_ - cd $(srcdir) && $(XGETTEXT) -ctranslate --from-code=utf-8 -o $@ -f po/POTFILES-shell -j --language=Shell - -$(foreach lang, $(LINGUAS), $(srcdir)/po/$(lang).po): po/$(PACKAGE).pot - $(MSGMERGE) -U $@ $^ - -po/%.mo: po/%.po - $(MKDIR_P) $$(dirname $@) - $(MSGFMT) -c --statistics -o $@ $^ - -.PHONY: all install install-strip uninstall clean mostlyclean distclean -.PHONY: maintainer-clean info dvi dist check - -# Prevent an overflow. -.NOEXPORT: - -.DELETE_ON_ERROR: diff --git a/Makefile.util.def b/Makefile.util.def new file mode 100644 index 000000000..038253b37 --- /dev/null +++ b/Makefile.util.def @@ -0,0 +1,1470 @@ +AutoGen definitions Makefile.tpl; + +library = { + name = libgrubkern.a; + cflags = '$(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_GNULIB) -I$(srcdir)/grub-core/lib/json'; + + common = util/misc.c; + common = grub-core/kern/command.c; + common = grub-core/kern/device.c; + common = grub-core/kern/disk.c; + common = grub-core/lib/disk.c; + common = util/getroot.c; + common = grub-core/osdep/unix/getroot.c; + common = grub-core/osdep/getroot.c; + common = grub-core/osdep/devmapper/getroot.c; + common = grub-core/osdep/relpath.c; + extra_dist = grub-core/kern/disk_common.c; + extra_dist = grub-core/osdep/unix/relpath.c; + extra_dist = grub-core/osdep/aros/relpath.c; + extra_dist = grub-core/osdep/windows/relpath.c; + common = grub-core/kern/emu/hostdisk.c; + common = grub-core/osdep/devmapper/hostdisk.c; + common = grub-core/osdep/hostdisk.c; + common = grub-core/osdep/unix/hostdisk.c; + common = grub-core/osdep/exec.c; + common = grub-core/osdep/sleep.c; + common = grub-core/osdep/password.c; + common = grub-core/kern/emu/misc.c; + common = grub-core/kern/emu/mm.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/misc.c; + common = grub-core/kern/partition.c; + common = grub-core/lib/crypto.c; + common = grub-core/lib/json/json.c; + common = grub-core/disk/luks.c; + common = grub-core/disk/luks2.c; + common = grub-core/disk/geli.c; + common = grub-core/disk/key_protector.c; + common = grub-core/disk/cryptodisk.c; + common = grub-core/disk/AFSplitter.c; + common = grub-core/lib/pbkdf2.c; + common = grub-core/commands/extcmd.c; + common = grub-core/lib/arg.c; + common = grub-core/disk/ldm.c; + common = grub-core/disk/diskfilter.c; + common = grub-core/partmap/gpt.c; + common = grub-core/partmap/msdos.c; + common = grub-core/fs/proc.c; + common = grub-core/fs/archelp.c; +}; + +library = { + name = libgrubmods.a; + cflags = '-fno-builtin -Wno-undef -Wno-unused-but-set-variable'; + cppflags = '-I$(srcdir)/grub-core/lib/minilzo -I$(srcdir)/grub-core/lib/xzembed -I$(srcdir)/grub-core/lib/zstd -DMINILZO_HAVE_CONFIG_H'; + + 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/commands/blocklist.c; + common = grub-core/commands/macbless.c; + common = grub-core/commands/xnu_uuid.c; + common = grub-core/commands/testload.c; + common = grub-core/commands/ls.c; + common = grub-core/disk/dmraid_nvidia.c; + common = grub-core/disk/loopback.c; + common = grub-core/disk/lvm.c; + common = grub-core/disk/mdraid_linux.c; + common = grub-core/disk/mdraid_linux_be.c; + common = grub-core/disk/mdraid1x_linux.c; + common = grub-core/disk/raid5_recover.c; + common = grub-core/disk/raid6_recover.c; + common = grub-core/font/font.c; + common = grub-core/gfxmenu/font.c; + common = grub-core/normal/charset.c; + common = grub-core/video/fb/fbblit.c; + common = grub-core/video/fb/fbutil.c; + common = grub-core/video/fb/fbfill.c; + common = grub-core/video/fb/video_fb.c; + common = grub-core/video/video.c; + common = grub-core/video/capture.c; + common = grub-core/video/colors.c; + common = grub-core/unidata.c; + common = grub-core/io/bufio.c; + common = grub-core/fs/affs.c; + common = grub-core/fs/afs.c; + common = grub-core/fs/bfs.c; + common = grub-core/fs/btrfs.c; + common = grub-core/fs/cbfs.c; + common = grub-core/fs/cpio.c; + common = grub-core/fs/cpio_be.c; + common = grub-core/fs/odc.c; + common = grub-core/fs/newc.c; + common = grub-core/fs/erofs.c; + common = grub-core/fs/ext2.c; + common = grub-core/fs/fat.c; + common = grub-core/fs/exfat.c; + common = grub-core/fs/f2fs.c; + common = grub-core/fs/fshelp.c; + common = grub-core/fs/hfs.c; + common = grub-core/fs/hfsplus.c; + common = grub-core/fs/hfspluscomp.c; + common = grub-core/fs/iso9660.c; + common = grub-core/fs/jfs.c; + common = grub-core/fs/minix.c; + common = grub-core/fs/minix2.c; + common = grub-core/fs/minix3.c; + common = grub-core/fs/minix_be.c; + common = grub-core/fs/minix2_be.c; + common = grub-core/fs/minix3_be.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/romfs.c; + common = grub-core/fs/sfs.c; + common = grub-core/fs/squash4.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/ufs_be.c; + common = grub-core/fs/xfs.c; + common = grub-core/fs/zfs/zfscrypt.c; + common = grub-core/fs/zfs/zfs.c; + common = grub-core/fs/zfs/zfsinfo.c; + common = grub-core/fs/zfs/zfs_lzjb.c; + common = grub-core/fs/zfs/zfs_lz4.c; + common = grub-core/fs/zfs/zfs_sha256.c; + common = grub-core/fs/zfs/zfs_fletcher.c; + common = grub-core/lib/envblk.c; + common = grub-core/lib/hexdump.c; + common = grub-core/lib/LzFind.c; + common = grub-core/lib/LzmaEnc.c; + common = grub-core/lib/crc.c; + common = grub-core/lib/adler32.c; + common = grub-core/lib/crc64.c; + common = grub-core/lib/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/sun.c; + common = grub-core/partmap/plan.c; + common = grub-core/partmap/dvh.c; + common = grub-core/partmap/sunpc.c; + common = grub-core/partmap/bsdlabel.c; + common = grub-core/partmap/dfly.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; + common = grub-core/io/gzio.c; + common = grub-core/io/xzio.c; + common = grub-core/io/lzopio.c; + common = grub-core/kern/ia64/dl_helper.c; + common = grub-core/kern/arm/dl_helper.c; + common = grub-core/kern/arm64/dl_helper.c; + common = grub-core/kern/loongarch64/dl_helper.c; + common = grub-core/lib/minilzo/minilzo.c; + common = grub-core/lib/xzembed/xz_dec_bcj.c; + common = grub-core/lib/xzembed/xz_dec_lzma2.c; + common = grub-core/lib/xzembed/xz_dec_stream.c; + common = grub-core/lib/zstd/debug.c; + common = grub-core/lib/zstd/entropy_common.c; + common = grub-core/lib/zstd/error_private.c; + common = grub-core/lib/zstd/fse_decompress.c; + common = grub-core/lib/zstd/huf_decompress.c; + common = grub-core/lib/zstd/module.c; + common = grub-core/lib/zstd/xxhash.c; + common = grub-core/lib/zstd/zstd_common.c; + common = grub-core/lib/zstd/zstd_decompress.c; +}; + +program = { + name = grub-mkimage; + mansection = 1; + + common = util/grub-mkimage.c; + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = util/resolve.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + common = grub-core/osdep/config.c; + extra_dist = grub-core/osdep/aros/config.c; + extra_dist = grub-core/osdep/windows/config.c; + extra_dist = grub-core/osdep/unix/config.c; + common = util/config.c; + + extra_dist = util/grub-mkimagexx.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBLZMA)'; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-protect; + mansection = 1; + + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + common = grub-core/lib/tss2/buffer.c; + common = grub-core/lib/tss2/tss2_mu.c; + common = grub-core/lib/tss2/tpm2_cmd.c; + common = grub-core/commands/tpm2_key_protector/args.c; + common = grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c; + common = util/grub-protect.c; + common = util/probe.c; + + cflags = '-I$(srcdir)/grub-core/lib/tss2 -I$(srcdir)/grub-core/commands/tpm2_key_protector'; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBTASN1)'; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + + condition = COND_GRUB_PROTECT; +}; + +program = { + name = grub-mkrelpath; + mansection = 1; + + common = util/grub-mkrelpath.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-script-check; + mansection = 1; + + common = util/grub-script-check.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-editenv; + mansection = 1; + + common = util/grub-editenv.c; + common = util/editenv.c; + common = util/grub-install-common.c; + common = grub-core/osdep/init.c; + common = grub-core/osdep/compress.c; + extra_dist = grub-core/osdep/unix/compress.c; + extra_dist = grub-core/osdep/basic/compress.c; + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = grub-core/osdep/config.c; + common = util/config.c; + common = util/resolve.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-mkpasswd-pbkdf2; + mansection = 1; + + common = util/grub-mkpasswd-pbkdf2.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-macho2img; + mansection = 1; + common = util/grub-macho2img.c; + condition = COND_APPLE_LINKER; +}; + +program = { + name = grub-fstest; + mansection = 1; + common_nodist = grub_fstest_init.c; + common = util/grub-fstest.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-mount; + mansection = 1; + common_nodist = grub_fstest_init.c; + common = util/grub-mount.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + common = grub-core/osdep/init.c; + + cflags = '$(FUSE_CFLAGS)'; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM) $(FUSE_LIBS)'; + condition = COND_GRUB_MOUNT; +}; + +program = { + name = grub-mkfont; + mansection = 1; + common = util/grub-mkfont.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + cflags = '$(FREETYPE_CFLAGS)'; + cppflags = '-DGRUB_MKFONT=1'; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(FREETYPE_LIBS)'; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + condition = COND_GRUB_MKFONT; +}; + +program = { + name = grub-probe; + installdir = sbin; + mansection = 8; + common = util/grub-probe.c; + common = util/probe.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-bios-setup; + installdir = sbin; + mansection = 8; + common = util/grub-setup.c; + common = util/setup_bios.c; + extra_dist = util/setup.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/blocklist.c; + extra_dist = grub-core/osdep/generic/blocklist.c; + extra_dist = grub-core/osdep/linux/blocklist.c; + extra_dist = grub-core/osdep/windows/blocklist.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_bios_setup'; +}; + +program = { + name = grub-sparc64-setup; + installdir = sbin; + mansection = 8; + common = util/grub-setup.c; + common = util/setup_sparc.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubkern.a; + ldadd = libgrubgcry.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + cppflags = '-DGRUB_SETUP_FUNC=grub_util_sparc_setup'; +}; + +program = { + name = grub-ofpathname; + installdir = sbin; + mansection = 8; + common = util/ieee1275/grub-ofpathname.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-mklayout; + mansection = 1; + + common = util/grub-mklayout.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-macbless; + installdir = sbin; + mansection = 8; + common = util/grub-macbless.c; + common = grub-core/osdep/init.c; + common = grub-core/kern/emu/argp_common.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +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_illumos'; + common = util/grub.d/10_illumos.in; + installdir = grubconf; + condition = COND_HOST_ILLUMOS; +}; + +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 = '10_xnu'; + common = util/grub.d/10_xnu.in; + installdir = grubconf; + condition = COND_HOST_XNU; +}; + +script = { + name = '20_linux_xen'; + common = util/grub.d/20_linux_xen.in; + installdir = grubconf; + condition = COND_HOST_LINUX; +}; + +script = { + name = '25_bli'; + common = util/grub.d/25_bli.in; + installdir = grubconf; +}; + +script = { + name = '30_os-prober'; + common = util/grub.d/30_os-prober.in; + installdir = grubconf; +}; + +script = { + name = '30_uefi-firmware'; + common = util/grub.d/30_uefi-firmware.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; +}; + +program = { + mansection = 1; + name = grub-mkrescue; + + common = util/grub-mkrescue.c; + common = util/render-label.c; + common = util/glue-efi.c; + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = util/grub-install-common.c; + common = util/setup_bios.c; + common = util/setup_sparc.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + common = grub-core/osdep/compress.c; + extra_dist = grub-core/osdep/unix/compress.c; + extra_dist = grub-core/osdep/basic/compress.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; + common = util/config.c; + + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + + common = util/resolve.c; + + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + + condition = COND_HAVE_EXEC; +}; + +program = { + mansection = 1; + name = grub-mkstandalone; + common = util/grub-mkstandalone.c; + + common = util/render-label.c; + common = util/glue-efi.c; + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = util/grub-install-common.c; + common = util/setup_bios.c; + common = util/setup_sparc.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + extra_dist = grub-core/osdep/linux/platform.c; + extra_dist = grub-core/osdep/windows/platform.c; + extra_dist = grub-core/osdep/basic/platform.c; + extra_dist = grub-core/osdep/basic/no_platform.c; + extra_dist = grub-core/osdep/unix/platform.c; + common = grub-core/osdep/compress.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; + common = util/config.c; + + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + + common = util/resolve.c; + + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + mansection = 8; + installdir = sbin; + name = grub-install; + + common = util/grub-install.c; + common = util/probe.c; + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = util/grub-install-common.c; + common = util/setup_bios.c; + common = util/setup_sparc.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + common = grub-core/osdep/compress.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; + common = util/config.c; + common = util/render-label.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + + common = util/resolve.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + mansection = 1; + installdir = bin; + name = grub-mknetdir; + + common = util/grub-mknetdir.c; + + common = util/mkimage.c; + common = util/grub-mkimage32.c; + common = util/grub-mkimage64.c; + common = util/grub-install-common.c; + common = util/setup_bios.c; + common = util/setup_sparc.c; + common = grub-core/lib/reed_solomon.c; + common = grub-core/osdep/random.c; + common = grub-core/osdep/ofpath.c; + common = grub-core/osdep/platform.c; + common = grub-core/osdep/platform_unix.c; + common = grub-core/osdep/compress.c; + common = util/editenv.c; + common = grub-core/osdep/blocklist.c; + common = grub-core/osdep/config.c; + common = util/config.c; + + common = util/resolve.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = '$(LIBLZMA)'; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +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 = grub-kbdcomp; + common = util/grub-kbdcomp.in; + mansection = 1; +}; + +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 = { + name = grub-shell-luks-tester; + common = tests/util/grub-shell-luks-tester.in; + installdir = noinst; +}; + +script = { + name = grub-fs-tester; + common = tests/util/grub-fs-tester.in; + installdir = noinst; + dependencies = 'garbage-gen$(BUILD_EXEEXT)'; +}; + +script = { + testcase = native; + name = erofs_test; + common = tests/erofs_test.in; +}; + +script = { + testcase = native; + name = ext234_test; + common = tests/ext234_test.in; +}; + +script = { + testcase = native; + name = squashfs_test; + common = tests/squashfs_test.in; +}; + +script = { + testcase = native; + name = iso9660_test; + common = tests/iso9660_test.in; +}; + +script = { + testcase = native; + name = hfsplus_test; + common = tests/hfsplus_test.in; +}; + +script = { + testcase = native; + name = ntfs_test; + common = tests/ntfs_test.in; +}; + +script = { + testcase = native; + name = reiserfs_test; + common = tests/reiserfs_test.in; +}; + +script = { + testcase = native; + name = fat_test; + common = tests/fat_test.in; +}; + +script = { + testcase = native; + name = minixfs_test; + common = tests/minixfs_test.in; +}; + +script = { + testcase = native; + name = xfs_test; + common = tests/xfs_test.in; +}; + +script = { + testcase = native; + name = f2fs_test; + common = tests/f2fs_test.in; +}; + +script = { + testcase = native; + name = nilfs2_test; + common = tests/nilfs2_test.in; +}; + +script = { + testcase = native; + name = romfs_test; + common = tests/romfs_test.in; +}; + +script = { + testcase = native; + name = exfat_test; + common = tests/exfat_test.in; +}; + +script = { + testcase = native; + name = tar_test; + common = tests/tar_test.in; +}; + +script = { + testcase = native; + name = udf_test; + common = tests/udf_test.in; +}; + +script = { + testcase = native; + name = hfs_test; + common = tests/hfs_test.in; +}; + +script = { + testcase = native; + name = jfs_test; + common = tests/jfs_test.in; +}; + +script = { + testcase = native; + name = btrfs_test; + common = tests/btrfs_test.in; +}; + +script = { + testcase = native; + name = zfs_test; + common = tests/zfs_test.in; +}; + +script = { + testcase = native; + name = cpio_test; + common = tests/cpio_test.in; +}; + +script = { + testcase = native; + name = example_scripted_test; + common = tests/example_scripted_test.in; +}; + +script = { + testcase = native; + name = gettext_strings_test; + common = tests/gettext_strings_test.in; + extra_dist = po/exclude.pot; +}; + +script = { + testcase = nonnative; + name = pata_test; + common = tests/pata_test.in; +}; + +script = { + testcase = nonnative; + name = ahci_test; + common = tests/ahci_test.in; +}; + +script = { + testcase = nonnative; + name = uhci_test; + common = tests/uhci_test.in; +}; + +script = { + testcase = nonnative; + name = ohci_test; + common = tests/ohci_test.in; +}; + +script = { + testcase = nonnative; + name = ehci_test; + common = tests/ehci_test.in; +}; + +script = { + testcase = nonnative; + name = example_grub_script_test; + common = tests/example_grub_script_test.in; +}; + +script = { + testcase = nonnative; + name = grub_script_eval; + common = tests/grub_script_eval.in; +}; + +script = { + testcase = nonnative; + name = grub_script_test; + common = tests/grub_script_test.in; +}; + +script = { + testcase = nonnative; + name = grub_script_echo1; + common = tests/grub_script_echo1.in; +}; + +script = { + testcase = nonnative; + name = grub_script_leading_whitespace; + common = tests/grub_script_leading_whitespace.in; +}; + +script = { + testcase = nonnative; + name = grub_script_echo_keywords; + common = tests/grub_script_echo_keywords.in; +}; + +script = { + testcase = nonnative; + name = grub_script_vars1; + common = tests/grub_script_vars1.in; +}; + +script = { + testcase = nonnative; + name = grub_script_for1; + common = tests/grub_script_for1.in; +}; + +script = { + testcase = nonnative; + name = grub_script_while1; + common = tests/grub_script_while1.in; +}; + +script = { + testcase = nonnative; + name = grub_script_if; + common = tests/grub_script_if.in; +}; + +script = { + testcase = native; + name = grub_script_blanklines; + common = tests/grub_script_blanklines.in; +}; + +script = { + testcase = native; + name = grub_script_final_semicolon; + common = tests/grub_script_final_semicolon.in; +}; + +script = { + testcase = native; + name = grub_script_dollar; + common = tests/grub_script_dollar.in; +}; + +script = { + testcase = nonnative; + name = grub_script_comments; + common = tests/grub_script_comments.in; +}; + +script = { + testcase = nonnative; + name = grub_script_functions; + common = tests/grub_script_functions.in; +}; + +script = { + testcase = nonnative; + name = grub_script_break; + common = tests/grub_script_break.in; +}; + +script = { + testcase = nonnative; + name = grub_script_continue; + common = tests/grub_script_continue.in; +}; + +script = { + testcase = nonnative; + name = grub_script_shift; + common = tests/grub_script_shift.in; +}; + +script = { + testcase = nonnative; + name = grub_script_blockarg; + common = tests/grub_script_blockarg.in; +}; + +script = { + testcase = nonnative; + name = grub_script_setparams; + common = tests/grub_script_setparams.in; +}; + +script = { + testcase = nonnative; + name = grub_script_return; + common = tests/grub_script_return.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_cryptomount; + common = tests/grub_cmd_cryptomount.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_regexp; + common = tests/grub_cmd_regexp.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_date; + common = tests/grub_cmd_date.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_set_date; + common = tests/grub_cmd_set_date.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_sleep; + common = tests/grub_cmd_sleep.in; +}; + +script = { + testcase = nonnative; + name = grub_script_expansion; + common = tests/grub_script_expansion.in; +}; + +script = { + testcase = nonnative; + name = grub_script_not; + common = tests/grub_script_not.in; +}; + +script = { + testcase = native; + name = grub_script_no_commands; + common = tests/grub_script_no_commands.in; +}; + +script = { + testcase = nonnative; + name = partmap_test; + common = tests/partmap_test.in; +}; + +script = { + testcase = nonnative; + name = hddboot_test; + common = tests/hddboot_test.in; +}; + +script = { + testcase = nonnative; + name = fddboot_test; + common = tests/fddboot_test.in; +}; + +script = { + testcase = nonnative; + name = cdboot_test; + common = tests/cdboot_test.in; +}; + +script = { + testcase = nonnative; + name = netboot_test; + common = tests/netboot_test.in; +}; + +script = { + testcase = nonnative; + name = serial_test; + common = tests/serial_test.in; +}; + +script = { + testcase = nonnative; + name = pseries_test; + common = tests/pseries_test.in; +}; + +script = { + testcase = nonnative; + name = core_compress_test; + common = tests/core_compress_test.in; +}; + +script = { + testcase = nonnative; + name = xzcompress_test; + common = tests/xzcompress_test.in; +}; + +script = { + testcase = nonnative; + name = gzcompress_test; + common = tests/gzcompress_test.in; +}; + +script = { + testcase = nonnative; + name = lzocompress_test; + common = tests/lzocompress_test.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_echo; + common = tests/grub_cmd_echo.in; +}; + +script = { + testcase = nonnative; + name = help_test; + common = tests/help_test.in; +}; + +script = { + testcase = nonnative; + name = grub_script_gettext; + common = tests/grub_script_gettext.in; +}; + +script = { + testcase = nonnative; + name = grub_script_escape_comma; + common = tests/grub_script_escape_comma.in; +}; + +script = { + testcase = nonnative; + name = grub_script_strcmp; + common = tests/grub_script_strcmp.in; +}; + +script = { + testcase = nonnative; + name = test_sha512sum; + common = tests/test_sha512sum.in; +}; + +script = { + testcase = nonnative; + name = test_unset; + common = tests/test_unset.in; +}; + +script = { + testcase = nonnative; + name = grub_func_test; + common = tests/grub_func_test.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_tr; + common = tests/grub_cmd_tr.in; +}; + +script = { + testcase = nonnative; + name = file_filter_test; + common = tests/file_filter_test.in; +}; + +script = { + testcase = nonnative; + name = grub_cmd_test; + common = tests/grub_cmd_test.in; +}; + +script = { + testcase = native; + name = syslinux_test; + common = tests/syslinux_test.in; +}; + +script = { + testcase = native; + name = luks1_test; + common = tests/luks1_test.in; +}; + +script = { + testcase = native; + name = luks2_test; + common = tests/luks2_test.in; +}; + +script = { + testcase = native; + name = asn1_test; + common = tests/asn1_test.in; +}; + +script = { + testcase = native; + name = tpm2_key_protector_test; + common = tests/tpm2_key_protector_test.in; +}; + +program = { + testcase = native; + 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; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + testcase = native; + name = printf_test; + common = tests/printf_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; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + testcase = native; + name = date_test; + common = tests/date_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; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + testcase = native; + name = priority_queue_unit_test; + common = tests/priority_queue_unit_test.cc; + 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; + common = grub-core/lib/priority_queue.c; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + condition = COND_HAVE_CXX; +}; + +program = { + testcase = native; + name = cmp_test; + common = tests/cmp_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; + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-menulst2cfg; + mansection = 1; + common = util/grub-menulst2cfg.c; + common = grub-core/lib/legacy_parse.c; + common = grub-core/lib/i386/pc/vesa_modes_table.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-syslinux2cfg; + mansection = 1; + common = util/grub-syslinux2cfg.c; + common = grub-core/lib/syslinux_parse.c; + common = grub-core/lib/getline.c; + common = grub-core/osdep/init.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + common = grub-core/kern/emu/argp_common.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-glue-efi; + mansection = 1; + + common = util/grub-glue-efi.c; + common = util/glue-efi.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-render-label; + mansection = 1; + + common = util/grub-render-label.c; + common = util/render-label.c; + common = grub-core/kern/emu/argp_common.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + +program = { + name = grub-file; + mansection = 1; + + common = util/grub-file.c; + common = util/render-label.c; + common = grub-core/commands/file.c; + common = grub-core/commands/file32.c; + common = grub-core/commands/file64.c; + common = grub-core/loader/i386/xen_file.c; + common = grub-core/loader/i386/xen_file32.c; + common = grub-core/loader/i386/xen_file64.c; + common = grub-core/io/offset.c; + common = grub-core/kern/elf.c; + common = grub-core/loader/lzss.c; + common = grub-core/loader/macho.c; + common = grub-core/loader/macho32.c; + common = grub-core/loader/macho64.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; + common = grub-core/osdep/init.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/lib/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; diff --git a/NEWS b/NEWS index 1e3334f18..310130962 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,492 @@ +New in 2.12: + +* GCC 13 support. +* clang 14 support. +* binutils 2.38 support. +* Unification of EFI Linux kernel loader across architectures. +* Transition to EFI Linux kernel stub loader for x86 architecture. +* Initial support for Boot Loader Interface. +* Support for dynamic GRUB runtime memory addition using firmware calls. +* PCI and MMIO UARTs support. +* SDL2 support. +* LoongArch support. +* TPM driver fixes. +* Many filesystems fixes. +* Many CVE and Coverity fixes. +* Debugging support improvements. +* Tests improvements. +* Documentation improvements. +* ...and tons of other fixes and cleanups... + +New in 2.06: + +* GCC 10 support. +* clang 10 support. +* SBAT support. +* LUKS2 support. +* Drop small MBR gap support. +* Xen Security Modules (XSM/FLASK) support. +* The lockdown mechanism similar to the Linux kernel one. +* Disable the os-prober by default. +* Many backports of GRUB distros specific patches. +* BootHole and BootHole2 fixes. +* ...and tons of other fixes and cleanups... + +New in 2.04: + +* GCC 8 and 9 support. +* Gnulib integration overhaul. +* RISC-V support. +* Xen PVH support. +* Native UEFI secure boot support. +* UEFI TPM driver. +* New IEEE 1275 obdisk driver. +* Btrfs RAID 5 and RIAD 6 support. +* PARTUUID support. +* VLAN support. +* Native DHCP support. +* Many ARM and ARM64 fixes. +* Many SPARC fixes. +* Many IEEE 1275 fixes. +* ...and tons of other fixes and cleanups... + +New in 2.02: + +* New/improved filesystem and disk support: + * Big-endian UFS1. + * Experimental 64-bit ext2 support. + * Various fixes for non-512-byte sector devices. + * New `proc' filesystem framework, used by LUKS disks. + * Fix DM-RAID partition handling. + * New `nativedisk' command to switch from firmware to native disk drivers. + * Compressed HFS+. + * DragonFly BSD labels. + * CBFS (coreboot). + * Handle partitioned LVM properly. + * Use LVM UUIDs whenever possible. + * GPT PReP. + * New `progress' module that shows progress information while reading + files. + * ZFS features support. + * ZFS LZ4 support. + * XFS V5 format support. + * LVM RAID1 support. + +* New/improved terminal and video support: + * Monochrome text (matching `hercules' in GRUB Legacy). + * Morse code output using system speaker. + * `spkmodem' output (simple data protocol using system speaker). + * Handle Japanese special keys. + * coreboot framebuffer. + * Serial on ARC. + * Native vt100 handling for grub-emu, replacing the use of the curses + library. + * New gfxmenu options for terminal window positioning, theme background + image handling, and scrollbar padding, plus `item_pixmap_style' and + `highlight_overlay'. + * Support several more image types (paletted and greyscale). + +* Boot protocol improvements: + * Support Apple FAT binaries on non-Apple platforms. + * Improve FreeDOS direct loading support compatibility. + * Enable `linux16' on all x86 platforms, not just BIOS. + * New TrueCrypt ISO loader. + * multiboot2 boot-services EFI specification. + * multiboot2 EFI memory map specification. + * multiboot2 full-file specfication. + +* New/improved network support: + * New variables `net_default_*' containing properties of the default + interface. + * Autoload `http' and `tftp' modules if necessary. + * Improve TFTP robustness. + * Parse `nd' disk names in GRUB Legacy configuration files. + * Issue separate DNS queries for IPv4 and IPv6. + * Support IPv6 Router Advertisement to configure default router. + * New variable net__next_server containing next server + from BOOTP reply. + +* Coreboot improvements: + * CBFS support both in on-disk images (loopback) and flash. + * Ability to launch another payload from flash or disk + * Coreboot framebuffer + * CBMEMC support (both logging and inspecting logs) + * Command for inspecting coreboot timestamps (`coreboot_boottime'). + * Command for inspecting coreboot tables (`lscoreboot'). + * New target default_payload.elf. + * Increased maximal core size. + * Prefer pmtimer for TSC calibration. + +* New/improved platform support: + * New `efifwsetup' and `lsefi' commands on EFI platforms. + * New `cmosdump' and `cmosset' commands on platforms with CMOS support. + * New command `pcidump' for PCI platforms. + * Improve opcode parsing in ACPI halt implementation. + * Use the TSC as a possible time source on i386-ieee1275. + * Merge PowerPC grub-mkrescue implementation with the common one. + * Support grub-mkrescue on i386-ieee1275, sparc64, bootinfo machines such + as pSeries, and mips-arc. + * Make grub-mkrescue better support Apple Intel Macs on CD. + * Enable GRUB Legacy configuration file parsing on EFI. + * Support halt for Loongson 2E. + * ARM U-Boot and EFI ports. + * Reorganise platform-dependent code in utilities to avoid #ifdef mess. + * AROS and Haiku support for userspace utilities. + * Xen PV port. + * Fix EFI stack alignment. + * ARM64 EFI port. + * On Linux, read partition start offsets from sysfs if possible. + * New grub-macbless utility, and better integration with Mac firmware in + grub-install. + * Support Yeeloong 3A. + * Add `cpuid --pae' option to detect Physical Address Extension on x86. + * Support for USB debug dongles. + * Support for *-emu on all platforms (previously only i386/x86_64 worked). + * Support *-emu on Windows. + * New platform `none' which builds only user level utilities. This is now + default if target CPU is not supported. + * Support for booting little-endian Linux kernel on powerpc. + * Support network boot with Oracle sun4v vnet devices. + * Added SAS disks to the IEEE 1275 Open Firmware device list. + * Try multiple methods for TSC (timestamp counter) calibration - PIT, pmtimer, + EFI Stall. If everything fails, use hardcoded frequency 800MHz. + * Support Hyper-V Gen2 platforms which lack PIT for TSC calibration. + * Map UEFI Persistent Memory to E820 persistent memory. + * New Xen loader on ARM64. + * Respect alignment requirement for block device IO buffers on EFI. + +* Security: + * Add optional facility to enforce that all files read by the core image + from disk have a valid detached digital signature. + +* Performance: + * Avoid costly division operations in many places. + * New boot time analysis framework (`./configure --enable-boot-time'). + * Initialise USB ports in parallel. + * New `testspeed' command to test file read speed. + * Speed-up gfxterm by storing intermediate results in more compact format. + * Lazy LVM/mdraid scan. + * Disk hints. + +* Scripting: + * New `eval' and `tr' commands. + * grub-script-check fails on scripts containing no commands. + +* Installation and other utility improvements: + * Add option to compress files on installation or image creation. + * Using grub-reboot no longer requires setting `GRUB_DEFAULT=saved'. + * Support probing EFI System Partition (requires os-prober >= 1.58). + * Fix inconsistent use of `GRUB_CRYPTODISK_ENABLE' and + `GRUB_ENABLE_CRYPTODISK'; the latter is now used consistently. + * grub-mount handles symbolic links to directories. + * Support disabling submenus with `GRUB_DISABLE_SUBMENU' configuration key + for grub-mkconfig. + * grub-install, grub-mknetdir, grub-mkrescue, and grub-mkstandalone + rewritten in C. They should now work in supported non-Unix-like + environments. + * Native mingw support. + * Ability to install on EFI under windows. + * Reorganise timeout handling using new `timeout_style' environment + variable and `GRUB_TIMEOUT_STYLE' configuration key for grub-mkconfig. + Menu hotkeys pressed during a hidden timeout now boot the corresponding + menu entry immediately. + * New `file' command and grub-file utility to check file types. + * New syslinux configuration file parser. + * Set menu entry class to primary OS name returned by os-prober to display + OS specific icon. + * On Linux x86 detect EFI word size in grub-install and automatically select + correct platform (x86_64-efi or i386-efi) to install. Requires Linux kernel + 4.0 or higher. + +* Build system: + * Remove all uses of nested functions; GRUB no longer requires an + executable stack. + * Fix documentation build with Texinfo >= 5.1. + * More robust and documented cross-compiling support. + * Partial clang support for some platforms (experimental). + * Partial mingw64 x86_64-efi compile support (highly experimental). + * Partial mingw32 i386-* (other than already present i386-pc) + compile support (highly experimental). + * Support for grub-mkpasswd on Windows. + * Eliminate the use of AutoGen. This allowed some performance + improvements to the build system. + * Remove variable length arrays. + * OpenBSD compile and tools support (NetBSD and FreeBSD were already supported). + * Fix build with FreeType >= 2.5.1. + * Make gentpl.py compatible with Python 3. It now requires at least + Python 2.6. + * modinfo.sh contains build information now. + * Added many new tests to improve robustness. + * Target is built without libgcc now. Necessary builtins are reimplemented + directly. This removes requirement for target-specific runtime on build + system. + * emu libusb support removed (was broken and unmaintained). + * powerpc64le compile support. + * Use fixed timestamp when generating GRUB image for reproducible builds. + * Verify at build time that modules contain only supported relocations and their + structure matches what boot-time module loader expects. + * Do not require fonts on powerpc-ieee1275. + +* Revision control moved to git. + +New in 2.00: + +* Appearance: + * Official theme for gfxmenu (starfield) + * Menu is organised with submenus. + * Better default video mode selection using EDID. + +* New platforms: + * Itanium port. + * Fuloong2F support (including GRUB as firmware) + * Fuloong2E support (except GRUB as firmware) + * ARCS (SGI machines) port. + * qemu -M mips port. + +* grub-mount to mount filesystems using GRUB FS drivers and FUSE. + +* Changed security default so entries are locked by default if any superuser is + defined. + +* New drivers: + * EHCI. + * AHCI. + * ESCC serial. + * IEEE1275 serial. + * EFI serial. + * Network stack for BIOS, IEEE1275, EMU and EFI, including TFTP, HTTP and DNS. + +* New filesystem, filters and disks formats: + * DVH partition map. + * Plan9 partition map. + * Big-endian mdraid. + * Big-endian cpio. + * ODC and NEWC cpio. + * ExFAT. + * Minix3fs. + * Big-endian minixfs. + * RomFS. + * Squash4. + * Support non-512B disk blocks. + * LUKS and GELI support. + * LDM read support (no install yet). + * LZOP. + +* Improved filesystem and disks formats support: + * HFS+ label support. + * Improved reiserfs support. + * multidevice, mirrored and raidz(2,3) ZFS support. + * RAID LVM (internal RAIDing) support. + * ZFS crypto support. + * ZLE, LZ4 and GZIP on ZFS support. + * Support ZFS up to 33. + * HFS string is now treated like mac-roman and not UTF-8 + * HFS mtime support. + * Improved AFFS and SFS support. + * LZO-compressed btrfs support. + * cpio and tar symlinks support. + * Better FS detection to reduce false positives. + +* New boot protocols: + * Ability to load another coreboot payload when on coreboot. + * Plan9. + * Freedos. + * Ntldr/bootmgr (to load Windows bootloader). + * chainloader --bpb support to patch FAT or NTFS BPB in memory to correct + wrong partition offset. + * PXE chainloading support. + * Darwin 11 (Mac OS X Lion) protocol support. + +* Boot protocol improvements: + * Multiple initrd support. + * Basic illumos and xnu autoconfig. + +* Testing and debugging: + * New grub-fstest commands: cat, zfsinfo, testload xnu_uuid + * grub-fstest recursive directory compare for quickly checking that + a directory is read correctly. + * Backtace on crash (if gdb module is loaded, x86 only) + * Disk cache statistics gathering. + * GDB stub and GDB support script. + * "make check" and "make bootcheck" expanded to almost all platforms + (except i386-ieee1275, mips-arc, sparc64-ieee1275, ia64-efi and emu) + * New `time' command. + +* Performance: + * Lazy scanning to avoid accessing devices which aren't really used. + This avoids boot delay due to slow device scanning. + * Use CPU cache when accessing video memory. + * Search hints to first try the most likely device when searching for a + device with given UUID. This avoids slow scanning in most cases. + +* Internationalisation: + * Updated to Unicode 6.0. + * $"..." syntax for translation in grub scripting language. This allows easy + translation of grub.cfg at runtime. + * Translations to many languages included in official distribution. + +* Scripting: + * $grub_cpu and $grub_platform variables for conditioning grub.cfg on platform + at runtime. + * $feature_* variables to condition scripts on available features. + * Use of ids to identify menu entries. + * all_video module which is empty but depends on all video modules thus + allowing easy loading of all of them. + +* Installation: + * grub-mknetdir script for easy creation of netbootable GRUB directory. + * Itanium and mips support in grub-mkrescue. + * grub-install support for all platforms except emu. + * PreP partition install support. + * No files conflict between flavours (except grub-mkrescue for ppc). This + allows easy install of GRUB for several platforms. + * grub-mkstandalone script for easy creating of image including all modules + for platforms with generous limit on image size. + * program-transform-name now functions according to usual conventions. + Use --grubdir and --bootdir to get old behaviour. + +* ADLER32 and CRC64 support (for XZ and hashsum). + +* ofconsole renamed to console + +* Experimental support for compiling with Apple toolchain. + +* grub-mkdevicemap removed. Now all devices are detected on invocation of + any grub utility. + +New in 1.99: + +* Keyboard layouts support. + +* New `lsapm' command (i386-pc only). + +* Parser for GRUB Legacy configuration files. + +* Support RAID on virtio devices. + +* Remove deprecated `root' command. + +* New `euro.pf2' font which supports most European languages. + +* Avoid opening the same device twice on Open Firmware platforms. + +* Extend `vbeinfo' and `vbetest' commands to non-VBE graphics, as + `videoinfo' and `videotest'. + +* New `lsefisystab', `lssal', and `lsefimmap' commands on EFI platforms. + +* Support explicit user claim that a device is BIOS-visible. Devices + listed in device.map will be assumed to be readable using only BIOS + facilities, rather than anything more complex such as LVM or RAID. + +* New bash-completion script for GRUB utilities. + +* Use ACPI to shut down if possible. + +* New `lsacpi' command. + +* Btrfs support. + +* New `--boot-directory' option to `grub-install', `grub-reboot', and + `grub-set-default', with clearer semantics than the previous + `--root-directory' option. + +* Rename CD-ROM device to "cd" on BIOS platforms. + +* Transparent decompression filters. + +* Simpler PXE image generation. New `grub-mknetdir' utility to generate + netboot directory trees. + +* New relocator. Allows for more kernel support and more + straightforward loader writing. + +* Handle USB pendrives exposed as floppies. + +* New Automake-based build system. + +* Add `sendkey' command (i386-pc only). + +* ZFS support. + +* Support 1.x versions of mdadm metadata. + +* Fix corruption when reading Reiserfs directory entries. + +* Bidirectional text and diacritics support. + +* Skip LVM snapshots. + +* MIPS Yeeloong firmware port. + +* Change grub-mkdevicemap to emit /dev/disk/by-id/ names where possible + on GNU/Linux. + +* Add `grub-mkconfig' support for Xen with Linux. + +* Add `grub-mkconfig' support for initrd images on Fedora 13. + +* Support >3GiB and <16MiB RAM in i386-qemu. + +* Add support for Cirrus 5446 and Bochs video cards. + +* Load more appropriate video drivers automatically in `grub-mkconfig'. + +* USB improvements, including hotplugging/hotunplugging, hub support, + and USB serial support. + +* AMD Geode CS5536 support. + +* Extensive updates to the Texinfo documentation. + +* Handle symbolic links under /dev/mapper on GNU/Linux. + +* Handle installation across multiple partition table types. + +* Add `cmostest' command (i386/x86_64 only). + +* Add support for DM-RAID disk devices on GNU/Linux. + +* Remove `grub-mkisofs'. `grub-mkrescue' now uses GNU xorriso to build + CD images. + +* `grub-mkrescue' support for EFI, coreboot, and QEMU platforms. + +* Unify `grub-mkimage', `grub-setup', and `grub-install' source code + across platforms. + +* Fix VGA (as opposed to VBE) video driver, formerly a terminal driver. + +* Add menu hotkey support. + +* Add support for the nilfs2 filesystem. + +* `grub-probe' and `grub-mkconfig' support for NetBSD. + +* Support setting a background image in `grub-mkconfig'. + +* Support multiple terminals in `grub-mkconfig'. + +* Regexp support. + +* MIPS multiboot2 support. + +* Multiboot2 tag support. + +* sunpc partition table support. + +* Add a number of new language features to GRUB script: `for', `while', + `until', `elif', function parameters, `break', `continue', `shift', + multi-line quoted strings, positional parameters with `setparams', + `return', filename wildcard expansion, and `!'. + +* Support nested partition tables. GRUB now prefers to name partitions + in the form `(hd0,msdos1,bsd1)' rather than `(hd0,1,a)'. + +* Speed up consecutive hostdisk operations on the same device. + +* Compile parts of `grub-emu' as modules. + New in 1.98 - 2010-03-06: * Multiboot on EFI support. diff --git a/README b/README index b6c7fd6d7..49ce15ea3 100644 --- a/README +++ b/README @@ -7,8 +7,20 @@ See the file NEWS for a description of recent changes to GRUB 2. See the file INSTALL for instructions on how to build and install the GRUB 2 data and program files. +See the file MAINTAINERS for information about the GRUB maintainers, etc. + +If you found a security vulnerability in the GRUB please check the SECURITY +file to get more information how to properly report this kind of bugs to +the maintainers. + Please visit the official web page of GRUB 2, for more information. The URL is . -For now, there is not much documentation yet. Please look at the GRUB -Wiki for testing procedures. +More extensive documentation is available in the Info manual, +accessible using 'info grub' after building and installing GRUB 2. + +There are a number of important user-visible differences from the +first version of GRUB, now known as GRUB Legacy. For a summary, please +see: + + info grub Introduction 'Changes from GRUB Legacy' diff --git a/SECURITY b/SECURITY new file mode 100644 index 000000000..1c3e8ef36 --- /dev/null +++ b/SECURITY @@ -0,0 +1,60 @@ +Security Policy +=============== + +To report a vulnerability see "Reporting a Vulnerability" below. + + +Security Incident Policy +======================== + +Security bug reports are treated with special attention and are handled +differently from normal bugs. In particular, security sensitive bugs are not +handled in public but in private. Information about the bug and access to it +is restricted to people in the security group, the individual engineers that +work on fixing it, and any other person who needs to be involved for organisational +reasons. The process is handled by the security team, which decides on the people +involved in order to fix the issue. It is also guaranteed that the person reporting +the issue has visibility into the process of fixing it. Any security issue gets +prioritized according to its security rating. The issue is opened up to the public +in coordination with the release schedule and the reporter. + + +Disclosure Policy +================= + +Everyone involved in the handling of a security issue - including the reporter - +is required to adhere to the following policy. Any information related to +a security issue must be treated as confidential and only shared with trusted +partners if necessary, for example to coordinate a release or manage exposure +of clients to the issue. No information must be disclosed to the public before +the embargo ends. The embargo time is agreed upon by all involved parties. It +should be as short as possible without putting any users at risk. + + +Supported Versions +================== + +Only the most recent version of the GRUB is supported. + + +Reporting a Vulnerability +========================= + +The security report should be encrypted with the PGP keys and sent to ALL email +addresses listed below. Every vulnerability report will be assessed within +72 hours of receiving it. If the outcome of the assessment is that the report +describes a security issue, the report will be transferred into an issue on the +internal vulnerability project for further processing. The reporter is updated +on each step of the process. + +While there's currently no bug bounty program we appreciate every report. + +* Contact: Daniel Kiper and + Daniel Kiper +* PGP Key Fingerprint: BE5C 2320 9ACD DACE B20D B0A2 8C81 89F1 988C 2166 + +* Contact: Alex Burmashev +* PGP Key Fingerprint: 50A4 EC06 EF7E B84D 67E0 3BB6 2AE2 C87E 28EF 2E6E + +* Contact: Vladimir 'phcoder' Serbinenko +* PGP Key Fingerprint: E53D 497F 3FA4 2AD8 C9B4 D1E8 35A9 3B74 E82E 4209 diff --git a/TODO b/TODO index 6ec1521cd..a9b6d3523 100644 --- a/TODO +++ b/TODO @@ -7,7 +7,3 @@ glance. So write to first. For bug tracking, refer to: http://savannah.gnu.org/bugs/?group=grub - -Our wiki also lists some areas that need work: - - http://grub.enbug.org/ diff --git a/acinclude.m4 b/acinclude.m4 index 692404e20..fa7840f09 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -19,6 +19,8 @@ AC_DEFUN([grub_PROG_TARGET_CC], AC_CACHE_VAL(grub_cv_prog_target_cc, [AC_LINK_IFELSE([AC_LANG_PROGRAM([[ asm (".globl start; start:"); +void __main (void); +void __main (void) {} int main (void); ]], [[]])], [grub_cv_prog_target_cc=yes], @@ -38,6 +40,7 @@ dnl Written by Pavel Roskin. Based on grub_ASM_EXT_C written by dnl Erich Boleyn and modified by Yoshinori K. Okuji. AC_DEFUN([grub_ASM_USCORE], [AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_EGREP]) AC_MSG_CHECKING([if C symbols get an underscore after compilation]) AC_CACHE_VAL(grub_cv_asm_uscore, [cat > conftest.c <<\EOF @@ -56,19 +59,16 @@ else AC_MSG_ERROR([${CC-cc} failed to produce assembly code]) fi -if grep _func conftest.s >/dev/null 2>&1; then +if $EGREP '(^|[^_[:alnum]])_func' conftest.s >/dev/null 2>&1; then + HAVE_ASM_USCORE=1 grub_cv_asm_uscore=yes else + HAVE_ASM_USCORE=0 grub_cv_asm_uscore=no fi rm -f conftest*]) -if test "x$grub_cv_asm_uscore" = xyes; then - AC_DEFINE_UNQUOTED([HAVE_ASM_USCORE], $grub_cv_asm_uscore, - [Define if C symbols get an underscore after compilation]) -fi - AC_MSG_RESULT([$grub_cv_asm_uscore]) ]) @@ -76,7 +76,7 @@ AC_MSG_RESULT([$grub_cv_asm_uscore]) dnl Some versions of `objcopy -O binary' vary their output depending dnl on the link address. AC_DEFUN([grub_PROG_OBJCOPY_ABSOLUTE], -[AC_MSG_CHECKING([whether ${OBJCOPY} works for absolute addresses]) +[AC_MSG_CHECKING([whether ${TARGET_OBJCOPY} works for absolute addresses]) AC_CACHE_VAL(grub_cv_prog_objcopy_absolute, [cat > conftest.c <<\EOF void cmain (void); @@ -93,13 +93,13 @@ else fi grub_cv_prog_objcopy_absolute=yes for link_addr in 0x2000 0x8000 0x7C00; do - if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -nostdlib ${TARGET_IMG_LDFLAGS_AC} -Wl,-Ttext -Wl,$link_addr conftest.o -o conftest.exec]); then : + if AC_TRY_COMMAND([${CC-cc} ${TARGET_CFLAGS} ${TARGET_LDFLAGS} -nostdlib ${TARGET_IMG_LDFLAGS_AC} ${TARGET_IMG_BASE_LDOPT},$link_addr conftest.o -o conftest.exec]); then : else AC_MSG_ERROR([${CC-cc} cannot link at address $link_addr]) fi - if AC_TRY_COMMAND([${OBJCOPY-objcopy} --only-section=.text -O binary conftest.exec conftest]); then : + if AC_TRY_COMMAND([${TARGET_OBJCOPY-objcopy} --only-section=.text -O binary conftest.exec conftest]); then : else - AC_MSG_ERROR([${OBJCOPY-objcopy} cannot create binary files]) + AC_MSG_ERROR([${TARGET_OBJCOPY-objcopy} cannot create binary files]) fi if test ! -f conftest.old || AC_TRY_COMMAND([cmp -s conftest.old conftest]); then mv -f conftest conftest.old @@ -136,142 +136,78 @@ if test "x$grub_cv_prog_ld_build_id_none" = xyes; then fi ]) - -dnl Mass confusion! -dnl Older versions of GAS interpret `.code16' to mean ``generate 32-bit -dnl instructions, but implicitly insert addr32 and data32 bytes so -dnl that the code works in real mode''. -dnl -dnl Newer versions of GAS interpret `.code16' to mean ``generate 16-bit -dnl instructions,'' which seems right. This requires the programmer -dnl to explicitly insert addr32 and data32 instructions when they want -dnl them. -dnl -dnl We only support the newer versions, because the old versions cause -dnl major pain, by requiring manual assembly to get 16-bit instructions into -dnl asm files. -AC_DEFUN([grub_I386_ASM_ADDR32], -[AC_REQUIRE([AC_PROG_CC]) -AC_REQUIRE([grub_I386_ASM_PREFIX_REQUIREMENT]) -AC_MSG_CHECKING([for .code16 addr32 assembler support]) -AC_CACHE_VAL(grub_cv_i386_asm_addr32, -[cat > conftest.s.in <<\EOF - .code16 -l1: @ADDR32@ movb %al, l1 -EOF - -if test "x$grub_cv_i386_asm_prefix_requirement" = xyes; then - sed -e s/@ADDR32@/addr32/ < conftest.s.in > conftest.s +dnl Check nm +AC_DEFUN([grub_PROG_NM_WORKS], +[AC_MSG_CHECKING([whether nm works]) +AC_CACHE_VAL(grub_cv_prog_nm_works, +[ +nm_works_tmp_dir="$(mktemp -d "./confXXXXXX")" +AC_LANG_CONFTEST([AC_LANG_PROGRAM([[]], [[]])]) +$TARGET_CC $TARGET_CFLAGS -c conftest.c -o "$nm_works_tmp_dir/ef" +if $TARGET_NM "$nm_works_tmp_dir/ef" > /dev/null; then + grub_cv_prog_nm_works=yes else - sed -e s/@ADDR32@/addr32\;/ < conftest.s.in > conftest.s + grub_cv_prog_nm_minus_p=no fi +rm "$nm_works_tmp_dir/ef" +rmdir "$nm_works_tmp_dir" +]) +AC_MSG_RESULT([$grub_cv_prog_nm_works]) -if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -c conftest.s]) && test -s conftest.o; then - grub_cv_i386_asm_addr32=yes -else - grub_cv_i386_asm_addr32=no -fi - -rm -f conftest*]) - -AC_MSG_RESULT([$grub_cv_i386_asm_addr32])]) - -dnl check if our compiler is apple cc -dnl because it requires numerous workarounds -AC_DEFUN([grub_apple_cc], -[AC_REQUIRE([AC_PROG_CC]) -AC_MSG_CHECKING([whether our compiler is apple cc]) -AC_CACHE_VAL(grub_cv_apple_cc, -[if $CC -v 2>&1 | grep "Apple Inc." > /dev/null; then - grub_cv_apple_cc=yes -else - grub_cv_apple_cc=no +if test "x$grub_cv_prog_nm_works" != xyes; then + AC_MSG_ERROR([nm does not work]) fi ]) -AC_MSG_RESULT([$grub_cv_apple_cc])]) - -dnl check if our target compiler is apple cc -dnl because it requires numerous workarounds -AC_DEFUN([grub_apple_target_cc], -[AC_REQUIRE([AC_PROG_CC]) -AC_MSG_CHECKING([whether our target compiler is apple cc]) -AC_CACHE_VAL(grub_cv_apple_target_cc, -[if $CC -v 2>&1 | grep "Apple Inc." > /dev/null; then - grub_cv_apple_target_cc=yes +dnl Supply -P to nm +AC_DEFUN([grub_PROG_NM_MINUS_P], +[AC_MSG_CHECKING([whether nm accepts -P]) +AC_CACHE_VAL(grub_cv_prog_nm_minus_p, +[ +nm_minus_p_tmp_dir="$(mktemp -d "./confXXXXXX")" +AC_LANG_CONFTEST([AC_LANG_PROGRAM([[]], [[]])]) +$TARGET_CC $TARGET_CFLAGS -c conftest.c -o "$nm_minus_p_tmp_dir/ef" +if $TARGET_NM -P "$nm_minus_p_tmp_dir/ef" 2>&1 > /dev/null; then + grub_cv_prog_nm_minus_p=yes else - grub_cv_apple_target_cc=no + grub_cv_prog_nm_minus_p=no +fi +rm "$nm_minus_p_tmp_dir/ef" +rmdir "$nm_minus_p_tmp_dir" +]) +AC_MSG_RESULT([$grub_cv_prog_nm_minus_p]) + +if test "x$grub_cv_prog_nm_minus_p" = xyes; then + TARGET_NMFLAGS_MINUS_P="-P" +else + TARGET_NMFLAGS_MINUS_P= fi ]) -AC_MSG_RESULT([$grub_cv_apple_target_cc])]) - - -dnl Later versions of GAS requires that addr32 and data32 prefixes -dnl appear in the same lines as the instructions they modify, while -dnl earlier versions requires that they appear in separate lines. -AC_DEFUN([grub_I386_ASM_PREFIX_REQUIREMENT], -[AC_REQUIRE([AC_PROG_CC]) -AC_MSG_CHECKING(dnl -[whether addr32 must be in the same line as the instruction]) -AC_CACHE_VAL(grub_cv_i386_asm_prefix_requirement, -[cat > conftest.s <<\EOF - .code16 -l1: addr32 movb %al, l1 -EOF - -if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -c conftest.s]) && test -s conftest.o; then - grub_cv_i386_asm_prefix_requirement=yes +dnl Supply --defined-only to nm +AC_DEFUN([grub_PROG_NM_DEFINED_ONLY], +[AC_MSG_CHECKING([whether nm accepts --defined-only]) +AC_CACHE_VAL(grub_cv_prog_nm_defined_only, +[ +nm_defined_only_tmp_dir="$(mktemp -d "./confXXXXXX")" +AC_LANG_CONFTEST([AC_LANG_PROGRAM([[]], [[]])]) +$TARGET_CC $TARGET_CFLAGS -c conftest.c -o "$nm_defined_only_tmp_dir/ef" +if $TARGET_NM --defined-only "$nm_defined_only_tmp_dir/ef" 2>&1 > /dev/null; then + grub_cv_prog_nm_defined_only=yes else - grub_cv_i386_asm_prefix_requirement=no + grub_cv_prog_nm_defined_only=no fi +rm "$nm_defined_only_tmp_dir/ef" +rmdir "$nm_defined_only_tmp_dir" +]) +AC_MSG_RESULT([$grub_cv_prog_nm_defined_only]) -rm -f conftest*]) - -if test "x$grub_cv_i386_asm_prefix_requirement" = xyes; then - grub_tmp_addr32="addr32" - grub_tmp_data32="data32" +if test "x$grub_cv_prog_nm_defined_only" = xyes; then + TARGET_NMFLAGS_DEFINED_ONLY=--defined-only else - grub_tmp_addr32="addr32;" - grub_tmp_data32="data32;" + TARGET_NMFLAGS_DEFINED_ONLY= fi - -AC_DEFINE_UNQUOTED([ADDR32], $grub_tmp_addr32, - [Define it to \"addr32\" or \"addr32;\" to make GAS happy]) -AC_DEFINE_UNQUOTED([DATA32], $grub_tmp_data32, - [Define it to \"data32\" or \"data32;\" to make GAS happy]) - -AC_MSG_RESULT([$grub_cv_i386_asm_prefix_requirement])]) - - -dnl Older versions of GAS require that absolute indirect calls/jumps are -dnl not prefixed with `*', while later versions warn if not prefixed. -AC_DEFUN([grub_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK], -[AC_REQUIRE([AC_PROG_CC]) -AC_MSG_CHECKING(dnl -[whether an absolute indirect call/jump must not be prefixed with an asterisk]) -AC_CACHE_VAL(grub_cv_i386_asm_absolute_without_asterisk, -[cat > conftest.s <<\EOF - lcall *(offset) -offset: - .long 0 - .word 0 -EOF - -if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -c conftest.s]) && test -s conftest.o; then - grub_cv_i386_asm_absolute_without_asterisk=no -else - grub_cv_i386_asm_absolute_without_asterisk=yes -fi - -rm -f conftest*]) - -if test "x$grub_cv_i386_asm_absolute_without_asterisk" = xyes; then - AC_DEFINE([ABSOLUTE_WITHOUT_ASTERISK], 1, - [Define it if GAS requires that absolute indirect calls/jumps are not prefixed with an asterisk]) -fi - -AC_MSG_RESULT([$grub_cv_i386_asm_absolute_without_asterisk])]) +]) dnl Check what symbol is defined as a bss start symbol. @@ -280,7 +216,12 @@ AC_DEFUN([grub_CHECK_BSS_START_SYMBOL], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if __bss_start is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_uscore_uscore_bss_start_symbol, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void); +]], [[asm ("incl __bss_start")]])], [grub_cv_check_uscore_uscore_bss_start_symbol=yes], [grub_cv_check_uscore_uscore_bss_start_symbol=no])]) @@ -289,7 +230,11 @@ AC_MSG_RESULT([$grub_cv_check_uscore_uscore_bss_start_symbol]) AC_MSG_CHECKING([if edata is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_edata_symbol, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void);]], [[asm ("incl edata")]])], [grub_cv_check_edata_symbol=yes], [grub_cv_check_edata_symbol=no])]) @@ -298,21 +243,23 @@ AC_MSG_RESULT([$grub_cv_check_edata_symbol]) AC_MSG_CHECKING([if _edata is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_uscore_edata_symbol, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void);]], [[asm ("incl _edata")]])], [grub_cv_check_uscore_edata_symbol=yes], [grub_cv_check_uscore_edata_symbol=no])]) AC_MSG_RESULT([$grub_cv_check_uscore_edata_symbol]) -AH_TEMPLATE([BSS_START_SYMBOL], [Define it to one of __bss_start, edata and _edata]) - if test "x$grub_cv_check_uscore_uscore_bss_start_symbol" = xyes; then - AC_DEFINE([BSS_START_SYMBOL], [__bss_start]) + BSS_START_SYMBOL=__bss_start elif test "x$grub_cv_check_edata_symbol" = xyes; then - AC_DEFINE([BSS_START_SYMBOL], [edata]) + BSS_START_SYMBOL=edata elif test "x$grub_cv_check_uscore_edata_symbol" = xyes; then - AC_DEFINE([BSS_START_SYMBOL], [_edata]) + BSS_START_SYMBOL=_edata else AC_MSG_ERROR([none of __bss_start, edata or _edata is defined]) fi @@ -324,7 +271,11 @@ AC_DEFUN([grub_CHECK_END_SYMBOL], [AC_REQUIRE([AC_PROG_CC]) AC_MSG_CHECKING([if end is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_end_symbol, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void);]], [[asm ("incl end")]])], [grub_cv_check_end_symbol=yes], [grub_cv_check_end_symbol=no])]) @@ -333,58 +284,36 @@ AC_MSG_RESULT([$grub_cv_check_end_symbol]) AC_MSG_CHECKING([if _end is defined by the compiler]) AC_CACHE_VAL(grub_cv_check_uscore_end_symbol, -[AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], +[AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void);]], [[asm ("incl _end")]])], [grub_cv_check_uscore_end_symbol=yes], [grub_cv_check_uscore_end_symbol=no])]) AC_MSG_RESULT([$grub_cv_check_uscore_end_symbol]) -AH_TEMPLATE([END_SYMBOL], [Define it to either end or _end]) - if test "x$grub_cv_check_end_symbol" = xyes; then - AC_DEFINE([END_SYMBOL], [end]) + END_SYMBOL=end elif test "x$grub_cv_check_uscore_end_symbol" = xyes; then - AC_DEFINE([END_SYMBOL], [_end]) + END_SYMBOL=_end else AC_MSG_ERROR([neither end nor _end is defined]) fi ]) -dnl Check if the C compiler generates calls to `__enable_execute_stack()'. -AC_DEFUN([grub_CHECK_ENABLE_EXECUTE_STACK],[ -AC_MSG_CHECKING([whether `$CC' generates calls to `__enable_execute_stack()']) -AC_LANG_CONFTEST([[ -void f (int (*p) (void)); -void g (int i) -{ - int nestedfunc (void) { return i; } - f (nestedfunc); -} -]]) -if AC_TRY_COMMAND([${CC-cc} ${CFLAGS} -S conftest.c]) && test -s conftest.s; then - true -else - AC_MSG_ERROR([${CC-cc} failed to produce assembly code]) -fi -if grep __enable_execute_stack conftest.s >/dev/null 2>&1; then - AC_DEFINE([NEED_ENABLE_EXECUTE_STACK], 1, - [Define to 1 if GCC generates calls to __enable_execute_stack()]) - AC_MSG_RESULT([yes]) -else - AC_MSG_RESULT([no]) -fi -rm -f conftest* -]) - -dnl Check if the C compiler supports `-fstack-protector'. +dnl Check if the C compiler supports the stack protector AC_DEFUN([grub_CHECK_STACK_PROTECTOR],[ -[# Smashing stack protector. +[# Stack smashing protector. ssp_possible=yes] AC_MSG_CHECKING([whether `$CC' accepts `-fstack-protector']) # Is this a reliable test case? -AC_LANG_CONFTEST([[void foo (void) { volatile char a[8]; a[3]; }]]) +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +void foo (void) { volatile char a[8]; a[3]; } +]])]) [# `$CC -c -o ...' might not be portable. But, oh, well... Is calling # `ac_compile' like this correct, after all? if eval "$ac_compile -S -fstack-protector -o conftest.s" 2> /dev/null; then] @@ -395,6 +324,40 @@ else ssp_possible=no] AC_MSG_RESULT([no]) [fi] +[# Strong stack smashing protector. +ssp_strong_possible=yes] +AC_MSG_CHECKING([whether `$CC' accepts `-fstack-protector-strong']) +# Is this a reliable test case? +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +void foo (void) { volatile char a[8]; a[3]; } +]])]) +[# `$CC -c -o ...' might not be portable. But, oh, well... Is calling +# `ac_compile' like this correct, after all? +if eval "$ac_compile -S -fstack-protector-strong -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.s +else + ssp_strong_possible=no] + AC_MSG_RESULT([no]) +[fi] +[# Global stack smashing protector. +ssp_global_possible=yes] +AC_MSG_CHECKING([whether `$CC' accepts `-mstack-protector-guard=global']) +# Is this a reliable test case? +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +void foo (void) { volatile char a[8]; a[3]; } +]])]) +[# `$CC -c -o ...' might not be portable. But, oh, well... Is calling +# `ac_compile' like this correct, after all? +if eval "$ac_compile -S -fstack-protector -mstack-protector-guard=global -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.s +else + ssp_global_possible=no] + AC_MSG_RESULT([no]) +[fi] ]) dnl Check if the C compiler supports `-mstack-arg-probe' (Cygwin). @@ -402,8 +365,10 @@ AC_DEFUN([grub_CHECK_STACK_ARG_PROBE],[ [# Smashing stack arg probe. sap_possible=yes] AC_MSG_CHECKING([whether `$CC' accepts `-mstack-arg-probe']) -AC_LANG_CONFTEST([[void foo (void) { volatile char a[8]; a[3]; }]]) -[if eval "$ac_compile -S -mstack-arg-probe -o conftest.s" 2> /dev/null; then] +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +void foo (void) { volatile char a[8]; a[3]; } +]])]) +[if eval "$ac_compile -S -mstack-arg-probe -Werror -o conftest.s" 2> /dev/null; then] AC_MSG_RESULT([yes]) [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? rm -f conftest.s @@ -413,15 +378,15 @@ else [fi] ]) -dnl Check if ln can handle directories properly (mingw). +dnl Check if ln -s can handle directories properly (mingw). AC_DEFUN([grub_CHECK_LINK_DIR],[ -AC_MSG_CHECKING([whether ln can handle directories properly]) +AC_MSG_CHECKING([whether ln -s can handle directories properly]) [mkdir testdir 2>/dev/null case $srcdir in [\\/$]* | ?:[\\/]* ) reldir=$srcdir/include/grub/util ;; *) reldir=../$srcdir/include/grub/util ;; esac -if ln -s $reldir testdir/util 2>/dev/null ; then] +if ln -s $reldir testdir/util 2>/dev/null && rm -f testdir/util 2>/dev/null ; then] AC_MSG_RESULT([yes]) [link_dir=yes else @@ -437,7 +402,7 @@ AC_DEFUN([grub_CHECK_PIE],[ pie_possible=yes] AC_MSG_CHECKING([whether `$CC' has `-fPIE' as default]) # Is this a reliable test case? -AC_LANG_CONFTEST([[ +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ #ifdef __PIE__ int main() { return 0; @@ -445,7 +410,7 @@ int main() { #else #error NO __PIE__ DEFINED #endif -]]) +]])]) [# `$CC -c -o ...' might not be portable. But, oh, well... Is calling # `ac_compile' like this correct, after all? @@ -458,3 +423,89 @@ else AC_MSG_RESULT([no]) [fi] ]) + +AC_DEFUN([grub_CHECK_LINK_PIE],[ +[# Position independent executable. +link_nopie_needed=no] +AC_MSG_CHECKING([whether linker needs disabling of PIE to work]) +AC_LANG_CONFTEST([AC_LANG_SOURCE([[]])]) + +[if eval "$ac_compile -Wl,-r -nostdlib -Werror -o conftest.o" 2> /dev/null; then] + AC_MSG_RESULT([no]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.o +else + link_nopie_needed=yes] + AC_MSG_RESULT([yes]) +[fi] +]) + + +dnl Check if the Linker supports `-no-pie'. +AC_DEFUN([grub_CHECK_NO_PIE], +[AC_MSG_CHECKING([whether linker accepts -no-pie]) +AC_CACHE_VAL(grub_cv_cc_ld_no_pie, +[save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -no-pie -nostdlib -Werror" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_ld_no_pie=yes], + [grub_cv_cc_ld_no_pie=no]) +LDFLAGS="$save_LDFLAGS" +]) +AC_MSG_RESULT([$grub_cv_cc_ld_no_pie]) +nopie_possible=no +if test "x$grub_cv_cc_ld_no_pie" = xyes ; then + nopie_possible=yes +fi +]) + +AC_DEFUN([grub_CHECK_NO_PIE_ONEWORD], +[AC_MSG_CHECKING([whether linker accepts -nopie]) +AC_CACHE_VAL(grub_cv_cc_ld_no_pie_oneword, +[save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -nopie -nostdlib -Werror" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_ld_no_pie_oneword=yes], + [grub_cv_cc_ld_no_pie_oneword=no]) +LDFLAGS="$save_LDFLAGS" +]) +AC_MSG_RESULT([$grub_cv_cc_ld_no_pie_oneword]) +nopie_oneword_possible=no +if test "x$grub_cv_cc_ld_no_pie_oneword" = xyes ; then + nopie_oneword_possible=yes +fi +]) + +dnl Check if the C compiler supports `-fPIC'. +AC_DEFUN([grub_CHECK_PIC],[ +[# Position independent executable. +pic_possible=yes] +AC_MSG_CHECKING([whether `$CC' has `-fPIC' as default]) +# Is this a reliable test case? +AC_LANG_CONFTEST([AC_LANG_SOURCE([[ +#ifdef __PIC__ +int main() { + return 0; +} +#else +#error NO __PIC__ DEFINED +#endif +]])]) + +[# `$CC -c -o ...' might not be portable. But, oh, well... Is calling +# `ac_compile' like this correct, after all? +if eval "$ac_compile -S -o conftest.s" 2> /dev/null; then] + AC_MSG_RESULT([yes]) + [# Should we clear up other files as well, having called `AC_LANG_CONFTEST'? + rm -f conftest.s +else + pic_possible=no] + AC_MSG_RESULT([no]) +[fi] +]) + +dnl Create an output variable with the transformed name of a GRUB utility +dnl program. +AC_DEFUN([grub_TRANSFORM],[dnl +AC_SUBST(AS_TR_SH([$1]), [`AS_ECHO([$1]) | sed "$program_transform_name"`])dnl +]) diff --git a/asm-tests/arm.S b/asm-tests/arm.S new file mode 100644 index 000000000..97c2546bf --- /dev/null +++ b/asm-tests/arm.S @@ -0,0 +1,20 @@ +/* on arm clang doesn't support .arch directive */ + + .text + .syntax unified + +#if !defined (__thumb2__) + .arch armv7a + .arm +#else + .arch armv7 + .thumb +#endif + mcr p15, 0, r11, c7, c14, 2 + + /* clang restricts access to dsb/isb despite .arch */ + dsb + isb + + + diff --git a/asm-tests/i386-pc.S b/asm-tests/i386-pc.S new file mode 100644 index 000000000..d037744f9 --- /dev/null +++ b/asm-tests/i386-pc.S @@ -0,0 +1,18 @@ +/* on x86 old clang doesn't support .code16 + newer clang supports it but creates 6-byte jumps instead of 3-byte ones + which makes us go over boot sector size. + Starting with 3.9 clang emits 3-byte jumps but still creates 8-bytes movl + instead of 5-bytes, so code overflows into data. */ + + .code16 + jmp far + .org 4 + jmp nearer + .org 6 + movl nearer, %ebx + .org 11 + .space 100 +nearer: + .space 200 +far: + .byte 0 diff --git a/asm-tests/i386.S b/asm-tests/i386.S new file mode 100644 index 000000000..30adc4fe2 --- /dev/null +++ b/asm-tests/i386.S @@ -0,0 +1,4 @@ +/* on x86 old clang doesn't support .code16 */ + + .code16 + movb %al, %bl diff --git a/asm-tests/mips.S b/asm-tests/mips.S new file mode 100644 index 000000000..1312d47d5 --- /dev/null +++ b/asm-tests/mips.S @@ -0,0 +1,11 @@ +/* on mips clang doesn't support privilegied instructions, doubleword store/load + and crashes with hand-written assembly + */ + + .set mips3 + sync + ld $t2, 0($t1) + +a: + addiu $t1, $s0, (b - a) +b: nop diff --git a/asm-tests/powerpc.S b/asm-tests/powerpc.S new file mode 100644 index 000000000..396a6cce9 --- /dev/null +++ b/asm-tests/powerpc.S @@ -0,0 +1,8 @@ +/* clang <= 3.3 doesn't handle most of ppc assembly, not even inline assembly + used by gcrypt */ +/* Cache invalidation loop is a fair test. */ + li 5, 0 +1: icbi 5, 3 + addi 5, 5, 32 + cmpw 5, 4 + blt 1b diff --git a/asm-tests/sparc64.S b/asm-tests/sparc64.S new file mode 100644 index 000000000..03c5fe02a --- /dev/null +++ b/asm-tests/sparc64.S @@ -0,0 +1,9 @@ + .text +1: + /* A small list of examples of what clang doesn't support. */ + clr %o0 + lduw [%o4 + 4], %o4 + and %o6, ~0xff, %o6 + stw %o5, [%o3] + bne,pt %icc, 1b + nop diff --git a/autogen.sh b/autogen.sh index eb251f9f0..ebd614792 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,23 +1,149 @@ -#! /bin/sh +#! /usr/bin/env bash set -e -aclocal -autoconf -autoheader +if [ ! -e grub-core/lib/gnulib/stdlib.in.h ]; then + echo "Gnulib not yet bootstrapped; run ./bootstrap instead." >&2 + exit 1 +fi -# FIXME: automake doesn't like that there's no Makefile.am -automake -a -c -f || true +# Detect python +if [ -z "$PYTHON" ]; then + for i in python3 python3.10 python; do + if command -v "$i" > /dev/null 2>&1; then + PYTHON="$i" + echo "Using $PYTHON..." + break + fi + done -echo timestamp > stamp-h.in + if [ -z "$PYTHON" ]; then + echo "python not found." >&2 + exit 1 + fi +fi -python util/import_gcry.py lib/libgcrypt/ . +export LC_COLLATE=C +unset LC_ALL -for rmk in conf/*.rmk ${GRUB_CONTRIB}/*/conf/*.rmk; do - if test -e $rmk ; then - ruby genmk.rb < $rmk > `echo $rmk | sed 's/\.rmk$/.mk/'` +find . -iname '*.[ch]' ! -ipath './grub-core/lib/libgcrypt-grub/*' ! -ipath './build-aux/*' ! -ipath './grub-core/lib/libgcrypt/src/misc.c' ! -ipath './grub-core/lib/libgcrypt/src/global.c' ! -ipath './grub-core/lib/libgcrypt/src/secmem.c' ! -ipath './util/grub-gen-widthspec.c' ! -ipath './util/grub-gen-asciih.c' ! -ipath './gnulib/*' ! -ipath './grub-core/lib/gnulib/*' |sort > po/POTFILES.in +find util -iname '*.in' ! -name Makefile.in |sort > po/POTFILES-shell.in + +echo "Importing unicode..." +${PYTHON} util/import_unicode.py unicode/UnicodeData.txt unicode/BidiMirroring.txt unicode/ArabicShaping.txt grub-core/unidata.c + +echo "Importing libgcrypt..." +${PYTHON} util/import_gcry.py grub-core/lib/libgcrypt/ grub-core +sed -n -f util/import_gcrypth.sed < grub-core/lib/libgcrypt/src/gcrypt.h.in > include/grub/gcrypt/gcrypt.h +if [ -f include/grub/gcrypt/g10lib.h ]; then + rm include/grub/gcrypt/g10lib.h +fi +if [ -d grub-core/lib/libgcrypt-grub/mpi/generic ]; then + rm -rf grub-core/lib/libgcrypt-grub/mpi/generic +fi +cp grub-core/lib/libgcrypt-grub/src/g10lib.h include/grub/gcrypt/g10lib.h +cp -R grub-core/lib/libgcrypt/mpi/generic grub-core/lib/libgcrypt-grub/mpi/generic + +for x in mpi-asm-defs.h mpih-add1.c mpih-sub1.c mpih-mul1.c mpih-mul2.c mpih-mul3.c mpih-lshift.c mpih-rshift.c; do + if [ -h grub-core/lib/libgcrypt-grub/mpi/"$x" ] || [ -f grub-core/lib/libgcrypt-grub/mpi/"$x" ]; then + rm grub-core/lib/libgcrypt-grub/mpi/"$x" + fi + cp grub-core/lib/libgcrypt-grub/mpi/generic/"$x" grub-core/lib/libgcrypt-grub/mpi/"$x" +done + +echo "Importing libtasn1..." +if [ -d grub-core/lib/libtasn1-grub ]; then + rm -rf grub-core/lib/libtasn1-grub +fi + +mkdir -p grub-core/lib/libtasn1-grub/lib +cp grub-core/lib/libtasn1/lib/*.[ch] grub-core/lib/libtasn1-grub/lib +cp grub-core/lib/libtasn1/libtasn1.h grub-core/lib/libtasn1-grub/ + +if [ -d grub-core/tests/asn1/tests ]; then + rm -rf grub-core/tests/asn1/tests +fi + +mkdir grub-core/tests/asn1/tests +cp grub-core/lib/libtasn1/tests/*.[ch] grub-core/tests/asn1/tests + +for patch in \ + 0001-libtasn1-disable-code-not-needed-in-grub.patch \ + 0002-libtasn1-replace-strcat-with-strcpy-in-_asn1_str_cat.patch \ + 0003-libtasn1-replace-strcat-with-_asn1_str_cat.patch \ + 0004-libtasn1-adjust-the-header-paths-in-libtasn1.h.patch \ + 0005-libtasn1-Use-grub_divmod64-for-division.patch \ + 0006-libtasn1-fix-the-potential-buffer-overrun.patch \ + 0007-asn1_test-include-asn1_test.h-only.patch \ + 0008-asn1_test-rename-the-main-functions-to-the-test-name.patch \ + 0009-asn1_test-return-either-0-or-1-to-reflect-the-result.patch \ + 0010-asn1_test-remove-verbose-and-the-unnecessary-printf.patch \ + 0011-asn1_test-print-the-error-messages-with-grub_printf.patch \ + 0012-asn1_test-use-the-grub-specific-functions-and-types.patch \ + 0013-asn1_test-enable-the-testcase-only-when-GRUB_LONG_MA.patch ; do + patch -p1 -i grub-core/lib/libtasn1-patches/$patch +done + +echo "Generating Automake input..." + +# Automake doesn't like including files from a path outside the project. +rm -f contrib grub-core/contrib +if [ "x${GRUB_CONTRIB}" != x ]; then + [ "${GRUB_CONTRIB}" = contrib ] || ln -s "${GRUB_CONTRIB}" contrib + [ "${GRUB_CONTRIB}" = grub-core/contrib ] || ln -s ../contrib grub-core/contrib +fi + +UTIL_DEFS='Makefile.util.def Makefile.utilgcry.def' +CORE_DEFS='grub-core/Makefile.core.def grub-core/Makefile.gcry.def' + +for extra in contrib/*/Makefile.util.def; do + if test -e "$extra"; then + UTIL_DEFS="$UTIL_DEFS $extra" fi done -sh gendistlist.sh > DISTLIST + +for extra in contrib/*/Makefile.core.def; do + if test -e "$extra"; then + CORE_DEFS="$CORE_DEFS $extra" + fi +done + +${PYTHON} gentpl.py $UTIL_DEFS > Makefile.util.am +${PYTHON} gentpl.py $CORE_DEFS > grub-core/Makefile.core.am + +for extra in contrib/*/Makefile.common; do + if test -e "$extra"; then + echo "include $extra" >> Makefile.util.am + echo "include $extra" >> grub-core/Makefile.core.am + fi +done + +for extra in contrib/*/Makefile.util.common; do + if test -e "$extra"; then + echo "include $extra" >> Makefile.util.am + fi +done + +for extra in contrib/*/Makefile.core.common; do + if test -e "$extra"; then + echo "include $extra" >> grub-core/Makefile.core.am + fi +done + +echo "Saving timestamps..." +echo timestamp > stamp-h.in + +if [ -z "$FROM_BOOTSTRAP" ]; then + # Unaided autoreconf is likely to install older versions of many files + # than the ones provided by Gnulib, but in most cases this won't matter + # very much. This mode is provided so that you can run ./autogen.sh to + # regenerate the GRUB build system in an unpacked release tarball (perhaps + # after patching it), even on systems that don't have access to + # gnulib.git. + echo "Running autoreconf..." + cp -a INSTALL INSTALL.grub + autoreconf -vif + mv INSTALL.grub INSTALL +fi exit 0 diff --git a/bootstrap b/bootstrap new file mode 100755 index 000000000..dc2238f4a --- /dev/null +++ b/bootstrap @@ -0,0 +1,1118 @@ +#! /bin/sh +# Print a version string. +scriptversion=2022-01-26.05; # UTC + +# Bootstrap this package from checked-out sources. + +# Copyright (C) 2003-2022 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Originally written by Paul Eggert. The canonical version of this +# script is maintained as build-aux/bootstrap in gnulib, however, to +# be useful to your project, you should place a copy of it under +# version control in the top-level directory of your project. The +# intent is that all customization can be done with a bootstrap.conf +# file also maintained in your version control; gnulib comes with a +# template build-aux/bootstrap.conf to get you started. + +# Please report bugs or propose patches to bug-gnulib@gnu.org. + +nl=' +' + +# Ensure file names are sorted consistently across platforms. +LC_ALL=C +export LC_ALL + +# Ensure that CDPATH is not set. Otherwise, the output from cd +# would cause trouble in at least one use below. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +local_gl_dir=gl + +# Honor $PERL, but work even if there is none. +PERL="${PERL-perl}" + +me=$0 + +default_gnulib_url=https://git.savannah.gnu.org/git/gnulib.git + +usage() { + cat <&2 +} + +# warn_ WORD1... +warn_ () +{ + # If IFS does not start with ' ', set it and emit the warning in a subshell. + case $IFS in + ' '*) warnf_ '%s\n' "$*";; + *) (IFS=' '; warn_ "$@");; + esac +} + +# die WORD1... +die() { warn_ "$@"; exit 1; } + +# Configuration. + +# Name of the Makefile.am +gnulib_mk=gnulib.mk + +# List of gnulib modules needed. +gnulib_modules= + +# Any gnulib files needed that are not in modules. +gnulib_files= + +: ${AUTOPOINT=autopoint} +: ${AUTORECONF=autoreconf} + +# A function to be called for each unrecognized option. Returns 0 if +# the option in $1 has been processed by the function. Returns 1 if +# the option has not been processed by the function. Override it via +# your own definition in bootstrap.conf + +bootstrap_option_hook() { return 1; } + +# A function to be called in order to print the --help information +# corresponding to user-defined command-line options. + +bootstrap_print_option_usage_hook() { :; } + +# A function to be called right after gnulib-tool is run. +# Override it via your own definition in bootstrap.conf. +bootstrap_post_import_hook() { :; } + +# A function to be called after everything else in this script. +# Override it via your own definition in bootstrap.conf. +bootstrap_epilogue() { :; } + +# The command to download all .po files for a specified domain into a +# specified directory. Fill in the first %s with the destination +# directory and the second with the domain name. +po_download_command_format=\ +"wget --mirror --level=1 -nd -nv -A.po -P '%s' \ + https://translationproject.org/latest/%s/" + +# Prefer a non-empty tarname (4th argument of AC_INIT if given), else +# fall back to the package name (1st argument with munging). +extract_package_name=' + /^AC_INIT(\[*/{ + s/// + /^[^,]*,[^,]*,[^,]*,[ []*\([^][ ,)]\)/{ + s//\1/ + s/[],)].*// + p + q + } + s/[],)].*// + s/^GNU // + y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + s/[^abcdefghijklmnopqrstuvwxyz0123456789_]/-/g + p + } +' +package=$(${AUTOCONF:-autoconf} --trace AC_INIT:\$4 configure.ac 2>/dev/null) +if test -z "$package"; then + package=$(sed -n "$extract_package_name" configure.ac) \ + || die 'cannot find package name in configure.ac' +fi +gnulib_name=lib$package + +build_aux=build-aux +source_base=lib +m4_base=m4 +doc_base=doc +tests_base=tests +gnulib_extra_files=" + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub + doc/INSTALL +" + +# Additional gnulib-tool options to use. Use "\newline" to break lines. +gnulib_tool_option_extras= + +# Other locale categories that need message catalogs. +EXTRA_LOCALE_CATEGORIES= + +# Additional xgettext options to use. Use "\\\newline" to break lines. +XGETTEXT_OPTIONS='\\\ + --flag=_:1:pass-c-format\\\ + --flag=N_:1:pass-c-format\\\ + --flag=error:3:c-format --flag=error_at_line:5:c-format\\\ +' + +# Package bug report address and copyright holder for gettext files +COPYRIGHT_HOLDER='Free Software Foundation, Inc.' +MSGID_BUGS_ADDRESS=bug-$package@gnu.org + +# Files we don't want to import. +excluded_files= + +# File that should exist in the top directory of a checked out hierarchy, +# but not in a distribution tarball. +checkout_only_file=README-hacking + +# Whether to use copies instead of symlinks. +copy=false + +# Set this to '.cvsignore .gitignore' in bootstrap.conf if you want +# those files to be generated in directories like lib/, m4/, and po/. +# Or set it to 'auto' to make this script select which to use based +# on which version control system (if any) is used in the source directory. +vc_ignore=auto + +# Set this to true in bootstrap.conf to enable --bootstrap-sync by +# default. +bootstrap_sync=false + +# Use git to update gnulib sources +use_git=true + +check_exists() { + if test "$1" = "--verbose"; then + ($2 --version /dev/null 2>&1 + if test $? -ge 126; then + # If not found, run with diagnostics as one may be + # presented with env variables to set to find the right version + ($2 --version /dev/null 2>&1 + fi + + test $? -lt 126 +} + +# find_tool ENVVAR NAMES... +# ------------------------- +# Search for a required program. Use the value of ENVVAR, if set, +# otherwise find the first of the NAMES that can be run. +# If found, set ENVVAR to the program name, die otherwise. +# +# FIXME: code duplication, see also gnu-web-doc-update. +find_tool () +{ + find_tool_envvar=$1 + shift + find_tool_names=$@ + eval "find_tool_res=\$$find_tool_envvar" + if test x"$find_tool_res" = x; then + for i; do + if check_exists $i; then + find_tool_res=$i + break + fi + done + fi + if test x"$find_tool_res" = x; then + warn_ "one of these is required: $find_tool_names;" + die "alternatively set $find_tool_envvar to a compatible tool" + fi + eval "$find_tool_envvar=\$find_tool_res" + eval "export $find_tool_envvar" +} + +# Strip blank and comment lines to leave significant entries. +gitignore_entries() { + sed '/^#/d; /^$/d' "$@" +} + +# If $STR is not already on a line by itself in $FILE, insert it at the start. +# Entries are inserted at the start of the ignore list to ensure existing +# entries starting with ! are not overridden. Such entries support +# whitelisting exceptions after a more generic blacklist pattern. +insert_if_absent() { + file=$1 + str=$2 + test -f $file || touch $file + test -r $file || die "Error: failed to read ignore file: $file" + duplicate_entries=$(gitignore_entries $file | sort | uniq -d) + if [ "$duplicate_entries" ] ; then + die "Error: Duplicate entries in $file: " $duplicate_entries + fi + linesold=$(gitignore_entries $file | wc -l) + linesnew=$( { echo "$str"; cat $file; } | gitignore_entries | sort -u | wc -l) + if [ $linesold != $linesnew ] ; then + { echo "$str" | cat - $file > $file.bak && mv $file.bak $file; } \ + || die "insert_if_absent $file $str: failed" + fi +} + +# Adjust $PATTERN for $VC_IGNORE_FILE and insert it with +# insert_if_absent. +insert_vc_ignore() { + vc_ignore_file="$1" + pattern="$2" + case $vc_ignore_file in + *.gitignore) + # A .gitignore entry that does not start with '/' applies + # recursively to subdirectories, so prepend '/' to every + # .gitignore entry. + pattern=$(echo "$pattern" | sed s,^,/,);; + esac + insert_if_absent "$vc_ignore_file" "$pattern" +} + +symlink_to_dir() +{ + src=$1/$2 + dst=${3-$2} + + test -f "$src" && { + + # If the destination directory doesn't exist, create it. + # This is required at least for "lib/uniwidth/cjk.h". + dst_dir=$(dirname "$dst") + if ! test -d "$dst_dir"; then + mkdir -p "$dst_dir" + + # If we've just created a directory like lib/uniwidth, + # tell version control system(s) it's ignorable. + # FIXME: for now, this does only one level + parent=$(dirname "$dst_dir") + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore $ig "${dst_dir##*/}" + done + fi + + if $copy; then + { + test ! -h "$dst" || { + echo "$me: rm -f $dst" && + rm -f "$dst" + } + } && + test -f "$dst" && + cmp -s "$src" "$dst" || { + echo "$me: cp -fp $src $dst" && + cp -fp "$src" "$dst" + } + else + # Leave any existing symlink alone, if it already points to the source, + # so that broken build tools that care about symlink times + # aren't confused into doing unnecessary builds. Conversely, if the + # existing symlink's timestamp is older than the source, make it afresh, + # so that broken tools aren't confused into skipping needed builds. See + # . + test -h "$dst" && + src_ls=$(ls -diL "$src" 2>/dev/null) && set $src_ls && src_i=$1 && + dst_ls=$(ls -diL "$dst" 2>/dev/null) && set $dst_ls && dst_i=$1 && + test "$src_i" = "$dst_i" && + both_ls=$(ls -dt "$src" "$dst") && + test "X$both_ls" = "X$dst$nl$src" || { + dot_dots= + case $src in + /*) ;; + *) + case /$dst/ in + *//* | */../* | */./* | /*/*/*/*/*/) + die "invalid symlink calculation: $src -> $dst";; + /*/*/*/*/) dot_dots=../../../;; + /*/*/*/) dot_dots=../../;; + /*/*/) dot_dots=../;; + esac;; + esac + + echo "$me: ln -fs $dot_dots$src $dst" && + ln -fs "$dot_dots$src" "$dst" + } + fi + } +} + +# Override the default configuration, if necessary. +# Make sure that bootstrap.conf is sourced from the current directory +# if we were invoked as "sh bootstrap". +case "$0" in + */*) test -r "$0.conf" && . "$0.conf" ;; + *) test -r "$0.conf" && . ./"$0.conf" ;; +esac + +if test "$vc_ignore" = auto; then + vc_ignore= + test -d .git && vc_ignore=.gitignore + test -d CVS && vc_ignore="$vc_ignore .cvsignore" +fi + +if test x"$gnulib_modules$gnulib_files$gnulib_extra_files" = x; then + use_gnulib=false +else + use_gnulib=true +fi + +# Translate configuration into internal form. + +# Parse options. + +for option +do + case $option in + --help) + usage + exit;; + --version) + set -e + echo "bootstrap $scriptversion" + echo "$copyright" + exit 0 + ;; + --gnulib-srcdir=*) + GNULIB_SRCDIR=${option#--gnulib-srcdir=};; + --skip-po) + SKIP_PO=t;; + --force) + checkout_only_file=;; + --copy) + copy=true;; + --bootstrap-sync) + bootstrap_sync=true;; + --no-bootstrap-sync) + bootstrap_sync=false;; + --no-git) + use_git=false;; + *) + bootstrap_option_hook $option || die "$option: unknown option";; + esac +done + +$use_git || test -d "$GNULIB_SRCDIR" \ + || die "Error: --no-git requires --gnulib-srcdir" + +if test -n "$checkout_only_file" && test ! -r "$checkout_only_file"; then + die "Bootstrapping from a non-checked-out distribution is risky." +fi + +# Die if there is no AC_CONFIG_AUX_DIR($build_aux) line in configure.ac. +found_aux_dir=no +grep '^[ ]*AC_CONFIG_AUX_DIR(\['"$build_aux"'\])' configure.ac \ + >/dev/null && found_aux_dir=yes +grep '^[ ]*AC_CONFIG_AUX_DIR('"$build_aux"')' configure.ac \ + >/dev/null && found_aux_dir=yes +test $found_aux_dir = yes \ + || die "configure.ac lacks 'AC_CONFIG_AUX_DIR([$build_aux])'; add it" + +# If $build_aux doesn't exist, create it now, otherwise some bits +# below will malfunction. If creating it, also mark it as ignored. +if test ! -d $build_aux; then + mkdir $build_aux + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + insert_vc_ignore $dot_ig $build_aux + done +fi + +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +sort_ver() { # sort -V is not generally available + ver1="$1" + ver2="$2" + + # split on '.' and compare each component + i=1 + while : ; do + p1=$(echo "$ver1" | cut -d. -f$i) + p2=$(echo "$ver2" | cut -d. -f$i) + if [ ! "$p1" ]; then + echo "$1 $2" + break + elif [ ! "$p2" ]; then + echo "$2 $1" + break + elif [ ! "$p1" = "$p2" ]; then + if [ "$p1" -gt "$p2" ] 2>/dev/null; then # numeric comparison + echo "$2 $1" + elif [ "$p2" -gt "$p1" ] 2>/dev/null; then # numeric comparison + echo "$1 $2" + else # numeric, then lexicographic comparison + lp=$(printf "$p1\n$p2\n" | LANG=C sort -n | tail -n1) + if [ "$lp" = "$p2" ]; then + echo "$1 $2" + else + echo "$2 $1" + fi + fi + break + fi + i=$(($i+1)) + done +} + +get_version_sed=' +# Move version to start of line. +s/.*[v ]\([0-9]\)/\1/ + +# Skip lines that do not start with version. +/^[0-9]/!d + +# Remove characters after the version. +s/[^.a-z0-9-].*// + +# The first component must be digits only. +s/^\([0-9]*\)[a-z-].*/\1/ + +#the following essentially does s/5.005/5.5/ +s/\.0*\([1-9]\)/.\1/g +p +q' + +get_version() { + app=$1 + + $app --version >/dev/null 2>&1 || { $app --version; return 1; } + + $app --version 2>&1 | sed -n "$get_version_sed" +} + +check_versions() { + ret=0 + + while read app req_ver; do + # We only need libtoolize from the libtool package. + if test "$app" = libtool; then + app=libtoolize + fi + # Exempt git if --no-git is in effect. + if test "$app" = git; then + $use_git || continue + fi + # Honor $APP variables ($TAR, $AUTOCONF, etc.) + appvar=$(echo $app | LC_ALL=C tr '[a-z]-' '[A-Z]_') + test "$appvar" = TAR && appvar=AMTAR + case $appvar in + GZIP) ;; # Do not use $GZIP: it contains gzip options. + PERL::*) ;; # Keep perl modules as-is + *) eval "app=\${$appvar-$app}" ;; + esac + + # Handle the still-experimental Automake-NG programs specially. + # They remain named as the mainstream Automake programs ("automake", + # and "aclocal") to avoid gratuitous incompatibilities with + # pre-existing usages (by, say, autoreconf, or custom autogen.sh + # scripts), but correctly identify themselves (as being part of + # "GNU automake-ng") when asked their version. + case $app in + automake-ng|aclocal-ng) + app=${app%-ng} + ($app --version | grep '(GNU automake-ng)') >/dev/null 2>&1 || { + warn_ "Error: '$app' not found or not from Automake-NG" + ret=1 + continue + } ;; + # Another check is for perl modules. These can be written as + # e.g. perl::XML::XPath in case of XML::XPath module, etc. + perl::*) + # Extract module name + app="${app#perl::}" + if ! $PERL -m"$app" -e 'exit 0' >/dev/null 2>&1; then + warn_ "Error: perl module '$app' not found" + ret=1 + fi + continue + ;; + esac + if [ "$req_ver" = "-" ]; then + # Merely require app to exist; not all prereq apps are well-behaved + # so we have to rely on $? rather than get_version. + if ! check_exists --verbose $app; then + warn_ "Error: '$app' not found" + ret=1 + fi + else + # Require app to produce a new enough version string. + inst_ver=$(get_version $app) + if [ ! "$inst_ver" ]; then + warn_ "Error: '$app' not found" + ret=1 + else + latest_ver=$(sort_ver $req_ver $inst_ver | cut -d' ' -f2) + if [ ! "$latest_ver" = "$inst_ver" ]; then + warnf_ '%s\n' \ + "Error: '$app' version == $inst_ver is too old" \ + " '$app' version >= $req_ver is required" + ret=1 + fi + fi + fi + done + + return $ret +} + +print_versions() { + echo "Program Min_version" + echo "----------------------" + printf %s "$buildreq" + echo "----------------------" + # can't depend on column -t +} + +# Find sha1sum, named gsha1sum on MacPorts, shasum on Mac OS X 10.6. +# Also find the compatible sha1 utility on the BSDs +if test x"$SKIP_PO" = x; then + find_tool SHA1SUM sha1sum gsha1sum shasum sha1 +fi + +use_libtool=0 +# We'd like to use grep -E, to see if any of LT_INIT, +# AC_PROG_LIBTOOL, AM_PROG_LIBTOOL is used in configure.ac, +# but that's not portable enough (e.g., for Solaris). +grep '^[ ]*A[CM]_PROG_LIBTOOL' configure.ac >/dev/null \ + && use_libtool=1 +grep '^[ ]*LT_INIT' configure.ac >/dev/null \ + && use_libtool=1 +if test $use_libtool = 1; then + find_tool LIBTOOLIZE glibtoolize libtoolize +fi + +# gnulib-tool requires at least automake and autoconf. +# If either is not listed, add it (with minimum version) as a prerequisite. +case $buildreq in + *automake*) ;; + *) buildreq="automake 1.9 +$buildreq" ;; +esac +case $buildreq in + *autoconf*) ;; + *) buildreq="autoconf 2.59 +$buildreq" ;; +esac + +# When we can deduce that gnulib-tool will require patch, +# and when patch is not already listed as a prerequisite, add it, too. +if test -d "$local_gl_dir" \ + && ! find "$local_gl_dir" -name '*.diff' -exec false {} +; then + case $buildreq in + *patch*) ;; + *) buildreq="patch - +$buildreq" ;; + esac +fi + +if ! printf "$buildreq" | check_versions; then + echo >&2 + if test -f README-prereq; then + die "See README-prereq for how to get the prerequisite programs" + else + die "Please install the prerequisite programs" + fi +fi + +# Warn the user if autom4te appears to be broken; this causes known +# issues with at least gettext 0.18.3. +probe=$(echo 'm4_quote([hi])' | autom4te -l M4sugar -t 'm4_quote:$%' -) +if test "x$probe" != xhi; then + warn_ "WARNING: your autom4te wrapper eats stdin;" + warn_ "if bootstrap fails, consider upgrading your autotools" +fi + +echo "$0: Bootstrapping from checked-out $package sources..." + +# See if we can use gnulib's git-merge-changelog merge driver. +if $use_git && test -d .git && check_exists git; then + if git config merge.merge-changelog.driver >/dev/null ; then + : + elif check_exists git-merge-changelog; then + echo "$0: initializing git-merge-changelog driver" + git config merge.merge-changelog.name 'GNU-style ChangeLog merge driver' + git config merge.merge-changelog.driver 'git-merge-changelog %O %A %B' + else + echo "$0: consider installing git-merge-changelog from gnulib" + fi +fi + + +cleanup_gnulib() { + status=$? + rm -fr "$gnulib_path" + exit $status +} + +git_modules_config () { + test -f .gitmodules && git config --file .gitmodules "$@" +} + +if $use_gnulib; then + if $use_git; then + gnulib_path=$(git_modules_config submodule.gnulib.path) + test -z "$gnulib_path" && gnulib_path=gnulib + fi + + # Get gnulib files. Populate $GNULIB_SRCDIR, possibly updating a + # submodule, for use in the rest of the script. + + case ${GNULIB_SRCDIR--} in + -) + # Note that $use_git is necessarily true in this case. + if git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + git submodule init -- "$gnulib_path" || exit $? + git submodule update -- "$gnulib_path" || exit $? + + elif [ ! -d "$gnulib_path" ]; then + echo "$0: getting gnulib files..." + + trap cleanup_gnulib 1 2 13 15 + + shallow= + if test -z "$GNULIB_REVISION"; then + git clone -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' + git clone $shallow ${GNULIB_URL:-$default_gnulib_url} "$gnulib_path" \ + || cleanup_gnulib + else + git fetch -h 2>&1 | grep -- --depth > /dev/null && shallow='--depth 2' + mkdir -p "$gnulib_path" + # Only want a shallow checkout of $GNULIB_REVISION, but git does not + # support cloning by commit hash. So attempt a shallow fetch by commit + # hash to minimize the amount of data downloaded and changes needed to + # be processed, which can drastically reduce download and processing + # time for checkout. If the fetch by commit fails, a shallow fetch can + # not be performed because we do not know what the depth of the commit + # is without fetching all commits. So fallback to fetching all commits. + git -C "$gnulib_path" init + git -C "$gnulib_path" remote add origin ${GNULIB_URL:-$default_gnulib_url} + git -C "$gnulib_path" fetch $shallow origin "$GNULIB_REVISION" \ + || git -C "$gnulib_path" fetch origin \ + || cleanup_gnulib + git -C "$gnulib_path" reset --hard FETCH_HEAD + fi + + trap - 1 2 13 15 + fi + GNULIB_SRCDIR=$gnulib_path + ;; + *) + # Use GNULIB_SRCDIR directly or as a reference. + if $use_git && test -d "$GNULIB_SRCDIR"/.git && \ + git_modules_config submodule.gnulib.url >/dev/null; then + echo "$0: getting gnulib files..." + if git submodule -h|grep -- --reference > /dev/null; then + # Prefer the one-liner available in git 1.6.4 or newer. + git submodule update --init --reference "$GNULIB_SRCDIR" \ + "$gnulib_path" || exit $? + else + # This fallback allows at least git 1.5.5. + if test -f "$gnulib_path"/gnulib-tool; then + # Since file already exists, assume submodule init already complete. + git submodule update -- "$gnulib_path" || exit $? + else + # Older git can't clone into an empty directory. + rmdir "$gnulib_path" 2>/dev/null + git clone --reference "$GNULIB_SRCDIR" \ + "$(git_modules_config submodule.gnulib.url)" "$gnulib_path" \ + && git submodule init -- "$gnulib_path" \ + && git submodule update -- "$gnulib_path" \ + || exit $? + fi + fi + GNULIB_SRCDIR=$gnulib_path + fi + ;; + esac + + if test -d "$GNULIB_SRCDIR"/.git && test -n "$GNULIB_REVISION" \ + && ! git_modules_config submodule.gnulib.url >/dev/null; then + (cd "$GNULIB_SRCDIR" && git checkout "$GNULIB_REVISION") || cleanup_gnulib + fi + + # $GNULIB_SRCDIR now points to the version of gnulib to use, and + # we no longer need to use git or $gnulib_path below here. + + if $bootstrap_sync; then + cmp -s "$0" "$GNULIB_SRCDIR/build-aux/bootstrap" || { + echo "$0: updating bootstrap and restarting..." + case $(sh -c 'echo "$1"' -- a) in + a) ignored=--;; + *) ignored=ignored;; + esac + exec sh -c \ + 'cp "$1" "$2" && shift && exec "${CONFIG_SHELL-/bin/sh}" "$@"' \ + $ignored "$GNULIB_SRCDIR/build-aux/bootstrap" \ + "$0" "$@" --no-bootstrap-sync + } + fi + + gnulib_tool=$GNULIB_SRCDIR/gnulib-tool + <$gnulib_tool || exit $? +fi + +# Get translations. + +download_po_files() { + subdir=$1 + domain=$2 + echo "$me: getting translations into $subdir for $domain..." + cmd=$(printf "$po_download_command_format" "$subdir" "$domain") + eval "$cmd" +} + +# Mirror .po files to $po_dir/.reference and copy only the new +# or modified ones into $po_dir. Also update $po_dir/LINGUAS. +# Note po files that exist locally only are left in $po_dir but will +# not be included in LINGUAS and hence will not be distributed. +update_po_files() { + # Directory containing primary .po files. + # Overwrite them only when we're sure a .po file is new. + po_dir=$1 + domain=$2 + + # Mirror *.po files into this dir. + # Usually contains *.s1 checksum files. + ref_po_dir="$po_dir/.reference" + + test -d $ref_po_dir || mkdir $ref_po_dir || return + download_po_files $ref_po_dir $domain \ + && ls "$ref_po_dir"/*.po 2>/dev/null | + sed 's|.*/||; s|\.po$||' > "$po_dir/LINGUAS" || return + + langs=$(cd $ref_po_dir && echo *.po | sed 's/\.po//g') + test "$langs" = '*' && langs=x + for po in $langs; do + case $po in x) continue;; esac + new_po="$ref_po_dir/$po.po" + cksum_file="$ref_po_dir/$po.s1" + if ! test -f "$cksum_file" || + ! test -f "$po_dir/$po.po" || + ! $SHA1SUM -c "$cksum_file" < "$new_po" > /dev/null 2>&1; then + echo "$me: updated $po_dir/$po.po..." + cp "$new_po" "$po_dir/$po.po" \ + && $SHA1SUM < "$new_po" > "$cksum_file" || return + fi + done +} + +case $SKIP_PO in +'') + if test -d po; then + update_po_files po $package || exit + fi + + if test -d runtime-po; then + update_po_files runtime-po $package-runtime || exit + fi;; +esac + +version_controlled_file() { + parent=$1 + file=$2 + if test -d .git; then + git rm -n "$file" > /dev/null 2>&1 + elif test -d .svn; then + svn log -r HEAD "$file" > /dev/null 2>&1 + elif test -d CVS; then + grep -F "/${file##*/}/" "$parent/CVS/Entries" 2>/dev/null | + grep '^/[^/]*/[0-9]' > /dev/null + else + warn_ "no version control for $file?" + false + fi +} + +# NOTE: we have to be careful to run both autopoint and libtoolize +# before gnulib-tool, since gnulib-tool is likely to provide newer +# versions of files "installed" by these two programs. +# Then, *after* gnulib-tool (see below), we have to be careful to +# run autoreconf in such a way that it does not run either of these +# two just-pre-run programs. + +# Import from gettext. +with_gettext=yes +grep '^[ ]*AM_GNU_GETTEXT_VERSION(' configure.ac >/dev/null || \ + with_gettext=no + +if test $with_gettext = yes || test $use_libtool = 1; then + + tempbase=.bootstrap$$ + trap "rm -f $tempbase.0 $tempbase.1" 1 2 13 15 + + > $tempbase.0 > $tempbase.1 && + find . ! -type d -print | sort > $tempbase.0 || exit + + if test $with_gettext = yes; then + # Released autopoint has the tendency to install macros that have been + # obsoleted in current gnulib, so run this before gnulib-tool. + echo "$0: $AUTOPOINT --force" + $AUTOPOINT --force || exit + fi + + # Autoreconf runs aclocal before libtoolize, which causes spurious + # warnings if the initial aclocal is confused by the libtoolized + # (or worse out-of-date) macro directory. + # libtoolize 1.9b added the --install option; but we support back + # to libtoolize 1.5.22, where the install action was default. + if test $use_libtool = 1; then + install= + case $($LIBTOOLIZE --help) in + *--install*) install=--install ;; + esac + echo "running: $LIBTOOLIZE $install --copy" + $LIBTOOLIZE $install --copy + fi + + find . ! -type d -print | sort >$tempbase.1 + old_IFS=$IFS + IFS=$nl + for file in $(comm -13 $tempbase.0 $tempbase.1); do + IFS=$old_IFS + parent=${file%/*} + version_controlled_file "$parent" "$file" || { + for dot_ig in x $vc_ignore; do + test $dot_ig = x && continue + ig=$parent/$dot_ig + insert_vc_ignore "$ig" "${file##*/}" + done + } + done + IFS=$old_IFS + + rm -f $tempbase.0 $tempbase.1 + trap - 1 2 13 15 +fi + +# Import from gnulib. + +if $use_gnulib; then + gnulib_tool_options="\ + --no-changelog\ + --aux-dir=$build_aux\ + --doc-base=$doc_base\ + --lib=$gnulib_name\ + --m4-base=$m4_base/\ + --source-base=$source_base/\ + --tests-base=$tests_base\ + --local-dir=$local_gl_dir\ + $gnulib_tool_option_extras\ + " + if test $use_libtool = 1; then + case "$gnulib_tool_options " in + *' --libtool '*) ;; + *) gnulib_tool_options="$gnulib_tool_options --libtool" ;; + esac + fi + echo "$0: $gnulib_tool $gnulib_tool_options --import ..." + $gnulib_tool $gnulib_tool_options --import $gnulib_modules \ + || die "gnulib-tool failed" + + for file in $gnulib_files; do + symlink_to_dir "$GNULIB_SRCDIR" $file \ + || die "failed to symlink $file" + done +fi + +bootstrap_post_import_hook \ + || die "bootstrap_post_import_hook failed" + +# Don't proceed if there are uninitialized submodules. In particular, +# the next step will remove dangling links, which might be links into +# uninitialized submodules. +# +# Uninitialized submodules are listed with an initial dash. +if $use_git && git submodule | grep '^-' >/dev/null; then + die "some git submodules are not initialized. " \ + "Run 'git submodule update --init' and bootstrap again." +fi + +# Remove any dangling symlink matching "*.m4" or "*.[ch]" in some +# gnulib-populated directories. Such .m4 files would cause aclocal to fail. +# The following requires GNU find 4.2.3 or newer. Considering the usual +# portability constraints of this script, that may seem a very demanding +# requirement, but it should be ok. Ignore any failure, which is fine, +# since this is only a convenience to help developers avoid the relatively +# unusual case in which a symlinked-to .m4 file is git-removed from gnulib +# between successive runs of this script. +find "$m4_base" "$source_base" \ + -depth \( -name '*.m4' -o -name '*.[ch]' \) \ + -type l -xtype l -delete > /dev/null 2>&1 + +# Invoke autoreconf with --force --install to ensure upgrades of tools +# such as ylwrap. +AUTORECONFFLAGS="--verbose --install --force -I $m4_base $ACLOCAL_FLAGS" + +# Some systems (RHEL 5) are using ancient autotools, for which the +# --no-recursive option had not been invented. Detect that lack and +# omit the option when it's not supported. FIXME in 2017: remove this +# hack when RHEL 5 autotools are updated, or when they become irrelevant. +case $($AUTORECONF --help) in + *--no-recursive*) AUTORECONFFLAGS="$AUTORECONFFLAGS --no-recursive";; +esac + +# Tell autoreconf not to invoke autopoint or libtoolize; they were run above. +echo "running: AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS" +AUTOPOINT=true LIBTOOLIZE=true $AUTORECONF $AUTORECONFFLAGS \ + || die "autoreconf failed" + +# Get some extra files from gnulib, overriding existing files. +for file in $gnulib_extra_files; do + case $file in + */INSTALL) dst=INSTALL;; + build-aux/*) dst=$build_aux/${file#build-aux/};; + *) dst=$file;; + esac + symlink_to_dir "$GNULIB_SRCDIR" $file $dst \ + || die "failed to symlink $file" +done + +if test $with_gettext = yes; then + # Create gettext configuration. + echo "$0: Creating po/Makevars from po/Makevars.template ..." + rm -f po/Makevars + sed ' + /^EXTRA_LOCALE_CATEGORIES *=/s/=.*/= '"$EXTRA_LOCALE_CATEGORIES"'/ + /^COPYRIGHT_HOLDER *=/s/=.*/= '"$COPYRIGHT_HOLDER"'/ + /^MSGID_BUGS_ADDRESS *=/s|=.*|= '"$MSGID_BUGS_ADDRESS"'| + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >po/Makevars \ + || die 'cannot generate po/Makevars' + + # If the 'gettext' module is in use, grab the latest Makefile.in.in. + # If only the 'gettext-h' module is in use, assume autopoint already + # put the correct version of this file into place. + case $gnulib_modules in + *gettext-h*) ;; + *gettext*) + cp $GNULIB_SRCDIR/build-aux/po/Makefile.in.in po/Makefile.in.in \ + || die "cannot create po/Makefile.in.in" + ;; + esac + + if test -d runtime-po; then + # Similarly for runtime-po/Makevars, but not quite the same. + rm -f runtime-po/Makevars + sed ' + /^DOMAIN *=.*/s/=.*/= '"$package"'-runtime/ + /^subdir *=.*/s/=.*/= runtime-po/ + /^MSGID_BUGS_ADDRESS *=/s/=.*/= bug-'"$package"'@gnu.org/ + /^XGETTEXT_OPTIONS *=/{ + s/$/ \\/ + a\ + '"$XGETTEXT_OPTIONS_RUNTIME"' $${end_of_xgettext_options+} + } + ' po/Makevars.template >runtime-po/Makevars \ + || die 'cannot generate runtime-po/Makevars' + + # Copy identical files from po to runtime-po. + (cd po && cp -p Makefile.in.in *-quot *.header *.sed *.sin ../runtime-po) + fi +fi + +bootstrap_epilogue + +echo "$0: done. Now you can run './configure'." + +# Local Variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/bootstrap.conf b/bootstrap.conf new file mode 100644 index 000000000..7a7813d28 --- /dev/null +++ b/bootstrap.conf @@ -0,0 +1,103 @@ +# Bootstrap configuration. + +# Copyright (C) 2006-2022 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +GNULIB_REVISION=9f48fb992a3d7e96610c4ce8be969cff2d61a01b + +# gnulib modules used by this package. +# mbswidth is used by fix-width.diff's changes to argp rather than directly. +gnulib_modules=" + argp + base64 + error + fnmatch + getdelim + getline + gettext-h + gitlog-to-changelog + mbswidth + progname + realloc-gnu + regex + save-cwd + stdbool +" + +gnulib_tool_option_extras="\ + --no-conditional-dependencies \ + --no-vc-files \ +" + +gnulib_name=libgnu +source_base=grub-core/lib/gnulib +gnulib_extra_files=" + build-aux/install-sh + build-aux/mdate-sh + build-aux/texinfo.tex + build-aux/depcomp + build-aux/config.guess + build-aux/config.sub +" + +# Additional xgettext options to use. Use "\\\newline" to break lines. +XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\ + --from-code=UTF-8\\\ +' + +checkout_only_file= +copy=true +vc_ignore= + +SKIP_PO=t + +# Build prerequisites +buildreq="\ +autoconf 2.64 +automake 1.14 +gettext - +git 1.5.5 +patch - +tar - +" + +# bootstrap doesn't give us a reasonable way to stop Automake from +# overwriting this, so we just copy our version aside and put it back later. +cp -a INSTALL INSTALL.grub + +bootstrap_post_import_hook () { + set -e + + # Instead of patching our gnulib and therefore maintaining a fork, submit + # changes to gnulib and update the hash above when they've merged. Do not + # add new patches here. + patch -d grub-core/lib/gnulib -p2 < grub-core/lib/gnulib-patches/fix-width.patch + + for patchname in \ + 0001-Support-POTFILES-shell \ + 0002-Handle-gettext_printf-shell-function \ + 0003-Make-msgfmt-output-in-little-endian \ + 0004-Use-SHELL-rather-than-bin-sh; do + patch -d po -p3 \ + < "po/gettext-patches/$patchname.patch" + done + FROM_BOOTSTRAP=1 ./autogen.sh + set +e # bootstrap expects this +} + +bootstrap_epilogue () { + mv INSTALL.grub INSTALL +} diff --git a/bus/bonito.c b/bus/bonito.c deleted file mode 100644 index 3f794c45a..000000000 --- a/bus/bonito.c +++ /dev/null @@ -1,90 +0,0 @@ -/* bonito.c - PCI bonito interface. */ -/* - * 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 . - */ - -#include -#include - -static grub_uint32_t base_win[GRUB_MACHINE_PCI_NUM_WIN]; -static const grub_size_t sizes_win[GRUB_MACHINE_PCI_NUM_WIN] = - {GRUB_MACHINE_PCI_WIN1_SIZE, GRUB_MACHINE_PCI_WIN_SIZE, - GRUB_MACHINE_PCI_WIN_SIZE}; -/* Usage counters. */ -static int usage_win[GRUB_MACHINE_PCI_NUM_WIN]; -static grub_addr_t addr_win[GRUB_MACHINE_PCI_NUM_WIN] = - {GRUB_MACHINE_PCI_WIN1_ADDR, GRUB_MACHINE_PCI_WIN2_ADDR, - GRUB_MACHINE_PCI_WIN3_ADDR}; - -static inline void -write_bases (void) -{ - int i; - grub_uint32_t reg = 0; - for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) - reg |= (((base_win[i] >> GRUB_MACHINE_PCI_WIN_SHIFT) - & GRUB_MACHINE_PCI_WIN_MASK) - >> (i * GRUB_MACHINE_PCI_WIN_MASK_SIZE)); - GRUB_MACHINE_PCI_IO_CTRL_REG = reg; -} - -volatile void * -grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), - grub_addr_t base, grub_size_t size) -{ - int i; - grub_addr_t newbase; - - /* First try already used registers. */ - for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) - if (usage_win[i] && base_win[i] <= base - && base_win[i] + sizes_win[i] > base + size) - { - usage_win[i]++; - return (void *) - (addr_win[i] | (base & GRUB_MACHINE_PCI_WIN_OFFSET_MASK)); - } - /* Map new register. */ - newbase = base & ~GRUB_MACHINE_PCI_WIN_OFFSET_MASK; - for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) - if (!usage_win[i] && newbase <= base - && newbase + sizes_win[i] > base + size) - { - usage_win[i]++; - base_win[i] = newbase; - write_bases (); - return (void *) - (addr_win[i] | (base & GRUB_MACHINE_PCI_WIN_OFFSET_MASK)); - } - grub_fatal ("Out of PCI windows."); -} - -void -grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), - volatile void *mem __attribute__ ((unused)), - grub_size_t size __attribute__ ((unused))) -{ - int i; - for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) - if (usage_win[i] && addr_win[i] - == (((grub_addr_t) mem) & ~GRUB_MACHINE_PCI_WIN_OFFSET_MASK)) - { - usage_win[i]--; - return; - } - grub_fatal ("Tried to unmap not mapped region"); -} diff --git a/bus/pci.c b/bus/pci.c deleted file mode 100644 index a08e53446..000000000 --- a/bus/pci.c +++ /dev/null @@ -1,65 +0,0 @@ -/* pci.c - Generic PCI interfaces. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include - -grub_pci_address_t -grub_pci_make_address (grub_pci_device_t dev, int reg) -{ - return (1 << 31) | (dev.bus << 16) | (dev.device << 11) - | (dev.function << 8) | reg; -} - -void -grub_pci_iterate (grub_pci_iteratefunc_t hook) -{ - grub_pci_device_t dev; - grub_pci_address_t addr; - grub_pci_id_t id; - grub_uint32_t hdr; - - for (dev.bus = 0; dev.bus < GRUB_PCI_NUM_BUS; dev.bus++) - { - for (dev.device = 0; dev.device < GRUB_PCI_NUM_DEVICES; dev.device++) - { - for (dev.function = 0; dev.function < 8; dev.function++) - { - addr = grub_pci_make_address (dev, GRUB_PCI_REG_PCI_ID); - id = grub_pci_read (addr); - - /* Check if there is a device present. */ - if (id >> 16 == 0xFFFF) - continue; - - if (hook (dev, id)) - return; - - /* Probe only func = 0 if the device if not multifunction */ - if (dev.function == 0) - { - addr = grub_pci_make_address (dev, GRUB_PCI_REG_CACHELINE); - hdr = grub_pci_read (addr); - if (!(hdr & 0x800000)) - break; - } - } - } - } -} diff --git a/bus/usb/ohci.c b/bus/usb/ohci.c deleted file mode 100644 index 6d185bc7f..000000000 --- a/bus/usb/ohci.c +++ /dev/null @@ -1,611 +0,0 @@ -/* 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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)); - -struct grub_ohci_td -{ - /* Information used to construct the TOKEN packet. */ - grub_uint32_t token; - - grub_uint32_t buffer; - grub_uint32_t next_td; - grub_uint32_t buffer_end; -} __attribute__((packed)); - -typedef struct grub_ohci_td *grub_ohci_td_t; -typedef struct grub_ohci_ed *grub_ohci_ed_t; - -struct grub_ohci -{ - volatile grub_uint32_t *iobase; - volatile struct grub_ohci_hcca *hcca; - struct grub_ohci *next; -}; - -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_RHUBA = 18, - GRUB_OHCI_REG_RHUBPORT = 21 -} grub_ohci_reg_t; - -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); -} - - - -/* 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 __attribute__((unused))) -{ - grub_uint32_t class_code; - grub_uint32_t class; - grub_uint32_t subclass; - grub_uint32_t interf; - grub_uint32_t base; - grub_pci_address_t addr; - struct grub_ohci *o; - grub_uint32_t revision; - grub_uint32_t frame_interval; - - 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; - - /* Determine IO base address. */ - 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 - - /* Allocate memory for the controller and register it. */ - o = grub_malloc (sizeof (*o)); - if (! o) - return 1; - - o->iobase = (grub_uint32_t *) base; - - /* Reserve memory for the HCCA. */ - o->hcca = (struct grub_ohci_hcca *) grub_memalign (256, 256); - - grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x base=%p\n", - class, subclass, interf, o->iobase); - - /* 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; - - /* Backup the frame interval register. */ - frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL); - - /* 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"); - - /* Restore the frame interval register. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval); - - /* Setup the HCCA. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca); - grub_dprintf ("ohci", "OHCI HCCA\n"); - - /* Enable the OHCI. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, - (2 << 6)); - grub_dprintf ("ohci", "OHCI enable: 0x%02x\n", - (grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3); - - /* Link to ohci now that initialisation is successful. */ - o->next = ohci; - ohci = o; - - return 0; - - fail: - if (o) - grub_free ((void *) o->hcca); - grub_free (o); - - return 1; -} - - -static void -grub_ohci_inithw (void) -{ - grub_pci_iterate (grub_ohci_pci_iter); -} - - - -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 void -grub_ohci_transaction (grub_ohci_td_t td, - grub_transfer_type_t type, unsigned int toggle, - grub_size_t size, char *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; - } - - /* Generate no interrupts. */ - token |= 7 << 21; - - /* Set the token. */ - token |= toggle << 24; - token |= 1 << 25; - - buffer = (grub_uint32_t) data; - buffer_end = buffer + size - 1; - - td->token = grub_cpu_to_le32 (token); - td->buffer = grub_cpu_to_le32 (buffer); - td->next_td = 0; - td->buffer_end = grub_cpu_to_le32 (buffer_end); -} - -static grub_usb_err_t -grub_ohci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer) -{ - struct grub_ohci *o = (struct grub_ohci *) dev->data; - grub_ohci_ed_t ed; - grub_ohci_td_t td_list; - grub_uint32_t target; - grub_uint32_t td_tail; - grub_uint32_t td_head; - grub_uint32_t status; - grub_uint32_t control; - grub_usb_err_t err; - int i; - - /* Allocate an Endpoint Descriptor. */ - ed = grub_memalign (16, sizeof (*ed)); - if (! ed) - return GRUB_USB_ERR_INTERNAL; - - td_list = grub_memalign (16, sizeof (*td_list) * (transfer->transcnt + 1)); - if (! td_list) - { - grub_free ((void *) ed); - return GRUB_USB_ERR_INTERNAL; - } - - grub_dprintf ("ohci", "alloc=%p\n", td_list); - - /* Setup all Transfer Descriptors. */ - for (i = 0; i < transfer->transcnt; i++) - { - grub_usb_transaction_t tr = &transfer->transactions[i]; - - grub_ohci_transaction (&td_list[i], tr->pid, tr->toggle, - tr->size, tr->data); - - td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]); - } - - /* Setup the Endpoint Descriptor. */ - - /* Set the device address. */ - target = transfer->devaddr; - - /* Set the endpoint. */ - target |= transfer->endpoint << 7; - - /* Set the device speed. */ - target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13; - - /* Set the maximum packet size. */ - target |= transfer->max << 16; - - td_head = (grub_uint32_t) td_list; - - td_tail = (grub_uint32_t) &td_list[transfer->transcnt]; - - ed->target = grub_cpu_to_le32 (target); - ed->td_head = grub_cpu_to_le32 (td_head); - ed->td_tail = grub_cpu_to_le32 (td_tail); - ed->next_ed = grub_cpu_to_le32 (0); - - grub_dprintf ("ohci", "program OHCI\n"); - - /* Program the OHCI to actually transfer. */ - switch (transfer->type) - { - case GRUB_USB_TRANSACTION_TYPE_BULK: - { - grub_dprintf ("ohci", "add to bulk list\n"); - - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear BulkListFilled. */ - status &= ~(1 << 2); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed); - - /* Enable the Bulk list. */ - control |= 1 << 5; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Set BulkListFilled. */ - status |= 1 << 2; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - break; - } - - case GRUB_USB_TRANSACTION_TYPE_CONTROL: - { - grub_dprintf ("ohci", "add to control list\n"); - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - - /* Disable the Control and Bulk lists. */ - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear ControlListFilled. */ - status &= ~(1 << 1); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, - (grub_uint32_t) ed); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1, - (grub_uint32_t) ed); - - /* Enable the Control list. */ - control |= 1 << 4; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Set ControlListFilled. */ - status |= 1 << 1; - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - break; - } - } - - grub_dprintf ("ohci", "wait for completion\n"); - grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n", - grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL), - grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS)); - - /* Wait until the transfer is completed or STALLs. */ - while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf)) - { - grub_cpu_idle (); - - grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail); - - /* Detected a STALL. */ - if (ed->td_head & 1) - break; - } - - grub_dprintf ("ohci", "complete\n"); - -/* if (ed->td_head & 1) */ -/* err = GRUB_USB_ERR_STALL; */ -/* else if (ed->td */ - - - if (ed->td_head & 1) - { - grub_uint8_t errcode; - grub_ohci_td_t tderr; - - tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o, - GRUB_OHCI_REG_DONEHEAD); - errcode = tderr->token >> 28; - - switch (errcode) - { - case 0: - /* XXX: Should not happen! */ - grub_error (GRUB_ERR_IO, "OHCI 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; - break; - - case 9: - /* XXX: Data underrun error. */ - err = GRUB_USB_ERR_DATA; - 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; - } - } - else - err = GRUB_USB_ERR_NONE; - - /* Disable the Control and Bulk lists. */ - control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL); - control &= ~(3 << 4); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control); - - /* Clear BulkListFilled and ControlListFilled. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS); - status &= ~((1 << 2) | (1 << 3)); - grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status); - - /* XXX */ - grub_free (td_list); - grub_free (ed); - - return err; -} - -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_uint32_t status; - - /* Reset the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 4); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - grub_millisleep (100); - - /* End the reset signaling. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (1 << 20); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - grub_millisleep (10); - - /* Enable the port. */ - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - status |= (enable << 1); /* XXX: Magic. */ - grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status); - - status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); - grub_dprintf ("ohci", "portstatus=0x%02x\n", status); - - return GRUB_ERR_NONE; -} - -static grub_usb_speed_t -grub_ohci_detect_dev (grub_usb_controller_t dev, int port) -{ - 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); - - 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); - - /* The root hub has exactly two ports. */ - return portinfo & 0xFF; -} - - - -static struct grub_usb_controller_dev usb_controller = -{ - .name = "ohci", - .iterate = grub_ohci_iterate, - .transfer = grub_ohci_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_MOD_FINI(ohci) -{ - grub_usb_controller_dev_unregister (&usb_controller); -} diff --git a/bus/usb/uhci.c b/bus/usb/uhci.c deleted file mode 100644 index 947f2367b..000000000 --- a/bus/usb/uhci.c +++ /dev/null @@ -1,689 +0,0 @@ -/* 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define GRUB_UHCI_IOMASK (0x7FF << 5) - -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 - - -/* 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; - - /* 256 Queue Heads. */ - grub_uhci_qh_t qh; - - /* 256 Transfer Descriptors. */ - grub_uhci_td_t td; - - /* Free Transfer Descriptors. */ - grub_uhci_td_t tdfree; - - 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; - grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n", - class, subclass, interf, u->iobase); - - /* Reserve a page for the frame list. */ - u->framelist = grub_memalign (4096, 4096); - if (! u->framelist) - goto fail; - - /* 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 - - /* 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. */ - for (i = 0; i < 256; 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; - } - - /* The last Queue Head should terminate. 256 are too many QHs so - just use 50. */ - u->qh[50 - 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 -grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td) -{ - /* Free the TDs in this queue. */ - while (td) - { - grub_uhci_td_t tdprev; - - /* 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; - - for (; i < 255; i++) - { - if (u->qh[i].elinkptr & 1) - break; - } - qh = &u->qh[i]; - if (! (qh->elinkptr & 1)) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, - "no free queue heads available"); - return NULL; - } - - 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, - char *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=%p 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 = (grub_uint32_t) data; - - return td; -} - -static grub_usb_err_t -grub_uhci_transfer (grub_usb_controller_t dev, - grub_usb_transfer_t transfer) -{ - struct grub_uhci *u = (struct grub_uhci *) dev->data; - grub_uhci_qh_t qh; - grub_uhci_td_t td; - grub_uhci_td_t td_first = NULL; - grub_uhci_td_t td_prev = NULL; - grub_usb_err_t err = GRUB_USB_ERR_NONE; - int i; - grub_uint64_t endtime; - - /* Allocate a queue head for the transfer queue. */ - qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL); - if (! qh) - return grub_errno; - - for (i = 0; i < transfer->transcnt; i++) - { - grub_usb_transaction_t tr = &transfer->transactions[i]; - - td = grub_uhci_transaction (u, transfer->endpoint, tr->pid, - transfer->devaddr, tr->toggle, - tr->size, tr->data); - if (! td) - { - /* Terminate and free. */ - td_prev->linkptr2 = 0; - td_prev->linkptr = 1; - - if (td_first) - grub_free_queue (u, td_first); - - return GRUB_USB_ERR_INTERNAL; - } - - if (! td_first) - 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. */ - qh->elinkptr = (grub_uint32_t) td_first; - - grub_dprintf ("uhci", "initiate transaction\n"); - - /* Wait until either the transaction completed or an error - occurred. */ - endtime = grub_get_time_ms () + 1000; - for (;;) - { - grub_uhci_td_t errtd; - - errtd = (grub_uhci_td_t) (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 (qh->elinkptr & 1) - break; - - grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status); - - /* Check if the TD is not longer active. */ - if (! (errtd->ctrl_status & (1 << 23))) - { - grub_dprintf ("uhci", ">>t status=0x%02x\n", errtd->ctrl_status); - - /* 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; - - if (err) - goto fail; - - /* Fall through, no errors occurred, so the QH might be - updated. */ - grub_dprintf ("uhci", "transaction fallthrough\n"); - } - if (grub_get_time_ms () > endtime) - { - err = GRUB_USB_ERR_STALL; - grub_dprintf ("uhci", "transaction timed out\n"); - goto fail; - } - grub_cpu_idle (); - } - - grub_dprintf ("uhci", "transaction complete\n"); - - fail: - - grub_dprintf ("uhci", "transaction failed\n"); - - /* Place the QH back in the free list and deallocate the associated - TDs. */ - qh->elinkptr = 1; - grub_free_queue (u, td_first); - - return err; -} - -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", "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); - - /* Reset the port. */ - grub_uhci_writereg16 (u, reg, enable << 9); - - /* Wait for the reset to complete. XXX: How long exactly? */ - grub_millisleep (10); - 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, enable << 2); - grub_millisleep (10); - - grub_dprintf ("uhci", "waiting for the port to be enabled\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; -} - -static grub_usb_speed_t -grub_uhci_detect_dev (grub_usb_controller_t dev, int port) -{ - struct grub_uhci *u = (struct grub_uhci *) dev->data; - int reg; - unsigned int status; - - 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); - - 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; -} - - -static struct grub_usb_controller_dev usb_controller = -{ - .name = "uhci", - .iterate = grub_uhci_iterate, - .transfer = grub_uhci_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); -} diff --git a/bus/usb/usb.c b/bus/usb/usb.c deleted file mode 100644 index 8289185da..000000000 --- a/bus/usb/usb.c +++ /dev/null @@ -1,227 +0,0 @@ -/* usb.c - Generic USB interfaces. */ -/* - * 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 . - */ - -#include -#include -#include -#include - -static grub_usb_controller_dev_t grub_usb_list; - -void -grub_usb_controller_dev_register (grub_usb_controller_dev_t usb) -{ - auto int iterate_hook (grub_usb_controller_t dev); - - /* Iterate over all controllers found by the driver. */ - int iterate_hook (grub_usb_controller_t dev) - { - dev->dev = usb; - - /* Enable the ports of the USB Root Hub. */ - grub_usb_root_hub (dev); - - return 0; - } - - usb->next = grub_usb_list; - grub_usb_list = usb; - - if (usb->iterate) - usb->iterate (iterate_hook); -} - -void -grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb) -{ - grub_usb_controller_dev_t *p, q; - - for (p = &grub_usb_list, q = *p; q; p = &(q->next), q = q->next) - if (q == usb) - { - *p = q->next; - break; - } -} - -#if 0 -int -grub_usb_controller_iterate (int (*hook) (grub_usb_controller_t dev)) -{ - grub_usb_controller_dev_t p; - - auto int iterate_hook (grub_usb_controller_t dev); - - int iterate_hook (grub_usb_controller_t dev) - { - dev->dev = p; - if (hook (dev)) - return 1; - return 0; - } - - /* Iterate over all controller drivers. */ - for (p = grub_usb_list; p; p = p->next) - { - /* Iterate over the busses of the controllers. XXX: Actually, a - hub driver should do this. */ - if (p->iterate (iterate_hook)) - return 1; - } - - return 0; -} -#endif - - -grub_usb_err_t -grub_usb_clear_halt (grub_usb_device_t dev, int endpoint) -{ - dev->toggle[endpoint] = 0; - return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT - | GRUB_USB_REQTYPE_STANDARD - | GRUB_USB_REQTYPE_TARGET_ENDP), - GRUB_USB_REQ_CLEAR_FEATURE, - GRUB_USB_FEATURE_ENDP_HALT, - endpoint, 0, 0); -} - -grub_usb_err_t -grub_usb_set_configuration (grub_usb_device_t dev, int configuration) -{ - int i; - - for (i = 0; i < 16; i++) - dev->toggle[i] = 0; - - return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT - | GRUB_USB_REQTYPE_STANDARD - | GRUB_USB_REQTYPE_TARGET_DEV), - GRUB_USB_REQ_SET_CONFIGURATION, configuration, - 0, 0, NULL); -} - -grub_usb_err_t -grub_usb_get_descriptor (grub_usb_device_t dev, - grub_uint8_t type, grub_uint8_t index, - grub_size_t size, char *data) -{ - return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN - | GRUB_USB_REQTYPE_STANDARD - | GRUB_USB_REQTYPE_TARGET_DEV), - GRUB_USB_REQ_GET_DESCRIPTOR, - (type << 8) | index, - 0, size, data); -} - -struct grub_usb_desc_endp * -grub_usb_get_endpdescriptor (grub_usb_device_t usbdev, int addr) -{ - int i; - - for (i = 0; i < usbdev->config[0].descconf->numif; i++) - { - struct grub_usb_desc_if *interf; - int j; - - interf = usbdev->config[0].interf[i].descif; - - for (j = 0; j < interf->endpointcnt; j++) - { - struct grub_usb_desc_endp *endp; - endp = &usbdev->config[0].interf[i].descendp[j]; - - if (endp->endp_addr == addr) - return endp; - } - } - - return NULL; -} - -grub_usb_err_t -grub_usb_device_initialize (grub_usb_device_t dev) -{ - struct grub_usb_desc_device *descdev; - struct grub_usb_desc_config config; - grub_usb_err_t err; - int i; - - err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, - 0, sizeof (struct grub_usb_desc_device), - (char *) &dev->descdev); - if (err) - return err; - descdev = &dev->descdev; - - for (i = 0; i < 8; i++) - dev->config[i].descconf = NULL; - - for (i = 0; i < descdev->configcnt; i++) - { - int pos; - int currif; - char *data; - - /* First just read the first 4 bytes of the configuration - descriptor, after that it is known how many bytes really have - to be read. */ - err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, 4, - (char *) &config); - - data = grub_malloc (config.totallen); - if (! data) - { - err = GRUB_USB_ERR_INTERNAL; - goto fail; - } - - dev->config[i].descconf = (struct grub_usb_desc_config *) data; - err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, - config.totallen, data); - if (err) - goto fail; - - /* Skip the configuration descriptor. */ - pos = sizeof (struct grub_usb_desc_config); - - /* Read all interfaces. */ - for (currif = 0; currif < dev->config[i].descconf->numif; currif++) - { - dev->config[i].interf[currif].descif - = (struct grub_usb_desc_if *) &data[pos]; - pos += sizeof (struct grub_usb_desc_if); - - /* Point to the first endpoint. */ - dev->config[i].interf[currif].descendp - = (struct grub_usb_desc_endp *) &data[pos]; - pos += (sizeof (struct grub_usb_desc_endp) - * dev->config[i].interf[currif].descif->endpointcnt); - } - } - - return GRUB_USB_ERR_NONE; - - fail: - - for (i = 0; i < 8; i++) - grub_free (dev->config[i].descconf); - - return err; -} diff --git a/bus/usb/usbhub.c b/bus/usb/usbhub.c deleted file mode 100644 index 523abf93e..000000000 --- a/bus/usb/usbhub.c +++ /dev/null @@ -1,192 +0,0 @@ -/* 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 . - */ - -#include -#include -#include -#include - -/* USB Supports 127 devices, with device 0 as special case. */ -static struct grub_usb_device *grub_usb_devs[128]; - -/* 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; - - dev = grub_zalloc (sizeof (struct grub_usb_device)); - if (! dev) - return NULL; - - dev->controller = *controller; - dev->speed = speed; - - grub_usb_device_initialize (dev); - - /* Assign a new address to the device. */ - for (i = 1; i < 128; i++) - { - if (! grub_usb_devs[i]) - break; - } - if (i == 128) - { - grub_error (GRUB_ERR_IO, "can't assign address to USB device"); - return NULL; - } - - 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); - - dev->addr = i; - dev->initialized = 1; - grub_usb_devs[i] = dev; - - return dev; -} - - -static grub_err_t -grub_usb_add_hub (grub_usb_device_t dev) -{ - struct grub_usb_usb_hubdesc hubdesc; - grub_err_t err; - int i; - - 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); - - /* Iterate over the Hub ports. */ - for (i = 1; i <= hubdesc.portcnt; i++) - { - grub_uint32_t status; - - /* 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_HUB_GET_PORT_STATUS, - 0, i, sizeof (status), (char *) &status); - - /* Just ignore the device if the Hub does not report the - status. */ - if (err) - continue; - - /* If connected, reset and enable the port. */ - if (status & GRUB_USB_HUB_STATUS_CONNECTED) - { - 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, not enable - the port. XXX: Why 0x03? According to some docs it - should be 0x0. Check the specification! */ - err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT - | GRUB_USB_REQTYPE_CLASS - | GRUB_USB_REQTYPE_TARGET_OTHER), - 0x3, 0x4, i, 0, 0); - - /* If the Hub does not cooperate for this port, just skip - the port. */ - if (err) - continue; - - /* Add the device and assign a device address to it. */ - grub_usb_hub_add_dev (&dev->controller, speed); - } - } - - return GRUB_ERR_NONE; -} - -grub_usb_err_t -grub_usb_root_hub (grub_usb_controller_t controller) -{ - grub_err_t err; - int ports; - int i; - - /* Query the number of ports the root Hub has. */ - ports = controller->dev->hubports (controller); - - for (i = 0; i < ports; i++) - { - grub_usb_speed_t speed = controller->dev->detect_dev (controller, i); - - if (speed != GRUB_USB_SPEED_NONE) - { - grub_usb_device_t dev; - - /* Enable the port. */ - err = controller->dev->portstatus (controller, i, 1); - if (err) - continue; - - /* Enable the port and create a device. */ - dev = grub_usb_hub_add_dev (controller, speed); - if (! dev) - continue; - - /* If the device is a Hub, scan it for more devices. */ - if (dev->descdev.class == 0x09) - grub_usb_add_hub (dev); - } - } - - return GRUB_USB_ERR_NONE; -} - -int -grub_usb_iterate (int (*hook) (grub_usb_device_t dev)) -{ - int i; - - for (i = 0; i < 128; i++) - { - if (grub_usb_devs[i]) - { - if (hook (grub_usb_devs[i])) - return 1; - } - } - - return 0; -} diff --git a/bus/usb/usbtrans.c b/bus/usb/usbtrans.c deleted file mode 100644 index 09e7af83e..000000000 --- a/bus/usb/usbtrans.c +++ /dev/null @@ -1,212 +0,0 @@ -/* 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 . - */ - -#include -#include -#include -#include -#include - -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 size, char *data) -{ - int i; - grub_usb_transfer_t transfer; - int datablocks; - struct grub_usb_packet_setup setupdata; - grub_usb_err_t err; - unsigned int max; - - 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 (struct grub_usb_transfer)); - if (! transfer) - return grub_errno; - - /* Determine the maximum packet size. */ - if (dev->initialized) - max = dev->descdev.maxsize0; - else - max = 64; - - 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); - 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 = (char *) &setupdata; - 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[i * max]; - size -= max; - } - - /* End with an empty OUT transaction. */ - transfer->transactions[datablocks + 1].size = 0; - transfer->transactions[datablocks + 1].data = NULL; - if (reqtype & 128) - 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; - - err = dev->controller.dev->transfer (&dev->controller, transfer); - - grub_free (transfer->transactions); - grub_free (transfer); - - return err; -} - -static grub_usb_err_t -grub_usb_bulk_readwrite (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data, - grub_transfer_type_t type) -{ - int i; - grub_usb_transfer_t transfer; - int datablocks; - unsigned int max; - grub_usb_err_t err; - int toggle = dev->toggle[endpoint]; - - /* 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) - return grub_errno; - - datablocks = ((size + max - 1) / max); - transfer->transcnt = datablocks; - transfer->size = size - 1; - transfer->endpoint = endpoint; - transfer->devaddr = dev->addr; - transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK; - 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); - return grub_errno; - } - - /* 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[i * max]; - size -= tr->size; - } - - err = dev->controller.dev->transfer (&dev->controller, transfer); - grub_dprintf ("usb", "toggle=%d\n", toggle); - dev->toggle[endpoint] = toggle; - - grub_free (transfer->transactions); - grub_free (transfer); - - return err; -} - -grub_usb_err_t -grub_usb_bulk_write (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data) -{ - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_OUT); -} - -grub_usb_err_t -grub_usb_bulk_read (grub_usb_device_t dev, - int endpoint, grub_size_t size, char *data) -{ - return grub_usb_bulk_readwrite (dev, endpoint, size, data, - GRUB_USB_TRANSFER_TYPE_IN); -} diff --git a/commands/blocklist.c b/commands/blocklist.c deleted file mode 100644 index fec59a828..000000000 --- a/commands/blocklist.c +++ /dev/null @@ -1,120 +0,0 @@ -/* blocklist.c - print the block list of a file */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - grub_file_t file; - char buf[GRUB_DISK_SECTOR_SIZE]; - unsigned long start_sector = 0; - unsigned num_sectors = 0; - int num_entries = 0; - grub_disk_addr_t part_start = 0; - auto void NESTED_FUNC_ATTR read_blocklist (grub_disk_addr_t sector, unsigned offset, - unsigned length); - auto void NESTED_FUNC_ATTR print_blocklist (grub_disk_addr_t sector, unsigned num, - unsigned offset, unsigned length); - - void NESTED_FUNC_ATTR read_blocklist (grub_disk_addr_t sector, unsigned offset, - unsigned length) - { - if (num_sectors > 0) - { - if (start_sector + num_sectors == sector - && offset == 0 && length == GRUB_DISK_SECTOR_SIZE) - { - num_sectors++; - return; - } - - print_blocklist (start_sector, num_sectors, 0, 0); - num_sectors = 0; - } - - if (offset == 0 && length == GRUB_DISK_SECTOR_SIZE) - { - start_sector = sector; - num_sectors++; - } - else - print_blocklist (sector, 0, offset, length); - } - - void NESTED_FUNC_ATTR print_blocklist (grub_disk_addr_t sector, unsigned num, - unsigned offset, unsigned length) - { - if (num_entries++) - grub_printf (","); - - grub_printf ("%llu", (unsigned long long) (sector - part_start)); - if (num > 0) - grub_printf ("+%u", num); - if (offset != 0 || length != 0) - grub_printf ("[%u-%u]", offset, offset + length); - } - - if (argc < 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified"); - - file = grub_file_open (args[0]); - if (! file) - return grub_errno; - - if (! file->device->disk) - return grub_error (GRUB_ERR_BAD_DEVICE, - "this command is available only for disk devices"); - - if (file->device->disk->partition) - part_start = grub_partition_get_start (file->device->disk->partition); - - file->read_hook = read_blocklist; - - while (grub_file_read (file, buf, sizeof (buf)) > 0) - ; - - if (num_sectors > 0) - print_blocklist (start_sector, num_sectors, 0, 0); - - grub_file_close (file); - - return grub_errno; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(blocklist) -{ - cmd = grub_register_command ("blocklist", grub_cmd_blocklist, - N_("FILE"), N_("Print a block list.")); -} - -GRUB_MOD_FINI(blocklist) -{ - grub_unregister_command (cmd); -} diff --git a/commands/cat.c b/commands/cat.c deleted file mode 100644 index 3bdafc4c6..000000000 --- a/commands/cat.c +++ /dev/null @@ -1,88 +0,0 @@ -/* 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_cat (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) - -{ - grub_file_t file; - char buf[GRUB_DISK_SECTOR_SIZE]; - grub_ssize_t size; - int key = 0; - - 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_putchar (c); - else - { - grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); - grub_printf ("<%x>", (int) c); - grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); - } - } - - while (grub_checkkey () >= 0 && - (key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != GRUB_TERM_ESC) - ; - } - - grub_putchar ('\n'); - grub_refresh (); - grub_file_close (file); - - return 0; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(cat) -{ - cmd = grub_register_command_p1 ("cat", grub_cmd_cat, - N_("FILE"), N_("Show the contents of a file.")); -} - -GRUB_MOD_FINI(cat) -{ - grub_unregister_command (cmd); -} diff --git a/commands/crc.c b/commands/crc.c deleted file mode 100644 index 1c1a690aa..000000000 --- a/commands/crc.c +++ /dev/null @@ -1,71 +0,0 @@ -/* crc.c - command to calculate the crc32 checksum of a file */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008,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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_crc (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) - -{ - grub_file_t file; - char buf[GRUB_DISK_SECTOR_SIZE]; - grub_ssize_t size; - grub_uint32_t crc; - - if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); - - file = grub_file_open (args[0]); - if (! file) - return 0; - - crc = 0; - while ((size = grub_file_read (file, buf, sizeof (buf))) > 0) - crc = grub_getcrc32 (crc, buf, size); - - if (grub_errno) - goto fail; - - grub_printf ("%08x\n", crc); - - fail: - grub_file_close (file); - return 0; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(crc) -{ - cmd = grub_register_command ("crc", grub_cmd_crc, - N_("FILE"), - N_("Calculate the crc32 checksum of a file.")); -} - -GRUB_MOD_FINI(crc) -{ - grub_unregister_command (cmd); -} diff --git a/commands/efi/acpi.c b/commands/efi/acpi.c deleted file mode 100644 index 93a560d9c..000000000 --- a/commands/efi/acpi.c +++ /dev/null @@ -1,59 +0,0 @@ -/* acpi.c - get acpi tables. */ -/* - * 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 . - */ - -#include -#include -#include -#include - -struct grub_acpi_rsdp_v10 * -grub_machine_acpi_get_rsdpv1 (void) -{ - unsigned i; - static grub_efi_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; - - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_efi_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_efi_guid_t))) - return (struct grub_acpi_rsdp_v10 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; -} - -struct grub_acpi_rsdp_v20 * -grub_machine_acpi_get_rsdpv2 (void) -{ - unsigned i; - static grub_efi_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; - - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_efi_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; - - if (! grub_memcmp (guid, &acpi20_guid, sizeof (grub_efi_guid_t))) - return (struct grub_acpi_rsdp_v20 *) - grub_efi_system_table->configuration_table[i].vendor_table; - } - return 0; -} diff --git a/commands/extcmd.c b/commands/extcmd.c deleted file mode 100644 index 16796febf..000000000 --- a/commands/extcmd.c +++ /dev/null @@ -1,96 +0,0 @@ -/* extcmd.c - support extended command */ -/* - * 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 . - */ - -#include -#include -#include -#include - -static grub_err_t -grub_extcmd_dispatcher (struct grub_command *cmd, - int argc, char **args) -{ - int new_argc; - char **new_args; - struct grub_arg_option *parser; - struct grub_arg_list *state; - int maxargs = 0; - grub_err_t ret; - grub_extcmd_t ext; - - ext = cmd->data; - parser = (struct grub_arg_option *) ext->options; - while (parser && (parser++)->doc) - maxargs++; - - /* Set up the option state. */ - state = grub_zalloc (sizeof (struct grub_arg_list) * maxargs); - - if (grub_arg_parse (ext, argc, args, state, &new_args, &new_argc)) - { - ext->state = state; - ret = (ext->func) (ext, new_argc, new_args); - grub_free (new_args); - } - else - ret = grub_errno; - - grub_free (state); - - return ret; -} - -grub_extcmd_t -grub_register_extcmd (const char *name, grub_extcmd_func_t func, - unsigned flags, const char *summary, - const char *description, - const struct grub_arg_option *parser) -{ - grub_extcmd_t ext; - grub_command_t cmd; - - ext = (grub_extcmd_t) grub_malloc (sizeof (*ext)); - if (! ext) - return 0; - - cmd = grub_register_command_prio (name, grub_extcmd_dispatcher, - summary, description, 1); - if (! cmd) - { - grub_free (ext); - return 0; - } - - cmd->flags = (flags | GRUB_COMMAND_FLAG_EXTCMD); - cmd->data = ext; - - ext->cmd = cmd; - ext->func = func; - ext->options = parser; - ext->data = 0; - - return ext; -} - -void -grub_unregister_extcmd (grub_extcmd_t ext) -{ - grub_unregister_command (ext->cmd); - grub_free (ext); -} diff --git a/commands/handler.c b/commands/handler.c deleted file mode 100644 index f9270972b..000000000 --- a/commands/handler.c +++ /dev/null @@ -1,101 +0,0 @@ -/* handler.c - commands to list or select handlers */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_handler (struct grub_command *cmd __attribute__ ((unused)), - int argc, char **args) -{ - void *curr_item = 0; - grub_handler_class_t head; - - auto int list_item (grub_named_list_t item); - int list_item (grub_named_list_t item) - { - if (item == curr_item) - grub_putchar ('*'); - - grub_printf ("%s\n", item->name); - - return 0; - } - - head = grub_handler_class_list; - if (argc == 0) - { - grub_list_iterate (GRUB_AS_LIST (head), (grub_list_hook_t) list_item); - } - else - { - char *class_name; - grub_handler_class_t class; - - class_name = args[0]; - argc--; - args++; - - class = grub_named_list_find (GRUB_AS_NAMED_LIST (head), class_name); - if (! class) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "class not found"); - - if (argc == 0) - { - curr_item = class->cur_handler; - grub_list_iterate (GRUB_AS_LIST (class->handler_list), - (grub_list_hook_t) list_item); - } - else - { - grub_handler_t handler; - - handler = - grub_named_list_find (GRUB_AS_NAMED_LIST (class->handler_list), - args[0]); - - if (! handler) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "handler not found"); - - grub_handler_set_current (class, handler); - } - } - - return 0; -} - -static grub_command_t cmd_handler; - -GRUB_MOD_INIT(handler) -{ - cmd_handler = - grub_register_command ("handler", grub_cmd_handler, - N_("[class [handler]]"), - N_("List or select a handler.")); -} - -GRUB_MOD_FINI(handler) -{ - grub_unregister_command (cmd_handler); -} diff --git a/commands/help.c b/commands/help.c deleted file mode 100644 index 1181c3bfb..000000000 --- a/commands/help.c +++ /dev/null @@ -1,143 +0,0 @@ -/* help.c - command to show a help text. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_help (grub_extcmd_t ext __attribute__ ((unused)), int argc, - char **args) -{ - int cnt = 0; - char *currarg; - - auto int print_command_info (grub_command_t cmd); - auto int print_command_help (grub_command_t cmd); - - int print_command_info (grub_command_t cmd) - { - if ((cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE) && - (cmd->flags & GRUB_COMMAND_FLAG_CMDLINE)) - { - struct grub_term_output *term; - const char *summary_translated = _(cmd->summary); - char *command_help; - grub_uint32_t *unicode_command_help; - grub_uint32_t *unicode_last_position; - - command_help = grub_xasprintf ("%s %s", cmd->name, summary_translated); - if (!command_help) - return 1; - - grub_utf8_to_ucs4_alloc (command_help, &unicode_command_help, - &unicode_last_position); - - FOR_ACTIVE_TERM_OUTPUTS(term) - { - unsigned stringwidth; - grub_uint32_t *unicode_last_screen_position; - - unicode_last_screen_position = unicode_command_help; - - stringwidth = 0; - - while (unicode_last_screen_position < unicode_last_position && - stringwidth < ((grub_term_width (term) / 2) - 2)) - { - stringwidth - += grub_term_getcharwidth (term, - *unicode_last_screen_position); - unicode_last_screen_position++; - } - - grub_print_ucs4 (unicode_command_help, - unicode_last_screen_position, term); - if (!(cnt % 2)) - grub_print_spaces (term, grub_term_width (term) / 2 - - stringwidth); - } - if (cnt % 2) - grub_printf ("\n"); - cnt++; - - grub_free (command_help); - grub_free (unicode_command_help); - } - return 0; - } - - int print_command_help (grub_command_t cmd) - { - if (cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE) - { - if (! grub_strncmp (cmd->name, currarg, grub_strlen (currarg))) - { - if (cnt++ > 0) - grub_printf ("\n\n"); - - if (cmd->flags & GRUB_COMMAND_FLAG_EXTCMD) - grub_arg_show_help ((grub_extcmd_t) cmd->data); - else - grub_printf ("%s %s %s\n%s\b", _("Usage:"), cmd->name, _(cmd->summary), - _(cmd->description)); - } - } - return 0; - } - - if (argc == 0) - { - grub_command_iterate (print_command_info); - if (!(cnt % 2)) - grub_printf ("\n"); - } - else - { - int i; - - for (i = 0; i < argc; i++) - { - currarg = args[i]; - grub_command_iterate (print_command_help); - } - } - - return 0; -} - -static grub_extcmd_t cmd; - -GRUB_MOD_INIT(help) -{ - cmd = grub_register_extcmd ("help", grub_cmd_help, - GRUB_COMMAND_FLAG_CMDLINE, - N_("[PATTERN ...]"), - N_("Show a help message."), 0); -} - -GRUB_MOD_FINI(help) -{ - grub_unregister_extcmd (cmd); -} diff --git a/commands/i386/pc/play.c b/commands/i386/pc/play.c deleted file mode 100644 index 44d98a1f0..000000000 --- a/commands/i386/pc/play.c +++ /dev/null @@ -1,269 +0,0 @@ -/* play.c - command to play a tune */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -/* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BASE_TEMPO (60 * GRUB_TICKS_PER_SECOND) - -/* The speaker port. */ -#define SPEAKER 0x61 - -/* If 0, follow state of SPEAKER_DATA bit, otherwise enable output - from timer 2. */ -#define SPEAKER_TMR2 0x01 - -/* If SPEAKER_TMR2 is not set, this provides direct input into the - speaker. Otherwise, this enables or disables the output from the - timer. */ -#define SPEAKER_DATA 0x02 - -/* The PIT channel value ports. You can write to and read from them. - Do not mess with timer 0 or 1. */ -#define PIT_COUNTER_0 0x40 -#define PIT_COUNTER_1 0x41 -#define PIT_COUNTER_2 0x42 - -/* The frequency of the PIT clock. */ -#define PIT_FREQUENCY 0x1234dd - -/* The PIT control port. You can only write to it. Do not mess with - timer 0 or 1. */ -#define PIT_CTRL 0x43 -#define PIT_CTRL_SELECT_MASK 0xc0 -#define PIT_CTRL_SELECT_0 0x00 -#define PIT_CTRL_SELECT_1 0x40 -#define PIT_CTRL_SELECT_2 0x80 - -/* Read and load control. */ -#define PIT_CTRL_READLOAD_MASK 0x30 -#define PIT_CTRL_COUNTER_LATCH 0x00 /* Hold timer value until read. */ -#define PIT_CTRL_READLOAD_LSB 0x10 /* Read/load the LSB. */ -#define PIT_CTRL_READLOAD_MSB 0x20 /* Read/load the MSB. */ -#define PIT_CTRL_READLOAD_WORD 0x30 /* Read/load the LSB then the MSB. */ - -/* Mode control. */ -#define PIT_CTRL_MODE_MASK 0x0e - -/* Interrupt on terminal count. Setting the mode sets output to low. - When counter is set and terminated, output is set to high. */ -#define PIT_CTRL_INTR_ON_TERM 0x00 - -/* Programmable one-shot. When loading counter, output is set to - high. When counter terminated, output is set to low. Can be - triggered again from that point on by setting the gate pin to - high. */ -#define PIT_CTRL_PROGR_ONE_SHOT 0x02 - -/* Rate generator. Output is low for one period of the counter, and - high for the other. */ -#define PIT_CTRL_RATE_GEN 0x04 - -/* Square wave generator. Output is low for one half of the period, - and high for the other half. */ -#define PIT_CTRL_SQUAREWAVE_GEN 0x06 - -/* Software triggered strobe. Setting the mode sets output to high. - When counter is set and terminated, output is set to low. */ -#define PIT_CTRL_SOFTSTROBE 0x08 - -/* Hardware triggered strobe. Like software triggered strobe, but - only starts the counter when the gate pin is set to high. */ -#define PIT_CTRL_HARDSTROBE 0x0a - -/* Count mode. */ -#define PIT_CTRL_COUNT_MASK 0x01 -#define PIT_CTRL_COUNT_BINARY 0x00 /* 16-bit binary counter. */ -#define PIT_CTRL_COUNT_BCD 0x01 /* 4-decade BCD counter. */ - -#define T_REST ((grub_uint16_t) 0) -#define T_FINE ((grub_uint16_t) -1) - -struct note -{ - grub_uint16_t pitch; - grub_uint16_t duration; -}; - -static void -beep_off (void) -{ - unsigned char status; - - status = grub_inb (SPEAKER); - grub_outb (status & ~(SPEAKER_TMR2 | SPEAKER_DATA), SPEAKER); -} - -static void -beep_on (grub_uint16_t pitch) -{ - unsigned char status; - unsigned int counter; - - if (pitch < 20) - pitch = 20; - else if (pitch > 20000) - pitch = 20000; - - counter = PIT_FREQUENCY / pitch; - - /* Program timer 2. */ - grub_outb (PIT_CTRL_SELECT_2 | PIT_CTRL_READLOAD_WORD - | PIT_CTRL_SQUAREWAVE_GEN | PIT_CTRL_COUNT_BINARY, PIT_CTRL); - grub_outb (counter & 0xff, PIT_COUNTER_2); /* LSB */ - grub_outb ((counter >> 8) & 0xff, PIT_COUNTER_2); /* MSB */ - - /* Start speaker. */ - status = grub_inb (SPEAKER); - grub_outb (status | SPEAKER_TMR2 | SPEAKER_DATA, SPEAKER); -} - -/* Returns whether playing should continue. */ -static int -play (unsigned tempo, struct note *note) -{ - unsigned int to; - - if (note->pitch == T_FINE || grub_checkkey () >= 0) - return 1; - - grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch, - note->duration); - - switch (note->pitch) - { - case T_REST: - beep_off (); - break; - - default: - beep_on (note->pitch); - break; - } - - to = grub_get_rtc () + BASE_TEMPO * note->duration / tempo; - while (((unsigned int) grub_get_rtc () <= to) && (grub_checkkey () < 0)) - ; - - return 0; -} - -static grub_err_t -grub_cmd_play (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - grub_file_t file; - - if (argc < 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name or tempo and notes required"); - - file = grub_file_open (args[0]); - if (file) - { - struct note buf; - grub_uint32_t tempo; - - if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo)) - { - grub_file_close (file); - return grub_error (GRUB_ERR_FILE_READ_ERROR, - "file doesn't even contains a full tempo record"); - } - - tempo = grub_le_to_cpu32 (tempo); - grub_dprintf ("play","tempo = %d\n", tempo); - - while (grub_file_read (file, &buf, - sizeof (struct note)) == sizeof (struct note)) - { - buf.pitch = grub_le_to_cpu16 (buf.pitch); - buf.duration = grub_le_to_cpu16 (buf.duration); - - if (play (tempo, &buf)) - break; - } - - grub_file_close (file); - } - else - { - char *end; - unsigned tempo; - struct note note; - int i; - - tempo = grub_strtoul (args[0], &end, 0); - - if (*end) - /* Was not a number either, assume it was supposed to be a file name. */ - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - - grub_dprintf ("play","tempo = %d\n", tempo); - - for (i = 1; i + 1 < argc; i += 2) - { - note.pitch = grub_strtoul (args[i], &end, 0); - if (*end) - { - grub_error (GRUB_ERR_BAD_NUMBER, "bogus pitch number"); - break; - } - - note.duration = grub_strtoul (args[i + 1], &end, 0); - if (*end) - { - grub_error (GRUB_ERR_BAD_NUMBER, "bogus duration number"); - break; - } - - if (play (tempo, ¬e)) - break; - } - } - - beep_off (); - - while (grub_checkkey () > 0) - grub_getkey (); - - return 0; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(play) -{ - cmd = grub_register_command ("play", grub_cmd_play, - N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "), - N_("Play a tune.")); -} - -GRUB_MOD_FINI(play) -{ - grub_unregister_command (cmd); -} diff --git a/commands/i386/pc/vbeinfo.c b/commands/i386/pc/vbeinfo.c deleted file mode 100644 index c266bbfcb..000000000 --- a/commands/i386/pc/vbeinfo.c +++ /dev/null @@ -1,185 +0,0 @@ -/* vbeinfo.c - command to list compatible VBE video modes. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static void * -real2pm (grub_vbe_farptr_t ptr) -{ - return (void *) ((((unsigned long) ptr & 0xFFFF0000) >> 12UL) - + ((unsigned long) ptr & 0x0000FFFF)); -} - -static grub_err_t -grub_cmd_vbeinfo (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - struct grub_vbe_info_block controller_info; - struct grub_vbe_mode_info_block mode_info_tmp; - grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; - grub_uint16_t *video_mode_list; - grub_uint16_t *p; - grub_uint16_t *saved_video_mode_list; - grub_size_t video_mode_list_size; - grub_err_t err; - char *modevar; - - err = grub_vbe_probe (&controller_info); - if (err != GRUB_ERR_NONE) - return err; - - grub_printf ("VBE info: version: %d.%d OEM software rev: %d.%d\n", - controller_info.version >> 8, - controller_info.version & 0xFF, - controller_info.oem_software_rev >> 8, - controller_info.oem_software_rev & 0xFF); - - /* The total_memory field is in 64 KiB units. */ - grub_printf (" total memory: %d KiB\n", - (controller_info.total_memory << 16) / 1024); - - /* Because the information on video modes is stored in a temporary place, - it is better to copy it to somewhere safe. */ - p = video_mode_list = real2pm (controller_info.video_mode_ptr); - while (*p++ != 0xFFFF) - ; - - video_mode_list_size = (grub_addr_t) p - (grub_addr_t) video_mode_list; - saved_video_mode_list = grub_malloc (video_mode_list_size); - if (! saved_video_mode_list) - return grub_errno; - - grub_memcpy (saved_video_mode_list, video_mode_list, video_mode_list_size); - - grub_printf ("List of compatible video modes:\n"); - grub_printf ("Legend: P=Packed pixel, D=Direct color, " - "mask/pos=R/G/B/reserved\n"); - - /* Walk through all video modes listed. */ - for (p = saved_video_mode_list; *p != 0xFFFF; p++) - { - const char *memory_model = 0; - grub_uint32_t mode = (grub_uint32_t) *p; - - err = grub_vbe_get_video_mode_info (mode, &mode_info_tmp); - if (err != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - continue; - } - - if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_SUPPORTED) == 0) - /* If not available, skip it. */ - continue; - - if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_RESERVED_1) == 0) - /* Not enough information. */ - continue; - - if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_COLOR) == 0) - /* Monochrome is unusable. */ - continue; - - if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_LFB_AVAIL) == 0) - /* We support only linear frame buffer modes. */ - continue; - - if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_GRAPHICS) == 0) - /* We allow only graphical modes. */ - continue; - - switch (mode_info_tmp.memory_model) - { - case GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL: - memory_model = "Packed"; - break; - case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR: - memory_model = "Direct"; - break; - - default: - break; - } - - if (! memory_model) - continue; - - grub_printf ("0x%03x: %4d x %4d x %2d %s", - mode, - mode_info_tmp.x_resolution, - mode_info_tmp.y_resolution, - mode_info_tmp.bits_per_pixel, - memory_model); - - /* Show mask and position details for direct color modes. */ - if (mode_info_tmp.memory_model == GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR) - grub_printf (", mask: %d/%d/%d/%d pos: %d/%d/%d/%d", - mode_info_tmp.red_mask_size, - mode_info_tmp.green_mask_size, - mode_info_tmp.blue_mask_size, - mode_info_tmp.rsvd_mask_size, - mode_info_tmp.red_field_position, - mode_info_tmp.green_field_position, - mode_info_tmp.blue_field_position, - mode_info_tmp.rsvd_field_position); - grub_printf ("\n"); - } - - grub_free (saved_video_mode_list); - - /* Check existence of vbe_mode environment variable. */ - modevar = grub_env_get ("vbe_mode"); - - if (modevar != 0) - { - unsigned long value; - - value = grub_strtoul (modevar, 0, 0); - if (grub_errno == GRUB_ERR_NONE) - use_mode = value; - else - grub_errno = GRUB_ERR_NONE; - } - - grub_printf ("Configured VBE mode (vbe_mode) = 0x%03x\n", use_mode); - - return 0; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(vbeinfo) -{ - cmd = - grub_register_command ("vbeinfo", grub_cmd_vbeinfo, 0, - N_("List compatible VESA BIOS extension video modes.")); -} - -GRUB_MOD_FINI(vbeinfo) -{ - grub_unregister_command (cmd); -} diff --git a/commands/i386/pc/vbetest.c b/commands/i386/pc/vbetest.c deleted file mode 100644 index d2921c09d..000000000 --- a/commands/i386/pc/vbetest.c +++ /dev/null @@ -1,179 +0,0 @@ -/* vbetest.c - command to test VESA BIOS Extension 2.0+ support. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_vbetest (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - grub_err_t err; - char *modevar; - struct grub_vbe_mode_info_block mode_info; - struct grub_vbe_info_block controller_info; - grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE; - grub_uint32_t old_mode; - grub_uint8_t *framebuffer = 0; - grub_uint32_t bytes_per_scan_line = 0; - unsigned char *ptr; - int i; - - grub_printf ("Probing for VESA BIOS Extension ... "); - - /* Check if VESA BIOS exists. */ - err = grub_vbe_probe (&controller_info); - if (err != GRUB_ERR_NONE) - return err; - - grub_printf ("found!\n"); - - /* Dump out controller information. */ - grub_printf ("VBE signature = %c%c%c%c\n", - controller_info.signature[0], - controller_info.signature[1], - controller_info.signature[2], - controller_info.signature[3]); - - grub_printf ("VBE version = %d.%d\n", - controller_info.version >> 8, - controller_info.version & 0xFF); - grub_printf ("OEM string ptr = %08x\n", - controller_info.oem_string_ptr); - grub_printf ("Total memory = %d\n", - controller_info.total_memory); - - err = grub_vbe_get_video_mode (&old_mode); - grub_printf ("Get video mode err = %04x\n", err); - - if (err == GRUB_ERR_NONE) - grub_printf ("Old video mode = %04x\n", old_mode); - else - grub_errno = GRUB_ERR_NONE; - - /* Check existence of vbe_mode environment variable. */ - modevar = grub_env_get ("vbe_mode"); - if (modevar != 0) - { - unsigned long value; - - value = grub_strtoul (modevar, 0, 0); - if (grub_errno == GRUB_ERR_NONE) - use_mode = value; - else - grub_errno = GRUB_ERR_NONE; - } - - err = grub_vbe_get_video_mode_info (use_mode, &mode_info); - if (err != GRUB_ERR_NONE) - return err; - - /* Dump out details about the mode being tested. */ - grub_printf ("mode: 0x%03x\n", - use_mode); - grub_printf ("width : %d\n", - mode_info.x_resolution); - grub_printf ("height: %d\n", - mode_info.y_resolution); - grub_printf ("memory model: %02x\n", - mode_info.memory_model); - grub_printf ("bytes/scanline: %d\n", - mode_info.bytes_per_scan_line); - grub_printf ("bytes/scanline (lin): %d\n", - mode_info.lin_bytes_per_scan_line); - grub_printf ("base address: %08x\n", - mode_info.phys_base_addr); - grub_printf ("red mask/pos: %d/%d\n", - mode_info.red_mask_size, - mode_info.red_field_position); - grub_printf ("green mask/pos: %d/%d\n", - mode_info.green_mask_size, - mode_info.green_field_position); - grub_printf ("blue mask/pos: %d/%d\n", - mode_info.blue_mask_size, - mode_info.blue_field_position); - - grub_printf ("Press any key to continue.\n"); - - grub_getkey (); - - /* Setup GFX mode. */ - err = grub_vbe_set_video_mode (use_mode, &mode_info); - if (err != GRUB_ERR_NONE) - return err; - - /* Determine framebuffer address and how many bytes are in scan line. */ - framebuffer = (grub_uint8_t *) mode_info.phys_base_addr; - ptr = framebuffer; - - if (controller_info.version >= 0x300) - { - bytes_per_scan_line = mode_info.lin_bytes_per_scan_line; - } - else - { - bytes_per_scan_line = mode_info.bytes_per_scan_line; - } - - /* Draw some random data to screen. */ - for (i = 0; i < 256 * 256; i++) - { - ptr[i] = i & 0x0F; - } - - /* Draw white line to screen. */ - for (i = 0; i < 100; i++) - { - ptr[mode_info.bytes_per_scan_line * 50 + i] = 0x0F; - } - - /* Draw another white line to screen. */ - grub_memset (ptr + bytes_per_scan_line * 51, 0x0f, bytes_per_scan_line); - - grub_getkey (); - - grub_video_restore (); - - /* Restore old video mode. */ - grub_vbe_set_video_mode (old_mode, 0); - - return grub_errno; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(vbetest) -{ - cmd = grub_register_command ("vbetest", grub_cmd_vbetest, - 0, N_("Test VESA BIOS Extension 2.0+ support.")); -} - -GRUB_MOD_FINI(vbetest) -{ - grub_unregister_command (cmd); -} diff --git a/commands/ls.c b/commands/ls.c deleted file mode 100644 index eb1049617..000000000 --- a/commands/ls.c +++ /dev/null @@ -1,276 +0,0 @@ -/* ls.c - command to list files and devices */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const struct grub_arg_option options[] = - { - {"long", 'l', 0, N_("Show a long list with more detailed information."), 0, 0}, - {"human-readable", 'h', 0, N_("Print sizes in a human readable format."), 0, 0}, - {"all", 'a', 0, N_("List all files."), 0, 0}, - {0, 0, 0, 0, 0, 0} - }; - -static const char grub_human_sizes[] = {' ', 'K', 'M', 'G', 'T'}; - -static grub_err_t -grub_ls_list_devices (int longlist) -{ - auto int grub_ls_print_devices (const char *name); - int grub_ls_print_devices (const char *name) - { - if (longlist) - grub_normal_print_device_info (name); - else - grub_printf ("(%s) ", name); - - return 0; - } - - grub_device_iterate (grub_ls_print_devices); - grub_putchar ('\n'); - grub_refresh (); - - return 0; -} - -static grub_err_t -grub_ls_list_files (char *dirname, int longlist, int all, int human) -{ - char *device_name; - grub_fs_t fs; - const char *path; - grub_device_t dev; - - auto int print_files (const char *filename, - const struct grub_dirhook_info *info); - auto int print_files_long (const char *filename, - const struct grub_dirhook_info *info); - - int print_files (const char *filename, const struct grub_dirhook_info *info) - { - if (all || filename[0] != '.') - grub_printf ("%s%s ", filename, info->dir ? "/" : ""); - - return 0; - } - - int print_files_long (const char *filename, - const struct grub_dirhook_info *info) - { - if ((! all) && (filename[0] == '.')) - return 0; - - if (! info->dir) - { - grub_file_t file; - char *pathname; - - if (dirname[grub_strlen (dirname) - 1] == '/') - pathname = grub_xasprintf ("%s%s", dirname, filename); - else - pathname = grub_xasprintf ("%s/%s", dirname, filename); - - if (!pathname) - return 1; - - /* XXX: For ext2fs symlinks are detected as files while they - should be reported as directories. */ - file = grub_file_open (pathname); - if (! file) - { - grub_errno = 0; - grub_free (pathname); - return 0; - } - - if (! human) - grub_printf ("%-12llu", (unsigned long long) file->size); - else - { - grub_uint64_t fsize = file->size * 100ULL; - int fsz = file->size; - int units = 0; - char buf[20]; - - while (fsz / 1024) - { - fsize = (fsize + 512) / 1024; - fsz /= 1024; - units++; - } - - if (units) - { - grub_uint32_t whole, fraction; - - whole = grub_divmod64 (fsize, 100, &fraction); - grub_snprintf (buf, sizeof (buf), - "%u.%02u%c", whole, fraction, - grub_human_sizes[units]); - grub_printf ("%-12s", buf); - } - else - grub_printf ("%-12llu", (unsigned long long) file->size); - - } - grub_file_close (file); - grub_free (pathname); - } - else - grub_printf ("%-12s", "DIR"); - - if (info->mtimeset) - { - struct grub_datetime datetime; - grub_unixtime2datetime (info->mtime, &datetime); - if (human) - grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ", - datetime.year, datetime.month, datetime.day, - datetime.hour, datetime.minute, - datetime.second, - grub_get_weekday_name (&datetime)); - else - grub_printf (" %04d%02d%02d%02d%02d%02d ", - datetime.year, datetime.month, - datetime.day, datetime.hour, - datetime.minute, datetime.second); - } - grub_printf ("%s%s\n", filename, info->dir ? "/" : ""); - - return 0; - } - - device_name = grub_file_get_device_name (dirname); - dev = grub_device_open (device_name); - if (! dev) - goto fail; - - fs = grub_fs_probe (dev); - path = grub_strchr (dirname, ')'); - if (! path) - path = dirname; - else - path++; - - if (! path && ! device_name) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); - goto fail; - } - - if (! *path) - { - if (grub_errno == GRUB_ERR_UNKNOWN_FS) - grub_errno = GRUB_ERR_NONE; - - grub_normal_print_device_info (device_name); - } - else if (fs) - { - if (longlist) - (fs->dir) (dev, path, print_files_long); - else - (fs->dir) (dev, path, print_files); - - if (grub_errno == GRUB_ERR_BAD_FILE_TYPE - && path[grub_strlen (path) - 1] != '/') - { - /* PATH might be a regular file. */ - char *p; - grub_file_t file; - struct grub_dirhook_info info; - grub_errno = 0; - - file = grub_file_open (dirname); - if (! file) - goto fail; - - grub_file_close (file); - - p = grub_strrchr (dirname, '/') + 1; - dirname = grub_strndup (dirname, p - dirname); - if (! dirname) - goto fail; - - all = 1; - grub_memset (&info, 0, sizeof (info)); - if (longlist) - print_files_long (p, &info); - else - print_files (p, &info); - - grub_free (dirname); - } - - if (grub_errno == GRUB_ERR_NONE) - grub_putchar ('\n'); - - grub_refresh (); - } - - fail: - if (dev) - grub_device_close (dev); - - grub_free (device_name); - - return 0; -} - -static grub_err_t -grub_cmd_ls (grub_extcmd_t cmd, int argc, char **args) -{ - struct grub_arg_list *state = cmd->state; - - if (argc == 0) - grub_ls_list_devices (state[0].set); - else - grub_ls_list_files (args[0], state[0].set, state[2].set, - state[1].set); - - return 0; -} - -static grub_extcmd_t cmd; - -GRUB_MOD_INIT(ls) -{ - cmd = grub_register_extcmd ("ls", grub_cmd_ls, GRUB_COMMAND_FLAG_BOTH, - N_("[-l|-h|-a] [FILE]"), - N_("List devices and files."), options); -} - -GRUB_MOD_FINI(ls) -{ - grub_unregister_extcmd (cmd); -} diff --git a/commands/minicmd.c b/commands/minicmd.c deleted file mode 100644 index 4ea9efead..000000000 --- a/commands/minicmd.c +++ /dev/null @@ -1,391 +0,0 @@ -/* minicmd.c - commands for the rescue mode */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2005,2006,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* cat FILE */ -static grub_err_t -grub_mini_cmd_cat (struct grub_command *cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - grub_file_t file; - char buf[GRUB_DISK_SECTOR_SIZE]; - grub_ssize_t size; - - if (argc < 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified"); - - file = grub_file_open (argv[0]); - if (! file) - return grub_errno; - - while ((size = grub_file_read (file, buf, sizeof (buf))) > 0) - { - int i; - - for (i = 0; i < size; i++) - { - unsigned char c = buf[i]; - - if ((grub_isprint (c) || grub_isspace (c)) && c != '\r') - grub_putchar (c); - else - { - grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); - grub_printf ("<%x>", (int) c); - grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); - } - } - } - - grub_putchar ('\n'); - grub_refresh (); - grub_file_close (file); - - return 0; -} - -/* help */ -static grub_err_t -grub_mini_cmd_help (struct grub_command *cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char *argv[] __attribute__ ((unused))) -{ - grub_command_t p; - - for (p = grub_command_list; p; p = p->next) - grub_printf ("%s (%d%c)\t%s\n", p->name, - p->prio & GRUB_PRIO_LIST_PRIO_MASK, - (p->prio & GRUB_PRIO_LIST_FLAG_ACTIVE) ? '+' : '-', - p->description); - - return 0; -} - -#if 0 -static void -grub_rescue_cmd_info (void) -{ - extern void grub_disk_cache_get_performance (unsigned long *, - unsigned long *); - unsigned long hits, misses; - - grub_disk_cache_get_performance (&hits, &misses); - grub_printf ("Disk cache: hits = %u, misses = %u ", hits, misses); - if (hits + misses) - { - unsigned long ratio = hits * 10000 / (hits + misses); - grub_printf ("(%u.%u%%)\n", ratio / 100, ratio % 100); - } - else - grub_printf ("(N/A)\n"); -} -#endif - -/* root [DEVICE] */ -static grub_err_t -grub_mini_cmd_root (struct grub_command *cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - grub_device_t dev; - grub_fs_t fs; - - if (argc > 0) - { - char *device_name = grub_file_get_device_name (argv[0]); - if (! device_name) - return grub_errno; - - grub_env_set ("root", device_name); - grub_free (device_name); - } - - dev = grub_device_open (0); - if (! dev) - return grub_errno; - - fs = grub_fs_probe (dev); - if (grub_errno == GRUB_ERR_UNKNOWN_FS) - grub_errno = GRUB_ERR_NONE; - - grub_printf ("(%s): Filesystem is %s.\n", - grub_env_get ("root"), fs ? fs->name : "unknown"); - - grub_device_close (dev); - - return 0; -} - -#if 0 -static void -grub_rescue_cmd_testload (int argc, char *argv[]) -{ - grub_file_t file; - char *buf; - grub_ssize_t size; - grub_ssize_t pos; - auto void read_func (unsigned long sector, unsigned offset, unsigned len); - - void read_func (unsigned long sector __attribute__ ((unused)), - unsigned offset __attribute__ ((unused)), - unsigned len __attribute__ ((unused))) - { - grub_putchar ('.'); - grub_refresh (); - } - - if (argc < 1) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "no file specified"); - return; - } - - file = grub_file_open (argv[0]); - if (! file) - return; - - size = grub_file_size (file) & ~(GRUB_DISK_SECTOR_SIZE - 1); - if (size == 0) - { - grub_file_close (file); - return; - } - - buf = grub_malloc (size); - if (! buf) - goto fail; - - grub_printf ("Reading %s sequentially", argv[0]); - file->read_hook = read_func; - if (grub_file_read (file, buf, size) != size) - goto fail; - grub_printf (" Done.\n"); - - /* Read sequentially again. */ - grub_printf ("Reading %s sequentially again", argv[0]); - if (grub_file_seek (file, 0) < 0) - goto fail; - - for (pos = 0; pos < size; pos += GRUB_DISK_SECTOR_SIZE) - { - char sector[GRUB_DISK_SECTOR_SIZE]; - - if (grub_file_read (file, sector, GRUB_DISK_SECTOR_SIZE) - != GRUB_DISK_SECTOR_SIZE) - goto fail; - - if (grub_memcmp (sector, buf + pos, GRUB_DISK_SECTOR_SIZE) != 0) - { - grub_printf ("\nDiffers in %d\n", pos); - goto fail; - } - } - grub_printf (" Done.\n"); - - /* Read backwards and compare. */ - grub_printf ("Reading %s backwards", argv[0]); - pos = size; - while (pos > 0) - { - char sector[GRUB_DISK_SECTOR_SIZE]; - - pos -= GRUB_DISK_SECTOR_SIZE; - - if (grub_file_seek (file, pos) < 0) - goto fail; - - if (grub_file_read (file, sector, GRUB_DISK_SECTOR_SIZE) - != GRUB_DISK_SECTOR_SIZE) - goto fail; - - if (grub_memcmp (sector, buf + pos, GRUB_DISK_SECTOR_SIZE) != 0) - { - int i; - - grub_printf ("\nDiffers in %d\n", pos); - - for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i++) - grub_putchar (buf[pos + i]); - - if (i) - grub_refresh (); - - goto fail; - } - } - grub_printf (" Done.\n"); - - fail: - - grub_file_close (file); - grub_free (buf); -} -#endif - -/* dump ADDRESS [SIZE] */ -static grub_err_t -grub_mini_cmd_dump (struct grub_command *cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - grub_uint8_t *addr; - grub_size_t size = 4; - - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no address specified"); - - addr = (grub_uint8_t *) grub_strtoul (argv[0], 0, 0); - if (grub_errno) - return grub_errno; - - if (argc > 1) - size = (grub_size_t) grub_strtoul (argv[1], 0, 0); - - while (size--) - { - grub_printf ("%x%x ", *addr >> 4, *addr & 0xf); - addr++; - } - - return 0; -} - -/* rmmod MODULE */ -static grub_err_t -grub_mini_cmd_rmmod (struct grub_command *cmd __attribute__ ((unused)), - int argc, char *argv[]) -{ - grub_dl_t mod; - - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified"); - - mod = grub_dl_get (argv[0]); - if (! mod) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such module"); - - if (grub_dl_unref (mod) <= 0) - grub_dl_unload (mod); - - return 0; -} - -/* lsmod */ -static grub_err_t -grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char *argv[] __attribute__ ((unused))) -{ - auto int print_module (grub_dl_t mod); - - int print_module (grub_dl_t mod) - { - grub_dl_dep_t dep; - - grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count); - for (dep = mod->dep; dep; dep = dep->next) - { - if (dep != mod->dep) - grub_putchar (','); - - grub_printf ("%s", dep->mod->name); - } - grub_putchar ('\n'); - - return 0; - } - - grub_printf ("Name\tRef Count\tDependencies\n"); - grub_dl_iterate (print_module); - - return 0; -} - -/* exit */ -static grub_err_t -grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char *argv[] __attribute__ ((unused))) -{ - grub_exit (); - return 0; -} - -/* 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_cat, cmd_help, cmd_root; -static grub_command_t cmd_dump, cmd_rmmod, cmd_lsmod, cmd_exit; -static grub_command_t cmd_clear; - -GRUB_MOD_INIT(minicmd) -{ - cmd_cat = - grub_register_command ("cat", grub_mini_cmd_cat, - N_("FILE"), N_("Show the contents of a file.")); - cmd_help = - grub_register_command ("help", grub_mini_cmd_help, - 0, N_("Show this message.")); - cmd_root = - grub_register_command ("root", grub_mini_cmd_root, - N_("[DEVICE]"), N_("Set the root device.")); - cmd_dump = - grub_register_command ("dump", grub_mini_cmd_dump, - N_("ADDR"), N_("Dump memory.")); - cmd_rmmod = - grub_register_command ("rmmod", grub_mini_cmd_rmmod, - N_("MODULE"), N_("Remove a module.")); - cmd_lsmod = - grub_register_command ("lsmod", grub_mini_cmd_lsmod, - 0, N_("Show loaded modules.")); - cmd_exit = - grub_register_command ("exit", grub_mini_cmd_exit, - 0, N_("Exit from GRUB.")); - cmd_clear = - grub_register_command ("clear", grub_mini_cmd_clear, - 0, N_("Clear the screen.")); -} - -GRUB_MOD_FINI(minicmd) -{ - grub_unregister_command (cmd_cat); - grub_unregister_command (cmd_help); - grub_unregister_command (cmd_root); - grub_unregister_command (cmd_dump); - grub_unregister_command (cmd_rmmod); - grub_unregister_command (cmd_lsmod); - grub_unregister_command (cmd_exit); - grub_unregister_command (cmd_clear); -} diff --git a/commands/search.c b/commands/search.c deleted file mode 100644 index afb2e98e8..000000000 --- a/commands/search.c +++ /dev/null @@ -1,174 +0,0 @@ -/* search.c - search devices based on a file or a filesystem label */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void -FUNC_NAME (const char *key, const char *var, int no_floppy) -{ - int count = 0; - grub_fs_autoload_hook_t saved_autoload; - - auto int iterate_device (const char *name); - int iterate_device (const char *name) - { - int found = 0; - - /* Skip floppy drives when requested. */ - if (no_floppy && - name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') - return 0; - -#ifdef DO_SEARCH_FILE - { - char *buf; - grub_file_t file; - - buf = grub_xasprintf ("(%s)%s", name, key); - if (! buf) - return 1; - - file = grub_file_open (buf); - if (file) - { - found = 1; - grub_file_close (file); - } - grub_free (buf); - } -#else - { - /* SEARCH_FS_UUID or SEARCH_LABEL */ - grub_device_t dev; - grub_fs_t fs; - char *quid; - - dev = grub_device_open (name); - if (dev) - { - fs = grub_fs_probe (dev); - -#ifdef DO_SEARCH_FS_UUID -#define compare_fn grub_strcasecmp -#define read_fn uuid -#else -#define compare_fn grub_strcmp -#define read_fn label -#endif - - if (fs && fs->read_fn) - { - fs->read_fn (dev, &quid); - - if (grub_errno == GRUB_ERR_NONE && quid) - { - if (compare_fn (quid, key) == 0) - found = 1; - - grub_free (quid); - } - } - - grub_device_close (dev); - } - } -#endif - - if (found) - { - count++; - if (var) - grub_env_set (var, name); - else - grub_printf (" %s", name); - } - - grub_errno = GRUB_ERR_NONE; - return (found && var); - } - - /* First try without autoloading if we're setting variable. */ - if (var) - { - saved_autoload = grub_fs_autoload_hook; - grub_fs_autoload_hook = 0; - grub_device_iterate (iterate_device); - - /* Restore autoload hook. */ - grub_fs_autoload_hook = saved_autoload; - - /* Retry with autoload if nothing found. */ - if (grub_errno == GRUB_ERR_NONE && count == 0) - grub_device_iterate (iterate_device); - } - else - grub_device_iterate (iterate_device); - - if (grub_errno == GRUB_ERR_NONE && count == 0) - grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key); -} - -static grub_err_t -grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc, - char **args) -{ - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no argument specified"); - - FUNC_NAME (args[0], argc == 1 ? 0 : args[1], 0); - - return grub_errno; -} - -static grub_command_t cmd; - -#ifdef DO_SEARCH_FILE -GRUB_MOD_INIT(search_file) -#elif defined (DO_SEARCH_FS_UUID) -GRUB_MOD_INIT(search_fs_uuid) -#else -GRUB_MOD_INIT(search_fs_label) -#endif -{ - cmd = - grub_register_command (COMMAND_NAME, grub_cmd_do_search, - N_("NAME [VARIABLE]"), - HELP_MESSAGE); -} - -#ifdef DO_SEARCH_FILE -GRUB_MOD_FINI(search_file) -#elif defined (DO_SEARCH_FS_UUID) -GRUB_MOD_FINI(search_fs_uuid) -#else -GRUB_MOD_FINI(search_fs_label) -#endif -{ - grub_unregister_command (cmd); -} diff --git a/commands/search_wrap.c b/commands/search_wrap.c deleted file mode 100644 index 2891d85d7..000000000 --- a/commands/search_wrap.c +++ /dev/null @@ -1,95 +0,0 @@ -/* search.c - search devices based on a file or a filesystem label */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const struct grub_arg_option options[] = - { - {"file", 'f', 0, N_("Search devices by a file."), 0, 0}, - {"label", 'l', 0, N_("Search devices by a filesystem label."), - 0, 0}, - {"fs-uuid", 'u', 0, N_("Search devices by a filesystem UUID."), - 0, 0}, - {"set", 's', GRUB_ARG_OPTION_OPTIONAL, - N_("Set a variable to the first device found."), "VAR", ARG_TYPE_STRING}, - {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, - {0, 0, 0, 0, 0, 0} - }; - -enum options - { - SEARCH_FILE, - SEARCH_LABEL, - SEARCH_FS_UUID, - SEARCH_SET, - SEARCH_NO_FLOPPY, - }; - -static grub_err_t -grub_cmd_search (grub_extcmd_t cmd, int argc, char **args) -{ - struct grub_arg_list *state = cmd->state; - const char *var = 0; - - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no argument specified"); - - if (state[SEARCH_SET].set) - var = state[SEARCH_SET].arg ? state[SEARCH_SET].arg : "root"; - - if (state[SEARCH_LABEL].set) - grub_search_label (args[0], var, state[SEARCH_NO_FLOPPY].set); - else if (state[SEARCH_FS_UUID].set) - grub_search_fs_uuid (args[0], var, state[SEARCH_NO_FLOPPY].set); - else if (state[SEARCH_FILE].set) - grub_search_fs_file (args[0], var, state[SEARCH_NO_FLOPPY].set); - else - return grub_error (GRUB_ERR_INVALID_COMMAND, "unspecified search type"); - - return grub_errno; -} - -static grub_extcmd_t cmd; - -GRUB_MOD_INIT(search) -{ - cmd = - grub_register_extcmd ("search", grub_cmd_search, - GRUB_COMMAND_FLAG_BOTH, - N_("search [-f|-l|-u|-s|-n] NAME"), - N_("Search devices by file, filesystem label" - " or filesystem UUID." - " If --set is specified, the first device found is" - " set to a variable. If no variable name is" - " specified, \"root\" is used."), - options); -} - -GRUB_MOD_FINI(search) -{ - grub_unregister_extcmd (cmd); -} diff --git a/commands/terminal.c b/commands/terminal.c deleted file mode 100644 index e725123b8..000000000 --- a/commands/terminal.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include - -struct grub_term_autoload *grub_term_input_autoload = NULL; -struct grub_term_autoload *grub_term_output_autoload = NULL; - -static grub_err_t -grub_cmd_terminal_input (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - int i; - grub_term_input_t term; - struct grub_term_autoload *aut; - - if (argc == 0) - { - grub_puts_ (N_ ("Active input terminals:")); - FOR_ACTIVE_TERM_INPUTS(term) - grub_printf ("%s ", term->name); - grub_printf ("\n"); - grub_puts_ (N_ ("Available input terminals:")); - FOR_DISABLED_TERM_INPUTS(term) - grub_printf ("%s ", term->name); - /* This is quadratic but we don't expect mode than 30 terminal - modules ever. */ - for (aut = grub_term_input_autoload; aut; aut = aut->next) - { - FOR_DISABLED_TERM_INPUTS(term) - if (grub_strcmp (term->name, aut->name) == 0) - break; - if (!term) - FOR_ACTIVE_TERM_INPUTS(term) - if (grub_strcmp (term->name, aut->name) == 0) - break; - if (!term) - grub_printf ("%s ", aut->name); - } - grub_printf ("\n"); - return GRUB_ERR_NONE; - } - i = 0; - - if (grub_strcmp (args[0], "--append") == 0 - || grub_strcmp (args[0], "--remove") == 0) - i++; - - if (i == argc) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no terminal specified")); - - for (; i < argc; i++) - { - int again = 0; - while (1) - { - FOR_DISABLED_TERM_INPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term == 0) - FOR_ACTIVE_TERM_INPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - break; - if (again) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n", - args[i]); - for (aut = grub_term_input_autoload; aut; aut = aut->next) - if (grub_strcmp (args[i], aut->name) == 0) - { - grub_dl_t mod; - mod = grub_dl_load (aut->modname); - if (mod) - grub_dl_ref (mod); - grub_errno = GRUB_ERR_NONE; - break; - } - if (!aut) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n", - args[i]); - again = 1; - } - } - - if (grub_strcmp (args[0], "--append") == 0) - { - for (i = 1; i < argc; i++) - { - FOR_DISABLED_TERM_INPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (term->init && term->init () != GRUB_ERR_NONE) - return grub_errno; - - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_inputs_disabled)), - GRUB_AS_LIST (term)); - grub_list_push (GRUB_AS_LIST_P (&grub_term_inputs), - GRUB_AS_LIST (term)); - } - } - return GRUB_ERR_NONE; - } - - if (grub_strcmp (args[0], "--remove") == 0) - { - for (i = 1; i < argc; i++) - { - FOR_ACTIVE_TERM_INPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (!term->next && term == grub_term_inputs) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "can't remove the last terminal"); - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_inputs)), - GRUB_AS_LIST (term)); - if (term->fini) - term->fini (); - grub_list_push (GRUB_AS_LIST_P (&grub_term_inputs_disabled), - GRUB_AS_LIST (term)); - } - } - return GRUB_ERR_NONE; - } - for (i = 0; i < argc; i++) - { - FOR_DISABLED_TERM_INPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (term->init && term->init () != GRUB_ERR_NONE) - return grub_errno; - - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_inputs_disabled)), - GRUB_AS_LIST (term)); - grub_list_push (GRUB_AS_LIST_P (&grub_term_inputs), - GRUB_AS_LIST (term)); - } - } - - FOR_ACTIVE_TERM_INPUTS(term) - { - for (i = 0; i < argc; i++) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (i == argc) - { - if (!term->next && term == grub_term_inputs) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "can't remove the last terminal"); - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_inputs)), - GRUB_AS_LIST (term)); - if (term->fini) - term->fini (); - grub_list_push (GRUB_AS_LIST_P (&grub_term_inputs_disabled), - GRUB_AS_LIST (term)); - } - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_cmd_terminal_output (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - int i; - grub_term_output_t term; - struct grub_term_autoload *aut; - - if (argc == 0) - { - grub_puts_ (N_ ("Active output terminals:")); - FOR_ACTIVE_TERM_OUTPUTS(term) - grub_printf ("%s ", term->name); - grub_printf ("\n"); - grub_puts_ (N_ ("Available output terminals:")); - FOR_DISABLED_TERM_OUTPUTS(term) - grub_printf ("%s ", term->name); - /* This is quadratic but we don't expect mode than 30 terminal - modules ever. */ - for (aut = grub_term_output_autoload; aut; aut = aut->next) - { - FOR_DISABLED_TERM_OUTPUTS(term) - if (grub_strcmp (term->name, aut->name) == 0) - break; - if (!term) - FOR_ACTIVE_TERM_OUTPUTS(term) - if (grub_strcmp (term->name, aut->name) == 0) - break; - if (!term) - grub_printf ("%s ", aut->name); - } - grub_printf ("\n"); - return GRUB_ERR_NONE; - } - i = 0; - - if (grub_strcmp (args[0], "--append") == 0 - || grub_strcmp (args[0], "--remove") == 0) - i++; - - if (i == argc) - return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no terminal specified")); - - for (; i < argc; i++) - { - int again = 0; - while (1) - { - FOR_DISABLED_TERM_OUTPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term == 0) - FOR_ACTIVE_TERM_OUTPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - break; - if (again) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n", - args[i]); - for (aut = grub_term_output_autoload; aut; aut = aut->next) - if (grub_strcmp (args[i], aut->name) == 0) - { - grub_dl_t mod; - mod = grub_dl_load (aut->modname); - if (mod) - grub_dl_ref (mod); - grub_errno = GRUB_ERR_NONE; - break; - } - if (!aut) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminal '%s'\n", - args[i]); - again = 1; - } - } - - if (grub_strcmp (args[0], "--append") == 0) - { - for (i = 1; i < argc; i++) - { - FOR_DISABLED_TERM_OUTPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (term->init && term->init () != GRUB_ERR_NONE) - return grub_errno; - - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_outputs_disabled)), - GRUB_AS_LIST (term)); - grub_list_push (GRUB_AS_LIST_P (&grub_term_outputs), - GRUB_AS_LIST (term)); - } - } - return GRUB_ERR_NONE; - } - - if (grub_strcmp (args[0], "--remove") == 0) - { - for (i = 1; i < argc; i++) - { - FOR_ACTIVE_TERM_OUTPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (!term->next && term == grub_term_outputs) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "can't remove the last terminal"); - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_outputs)), - GRUB_AS_LIST (term)); - if (term->fini) - term->fini (); - grub_list_push (GRUB_AS_LIST_P (&grub_term_outputs_disabled), - GRUB_AS_LIST (term)); - } - } - return GRUB_ERR_NONE; - } - - for (i = 0; i < argc; i++) - { - FOR_DISABLED_TERM_OUTPUTS(term) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (term) - { - if (term->init && term->init () != GRUB_ERR_NONE) - return grub_errno; - - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_outputs_disabled)), - GRUB_AS_LIST (term)); - grub_list_push (GRUB_AS_LIST_P (&grub_term_outputs), - GRUB_AS_LIST (term)); - } - } - - FOR_ACTIVE_TERM_OUTPUTS(term) - { - for (i = 0; i < argc; i++) - if (grub_strcmp (args[i], term->name) == 0) - break; - if (i == argc) - { - if (!term->next && term == grub_term_outputs) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "can't remove the last terminal"); - grub_list_remove (GRUB_AS_LIST_P (&(grub_term_outputs)), - GRUB_AS_LIST (term)); - if (term->fini) - term->fini (); - grub_list_push (GRUB_AS_LIST_P (&grub_term_outputs_disabled), - GRUB_AS_LIST (term)); - } - } - - return GRUB_ERR_NONE; -} - -static grub_command_t cmd_terminal_input, cmd_terminal_output; - -GRUB_MOD_INIT(terminal) -{ - cmd_terminal_input = - grub_register_command ("terminal_input", grub_cmd_terminal_input, - "[--append|--remove] " - "[TERMINAL1] [TERMINAL2] ...", - "List or select an input terminal."); - cmd_terminal_output = - grub_register_command ("terminal_output", grub_cmd_terminal_output, - "[--append|--remove] " - "[TERMINAL1] [TERMINAL2] ...", - "List or select an output terminal."); -} - -GRUB_MOD_FINI(terminal) -{ - grub_unregister_command (cmd_terminal_input); - grub_unregister_command (cmd_terminal_output); -} diff --git a/commands/videotest.c b/commands/videotest.c deleted file mode 100644 index 390811a71..000000000 --- a/commands/videotest.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_err_t -grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - grub_err_t err; - grub_video_color_t color; - unsigned int x; - unsigned int y; - unsigned int width; - unsigned int height; - int i; - grub_font_t sansbig; - grub_font_t sans; - grub_font_t sanssmall; - grub_font_t fixed; - struct grub_font_glyph *glyph; - struct grub_video_render_target *text_layer; - grub_video_color_t palette[16]; - const char *str; - int texty; - - err = grub_video_set_mode ("auto", GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); - if (err) - return err; - - grub_video_get_viewport (&x, &y, &width, &height); - - grub_video_create_render_target (&text_layer, width, height, - GRUB_VIDEO_MODE_TYPE_RGB - | GRUB_VIDEO_MODE_TYPE_ALPHA); - - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - - color = grub_video_map_rgb (0, 0, 0); - grub_video_fill_rect (color, 0, 0, width, height); - - color = grub_video_map_rgb (255, 0, 0); - grub_video_fill_rect (color, 0, 0, 100, 100); - - color = grub_video_map_rgb (0, 255, 255); - grub_video_fill_rect (color, 100, 100, 100, 100); - - sansbig = grub_font_get ("Unknown Regular 16"); - sans = grub_font_get ("Unknown Regular 16"); - sanssmall = grub_font_get ("Unknown Regular 16"); - fixed = grub_font_get ("Fixed 20"); - if (! sansbig || ! sans || ! sanssmall || ! fixed) - return grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); - - glyph = grub_font_get_glyph (fixed, '*'); - grub_font_draw_glyph (glyph, color, 200 ,0); - - grub_video_set_viewport (x + 150, y + 150, - width - 150 * 2, height - 150 * 2); - color = grub_video_map_rgb (77, 33, 77); - grub_video_fill_rect (color, 0, 0, width, height); - - grub_video_set_active_render_target (text_layer); - - color = grub_video_map_rgb (255, 255, 255); - - texty = 32; - grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", - sans, color, 16, texty); - texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); - - texty += grub_font_get_ascent (fixed); - grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", - fixed, color, 16, texty); - texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); - - /* To convert Unicode characters into UTF-8 for this test, the following - command is useful: - echo -ne '\x00\x00\x26\x3A' | iconv -f UTF-32BE -t UTF-8 | od -t x1 - This converts the Unicode character U+263A to UTF-8. */ - - /* Characters used: - Code point Description UTF-8 encoding - ----------- ------------------------------ -------------- - U+263A unfilled smiley face E2 98 BA - U+00A1 inverted exclamation point C2 A1 - U+00A3 British pound currency symbol C2 A3 - U+03C4 Greek tau CF 84 - U+00E4 lowercase letter a with umlaut C3 A4 - U+2124 set 'Z' symbol (integers) E2 84 A4 - U+2287 subset symbol E2 8A 87 - U+211D set 'R' symbol (real numbers) E2 84 9D */ - - str = - "Unicode test: happy\xE2\x98\xBA \xC2\xA3 5.00" - " \xC2\xA1\xCF\x84\xC3\xA4u! " - " \xE2\x84\xA4\xE2\x8A\x87\xE2\x84\x9D"; - color = grub_video_map_rgb (128, 128, 255); - - /* All characters in the string exist in the 'Fixed 20' (10x20) font. */ - texty += grub_font_get_ascent(fixed); - grub_font_draw_string (str, fixed, color, 16, texty); - texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); - - texty += grub_font_get_ascent(sansbig); - grub_font_draw_string (str, sansbig, color, 16, texty); - texty += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig); - - texty += grub_font_get_ascent(sans); - grub_font_draw_string (str, sans, color, 16, texty); - texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); - - texty += grub_font_get_ascent(sanssmall); - grub_font_draw_string (str, sanssmall, color, 16, texty); - texty += (grub_font_get_descent (sanssmall) - + grub_font_get_leading (sanssmall)); - - glyph = grub_font_get_glyph (fixed, '*'); - - for (i = 0; i < 16; i++) - { - color = grub_video_map_color (i); - palette[i] = color; - grub_font_draw_glyph (glyph, color, 16 + i * 16, 220); - } - - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - - for (i = 0; i < 5; i++) - { - color = grub_video_map_rgb (i, 33, 77); - grub_video_fill_rect (color, 0, 0, width, height); - grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, 0, 0, - 0, 0, width, height); - grub_video_swap_buffers (); - } - - grub_getkey (); - - grub_video_delete_render_target (text_layer); - - grub_video_restore (); - - for (i = 0; i < 16; i++) - grub_printf("color %d: %08x\n", i, palette[i]); - - grub_errno = GRUB_ERR_NONE; - return grub_errno; -} - -static grub_command_t cmd; - -GRUB_MOD_INIT(videotest) -{ - cmd = grub_register_command ("videotest", grub_cmd_videotest, - 0, N_("Test video subsystem.")); -} - -GRUB_MOD_FINI(videotest) -{ - grub_unregister_command (cmd); -} diff --git a/conf/Makefile.common b/conf/Makefile.common new file mode 100644 index 000000000..c60f55386 --- /dev/null +++ b/conf/Makefile.common @@ -0,0 +1,152 @@ +# -*- makefile -*- + +CFLAGS_PLATFORM= + +export LC_COLLATE := C +unexport LC_ALL + +# Platform specific options +if COND_sparc64_ieee1275 + LDFLAGS_PLATFORM = -Wl,-melf64_sparc +endif +if COND_arm +if !COND_emu + LDFLAGS_PLATFORM = -Wl,--wrap=__clear_cache +endif +endif +if COND_arm64 + CFLAGS_PLATFORM += -mcmodel=large +endif +if COND_powerpc_ieee1275 + CFLAGS_PLATFORM += -mcpu=powerpc +endif +if COND_HAVE_PCI + CFLAGS_PLATFORM += -DGRUB_HAS_PCI +endif + +# Other options + +CPPFLAGS_DEFAULT = -DGRUB_FILE=\"$(subst $(srcdir)/,,$<)\" +CPPFLAGS_DEFAULT += -I$(builddir) +CPPFLAGS_DEFAULT += -I$(srcdir) +CPPFLAGS_DEFAULT += -I$(top_builddir) +CPPFLAGS_DEFAULT += -I$(top_srcdir) +CPPFLAGS_DEFAULT += -I$(top_srcdir)/include +CPPFLAGS_DEFAULT += -I$(top_builddir)/include +CPPFLAGS_DEFAULT += -I$(top_srcdir)/grub-core/lib/libgcrypt-grub/src/ +CCASFLAGS_DEFAULT = $(CPPFLAGS_DEFAULT) -DASM_FILE=1 +BUILD_CPPFLAGS += $(CPPFLAGS_DEFAULT) + +CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding +LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) +CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 +CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) +STRIPFLAGS_KERNEL = -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx +if !COND_emu +if COND_HAVE_ASM_USCORE + LDFLAGS_KERNEL += -Wl,--defsym=_malloc=_grub_malloc -Wl,--defsym=_free=_grub_free +else + LDFLAGS_KERNEL += -Wl,--defsym=malloc=grub_malloc -Wl,--defsym=free=grub_free +endif +endif + +CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding +LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r +CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) +CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) + +CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin +LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S +CPPFLAGS_IMAGE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) +CCASFLAGS_IMAGE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) + +CFLAGS_PROGRAM = +LDFLAGS_PROGRAM = +CPPFLAGS_PROGRAM = +CCASFLAGS_PROGRAM = + +CFLAGS_LIBRARY = +CPPFLAGS_LIBRARY = +CCASFLAGS_LIBRARY = + +# Other variables + +grubconfdir = $(sysconfdir)/grub.d +platformdir = $(pkglibdir)/$(target_cpu)-$(platform) +starfielddir = $(pkgdatadir)/themes/starfield + +CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion -Wno-error=attributes +CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib + +CFLAGS_POSIX = -fno-builtin +CPPFLAGS_POSIX = -I$(top_srcdir)/grub-core/lib/posix_wrap + +CFLAGS_GCRY = -Wno-error -Wno-missing-field-initializers -Wno-redundant-decls -Wno-undef $(CFLAGS_POSIX) +CPPFLAGS_GCRY = -I$(top_srcdir)/grub-core/lib/libgcrypt_wrap $(CPPFLAGS_POSIX) -D_GCRYPT_IN_LIBGCRYPT=1 -I$(top_srcdir)/include/grub/gcrypt + +CPPFLAGS_EFIEMU = -I$(top_srcdir)/grub-core/efiemu/runtime + +# List file macros for recognizing /interesting/ modules +CPPFLAGS_FS_LIST = -Dgrub_fs_register=FS_LIST_MARKER +CPPFLAGS_VIDEO_LIST= -Dgrub_video_register=VIDEO_LIST_MARKER +CPPFLAGS_PARTMAP_LIST = -Dgrub_partition_map_register=PARTMAP_LIST_MARKER +CPPFLAGS_PARTTOOL_LIST = -Dgrub_parttool_register=PARTTOOL_LIST_MARKER +CPPFLAGS_TERMINAL_LIST = '-Dgrub_term_register_input(...)=INPUT_TERMINAL_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_TERMINAL_LIST += '-Dgrub_term_register_output(...)=OUTPUT_TERMINAL_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_COMMAND_LIST = '-Dgrub_register_command(...)=COMMAND_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_COMMAND_LIST += '-Dgrub_register_command_lockdown(...)=COMMAND_LOCKDOWN_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_COMMAND_LIST += '-Dgrub_register_extcmd(...)=EXTCOMMAND_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_COMMAND_LIST += '-Dgrub_register_extcmd_lockdown(...)=EXTCOMMAND_LOCKDOWN_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_COMMAND_LIST += '-Dgrub_register_command_p1(...)=P1COMMAND_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_FDT_LIST := '-Dgrub_fdtbus_register(...)=FDT_DRIVER_LIST_MARKER(__VA_ARGS__)' +CPPFLAGS_MARKER = $(CPPFLAGS_FS_LIST) $(CPPFLAGS_VIDEO_LIST) \ + $(CPPFLAGS_PARTTOOL_LIST) $(CPPFLAGS_PARTMAP_LIST) \ + $(CPPFLAGS_TERMINAL_LIST) $(CPPFLAGS_COMMAND_LIST) \ + $(CPPFLAGS_FDT_LIST) + +# Define these variables to calm down automake + +IMG_FILES = +MOD_FILES = +MODULE_FILES = +MARKER_FILES = +KERNEL_HEADER_FILES = +EXTRA_DEPS = + +bin_SCRIPTS = +bin_PROGRAMS = +check_SCRIPTS_native = +check_SCRIPTS_nonnative = +check_PROGRAMS_native = +check_PROGRAMS_nonnative = +dist_grubconf_DATA = +dist_noinst_DATA = +grubconf_SCRIPTS = +man_MANS = +noinst_DATA = +noinst_SCRIPTS = +noinst_PROGRAMS = +noinst_LIBRARIES = +pkgdata_DATA = +platform_DATA = +platform_SCRIPTS = +platform_PROGRAMS = +sbin_SCRIPTS = +sbin_PROGRAMS = + +EXTRA_DIST = +CLEANFILES = +BUILT_SOURCES = + +# Rules for Automake input + +.PRECIOUS: $(top_srcdir)/Makefile.util.am +$(top_srcdir)/Makefile.util.am: $(top_srcdir)/gentpl.py $(top_srcdir)/Makefile.util.def $(top_srcdir)/Makefile.utilgcry.def + $(PYTHON) $^ > $@.new || (rm -f $@.new; exit 1) + mv $@.new $@ + +.PRECIOUS: $(top_srcdir)/grub-core/Makefile.core.am +$(top_srcdir)/grub-core/Makefile.core.am: $(top_srcdir)/gentpl.py $(top_srcdir)/grub-core/Makefile.core.def $(top_srcdir)/grub-core/Makefile.gcry.def + if [ "x$$GRUB_CONTRIB" != x ]; then echo "You need to run ./bootstrap manually." >&2; exit 1; fi + $(PYTHON) $^ > $@.new || (rm -f $@.new; exit 1) + mv $@.new $@ diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist new file mode 100644 index 000000000..d9e2b8cc7 --- /dev/null +++ b/conf/Makefile.extra-dist @@ -0,0 +1,153 @@ +EXTRA_DIST += autogen.sh +EXTRA_DIST += geninit.sh + +EXTRA_DIST += gentpl.py +EXTRA_DIST += Makefile.util.def +EXTRA_DIST += Makefile.utilgcry.def + +EXTRA_DIST += asm-tests +EXTRA_DIST += unicode + +EXTRA_DIST += util/import_gcry.py +EXTRA_DIST += util/import_unicode.py + +EXTRA_DIST += docs/man +EXTRA_DIST += docs/autoiso.cfg +EXTRA_DIST += docs/grub.cfg +EXTRA_DIST += docs/osdetect.cfg + +EXTRA_DIST += conf/i386-cygwin-img-ld.sc + +EXTRA_DIST += grub-core/Makefile.core.def +EXTRA_DIST += grub-core/Makefile.gcry.def + +EXTRA_DIST += grub-core/extra_deps.lst +EXTRA_DIST += grub-core/genmoddep.awk +EXTRA_DIST += grub-core/genmod.sh.in +EXTRA_DIST += grub-core/gensyminfo.sh.in +EXTRA_DIST += grub-core/gensymlist.sh +EXTRA_DIST += grub-core/genemuinit.sh +EXTRA_DIST += grub-core/genemuinitheader.sh + +EXTRA_DIST += grub-core/lib/gnulib-patches/fix-width.patch + +EXTRA_DIST += grub-core/lib/libgcrypt +EXTRA_DIST += grub-core/lib/libgcrypt-grub/mpi/generic +EXTRA_DIST += $(shell find $(top_srcdir)/include -name '*.h') +EXTRA_DIST += $(shell find $(top_srcdir)/grub-core/lib -name '*.h') +EXTRA_DIST += grub-core/efiemu/runtime/config.h + +EXTRA_DIST += grub-core/lib/LzmaDec.c + +EXTRA_DIST += grub-core/fs/cpio_common.c + +EXTRA_DIST += BUGS +EXTRA_DIST += util/i386/efi/grub-dumpdevtree +EXTRA_DIST += util/spkmodem-recv.c +EXTRA_DIST += util/import_gcrypth.sed +EXTRA_DIST += util/bin2h.c +EXTRA_DIST += util/grub-gen-asciih.c +EXTRA_DIST += util/grub-gen-widthspec.c +EXTRA_DIST += util/grub-module-verifier.c +EXTRA_DIST += util/grub-module-verifier32.c +EXTRA_DIST += util/grub-module-verifier64.c +EXTRA_DIST += util/grub-module-verifierXX.c +EXTRA_DIST += util/grub-pe2elf.c + + +EXTRA_DIST += m4/gnulib-cache.m4 +EXTRA_DIST += m4/glibc2.m4 +EXTRA_DIST += m4/gnulib-tool.m4 +EXTRA_DIST += m4/intdiv0.m4 +EXTRA_DIST += m4/intl.m4 +EXTRA_DIST += m4/intldir.m4 +EXTRA_DIST += m4/intmax.m4 +EXTRA_DIST += m4/inttypes-pri.m4 +EXTRA_DIST += m4/lcmessage.m4 +EXTRA_DIST += m4/lock.m4 +EXTRA_DIST += m4/printf-posix.m4 +EXTRA_DIST += m4/threadlib.m4 +EXTRA_DIST += m4/uintmax_t.m4 +EXTRA_DIST += m4/visibility.m4 +EXTRA_DIST += m4/math_h.m4 + +EXTRA_DIST += grub-core/osdep/apple/hostdisk.c +EXTRA_DIST += grub-core/osdep/aros/hostdisk.c +EXTRA_DIST += grub-core/osdep/basic/hostdisk.c +EXTRA_DIST += grub-core/osdep/bsd/hostdisk.c +EXTRA_DIST += grub-core/osdep/freebsd/hostdisk.c +EXTRA_DIST += grub-core/osdep/hurd/hostdisk.c +EXTRA_DIST += grub-core/osdep/linux/hostdisk.c +EXTRA_DIST += grub-core/osdep/windows/hostdisk.c +EXTRA_DIST += grub-core/osdep/sun/hostdisk.c +EXTRA_DIST += grub-core/osdep/haiku/hostdisk.c + +EXTRA_DIST += grub-core/osdep/basic/init.c +EXTRA_DIST += grub-core/osdep/windows/init.c + +EXTRA_DIST += grub-core/osdep/apple/getroot.c +EXTRA_DIST += grub-core/osdep/aros/getroot.c +EXTRA_DIST += grub-core/osdep/basic/getroot.c +EXTRA_DIST += grub-core/osdep/bsd/getroot.c +EXTRA_DIST += grub-core/osdep/windows/getroot.c +EXTRA_DIST += grub-core/osdep/freebsd/getroot.c +EXTRA_DIST += grub-core/osdep/hurd/getroot.c +EXTRA_DIST += grub-core/osdep/linux/getroot.c +EXTRA_DIST += grub-core/osdep/sun/getroot.c +EXTRA_DIST += grub-core/osdep/haiku/getroot.c + +EXTRA_DIST += grub-core/osdep/basic/random.c +EXTRA_DIST += grub-core/osdep/basic/ofpath.c + +EXTRA_DIST += grub-core/osdep/unix/password.c +EXTRA_DIST += grub-core/osdep/unix/random.c +EXTRA_DIST += grub-core/osdep/unix/sleep.c + +EXTRA_DIST += grub-core/osdep/linux/ofpath.c + +EXTRA_DIST += grub-core/osdep/windows/password.c +EXTRA_DIST += grub-core/osdep/windows/random.c +EXTRA_DIST += grub-core/osdep/windows/sleep.c + +EXTRA_DIST += po/gettext-patches/0001-Support-POTFILES-shell.patch +EXTRA_DIST += po/gettext-patches/0002-Handle-gettext_printf-shell-function.patch +EXTRA_DIST += po/gettext-patches/0003-Make-msgfmt-output-in-little-endian.patch +EXTRA_DIST += po/gettext-patches/0004-Use-SHELL-rather-than-bin-sh.patch + +EXTRA_DIST += po/POTFILES-shell.in +EXTRA_DIST += po/README +EXTRA_DIST += po/Rules-translit +EXTRA_DIST += po/Rules-windowsdir +EXTRA_DIST += po/arabic.sed +EXTRA_DIST += po/cyrillic.sed +EXTRA_DIST += po/greek.sed +EXTRA_DIST += po/grub.d.sed +EXTRA_DIST += po/hebrew.sed + +EXTRA_DIST += tests/dfly-mbr-mbexample.mbr.img.gz +EXTRA_DIST += tests/dfly-mbr-mbexample.dfly.img.gz + +EXTRA_DIST += coreboot.cfg + +EXTRA_DIST += tests/file_filter/file +EXTRA_DIST += tests/file_filter/file.gz +EXTRA_DIST += tests/file_filter/file.gz.sig +EXTRA_DIST += tests/file_filter/file.lzop +EXTRA_DIST += tests/file_filter/file.lzop.sig +EXTRA_DIST += tests/file_filter/file.xz +EXTRA_DIST += tests/file_filter/file.xz.sig +EXTRA_DIST += tests/file_filter/keys +EXTRA_DIST += tests/file_filter/keys.pub +EXTRA_DIST += tests/file_filter/test.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/prompt.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/gfxboot.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/adtxt.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/isolinux.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/exithelp.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/txt.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/menu.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/stdmenu.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/dtmenu.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/po4a.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04/isolinux/rqtxt.cfg +EXTRA_DIST += tests/syslinux/ubuntu10.04_grub.cfg.in diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk deleted file mode 100644 index ff0d69c41..000000000 --- a/conf/any-emu.rmk +++ /dev/null @@ -1,129 +0,0 @@ -# -*- makefile -*- - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -sbin_UTILITIES += grub-emu -util/grub-emu.c_DEPENDENCIES = grub_emu_init.h -grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ - commands/configfile.c commands/echo.c commands/help.c \ - commands/handler.c commands/ls.c commands/test.c \ - commands/search_wrap.c commands/search_file.c \ - commands/search_label.c commands/search_uuid.c \ - commands/blocklist.c commands/hexdump.c \ - lib/hexdump.c commands/halt.c commands/reboot.c \ - lib/envblk.c commands/loadenv.c \ - commands/gptsync.c commands/probe.c commands/xnu_uuid.c \ - commands/password.c commands/keystatus.c \ - disk/host.c disk/loopback.c disk/scsi.c \ - fs/fshelp.c \ - \ - io/gzio.c \ - kern/device.c kern/disk.c kern/dl.c kern/elf.c kern/env.c \ - kern/err.c kern/list.c kern/handler.c \ - kern/command.c kern/corecmd.c commands/extcmd.c kern/file.c \ - kern/fs.c commands/boot.c kern/main.c kern/misc.c kern/parser.c \ - kern/partition.c kern/term.c \ - kern/rescue_reader.c kern/rescue_parser.c \ - lib/arg.c normal/cmdline.c normal/datetime.c normal/misc.c \ - normal/handler.c normal/auth.c lib/crypto.c normal/autofs.c \ - normal/completion.c normal/main.c normal/color.c \ - normal/menu.c normal/menu_entry.c \ - normal/menu_text.c normal/crypto.c normal/term.c \ - commands/terminal.c normal/context.c lib/charset.c \ - script/main.c script/execute.c script/function.c \ - script/lexer.c script/script.c grub_script.tab.c \ - partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ - partmap/acorn.c partmap/gpt.c \ - \ - fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ - \ - video/video.c video/fb/video_fb.c video/fb/fbblit.c \ - video/fb/fbfill.c video/fb/fbutil.c commands/videotest.c \ - video/bitmap.c video/bitmap_scale.c video/readers/tga.c \ - video/readers/jpeg.c video/readers/png.c font/font_cmd.c \ - font/font.c term/gfxterm.c io/bufio.c \ - \ - gfxmenu/gfxmenu.c gfxmenu/model.c gfxmenu/view.c \ - gfxmenu/icon_manager.c gfxmenu/theme_loader.c \ - gfxmenu/widget-box.c gfxmenu/gui_canvas.c \ - gfxmenu/gui_circular_progress.c gfxmenu/gui_box.c \ - gfxmenu/gui_label.c gfxmenu/gui_list.c gfxmenu/gui_image.c \ - gfxmenu/gui_progress_bar.c gfxmenu/gui_util.c \ - gfxmenu/gui_string_util.c gfxmenu/named_colors.c trigtables.c \ - \ - util/console.c util/hostfs.c util/grub-emu.c util/misc.c \ - util/hostdisk.c util/getroot.c \ - \ - disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ - disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ - commands/parttool.c parttool/msdospart.c \ - lib/libgcrypt-grub/cipher/md5.c \ - grub_emu_init.c gnulib/progname.c -grub_emu_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap - - -ifeq ($(target_cpu), i386) -grub_emu_SOURCES += commands/i386/cpuid.c -endif - -grub_emu_LDFLAGS = $(LIBCURSES) - -ifeq ($(enable_grub_emu_usb), yes) -grub_emu_SOURCES += disk/usbms.c util/usb.c bus/usb/usb.c \ - commands/usbtest.c -grub_emu_LDFLAGS += $(LIBCURSES) $(LIBUSB) -endif - -ifeq ($(enable_grub_emu_sdl), yes) -grub_emu_SOURCES += util/sdl.c -grub_emu_LDFLAGS += $(LIBSDL) -endif - -ifeq ($(enable_grub_emu_pci), yes) -grub_emu_SOURCES += util/pci.c commands/lspci.c -grub_emu_LDFLAGS += $(LIBPCIACCESS) -endif - -grub_emu_init.lst: geninit.sh $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_emu_init.lst - -grub_emu_init.h: grub_emu_init.lst $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ -DISTCLEANFILES += grub_emu_init.h - -grub_emu_init.c: grub_emu_init.lst $(filter-out grub_emu_init.c,$(grub_emu_SOURCES)) geninit.sh grub_emu_init.h - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ -DISTCLEANFILES += grub_emu_init.c - - - - -# FIXME: this could be shared with common.rmk - -trigtables.c: gentrigtables - ./gentrigtables > $@ -DISTCLEANFILES += trigtables.c -gentrigtables: gentrigtables.c - $(CC) -o $@ $^ $(CPPFLAGS) -lm -DISTCLEANFILES += gentrigtables - -# For grub-mkfont. -ifeq ($(enable_grub_mkfont), yes) -bin_UTILITIES += grub-mkfont -grub_mkfont_SOURCES = gnulib/progname.c util/grub-mkfont.c util/misc.c -grub_mkfont_CFLAGS = $(freetype_cflags) -grub_mkfont_LDFLAGS = $(freetype_libs) -endif - -grub_script.tab.c grub_script.tab.h: script/parser.y - $(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y -DISTCLEANFILES += grub_script.tab.c grub_script.tab.h - -bin_UTILITIES += grub-bin2h -grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c diff --git a/conf/common.rmk b/conf/common.rmk deleted file mode 100644 index 7effa8af3..000000000 --- a/conf/common.rmk +++ /dev/null @@ -1,782 +0,0 @@ -# -*- makefile -*- - -sbin_UTILITIES += grub-mkdevicemap -grub_mkdevicemap_SOURCES = gnulib/progname.c util/grub-mkdevicemap.c \ - util/deviceiter.c \ - util/misc.c - -ifeq ($(target_cpu)-$(platform), sparc64-ieee1275) -grub_mkdevicemap_SOURCES += util/ieee1275/ofpath.c util/ieee1275/devicemap.c -else -grub_mkdevicemap_SOURCES += util/devicemap.c -endif - -# For grub-mkelfimage. -bin_UTILITIES += grub-mkelfimage -grub_mkelfimage_SOURCES = gnulib/progname.c \ - util/elf/grub-mkimage.c util/misc.c \ - util/resolve.c -util/elf/grub-mkimage.c_DEPENDENCIES = Makefile - -# For grub-probe. -sbin_UTILITIES += grub-probe -util/grub-probe.c_DEPENDENCIES = grub_probe_init.h -grub_probe_SOURCES = gnulib/progname.c util/grub-probe.c \ - util/hostdisk.c util/misc.c util/getroot.c \ - kern/device.c kern/disk.c kern/err.c kern/misc.c \ - kern/parser.c kern/partition.c kern/file.c \ - \ - fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ - \ - partmap/msdos.c partmap/apple.c partmap/sun.c partmap/gpt.c\ - kern/fs.c kern/env.c fs/fshelp.c \ - disk/raid.c disk/mdraid_linux.c disk/lvm.c grub_probe_init.c - -ifeq ($(enable_grub_fstest), yes) -bin_UTILITIES += grub-fstest -endif - -bin_UTILITIES += grub-mkisofs -grub_mkisofs_SOURCES = util/mkisofs/eltorito.c \ - util/mkisofs/hash.c util/mkisofs/joliet.c \ - util/mkisofs/match.c util/mkisofs/mkisofs.c \ - util/mkisofs/multi.c util/mkisofs/name.c \ - util/mkisofs/rock.c util/mkisofs/tree.c \ - util/mkisofs/write.c \ - \ - gnulib/fnmatch.c gnulib/getopt1.c gnulib/getopt.c \ - gnulib/error.c gnulib/progname.c -grub_mkisofs_CFLAGS = -D_FILE_OFFSET_BITS=64 \ - -I$(srcdir)/util/mkisofs/include \ - -Wno-all -Werror - -# For grub-fstest. -util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h -grub_fstest_SOURCES = gnulib/progname.c util/grub-fstest.c util/hostfs.c \ - util/misc.c \ - kern/file.c kern/device.c kern/disk.c kern/err.c kern/misc.c \ - disk/host.c disk/loopback.c kern/list.c kern/command.c \ - lib/arg.c commands/extcmd.c normal/datetime.c normal/misc.c \ - lib/hexdump.c lib/crc.c commands/blocklist.c commands/ls.c \ - \ - fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c fs/befs.c \ - fs/befs_be.c fs/tar.c \ - \ - kern/partition.c partmap/msdos.c partmap/apple.c partmap/sun.c \ - partmap/gpt.c \ - kern/fs.c kern/env.c fs/fshelp.c disk/raid.c \ - disk/raid5_recover.c disk/raid6_recover.c \ - disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ - grub_fstest_init.c - -# For grub-mkfont. -ifeq ($(enable_grub_mkfont), yes) -bin_UTILITIES += grub-mkfont -grub_mkfont_SOURCES = gnulib/progname.c util/grub-mkfont.c util/misc.c -grub_mkfont_CFLAGS = $(freetype_cflags) -grub_mkfont_LDFLAGS = $(freetype_libs) -endif - -# For grub-mkrelpath. -bin_UTILITIES += grub-mkrelpath -grub_mkrelpath_SOURCES = gnulib/progname.c util/grub-mkrelpath.c util/misc.c - -bin_UTILITIES += grub-bin2h -grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c - -# For grub-script-check. -bin_UTILITIES += grub-script-check -util/grub-script-check.c_DEPENDENCIES = grub_script_check_init.h -grub_script_check_SOURCES = gnulib/progname.c gnulib/getdelim.c gnulib/getline.c \ - util/grub-script-check.c util/misc.c \ - script/main.c script/script.c script/function.c script/lexer.c \ - kern/handler.c kern/err.c kern/parser.c kern/list.c \ - kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c - -# For the parser. -grub_script.tab.c grub_script.tab.h: script/parser.y - $(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y -DISTCLEANFILES += grub_script.tab.c grub_script.tab.h - -# For grub-script-check. -grub_script_check_init.lst: geninit.sh $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_script_check_init.lst - -grub_script_check_init.h: grub_script_check_init.lst $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ -DISTCLEANFILES += grub_script_check_init.h - -grub_script_check_init.c: grub_script_check_init.lst $(filter-out grub_script_check_init.c,$(grub_script_check_SOURCES)) geninit.sh - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ -DISTCLEANFILES += grub_script_check_init.c - -# For grub-probe. -grub_probe_init.lst: geninit.sh $(filter-out grub_probe_init.c,$(grub_probe_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_probe_init.lst - -grub_probe_init.h: grub_probe_init.lst $(filter-out grub_probe_init.c,$(grub_probe_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ -DISTCLEANFILES += grub_probe_init.h - -grub_probe_init.c: grub_probe_init.lst $(filter-out grub_probe_init.c,$(grub_probe_SOURCES)) geninit.sh grub_probe_init.h - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ -DISTCLEANFILES += grub_probe_init.c - -# For grub-setup. -grub_setup_init.lst: geninit.sh $(filter-out grub_setup_init.c,$(grub_setup_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_setup_init.lst - -grub_setup_init.h: grub_setup_init.lst $(filter-out grub_setup_init.c,$(grub_setup_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ -DISTCLEANFILES += grub_setup_init.h - -grub_setup_init.c: grub_setup_init.lst $(filter-out grub_setup_init.c,$(grub_setup_SOURCES)) geninit.sh grub_setup_init.h - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ -DISTCLEANFILES += grub_setup_init.c - -# For grub-fstest. -grub_fstest_init.lst: geninit.sh $(filter-out grub_fstest_init.c,$(grub_fstest_SOURCES)) - rm -f $@; grep GRUB_MOD_INIT $(filter %.c,$^) /dev/null > $@ -DISTCLEANFILES += grub_fstest_init.lst - -grub_fstest_init.h: grub_fstest_init.lst $(filter-out grub_fstest_init.c,$(grub_fstest_SOURCES)) geninitheader.sh - rm -f $@; sh $(srcdir)/geninitheader.sh $< > $@ -DISTCLEANFILES += grub_fstest_init.h - -grub_fstest_init.c: grub_fstest_init.lst $(filter-out grub_fstest_init.c,$(grub_fstest_SOURCES)) geninit.sh grub_fstest_init.h - rm -f $@; sh $(srcdir)/geninit.sh $< $(filter %.c,$^) > $@ -DISTCLEANFILES += grub_fstest_init.c - -# for grub-editenv -bin_UTILITIES += grub-editenv -grub_editenv_SOURCES = gnulib/progname.c util/grub-editenv.c lib/envblk.c util/misc.c kern/misc.c kern/err.c -CLEANFILES += grub-editenv - -# Needed for genmk.rb to work -ifeq (0,1) -bin_UTILITIES += grub-macho2img grub-pe2elf -endif - -grub_pe2elf_SOURCES = gnulib/progname.c util/grub-pe2elf.c util/misc.c -CLEANFILES += grub-pe2elf - -grub_macho2img_SOURCES = util/grub-macho2img.c -CLEANFILES += grub-macho2img - -# For grub-mkconfig -grub-mkconfig: util/grub-mkconfig.in config.status - ./config.status --file=$@:$< - chmod +x $@ -sbin_SCRIPTS += grub-mkconfig -CLEANFILES += grub-mkconfig - -grub-mkconfig_lib: util/grub-mkconfig_lib.in config.status - ./config.status --file=$@:$< - chmod +x $@ -lib_SCRIPTS += grub-mkconfig_lib -CLEANFILES += grub-mkconfig_lib - -update-grub_lib: util/update-grub_lib.in config.status - ./config.status --file=$@:$< - chmod +x $@ -lib_SCRIPTS += update-grub_lib -CLEANFILES += update-grub_lib - -grub-gettext_lib: util/grub-gettext_lib.in config.status - ./config.status --file=$@:$< - chmod +x $@ -lib_DATA += grub-gettext_lib -CLEANFILES += grub-gettext_lib - -%: util/grub.d/%.in config.status - ./config.status --file=$@:$< - chmod +x $@ -grub-mkconfig_SCRIPTS = 00_header 30_os-prober 40_custom -ifneq (, $(host_kernel)) -grub-mkconfig_SCRIPTS += 10_$(host_kernel) -endif - -CLEANFILES += $(grub-mkconfig_SCRIPTS) - -grub-mkconfig_DATA += util/grub.d/README - -# For grub-set-default. -grub-set-default: util/grub-set-default.in config.status - ./config.status --file=$@:$< - chmod +x $@ -sbin_SCRIPTS += grub-set-default -CLEANFILES += grub-set-default - -# For grub-reboot. -grub-reboot: util/grub-reboot.in config.status - ./config.status --file=$@:$< - chmod +x $@ -sbin_SCRIPTS += grub-reboot -CLEANFILES += grub-reboot - -# Filing systems. -pkglib_MODULES += fshelp.mod fat.mod ufs1.mod ufs2.mod ext2.mod ntfs.mod \ - ntfscomp.mod minix.mod hfs.mod jfs.mod iso9660.mod xfs.mod \ - affs.mod sfs.mod hfsplus.mod reiserfs.mod cpio.mod tar.mod \ - udf.mod afs.mod afs_be.mod befs.mod befs_be.mod - -# For fshelp.mod. -fshelp_mod_SOURCES = fs/fshelp.c -fshelp_mod_CFLAGS = $(COMMON_CFLAGS) -fshelp_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For fat.mod. -fat_mod_SOURCES = fs/fat.c -fat_mod_CFLAGS = $(COMMON_CFLAGS) -fat_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ufs1.mod. -ufs1_mod_SOURCES = fs/ufs.c -ufs1_mod_CFLAGS = $(COMMON_CFLAGS) -ufs1_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ufs2.mod. -ufs2_mod_SOURCES = fs/ufs2.c -ufs2_mod_CFLAGS = $(COMMON_CFLAGS) -ufs2_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ext2.mod. -ext2_mod_SOURCES = fs/ext2.c -ext2_mod_CFLAGS = $(COMMON_CFLAGS) -ext2_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ntfs.mod. -ntfs_mod_SOURCES = fs/ntfs.c -ntfs_mod_CFLAGS = $(COMMON_CFLAGS) -ntfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ntfscomp.mod. -ntfscomp_mod_SOURCES = fs/ntfscomp.c -ntfscomp_mod_CFLAGS = $(COMMON_CFLAGS) -ntfscomp_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For minix.mod. -minix_mod_SOURCES = fs/minix.c -minix_mod_CFLAGS = $(COMMON_CFLAGS) -minix_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For hfs.mod. -hfs_mod_SOURCES = fs/hfs.c -hfs_mod_CFLAGS = $(COMMON_CFLAGS) -hfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For jfs.mod. -jfs_mod_SOURCES = fs/jfs.c -jfs_mod_CFLAGS = $(COMMON_CFLAGS) -jfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For iso9660.mod. -iso9660_mod_SOURCES = fs/iso9660.c -iso9660_mod_CFLAGS = $(COMMON_CFLAGS) -iso9660_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For xfs.mod. -xfs_mod_SOURCES = fs/xfs.c -xfs_mod_CFLAGS = $(COMMON_CFLAGS) -xfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For affs.mod. -affs_mod_SOURCES = fs/affs.c -affs_mod_CFLAGS = $(COMMON_CFLAGS) -affs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For sfs.mod. -sfs_mod_SOURCES = fs/sfs.c -sfs_mod_CFLAGS = $(COMMON_CFLAGS) -sfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For hfsplus.mod. -hfsplus_mod_SOURCES = fs/hfsplus.c -hfsplus_mod_CFLAGS = $(COMMON_CFLAGS) -hfsplus_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reiserfs.mod. -reiserfs_mod_SOURCES = fs/reiserfs.c -reiserfs_mod_CFLAGS = $(COMMON_CFLAGS) -reiserfs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For cpio.mod. -cpio_mod_SOURCES = fs/cpio.c -cpio_mod_CFLAGS = $(COMMON_CFLAGS) -cpio_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For tar.mod. -tar_mod_SOURCES = fs/tar.c -tar_mod_CFLAGS = $(COMMON_CFLAGS) -tar_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For udf.mod. -udf_mod_SOURCES = fs/udf.c -udf_mod_CFLAGS = $(COMMON_CFLAGS) -udf_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For afs.mod. -afs_mod_SOURCES = fs/afs.c -afs_mod_CFLAGS = $(COMMON_CFLAGS) -afs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For afs_be.mod. -afs_be_mod_SOURCES = fs/afs_be.c -afs_be_mod_CFLAGS = $(COMMON_CFLAGS) -afs_be_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For befs.mod. -befs_mod_SOURCES = fs/befs.c -befs_mod_CFLAGS = $(COMMON_CFLAGS) -befs_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For befs_be.mod. -befs_be_mod_SOURCES = fs/befs_be.c -befs_be_mod_CFLAGS = $(COMMON_CFLAGS) -befs_be_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Partition maps. - -pkglib_MODULES += part_amiga.mod -part_amiga_mod_SOURCES = partmap/amiga.c -part_amiga_mod_CFLAGS = $(COMMON_CFLAGS) -part_amiga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += part_apple.mod -part_apple_mod_SOURCES = partmap/apple.c -part_apple_mod_CFLAGS = $(COMMON_CFLAGS) -part_apple_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += part_msdos.mod -part_msdos_mod_SOURCES = partmap/msdos.c -part_msdos_mod_CFLAGS = $(COMMON_CFLAGS) -part_msdos_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += part_sun.mod -part_sun_mod_SOURCES = partmap/sun.c -part_sun_mod_CFLAGS = $(COMMON_CFLAGS) -part_sun_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += part_acorn.mod -part_acorn_mod_SOURCES = partmap/acorn.c -part_acorn_mod_CFLAGS = $(COMMON_CFLAGS) -part_acorn_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += part_gpt.mod -part_gpt_mod_SOURCES = partmap/gpt.c -part_gpt_mod_CFLAGS = $(COMMON_CFLAGS) -part_gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Special disk structures and generic drivers - -pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ - lvm.mod scsi.mod - -# For raid.mod -raid_mod_SOURCES = disk/raid.c -raid_mod_CFLAGS = $(COMMON_CFLAGS) -raid_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For raid5rec.mod -raid5rec_mod_SOURCES = disk/raid5_recover.c -raid5rec_mod_CFLAGS = $(COMMON_CFLAGS) -raid5rec_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For raid6rec.mod -raid6rec_mod_SOURCES = disk/raid6_recover.c -raid6rec_mod_CFLAGS = $(COMMON_CFLAGS) -raid6rec_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mdraid.mod -mdraid_mod_SOURCES = disk/mdraid_linux.c -mdraid_mod_CFLAGS = $(COMMON_CFLAGS) -mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For dm_nv.mod -dm_nv_mod_SOURCES = disk/dmraid_nvidia.c -dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) -dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lvm.mod -lvm_mod_SOURCES = disk/lvm.c -lvm_mod_CFLAGS = $(COMMON_CFLAGS) -lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For scsi.mod -scsi_mod_SOURCES = disk/scsi.c -scsi_mod_CFLAGS = $(COMMON_CFLAGS) -scsi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Commands. -pkglib_MODULES += minicmd.mod extcmd.mod hello.mod handler.mod \ - ls.mod cmp.mod cat.mod help.mod search.mod loopback.mod \ - configfile.mod echo.mod \ - terminfo.mod test.mod blocklist.mod hexdump.mod \ - read.mod sleep.mod loadenv.mod crc.mod parttool.mod \ - msdospart.mod memrw.mod normal.mod sh.mod \ - gptsync.mod true.mod probe.mod password.mod \ - keystatus.mod - -# For password.mod. -password_mod_SOURCES = commands/password.c -password_mod_CFLAGS = $(COMMON_CFLAGS) -password_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For gptsync.mod. -gptsync_mod_SOURCES = commands/gptsync.c -gptsync_mod_CFLAGS = $(COMMON_CFLAGS) -gptsync_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For minicmd.mod. -minicmd_mod_SOURCES = commands/minicmd.c -minicmd_mod_CFLAGS = $(COMMON_CFLAGS) -minicmd_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For extcmd.mod. -extcmd_mod_SOURCES = commands/extcmd.c lib/arg.c -extcmd_mod_CFLAGS = $(COMMON_CFLAGS) -extcmd_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For hello.mod. -hello_mod_SOURCES = hello/hello.c -hello_mod_CFLAGS = $(COMMON_CFLAGS) -hello_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For gfxmenu.mod. -pkglib_MODULES += gfxmenu.mod -gfxmenu_mod_SOURCES = \ - gfxmenu/gfxmenu.c \ - gfxmenu/model.c \ - gfxmenu/view.c \ - gfxmenu/icon_manager.c \ - gfxmenu/theme_loader.c \ - gfxmenu/widget-box.c \ - gfxmenu/gui_canvas.c \ - gfxmenu/gui_circular_progress.c \ - gfxmenu/gui_box.c \ - gfxmenu/gui_label.c \ - gfxmenu/gui_list.c \ - gfxmenu/gui_image.c \ - gfxmenu/gui_progress_bar.c \ - gfxmenu/gui_util.c \ - gfxmenu/gui_string_util.c \ - gfxmenu/named_colors.c -gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS) -gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For parttool.mod. -parttool_mod_SOURCES = commands/parttool.c -parttool_mod_CFLAGS = $(COMMON_CFLAGS) -parttool_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For msdospart.mod. -msdospart_mod_SOURCES = parttool/msdospart.c -msdospart_mod_CFLAGS = $(COMMON_CFLAGS) -msdospart_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For handler.mod. -handler_mod_SOURCES = commands/handler.c -handler_mod_CFLAGS = $(COMMON_CFLAGS) -handler_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ls.mod. -ls_mod_SOURCES = commands/ls.c -ls_mod_CFLAGS = $(COMMON_CFLAGS) -ls_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For cmp.mod. -cmp_mod_SOURCES = commands/cmp.c -cmp_mod_CFLAGS = $(COMMON_CFLAGS) -cmp_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For cat.mod. -cat_mod_SOURCES = commands/cat.c -cat_mod_CFLAGS = $(COMMON_CFLAGS) -cat_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For echo.mod -echo_mod_SOURCES = commands/echo.c -echo_mod_CFLAGS = $(COMMON_CFLAGS) -echo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For help.mod. -help_mod_SOURCES = commands/help.c -help_mod_CFLAGS = $(COMMON_CFLAGS) -help_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For search.mod. -search_mod_SOURCES = commands/search_wrap.c -search_mod_CFLAGS = $(COMMON_CFLAGS) -search_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += search_fs_file.mod search_fs_uuid.mod search_label.mod - -# For search.mod. -search_fs_file_mod_SOURCES = commands/search_file.c -search_fs_file_mod_CFLAGS = $(COMMON_CFLAGS) -search_fs_file_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For search.mod. -search_label_mod_SOURCES = commands/search_label.c -search_label_mod_CFLAGS = $(COMMON_CFLAGS) -search_label_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For search.mod. -search_fs_uuid_mod_SOURCES = commands/search_uuid.c -search_fs_uuid_mod_CFLAGS = $(COMMON_CFLAGS) -search_fs_uuid_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For test.mod. -test_mod_SOURCES = commands/test.c -test_mod_CFLAGS = $(COMMON_CFLAGS) -test_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loopback.mod -loopback_mod_SOURCES = disk/loopback.c -loopback_mod_CFLAGS = $(COMMON_CFLAGS) -loopback_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For configfile.mod -configfile_mod_SOURCES = commands/configfile.c -configfile_mod_CFLAGS = $(COMMON_CFLAGS) -configfile_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For terminfo.mod. -terminfo_mod_SOURCES = term/terminfo.c term/tparm.c -terminfo_mod_CFLAGS = $(COMMON_CFLAGS) -terminfo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For blocklist.mod. -blocklist_mod_SOURCES = commands/blocklist.c -blocklist_mod_CFLAGS = $(COMMON_CFLAGS) -blocklist_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For hexdump.mod. -hexdump_mod_SOURCES = commands/hexdump.c lib/hexdump.c -hexdump_mod_CFLAGS = $(COMMON_CFLAGS) -hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For read.mod. -read_mod_SOURCES = commands/read.c -read_mod_CFLAGS = $(COMMON_CFLAGS) -read_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For sleep.mod. -sleep_mod_SOURCES = commands/sleep.c -sleep_mod_CFLAGS = $(COMMON_CFLAGS) -sleep_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loadenv.mod. -loadenv_mod_SOURCES = commands/loadenv.c lib/envblk.c -loadenv_mod_CFLAGS = $(COMMON_CFLAGS) -loadenv_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For crc.mod. -crc_mod_SOURCES = commands/crc.c lib/crc.c -crc_mod_CFLAGS = $(COMMON_CFLAGS) -crc_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memrw.mod. -memrw_mod_SOURCES = commands/memrw.c -memrw_mod_CFLAGS = $(COMMON_CFLAGS) -memrw_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For true.mod -true_mod_SOURCES = commands/true.c -true_mod_CFLAGS = $(COMMON_CFLAGS) -true_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For probe.mod. -probe_mod_SOURCES = commands/probe.c -probe_mod_CFLAGS = $(COMMON_CFLAGS) -probe_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For keystatus.mod. -keystatus_mod_SOURCES = commands/keystatus.c -keystatus_mod_CFLAGS = $(COMMON_CFLAGS) -keystatus_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For normal.mod. -normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \ - normal/auth.c normal/autofs.c normal/handler.c \ - normal/color.c normal/completion.c normal/datetime.c normal/menu.c \ - normal/menu_entry.c normal/menu_text.c \ - normal/misc.c normal/crypto.c normal/term.c normal/context.c -normal_mod_CFLAGS = $(COMMON_CFLAGS) -normal_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For sh.mod. -sh_mod_SOURCES = script/main.c script/script.c script/execute.c \ - script/function.c script/lexer.c grub_script.tab.c -sh_mod_CFLAGS = $(COMMON_CFLAGS) -sh_mod_LDFLAGS = $(COMMON_LDFLAGS) - -ifneq (, $(FONT_SOURCE)) -font/font.c_DEPENDENCIES = ascii.h -endif - -# Common Video Subsystem specific modules. -# On Yeeloong it's part of kernel -ifneq ($(platform), yeeloong) - -# For video.mod. -pkglib_MODULES += video.mod -video_mod_SOURCES = video/video.c -video_mod_CFLAGS = $(COMMON_CFLAGS) -video_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += video_fb.mod -video_fb_mod_SOURCES = video/fb/video_fb.c video/fb/fbblit.c \ - video/fb/fbfill.c video/fb/fbutil.c -video_fb_mod_CFLAGS = $(COMMON_CFLAGS) -video_fb_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For bitmap.mod -pkglib_MODULES += bitmap.mod -bitmap_mod_SOURCES = video/bitmap.c -bitmap_mod_CFLAGS = $(COMMON_CFLAGS) -bitmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For bitmap_scale.mod -pkglib_MODULES += bitmap_scale.mod -bitmap_scale_mod_SOURCES = video/bitmap_scale.c -bitmap_scale_mod_CFLAGS = $(COMMON_CFLAGS) -bitmap_scale_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += font.mod -font_mod_SOURCES = font/font_cmd.c font/font.c -font_mod_CFLAGS = $(COMMON_CFLAGS) -font_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For gfxterm.mod. -pkglib_MODULES += gfxterm.mod -gfxterm_mod_SOURCES = term/gfxterm.c -gfxterm_mod_CFLAGS = $(COMMON_CFLAGS) -gfxterm_mod_LDFLAGS = $(COMMON_LDFLAGS) - -endif - -# For videotest.mod. -pkglib_MODULES += videotest.mod -videotest_mod_SOURCES = commands/videotest.c -videotest_mod_CFLAGS = $(COMMON_CFLAGS) -videotest_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For tga.mod -pkglib_MODULES += tga.mod -tga_mod_SOURCES = video/readers/tga.c -tga_mod_CFLAGS = $(COMMON_CFLAGS) -tga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For jpeg.mod. -pkglib_MODULES += jpeg.mod -jpeg_mod_SOURCES = video/readers/jpeg.c -jpeg_mod_CFLAGS = $(COMMON_CFLAGS) -jpeg_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For png.mod. -pkglib_MODULES += png.mod -png_mod_SOURCES = video/readers/png.c -png_mod_CFLAGS = $(COMMON_CFLAGS) -png_mod_LDFLAGS = $(COMMON_LDFLAGS) - - -# Misc. -pkglib_MODULES += gzio.mod elf.mod - -# For elf.mod. -elf_mod_SOURCES = kern/elf.c -elf_mod_CFLAGS = $(COMMON_CFLAGS) -elf_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For gzio.mod. -gzio_mod_SOURCES = io/gzio.c -gzio_mod_CFLAGS = $(COMMON_CFLAGS) -gzio_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# On Yeeloong it's part of kernel -ifneq ($(platform), yeeloong) -# For bufio.mod. -pkglib_MODULES += bufio.mod -bufio_mod_SOURCES = io/bufio.c -bufio_mod_CFLAGS = $(COMMON_CFLAGS) -bufio_mod_LDFLAGS = $(COMMON_LDFLAGS) -endif - -# For gettext.mod. -pkglib_MODULES += gettext.mod -gettext_mod_SOURCES = gettext/gettext.c -gettext_mod_CFLAGS = $(COMMON_CFLAGS) -gettext_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Misc. -pkglib_MODULES += xnu_uuid.mod - -# For elf.mod. -xnu_uuid_mod_SOURCES = commands/xnu_uuid.c -xnu_uuid_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_uuid_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += trig.mod -trig_mod_SOURCES = trigtables.c -trig_mod_CFLAGS = $(COMMON_CFLAGS) -trig_mod_LDFLAGS = $(COMMON_LDFLAGS) - -trigtables.c: gentrigtables - ./gentrigtables > $@ -DISTCLEANFILES += trigtables.c -gentrigtables: gentrigtables.c - $(CC) -o $@ $^ $(CPPFLAGS) -lm -DISTCLEANFILES += gentrigtables - -pkglib_MODULES += setjmp.mod -setjmp_mod_SOURCES = lib/$(target_cpu)/setjmp.S -setjmp_mod_ASFLAGS = $(COMMON_ASFLAGS) -setjmp_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += charset.mod -charset_mod_SOURCES = lib/charset.c -charset_mod_CFLAGS = $(COMMON_CFLAGS) -charset_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += terminal.mod -terminal_mod_SOURCES = commands/terminal.c -terminal_mod_CFLAGS = $(COMMON_CFLAGS) -terminal_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += crypto.mod -crypto_mod_SOURCES = lib/crypto.c -crypto_mod_CFLAGS = $(COMMON_CFLAGS) -crypto_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += hashsum.mod -hashsum_mod_SOURCES = commands/hashsum.c -hashsum_mod_CFLAGS = $(COMMON_CFLAGS) -hashsum_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += pbkdf2.mod -pbkdf2_mod_SOURCES = lib/pbkdf2.c -pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS) -pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For password_pbkdf2.mod. -pkglib_MODULES += password_pbkdf2.mod -password_pbkdf2_mod_SOURCES = commands/password_pbkdf2.c -password_pbkdf2_mod_CFLAGS = $(COMMON_CFLAGS) -password_pbkdf2_mod_LDFLAGS = $(COMMON_LDFLAGS) - -bin_UTILITIES += grub-mkpasswd-pbkdf2 -grub_mkpasswd_pbkdf2_SOURCES = gnulib/progname.c gnulib/getdelim.c gnulib/getline.c util/grub-mkpasswd-pbkdf2.c lib/crypto.c lib/libgcrypt-grub/cipher/sha512.c lib/pbkdf2.c util/misc.c kern/err.c -grub_mkpasswd_pbkdf2_CFLAGS += -Wno-missing-field-initializers -Wno-error -I$(srcdir)/lib/libgcrypt_wrap -DGRUB_MKPASSWD=1 - -include $(srcdir)/conf/gcry.mk diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk deleted file mode 100644 index 9563c0b2b..000000000 --- a/conf/i386-coreboot.rmk +++ /dev/null @@ -1,200 +0,0 @@ -# -*- makefile -*- - -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32 -COMMON_LDFLAGS = -m32 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. - -GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 - -ifeq ($(platform), coreboot) - -pkglib_PROGRAMS += kernel.img -kernel_img_SOURCES = kern/i386/coreboot/startup.S \ - kern/i386/misc.S \ - kern/i386/coreboot/init.c \ - kern/i386/multiboot_mmap.c \ - kern/i386/halt.c \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/$(target_cpu)/dl.c kern/parser.c kern/partition.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c \ - kern/env.c \ - term/i386/pc/vga_text.c term/i386/vga_common.c \ - symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h list.h handler.h command.h i18n.h \ - env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,$(GRUB_KERNEL_MACHINE_LINK_ADDR),-Bstatic - -endif - -ifeq ($(platform), qemu) - -GRUB_BOOT_MACHINE_LINK_ADDR = 0xffe00 - -pkglib_IMAGES += boot.img -boot_img_SOURCES = boot/i386/qemu/boot.S -boot_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) -boot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_BOOT_MACHINE_LINK_ADDR) -boot_img_FORMAT = binary - -bin_UTILITIES += grub-mkimage -grub_mkimage_SOURCES = util/grub-mkrawimage.c util/misc.c \ - util/resolve.c gnulib/progname.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - - -pkglib_IMAGES += kernel.img -kernel_img_SOURCES = kern/i386/qemu/startup.S \ - kern/i386/misc.S \ - kern/i386/coreboot/init.c \ - kern/i386/qemu/mmap.c \ - kern/i386/halt.c \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/$(target_cpu)/dl.c kern/parser.c kern/partition.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c \ - kern/env.c \ - term/i386/pc/vga_text.c term/i386/vga_common.c \ - symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h list.h handler.h command.h i18n.h \ - env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) -kernel_img_FORMAT = binary -endif - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -sbin_SCRIPTS += grub-install -grub_install_SOURCES = util/grub-install.in - -bin_SCRIPTS += grub-mkrescue -grub_mkrescue_SOURCES = util/grub-mkrescue.in - -# Modules. -pkglib_MODULES = linux.mod \ - aout.mod play.mod serial.mod \ - memdisk.mod pci.mod lspci.mod reboot.mod \ - halt.mod datetime.mod date.mod datehook.mod \ - lsmmap.mod mmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) -mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For aout.mod. -aout_mod_SOURCES = loader/aout.c -aout_mod_CFLAGS = $(COMMON_CFLAGS) -aout_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For bsd.mod -pkglib_MODULES += bsd.mod -bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S -bsd_mod_CFLAGS = $(COMMON_CFLAGS) -bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) -bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For play.mod. -play_mod_SOURCES = commands/i386/pc/play.c -play_mod_CFLAGS = $(COMMON_CFLAGS) -play_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/cmos_datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk diff --git a/conf/i386-pc-cygwin-img-ld.sc b/conf/i386-cygwin-img-ld.sc similarity index 76% rename from conf/i386-pc-cygwin-img-ld.sc rename to conf/i386-cygwin-img-ld.sc index a41cac75e..578da91b0 100644 --- a/conf/i386-pc-cygwin-img-ld.sc +++ b/conf/i386-cygwin-img-ld.sc @@ -5,6 +5,8 @@ SECTIONS .text : { start = . ; + _start = . ; + __start = . ; *(.text) etext = . ; } @@ -12,18 +14,16 @@ SECTIONS { __data_start__ = . ; *(.data) + /* Do not discard this section. */ + . = . ; __data_end__ = . ; - } - .rdata : - { __rdata_start__ = . ; *(.rdata) __rdata_end__ = . ; - } - .pdata : - { *(.pdata) edata = . ; + _edata = . ; + __edata = . ; } .bss : { @@ -36,7 +36,11 @@ SECTIONS .edata : { *(.edata) + /* Do not discard this section. */ + . = . ; end = . ; + _end = . ; + __end = . ; } .stab : { diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk deleted file mode 100644 index c03abb429..000000000 --- a/conf/i386-efi.rmk +++ /dev/null @@ -1,166 +0,0 @@ -# -*- makefile -*- - -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -m32 -COMMON_LDFLAGS = -melf_i386 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Utilities. -bin_UTILITIES = grub-mkimage - -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/i386/efi/grub-mkimage.c \ - util/misc.c util/resolve.c -util/i386/efi/grub-mkimage.c_DEPENDENCIES = Makefile - -# For grub-setup. -#grub_setup_SOURCES = util/i386/pc/grub-setup.c util/hostdisk.c \ -# util/misc.c util/getroot.c kern/device.c kern/disk.c \ -# kern/err.c kern/misc.c fs/fat.c fs/ext2.c fs/xfs.c fs/affs.c \ -# fs/sfs.c kern/parser.c kern/partition.c partmap/msdos.c \ -# fs/ufs.c fs/ufs2.c fs/minix.c fs/hfs.c fs/jfs.c fs/hfsplus.c kern/file.c \ -# kern/fs.c kern/env.c fs/fshelp.c - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/i386/efi/grub-install.in - -# Modules. -pkglib_PROGRAMS = kernel.img -pkglib_MODULES = chain.mod appleldr.mod \ - linux.mod halt.mod reboot.mod pci.mod lspci.mod \ - datetime.mod date.mod datehook.mod loadbios.mod \ - fixvideo.mod mmap.mod acpi.mod - -# For kernel.img. -kernel_img_RELOCATABLE = yes -kernel_img_SOURCES = kern/i386/efi/startup.S kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/$(target_cpu)/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ - kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ - term/efi/console.c disk/efi/efidisk.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - efi/efi.h efi/time.h efi/disk.h i386/pit.h list.h handler.h command.h \ - i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) - -MOSTLYCLEANFILES += symlist.c -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For acpi.mod. -acpi_mod_SOURCES = commands/acpi.c commands/efi/acpi.c -acpi_mod_CFLAGS = $(COMMON_CFLAGS) -acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ - mmap/efi/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For chain.mod. -chain_mod_SOURCES = loader/efi/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) -chain_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For appleldr.mod. -appleldr_mod_SOURCES = loader/efi/appleloader.c -appleldr_mod_CFLAGS = $(COMMON_CFLAGS) -appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/efi/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/efi/datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loadbios.mod -loadbios_mod_SOURCES = commands/efi/loadbios.c -loadbios_mod_CFLAGS = $(COMMON_CFLAGS) -loadbios_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For fixvideo.mod -fixvideo_mod_SOURCES = commands/efi/fixvideo.c -fixvideo_mod_CFLAGS = $(COMMON_CFLAGS) -fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_uga.mod -efi_uga_mod_SOURCES = video/efi_uga.c -efi_uga_mod_CFLAGS = $(COMMON_CFLAGS) -efi_uga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_gop.mod -efi_gop_mod_SOURCES = video/efi_gop.c -efi_gop_mod_CFLAGS = $(COMMON_CFLAGS) -efi_gop_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += xnu.mod -xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \ - loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c -xnu_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) -xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk deleted file mode 100644 index e19f6e9a1..000000000 --- a/conf/i386-ieee1275.rmk +++ /dev/null @@ -1,145 +0,0 @@ -# -*- makefile -*- - -COMMON_ASFLAGS = -m32 -nostdinc -fno-builtin -COMMON_CFLAGS = -ffreestanding -mrtd -mregparm=3 -COMMON_LDFLAGS = -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. -pkglib_PROGRAMS = kernel.img - -# For kernel.img. -kernel_img_SOURCES = kern/i386/ieee1275/startup.S \ - kern/i386/misc.S \ - kern/i386/ieee1275/init.c \ - kern/ieee1275/init.c \ - kern/ieee1275/mmap.c \ - kern/ieee1275/cmain.c kern/ieee1275/openfw.c \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/$(target_cpu)/dl.c kern/parser.c kern/partition.c \ - kern/env.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/generic/millisleep.c \ - kern/ieee1275/ieee1275.c \ - term/ieee1275/ofconsole.c \ - disk/ieee1275/ofdisk.c \ - symlist.c -kernel_img_HEADERS = cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - ieee1275/ieee1275.h machine/kernel.h machine/loader.h machine/memory.h \ - list.h handler.h command.h i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-S,-Ttext,0x10000,-Bstatic - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/ieee1275/grub-install.in - -# Modules. -pkglib_MODULES = halt.mod reboot.mod suspend.mod \ - aout.mod serial.mod linux.mod \ - nand.mod memdisk.mod pci.mod lspci.mod datetime.mod \ - date.mod datehook.mod lsmmap.mod mmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) -mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For aout.mod. -aout_mod_SOURCES = loader/aout.c -aout_mod_CFLAGS = $(COMMON_CFLAGS) -aout_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For suspend.mod -suspend_mod_SOURCES = commands/ieee1275/suspend.c -suspend_mod_CFLAGS = $(COMMON_CFLAGS) -suspend_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/ieee1275/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For nand.mod. -nand_mod_SOURCES = disk/ieee1275/nand.c -nand_mod_CFLAGS = $(COMMON_CFLAGS) -nand_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/cmos_datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk deleted file mode 100644 index 580bfea0a..000000000 --- a/conf/i386-pc.rmk +++ /dev/null @@ -1,373 +0,0 @@ -# -*- makefile -*- - -GRUB_KERNEL_MACHINE_LINK_ADDR = 0x8200 - -COMMON_ASFLAGS = -nostdinc -fno-builtin -m32 -COMMON_CFLAGS = -fno-builtin -mrtd -mregparm=3 -m32 -COMMON_LDFLAGS = -m32 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. -pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \ - pxeboot.img - -# For boot.img. -boot_img_SOURCES = boot/i386/pc/boot.S -boot_img_ASFLAGS = $(COMMON_ASFLAGS) -boot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x7C00 -boot_img_FORMAT = binary - -# For pxeboot.img -pxeboot_img_SOURCES = boot/i386/pc/pxeboot.S -pxeboot_img_ASFLAGS = $(COMMON_ASFLAGS) -pxeboot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x7C00 -pxeboot_img_FORMAT = binary - -# For diskboot.img. -diskboot_img_SOURCES = boot/i386/pc/diskboot.S -diskboot_img_ASFLAGS = $(COMMON_ASFLAGS) -diskboot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x8000 -diskboot_img_FORMAT = binary - -# For lnxboot.img. -lnxboot_img_SOURCES = boot/i386/pc/lnxboot.S -lnxboot_img_ASFLAGS = $(COMMON_ASFLAGS) -lnxboot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x6000 -lnxboot_img_FORMAT = binary - -# For cdboot.img. -cdboot_img_SOURCES = boot/i386/pc/cdboot.S -cdboot_img_ASFLAGS = $(COMMON_ASFLAGS) -cdboot_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)0x7C00 -cdboot_img_FORMAT = binary - -# For kernel.img. -kernel_img_SOURCES = kern/i386/pc/startup.S \ - kern/i386/misc.S \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/$(target_cpu)/dl.c kern/i386/pc/init.c kern/i386/pc/mmap.c \ - kern/parser.c kern/partition.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/rtc_get_time_ms.c \ - kern/generic/millisleep.c \ - kern/env.c \ - term/i386/pc/console.c term/i386/vga_common.c \ - symlist.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \ - machine/memory.h machine/loader.h machine/vga.h machine/vbe.h \ - machine/kernel.h machine/pxe.h i386/pit.h list.h handler.h command.h \ - i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) $(TARGET_IMG_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) $(TARGET_IMG_LDFLAGS)$(GRUB_KERNEL_MACHINE_LINK_ADDR) $(COMMON_CFLAGS) -kernel_img_FORMAT = binary - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# Utilities. -bin_UTILITIES = grub-mkimage -sbin_UTILITIES = grub-setup - -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkrawimage.c util/misc.c \ - util/resolve.c lib/LzmaEnc.c lib/LzFind.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(GRUB_KERNEL_MACHINE_LINK_ADDR) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - -# For grub-setup. -util/i386/pc/grub-setup.c_DEPENDENCIES = grub_setup_init.h -grub_setup_SOURCES = gnulib/progname.c \ - util/i386/pc/grub-setup.c util/hostdisk.c \ - util/misc.c util/getroot.c kern/device.c kern/disk.c \ - kern/err.c kern/misc.c kern/parser.c kern/partition.c \ - kern/file.c kern/fs.c kern/env.c fs/fshelp.c \ - \ - fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ - \ - partmap/msdos.c partmap/gpt.c \ - \ - disk/raid.c disk/mdraid_linux.c disk/lvm.c \ - util/raid.c util/lvm.c \ - grub_setup_init.c - -sbin_SCRIPTS += grub-install -grub_install_SOURCES = util/grub-install.in - -bin_SCRIPTS += grub-mkrescue -grub_mkrescue_SOURCES = util/grub-mkrescue.in - -pkglib_MODULES = biosdisk.mod chain.mod \ - reboot.mod halt.mod \ - vbe.mod vbetest.mod vbeinfo.mod play.mod serial.mod \ - vga.mod memdisk.mod pci.mod lspci.mod \ - aout.mod bsd.mod pxe.mod pxecmd.mod datetime.mod date.mod \ - datehook.mod lsmmap.mod ata_pthru.mod hdparm.mod \ - usb.mod uhci.mod ohci.mod usbtest.mod usbms.mod usb_keyboard.mod \ - efiemu.mod mmap.mod acpi.mod drivemap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For drivemap.mod. -drivemap_mod_SOURCES = commands/i386/pc/drivemap.c \ - commands/i386/pc/drivemap_int13h.S -drivemap_mod_ASFLAGS = $(COMMON_ASFLAGS) -drivemap_mod_CFLAGS = $(COMMON_CFLAGS) -drivemap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For efiemu.mod. -efiemu_mod_SOURCES = efiemu/main.c efiemu/i386/loadcore32.c \ - efiemu/i386/loadcore64.c efiemu/i386/pc/cfgtables.c \ - efiemu/mm.c efiemu/loadcore_common.c efiemu/symbols.c \ - efiemu/loadcore32.c efiemu/loadcore64.c \ - efiemu/prepare32.c efiemu/prepare64.c efiemu/pnvram.c \ - efiemu/i386/coredetect.c -efiemu_mod_CFLAGS = $(COMMON_CFLAGS) -efiemu_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For acpi.mod. -acpi_mod_SOURCES = commands/acpi.c commands/i386/pc/acpi.c -acpi_mod_CFLAGS = $(COMMON_CFLAGS) -acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ - mmap/i386/pc/mmap.c mmap/i386/pc/mmap_helper.S -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) -mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For biosdisk.mod. -biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c -biosdisk_mod_CFLAGS = $(COMMON_CFLAGS) -biosdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For chain.mod. -chain_mod_SOURCES = loader/i386/pc/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) -chain_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += linux16.mod -linux16_mod_SOURCES = loader/i386/pc/linux.c -linux16_mod_CFLAGS = $(COMMON_CFLAGS) -linux16_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += linux.mod -linux_mod_SOURCES = loader/i386/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += xnu.mod -xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/pc/xnu.c \ - loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c -xnu_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) -xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/i386/pc/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For serial.mod. -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For vbe.mod. -vbe_mod_SOURCES = video/i386/pc/vbe.c -vbe_mod_CFLAGS = $(COMMON_CFLAGS) -vbe_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For vbeinfo.mod. -vbeinfo_mod_SOURCES = commands/i386/pc/vbeinfo.c -vbeinfo_mod_CFLAGS = $(COMMON_CFLAGS) -vbeinfo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For vbetest.mod. -vbetest_mod_SOURCES = commands/i386/pc/vbetest.c -vbetest_mod_CFLAGS = $(COMMON_CFLAGS) -vbetest_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For play.mod. -play_mod_SOURCES = commands/i386/pc/play.c -play_mod_CFLAGS = $(COMMON_CFLAGS) -play_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For vga.mod. -vga_mod_SOURCES = term/i386/pc/vga.c -vga_mod_CFLAGS = $(COMMON_CFLAGS) -vga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For aout.mod -aout_mod_SOURCES = loader/aout.c -aout_mod_CFLAGS = $(COMMON_CFLAGS) -aout_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For bsd.mod -bsd_mod_SOURCES = loader/i386/bsd.c loader/i386/bsd32.c loader/i386/bsd64.c loader/i386/bsd_helper.S loader/i386/bsd_trampoline.S -bsd_mod_CFLAGS = $(COMMON_CFLAGS) -bsd_mod_LDFLAGS = $(COMMON_LDFLAGS) -bsd_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For usb.mod -usb_mod_SOURCES = bus/usb/usb.c bus/usb/usbtrans.c bus/usb/usbhub.c -usb_mod_CFLAGS = $(COMMON_CFLAGS) -usb_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For usbtest.mod -usbtest_mod_SOURCES = commands/usbtest.c -usbtest_mod_CFLAGS = $(COMMON_CFLAGS) -usbtest_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For uhci.mod -uhci_mod_SOURCES = bus/usb/uhci.c -uhci_mod_CFLAGS = $(COMMON_CFLAGS) -uhci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ohci.mod -ohci_mod_SOURCES = bus/usb/ohci.c -ohci_mod_CFLAGS = $(COMMON_CFLAGS) -ohci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For usbms.mod -usbms_mod_SOURCES = disk/usbms.c -usbms_mod_CFLAGS = $(COMMON_CFLAGS) -usbms_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For usb_keyboard.mod -usb_keyboard_mod_SOURCES = term/usb_keyboard.c -usb_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) -usb_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pxe.mod -pxe_mod_SOURCES = fs/i386/pc/pxe.c -pxe_mod_CFLAGS = $(COMMON_CFLAGS) -pxe_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pxecmd.mod -pxecmd_mod_SOURCES = commands/i386/pc/pxecmd.c -pxecmd_mod_CFLAGS = $(COMMON_CFLAGS) -pxecmd_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/cmos_datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ata_pthru.mod. -ata_pthru_mod_SOURCES = disk/ata_pthru.c -ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS) -ata_pthru_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For hdparm.mod. -hdparm_mod_SOURCES = commands/hdparm.c lib/hexdump.c -hdparm_mod_CFLAGS = $(COMMON_CFLAGS) -hdparm_mod_LDFLAGS = $(COMMON_LDFLAGS) - -ifeq ($(enable_efiemu), yes) - -efiemu32.o: efiemu/runtime/efiemu.c $(TARGET_OBJ2ELF) - -rm -f $@ -ifeq ($(TARGET_APPLE_CC), 1) - -rm -f $@.bin - $(TARGET_CC) -c -m32 -DELF32 -DAPPLE_CC -o $@.bin -Wall -Werror $< -nostdlib -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude - $(OBJCONV) -felf32 -nu -nd $@.bin $@ - -rm -f $@.bin -else - $(TARGET_CC) -c -m32 -DELF32 -o $@ -Wall -Werror $< -nostdlib -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude - if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi -endif - -efiemu64_c.o: efiemu/runtime/efiemu.c -ifeq ($(TARGET_APPLE_CC), 1) - $(TARGET_CC) -c -m64 -DAPPLE_CC=1 -DELF64 -o $@ -Wall -Werror $< -nostdlib -mno-red-zone -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude -else - $(TARGET_CC) -c -m64 -DELF64 -o $@ -Wall -Werror $< -nostdlib -mcmodel=large -mno-red-zone -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude -endif - -efiemu64_s.o: efiemu/runtime/efiemu.S - -rm -f $@ -ifeq ($(TARGET_APPLE_CC), 1) - $(TARGET_CC) -c -m64 -DAPPLE_CC=1 -DELF64 -o $@ -Wall -Werror $< -nostdlib -mno-red-zone -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude -else - $(TARGET_CC) -c -m64 -DELF64 -o $@ -Wall -Werror $< -nostdlib -mcmodel=large -mno-red-zone -O2 -I$(srcdir)/efiemu/runtime -I$(srcdir)/include -Iinclude -endif - -efiemu64.o: efiemu64_c.o efiemu64_s.o $(TARGET_OBJ2ELF) - -rm -f $@ -ifeq ($(TARGET_APPLE_CC), 1) - -rm -f $@.bin - $(TARGET_CC) -m64 -o $@.bin -Wl,-r $^ -nostdlib - $(OBJCONV) -felf64 -nu -nd $@.bin $@ - -rm -f $@.bin -else - $(TARGET_CC) -m64 -o $@ -Wl,-r $^ -nostdlib - if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi -endif - -CLEANFILES += efiemu32.o efiemu64.o efiemu64_c.o efiemu64_s.o -pkglib_DATA += efiemu32.o efiemu64.o - -endif - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk diff --git a/conf/i386-qemu.rmk b/conf/i386-qemu.rmk deleted file mode 100644 index 573a5d0f3..000000000 --- a/conf/i386-qemu.rmk +++ /dev/null @@ -1,2 +0,0 @@ -# -*- makefile -*- -include $(srcdir)/conf/i386-coreboot.mk diff --git a/conf/i386.rmk b/conf/i386.rmk deleted file mode 100644 index d240858fe..000000000 --- a/conf/i386.rmk +++ /dev/null @@ -1,49 +0,0 @@ -# -*- makefile -*- - -pkglib_MODULES += cpuid.mod -cpuid_mod_SOURCES = commands/i386/cpuid.c -cpuid_mod_CFLAGS = $(COMMON_CFLAGS) -cpuid_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += at_keyboard.mod -at_keyboard_mod_SOURCES = term/at_keyboard.c -at_keyboard_mod_CFLAGS = $(COMMON_CFLAGS) -at_keyboard_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += vga_text.mod -vga_text_mod_SOURCES = term/i386/pc/vga_text.c term/i386/vga_common.c -vga_text_mod_CFLAGS = $(COMMON_CFLAGS) -vga_text_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += relocator.mod -relocator_mod_SOURCES = lib/i386/relocator.c lib/i386/relocator_asm.S lib/i386/relocator_backward.S -relocator_mod_CFLAGS = $(COMMON_CFLAGS) -relocator_mod_ASFLAGS = $(COMMON_ASFLAGS) -relocator_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += ata.mod -ata_mod_SOURCES = disk/ata.c -ata_mod_CFLAGS = $(COMMON_CFLAGS) -ata_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For setpci.mod -pkglib_MODULES += setpci.mod -setpci_mod_SOURCES = commands/setpci.c -setpci_mod_CFLAGS = $(COMMON_CFLAGS) -setpci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += multiboot.mod -multiboot_mod_SOURCES = loader/i386/multiboot.c \ - loader/i386/multiboot_mbi.c \ - loader/multiboot_loader.c -multiboot_mod_CFLAGS = $(COMMON_CFLAGS) -multiboot_mod_LDFLAGS = $(COMMON_LDFLAGS) -multiboot_mod_ASFLAGS = $(COMMON_ASFLAGS) - -pkglib_MODULES += multiboot2.mod -multiboot2_mod_SOURCES = loader/i386/multiboot.c \ - loader/i386/multiboot_mbi.c \ - loader/multiboot_loader.c -multiboot2_mod_CFLAGS = $(COMMON_CFLAGS) -DGRUB_USE_MULTIBOOT2 -multiboot2_mod_LDFLAGS = $(COMMON_LDFLAGS) -multiboot2_mod_ASFLAGS = $(COMMON_ASFLAGS) diff --git a/conf/mips-qemu-mips.rmk b/conf/mips-qemu-mips.rmk deleted file mode 100644 index e06370122..000000000 --- a/conf/mips-qemu-mips.rmk +++ /dev/null @@ -1,23 +0,0 @@ -# -*- makefile -*- -LINK_BASE = 0x80010000 -target_machine=qemu-mips -COMMON_CFLAGS += -march=mips3 -COMMON_ASFLAGS += -march=mips3 -include $(srcdir)/conf/mips.mk - -pkglib_IMAGES = kernel.img -kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ - kern/main.c kern/device.c kern/$(target_cpu)/init.c \ - kern/$(target_cpu)/$(target_machine)/init.c \ - kern/disk.c kern/dl.c kern/err.c kern/file.c kern/fs.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/parser.c kern/partition.c kern/env.c kern/$(target_cpu)/dl.c \ - kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c kern/time.c \ - symlist.c kern/$(target_cpu)/cache.S -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic -kernel_img_FORMAT = binary diff --git a/conf/mips-yeeloong.rmk b/conf/mips-yeeloong.rmk deleted file mode 100644 index 9977f7881..000000000 --- a/conf/mips-yeeloong.rmk +++ /dev/null @@ -1,87 +0,0 @@ -# -*- makefile -*- -LINK_BASE = 0x80200000 -target_machine=yeeloong -COMMON_CFLAGS += -march=mips3 -COMMON_ASFLAGS += -march=mips3 - -kernel_img_HEADERS += bitmap.h video.h gfxterm.h font.h bitmap_scale.h bufio.h - -include $(srcdir)/conf/mips.mk - -pkglib_IMAGES = kernel.img -kernel_img_SOURCES = kern/$(target_cpu)/startup.S \ - kern/main.c kern/device.c kern/$(target_cpu)/init.c \ - kern/$(target_cpu)/$(target_machine)/init.c \ - kern/disk.c kern/dl.c kern/err.c kern/file.c kern/fs.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/parser.c kern/partition.c kern/env.c kern/$(target_cpu)/dl.c \ - kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c kern/time.c \ - kern/$(target_cpu)/cache.S \ - \ - term/at_keyboard.c \ - font/font_cmd.c font/font.c io/bufio.c \ - video/video.c video/fb/video_fb.c video/fb/fbblit.c \ - video/fb/fbfill.c video/fb/fbutil.c video/bitmap.c \ - video/bitmap_scale.c video/sm712.c bus/pci.c bus/bonito.c \ - term/gfxterm.c commands/extcmd.c lib/arg.c \ - symlist.c -kernel_img_CFLAGS = $(COMMON_CFLAGS) -DUSE_ASCII_FAILBACK -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,$(LINK_BASE),-Bstatic -kernel_img_FORMAT = binary - -# For ata.mod. -pkglib_MODULES += ata.mod -ata_mod_SOURCES = disk/ata.c -ata_mod_CFLAGS = $(COMMON_CFLAGS) -ata_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -pkglib_MODULES += lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For ata_pthru.mod. -pkglib_MODULES += ata_pthru.mod -ata_pthru_mod_SOURCES = disk/ata_pthru.c -ata_pthru_mod_CFLAGS = $(COMMON_CFLAGS) -ata_pthru_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -pkglib_MODULES += mmap.mod -mmap_mod_SOURCES = mmap/mmap.c mmap/mips/yeeloong/uppermem.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) -mmap_mod_ASFLAGS = $(COMMON_ASFLAGS) - -# For datetime.mod -pkglib_MODULES += datetime.mod -datetime_mod_SOURCES = lib/cmos_datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -pkglib_MODULES += date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -pkglib_MODULES += datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += linux.mod -linux_mod_SOURCES = loader/$(target_cpu)/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_ASFLAGS = $(COMMON_ASFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -sbin_SCRIPTS += grub-install -grub_install_SOURCES = util/grub-install.in - diff --git a/conf/mips.rmk b/conf/mips.rmk deleted file mode 100644 index 536d35cac..000000000 --- a/conf/mips.rmk +++ /dev/null @@ -1,76 +0,0 @@ - -# -*- makefile -*- - -COMMON_ASFLAGS += -nostdinc -COMMON_CFLAGS += -ffreestanding -mexplicit-relocs -mflush-func=grub_cpu_flush_cache -COMMON_LDFLAGS += -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS += boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h misc.h mm.h net.h parser.h reader.h \ - symbol.h term.h time.h types.h loader.h partition.h \ - msdos_partition.h machine/kernel.h handler.h list.h \ - command.h machine/memory.h cpu/libgcc.h cpu/cache.h i18n.h env_private.h - -ifeq ($(platform), yeeloong) -kernel_img_HEADERS += pci.h -endif - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# Scripts. -sbin_SCRIPTS = -bin_SCRIPTS = - -# For grub-mkimage. -bin_UTILITIES += grub-mkimage -grub_mkimage_SOURCES = gnulib/progname.c util/grub-mkrawimage.c util/misc.c \ - util/resolve.c lib/LzmaEnc.c lib/LzFind.c -grub_mkimage_CFLAGS = -DGRUB_KERNEL_MACHINE_LINK_ADDR=$(LINK_BASE) -util/grub-mkrawimage.c_DEPENDENCIES = Makefile - -# Modules. -pkglib_MODULES = memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For serial.mod. -pkglib_MODULES += serial.mod -serial_mod_SOURCES = term/serial.c -serial_mod_CFLAGS = $(COMMON_CFLAGS) -serial_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For relocator.mod. -pkglib_MODULES += relocator.mod -relocator_mod_SOURCES = lib/$(target_cpu)/relocator.c lib/$(target_cpu)/relocator_asm.S -relocator_mod_CFLAGS = $(COMMON_CFLAGS) -relocator_mod_ASFLAGS = $(COMMON_ASFLAGS) -relocator_mod_LDFLAGS = $(COMMON_LDFLAGS) - -include $(srcdir)/conf/common.mk diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk deleted file mode 100644 index 23bd2d620..000000000 --- a/conf/powerpc-ieee1275.rmk +++ /dev/null @@ -1,103 +0,0 @@ - -# -*- makefile -*- - -COMMON_ASFLAGS = -nostdinc -D__ASSEMBLY__ -COMMON_CFLAGS = -ffreestanding -COMMON_LDFLAGS += -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h misc.h mm.h net.h parser.h reader.h \ - symbol.h term.h time.h types.h powerpc/libgcc.h loader.h partition.h \ - msdos_partition.h ieee1275/ieee1275.h machine/kernel.h handler.h list.h \ - command.h i18n.h env_private.h - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# Programs -pkglib_PROGRAMS = kernel.img - -kernel_img_SOURCES = kern/powerpc/ieee1275/startup.S kern/ieee1275/cmain.c \ - kern/ieee1275/ieee1275.c kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/err.c kern/file.c kern/fs.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/ieee1275/init.c \ - kern/ieee1275/mmap.c \ - term/ieee1275/ofconsole.c \ - kern/ieee1275/openfw.c disk/ieee1275/ofdisk.c \ - kern/parser.c kern/partition.c kern/env.c kern/$(target_cpu)/dl.c \ - kern/generic/millisleep.c kern/time.c \ - symlist.c kern/$(target_cpu)/cache.S -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) -static-libgcc -lgcc \ - -Wl,-N,-S,-Ttext,0x200000,-Bstatic - -# Scripts. -sbin_SCRIPTS = grub-install -bin_SCRIPTS = grub-mkrescue - -# For grub-install. -grub_install_SOURCES = util/ieee1275/grub-install.in - -# For grub-mkrescue. -grub_mkrescue_SOURCES = util/powerpc/ieee1275/grub-mkrescue.in - -# Modules. -pkglib_MODULES = halt.mod \ - linux.mod \ - reboot.mod \ - suspend.mod \ - memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/powerpc/ieee1275/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For suspend.mod -suspend_mod_SOURCES = commands/ieee1275/suspend.c -suspend_mod_CFLAGS = $(COMMON_CFLAGS) -suspend_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -include $(srcdir)/conf/common.mk diff --git a/conf/sparc64-ieee1275.rmk b/conf/sparc64-ieee1275.rmk deleted file mode 100644 index befc7dce5..000000000 --- a/conf/sparc64-ieee1275.rmk +++ /dev/null @@ -1,136 +0,0 @@ - -# -*- makefile -*- - -COMMON_ASFLAGS = -nostdinc -m64 -COMMON_CFLAGS = -ffreestanding -m64 -mno-app-regs -COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Images. -pkglib_IMAGES = boot.img diskboot.img kernel.img - -# For boot.img. -boot_img_SOURCES = boot/sparc64/ieee1275/boot.S -boot_img_ASFLAGS = $(COMMON_ASFLAGS) -boot_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-Ttext,0x4000 -boot_img_FORMAT = a.out-sunos-big - -# For diskboot.img. -diskboot_img_SOURCES = boot/sparc64/ieee1275/diskboot.S -diskboot_img_ASFLAGS = $(COMMON_ASFLAGS) -diskboot_img_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-N,-Ttext,0x4200 -diskboot_img_FORMAT = binary - -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - list.h handler.h command.h i18n.h \ - sparc64/libgcc.h ieee1275/ieee1275.h machine/kernel.h \ - sparc64/ieee1275/ieee1275.h env_private.h -kernel_img_SOURCES = kern/sparc64/ieee1275/crt0.S kern/ieee1275/cmain.c \ - kern/ieee1275/ieee1275.c kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/err.c kern/file.c kern/fs.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/sparc64/ieee1275/ieee1275.c \ - kern/sparc64/ieee1275/init.c \ - kern/ieee1275/mmap.c \ - term/ieee1275/ofconsole.c \ - kern/ieee1275/openfw.c disk/ieee1275/ofdisk.c \ - kern/parser.c kern/partition.c kern/env.c kern/$(target_cpu)/dl.c \ - kern/generic/millisleep.c kern/time.c \ - symlist.c kern/$(target_cpu)/cache.S -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,0x200000,-Bstatic,-melf64_sparc -static-libgcc -lgcc -kernel_img_FORMAT = binary - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# Utilities. -bin_UTILITIES = grub-mkimage -sbin_UTILITIES = grub-setup grub-ofpathname - -# For grub-mkimage. -grub_mkimage_SOURCES = util/sparc64/ieee1275/grub-mkimage.c util/misc.c \ - util/resolve.c gnulib/progname.c - -# For grub-setup. -util/sparc64/ieee1275/grub-setup.c_DEPENDENCIES = grub_setup_init.h -grub_setup_SOURCES = util/sparc64/ieee1275/grub-setup.c util/hostdisk.c \ - util/misc.c util/getroot.c kern/device.c kern/disk.c \ - kern/err.c kern/misc.c kern/parser.c kern/partition.c \ - kern/file.c kern/fs.c kern/env.c fs/fshelp.c \ - \ - fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ - fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ - fs/ntfs.c fs/ntfscomp.c fs/reiserfs.c fs/sfs.c \ - fs/ufs.c fs/ufs2.c fs/xfs.c fs/afs.c fs/afs_be.c \ - fs/befs.c fs/befs_be.c fs/tar.c \ - \ - partmap/amiga.c partmap/apple.c partmap/msdos.c \ - partmap/sun.c partmap/acorn.c \ - \ - disk/raid.c disk/mdraid_linux.c disk/lvm.c \ - util/raid.c util/lvm.c gnulib/progname.c \ - grub_setup_init.c - -# For grub-ofpathname. -grub_ofpathname_SOURCES = util/sparc64/ieee1275/grub-ofpathname.c \ - util/ieee1275/ofpath.c util/misc.c gnulib/progname.c - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/grub-install.in - -# Modules. -pkglib_MODULES = halt.mod \ - linux.mod \ - reboot.mod \ - memdisk.mod \ - lsmmap.mod - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/sparc64/ieee1275/linux.c -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For memdisk.mod. -memdisk_mod_SOURCES = disk/memdisk.c -memdisk_mod_CFLAGS = $(COMMON_CFLAGS) -memdisk_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lsmmap.mod -lsmmap_mod_SOURCES = commands/lsmmap.c -lsmmap_mod_CFLAGS = $(COMMON_CFLAGS) -lsmmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -include $(srcdir)/conf/common.mk diff --git a/conf/tests.rmk b/conf/tests.rmk deleted file mode 100644 index 18b23ff5a..000000000 --- a/conf/tests.rmk +++ /dev/null @@ -1,50 +0,0 @@ -# -*- makefile -*- - -# For grub-shell -grub-shell: tests/util/grub-shell.in config.status - ./config.status --file=$@:$< - chmod +x $@ -check_SCRIPTS += grub-shell -CLEANFILES += grub-shell - -# For grub-shell-tester -grub-shell-tester: tests/util/grub-shell-tester.in config.status - ./config.status --file=$@:$< - chmod +x $@ -check_SCRIPTS += grub-shell-tester -CLEANFILES += grub-shell-tester - -pkglib_MODULES += functional_test.mod -functional_test_mod_SOURCES = tests/lib/functional_test.c tests/lib/test.c -functional_test_mod_CFLAGS = $(COMMON_CFLAGS) -functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Rules for unit tests -check_UTILITIES += example_unit_test -example_unit_test_SOURCES = tests/example_unit_test.c kern/list.c kern/misc.c tests/lib/test.c tests/lib/unit_test.c -example_unit_test_CFLAGS = -Wno-format - -# Rules for functional tests -pkglib_MODULES += example_functional_test.mod -example_functional_test_mod_SOURCES = tests/example_functional_test.c -example_functional_test_mod_CFLAGS = -Wno-format $(COMMON_CFLAGS) -example_functional_test_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# Rules for scripted tests -check_SCRIPTS += example_scripted_test -example_scripted_test_SOURCES = tests/example_scripted_test.in - -check_SCRIPTS += example_grub_script_test -example_grub_script_test_SOURCES = tests/example_grub_script_test.in - - -# List of tests to execute on "make check" -SCRIPTED_TESTS = example_scripted_test -SCRIPTED_TESTS += example_grub_script_test -UNIT_TESTS = example_unit_test -FUNCTIONAL_TESTS = example_functional_test.mod - -# dependencies between tests and testing-tools -$(SCRIPTED_TESTS): grub-shell grub-shell-tester -$(FUNCTIONAL_TESTS): functional_test.mod - diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk deleted file mode 100644 index d5c3c24cb..000000000 --- a/conf/x86_64-efi.rmk +++ /dev/null @@ -1,166 +0,0 @@ -# -*- makefile -*- - -COMMON_ASFLAGS = -nostdinc -fno-builtin -m64 -COMMON_CFLAGS = -fno-builtin -m64 -COMMON_LDFLAGS = -melf_x86_64 -nostdlib - -# Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h - -# Utilities. -bin_UTILITIES = grub-mkimage - -# For grub-mkimage. -grub_mkimage_SOURCES = gnulib/progname.c util/i386/efi/grub-mkimage.c \ - util/misc.c util/resolve.c - -# For grub-setup. -#grub_setup_SOURCES = util/i386/pc/grub-setup.c util/hostdisk.c \ -# util/misc.c util/getroot.c kern/device.c kern/disk.c \ -# kern/err.c kern/misc.c fs/fat.c fs/ext2.c fs/xfs.c fs/affs.c \ -# fs/sfs.c kern/parser.c kern/partition.c partmap/msdos.c \ -# fs/ufs.c fs/ufs2.c fs/minix.c fs/hfs.c fs/jfs.c fs/hfsplus.c kern/file.c \ -# kern/fs.c kern/env.c fs/fshelp.c - -# Scripts. -sbin_SCRIPTS = grub-install - -# For grub-install. -grub_install_SOURCES = util/i386/efi/grub-install.in - -# Modules. -pkglib_PROGRAMS = kernel.img -pkglib_MODULES = chain.mod appleldr.mod \ - halt.mod reboot.mod linux.mod pci.mod lspci.mod \ - datetime.mod date.mod datehook.mod loadbios.mod \ - fixvideo.mod mmap.mod acpi.mod - -# For kernel.img. -kernel_img_RELOCATABLE = yes -kernel_img_SOURCES = kern/x86_64/efi/startup.S kern/x86_64/efi/callwrap.S \ - kern/main.c kern/device.c \ - kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ - kern/misc.c kern/mm.c kern/term.c \ - kern/rescue_parser.c kern/rescue_reader.c \ - kern/$(target_cpu)/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ - kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ - kern/time.c kern/list.c kern/handler.c kern/command.c kern/corecmd.c \ - kern/i386/tsc.c kern/i386/pit.c \ - kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c \ - term/efi/console.c disk/efi/efidisk.c -kernel_img_HEADERS = boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ - env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ - partition.h msdos_partition.h reader.h symbol.h term.h time.h types.h \ - efi/efi.h efi/time.h efi/disk.h machine/loader.h i386/pit.h list.h \ - handler.h command.h i18n.h env_private.h -kernel_img_CFLAGS = $(COMMON_CFLAGS) -kernel_img_ASFLAGS = $(COMMON_ASFLAGS) -kernel_img_LDFLAGS = $(COMMON_LDFLAGS) - -MOSTLYCLEANFILES += symlist.c -MOSTLYCLEANFILES += symlist.c kernel_syms.lst -DEFSYMFILES += kernel_syms.lst - -symlist.c: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h gensymlist.sh - /bin/sh gensymlist.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -kernel_syms.lst: $(addprefix include/grub/,$(kernel_img_HEADERS)) config.h genkernsyms.sh - /bin/sh genkernsyms.sh $(filter %.h,$^) > $@ || (rm -f $@; exit 1) - -# For boot.mod. -pkglib_MODULES += boot.mod -boot_mod_SOURCES = commands/boot.c lib/i386/pc/biosnum.c -boot_mod_CFLAGS = $(COMMON_CFLAGS) -boot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For acpi.mod. -acpi_mod_SOURCES = commands/acpi.c commands/efi/acpi.c -acpi_mod_CFLAGS = $(COMMON_CFLAGS) -acpi_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For mmap.mod. -mmap_mod_SOURCES = mmap/mmap.c mmap/i386/uppermem.c mmap/i386/mmap.c \ - mmap/efi/mmap.c -mmap_mod_CFLAGS = $(COMMON_CFLAGS) -mmap_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For chain.mod. -chain_mod_SOURCES = loader/efi/chainloader.c -chain_mod_CFLAGS = $(COMMON_CFLAGS) -chain_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For appleldr.mod. -appleldr_mod_SOURCES = loader/efi/appleloader.c -appleldr_mod_CFLAGS = $(COMMON_CFLAGS) -appleldr_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For linux.mod. -linux_mod_SOURCES = loader/i386/efi/linux.c loader/i386/linux_trampoline.S -linux_mod_CFLAGS = $(COMMON_CFLAGS) -linux_mod_ASFLAGS = $(COMMON_ASFLAGS) -linux_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For halt.mod. -halt_mod_SOURCES = commands/halt.c -halt_mod_CFLAGS = $(COMMON_CFLAGS) -halt_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For reboot.mod. -reboot_mod_SOURCES = commands/reboot.c -reboot_mod_CFLAGS = $(COMMON_CFLAGS) -reboot_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For pci.mod -pci_mod_SOURCES = bus/pci.c -pci_mod_CFLAGS = $(COMMON_CFLAGS) -pci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For lspci.mod -lspci_mod_SOURCES = commands/lspci.c -lspci_mod_CFLAGS = $(COMMON_CFLAGS) -lspci_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datetime.mod -datetime_mod_SOURCES = lib/efi/datetime.c -datetime_mod_CFLAGS = $(COMMON_CFLAGS) -datetime_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For date.mod -date_mod_SOURCES = commands/date.c -date_mod_CFLAGS = $(COMMON_CFLAGS) -date_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For datehook.mod -datehook_mod_SOURCES = hook/datehook.c -datehook_mod_CFLAGS = $(COMMON_CFLAGS) -datehook_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For loadbios.mod -loadbios_mod_SOURCES = commands/efi/loadbios.c -loadbios_mod_CFLAGS = $(COMMON_CFLAGS) -loadbios_mod_LDFLAGS = $(COMMON_LDFLAGS) - -# For fixvideo.mod -fixvideo_mod_SOURCES = commands/efi/fixvideo.c -fixvideo_mod_CFLAGS = $(COMMON_CFLAGS) -fixvideo_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_uga.mod -efi_uga_mod_SOURCES = video/efi_uga.c -efi_uga_mod_CFLAGS = $(COMMON_CFLAGS) -efi_uga_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += efi_gop.mod -efi_gop_mod_SOURCES = video/efi_gop.c -efi_gop_mod_CFLAGS = $(COMMON_CFLAGS) -efi_gop_mod_LDFLAGS = $(COMMON_LDFLAGS) - -pkglib_MODULES += xnu.mod -xnu_mod_SOURCES = loader/xnu_resume.c loader/i386/xnu.c loader/i386/efi/xnu.c \ - loader/macho32.c loader/macho64.c loader/macho.c loader/xnu.c -xnu_mod_CFLAGS = $(COMMON_CFLAGS) -xnu_mod_LDFLAGS = $(COMMON_LDFLAGS) -xnu_mod_ASFLAGS = $(COMMON_ASFLAGS) - -include $(srcdir)/conf/i386.mk -include $(srcdir)/conf/common.mk diff --git a/config.h.in b/config.h.in new file mode 100644 index 000000000..9b1d39971 --- /dev/null +++ b/config.h.in @@ -0,0 +1,149 @@ +#undef _LARGEFILE_SOURCE +#undef _FILE_OFFSET_BITS +#define _LARGEFILE_SOURCE +#define _FILE_OFFSET_BITS 64 +#if defined(__PPC__) && !defined(__powerpc__) +#define __powerpc__ 1 +#endif + +#define GCRYPT_NO_DEPRECATED 1 +#define HAVE_MEMMOVE 1 + +#if @MM_DEBUG@ +#define MM_DEBUG @MM_DEBUG@ +#endif + +/* Define to 1 to enable disk cache statistics. */ +#define DISK_CACHE_STATS @DISK_CACHE_STATS@ +#define BOOT_TIME_STATS @BOOT_TIME_STATS@ + +/* We don't need those. */ +#define MINILZO_CFG_SKIP_LZO_PTR 1 +#define MINILZO_CFG_SKIP_LZO_UTIL 1 +#define MINILZO_CFG_SKIP_LZO_STRING 1 +#define MINILZO_CFG_SKIP_LZO_INIT 1 +#define MINILZO_CFG_SKIP_LZO1X_1_COMPRESS 1 +#define MINILZO_CFG_SKIP_LZO1X_DECOMPRESS 1 + +#if defined (GRUB_BUILD) +# undef ENABLE_NLS +# define BUILD_SIZEOF_LONG @BUILD_SIZEOF_LONG@ +# define BUILD_SIZEOF_VOID_P @BUILD_SIZEOF_VOID_P@ +# if defined __APPLE__ +# if defined __BIG_ENDIAN__ +# define BUILD_WORDS_BIGENDIAN 1 +# else +# define BUILD_WORDS_BIGENDIAN 0 +# endif +# else /* !defined __APPLE__ */ +# define BUILD_WORDS_BIGENDIAN @BUILD_WORDS_BIGENDIAN@ +# endif /* !defined __APPLE__ */ +#elif defined (GRUB_UTIL) || !defined (GRUB_MACHINE) +# include +#else /* !defined GRUB_UTIL && defined GRUB_MACHINE */ +# define HAVE_FONT_SOURCE @HAVE_FONT_SOURCE@ +/* Define if C symbols get an underscore after compilation. */ +# define HAVE_ASM_USCORE @HAVE_ASM_USCORE@ +/* Define it to one of __bss_start, edata and _edata. */ +# define BSS_START_SYMBOL @BSS_START_SYMBOL@ +/* Define it to either end or _end. */ +# define END_SYMBOL @END_SYMBOL@ +/* Name of package. */ +# define PACKAGE "@PACKAGE@" +/* Version number of package. */ +# define VERSION "@VERSION@" +/* Define to the full name and version of this package. */ +# define PACKAGE_STRING "@PACKAGE_STRING@" +/* Define to the version of this package. */ +# define PACKAGE_VERSION "@PACKAGE_VERSION@" +/* Define to the full name of this package. */ +# define PACKAGE_NAME "@PACKAGE_NAME@" +/* Define to the address where bug reports for this package should be sent. */ +# define PACKAGE_BUGREPORT "@PACKAGE_BUGREPORT@" + +# define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" +# define GRUB_PLATFORM "@GRUB_PLATFORM@" + +# define GRUB_STACK_PROTECTOR_INIT @GRUB_STACK_PROTECTOR_INIT@ + +# define RE_ENABLE_I18N 1 + +# define _GNU_SOURCE 1 + +# ifndef _GL_INLINE_HEADER_BEGIN +/* + * gnulib gets configured against the host, not the target, and the rest of + * our buildsystem works around that. This is difficult to avoid as gnulib's + * detection requires a more capable system than our target. Instead, we + * reach in and set values appropriately - intentionally setting more than the + * bare minimum. If, when updating gnulib, something breaks, there's probably + * a change needed here or in grub-core/Makefile.core.def. + */ +# define SIZE_MAX ((size_t) -1) +# define _GL_ATTRIBUTE_ALLOC_SIZE(args) \ + __attribute__ ((__alloc_size__ args)) +# define _GL_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((__always_inline__)) +# define _GL_ATTRIBUTE_ARTIFICIAL __attribute__ ((__artificial__)) +# define _GL_ATTRIBUTE_COLD __attribute__ ((cold)) +# define _GL_ATTRIBUTE_CONST __attribute__ ((const)) +# define _GL_ATTRIBUTE_DEALLOC(f, i) __attribute ((__malloc__ (f, i))) +# define _GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_DEALLOC (free, 1) +# define _GL_ATTRIBUTE_DEPRECATED __attribute__ ((__deprecated__)) +# define _GL_ATTRIBUTE_ERROR(msg) __attribute__ ((__error__ (msg))) +# define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE \ + __attribute__ ((externally_visible)) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +# define _GL_ATTRIBUTE_LEAF __attribute__ ((__leaf__)) +# define _GL_ATTRIBUTE_MALLOC __attribute__ ((malloc)) +# define _GL_ATTRIBUTE_MAYBE_UNUSED _GL_ATTRIBUTE_UNUSED +# define _GL_ATTRIBUTE_MAY_ALIAS __attribute__ ((__may_alias__)) +# define _GL_ATTRIBUTE_NODISCARD __attribute__ ((__warn_unused_result__)) +# define _GL_ATTRIBUTE_NOINLINE __attribute__ ((__noinline__)) +# define _GL_ATTRIBUTE_NONNULL(args) __attribute__ ((__nonnull__ args)) +# define _GL_ATTRIBUTE_NONSTRING __attribute__ ((__nonstring__)) +# define _GL_ATTRIBUTE_PACKED __attribute__ ((__packed__)) +# define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# define _GL_ATTRIBUTE_RETURNS_NONNULL \ + __attribute__ ((__returns_nonnull__)) +# define _GL_ATTRIBUTE_SENTINEL(pos) __attribute__ ((__sentinel__ pos)) +# define _GL_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# define _GL_ATTRIBUTE_WARNING(msg) __attribute__ ((__warning__ (msg))) +# define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2))) +# define _GL_GNUC_PREREQ GNUC_PREREQ +# define _GL_INLINE inline +# define _GL_UNUSED_LABEL _GL_ATTRIBUTE_UNUSED + +/* We can't use __has_attribute for these because gcc-5.1 is too old for + * that. Everything above is present in that version, though. */ +# if __GNUC__ >= 7 +# define _GL_ATTRIBUTE_FALLTHROUGH __attribute__ ((fallthrough)) +# else +# define _GL_ATTRIBUTE_FALLTHROUGH /* empty */ +# endif + +# ifndef ASM_FILE +typedef __INT_FAST32_TYPE__ int_fast32_t; +typedef __UINT_FAST32_TYPE__ uint_fast32_t; +# endif + +/* Ensure ialloc nests static/non-static inline properly. */ +# define IALLOC_INLINE static inline + +/* + * gnulib uses these for blocking out warnings they can't/won't fix. gnulib + * also makes the decision about whether to provide a declaration for + * reallocarray() at compile-time, so this is a convenient place to override - + * it's used by the ialloc module, which is used by base64. + */ +# define _GL_INLINE_HEADER_BEGIN _Pragma ("GCC diagnostic push") \ + void * \ + reallocarray (void *ptr, unsigned int nmemb, unsigned int size); +# define _GL_INLINE_HEADER_END _Pragma ("GCC diagnostic pop") +# endif /* !_GL_INLINE_HEADER_BEGIN */ + +/* gnulib doesn't build cleanly with older compilers. */ +# if __GNUC__ < 11 +_Pragma ("GCC diagnostic ignored \"-Wtype-limits\"") +# endif + +#endif diff --git a/config.rpath b/config.rpath deleted file mode 100755 index 85c2f209b..000000000 --- a/config.rpath +++ /dev/null @@ -1,672 +0,0 @@ -#! /bin/sh -# Output a system dependent set of variables, describing how to set the -# run time search path of shared libraries in an executable. -# -# Copyright 1996-2008 Free Software Foundation, Inc. -# Taken from GNU libtool, 2001 -# Originally by Gordon Matzigkeit , 1996 -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. -# -# The first argument passed to this file is the canonical host specification, -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld -# should be set by the caller. -# -# The set of defined variables is at the end of this script. - -# Known limitations: -# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer -# than 256 bytes, otherwise the compiler driver will dump core. The only -# known workaround is to choose shorter directory names for the build -# directory and/or the installation directory. - -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a -shrext=.so - -host="$1" -host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` -host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` -host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` - -# Code taken from libtool.m4's _LT_CC_BASENAME. - -for cc_temp in $CC""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` - -# Code taken from libtool.m4's _LT_COMPILER_PIC. - -wl= -if test "$GCC" = yes; then - wl='-Wl,' -else - case "$host_os" in - aix*) - wl='-Wl,' - ;; - darwin*) - case $cc_basename in - xlc*) - wl='-Wl,' - ;; - esac - ;; - mingw* | cygwin* | pw32* | os2* | cegcc*) - ;; - hpux9* | hpux10* | hpux11*) - wl='-Wl,' - ;; - irix5* | irix6* | nonstopux*) - wl='-Wl,' - ;; - newsos6) - ;; - linux* | k*bsd*-gnu) - case $cc_basename in - ecc*) - wl='-Wl,' - ;; - icc* | ifort*) - wl='-Wl,' - ;; - lf95*) - wl='-Wl,' - ;; - pgcc | pgf77 | pgf90) - wl='-Wl,' - ;; - ccc*) - wl='-Wl,' - ;; - como) - wl='-lopt=' - ;; - *) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - wl='-Wl,' - ;; - esac - ;; - esac - ;; - osf3* | osf4* | osf5*) - wl='-Wl,' - ;; - rdos*) - ;; - solaris*) - wl='-Wl,' - ;; - sunos4*) - wl='-Qoption ld ' - ;; - sysv4 | sysv4.2uw2* | sysv4.3*) - wl='-Wl,' - ;; - sysv4*MP*) - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - wl='-Wl,' - ;; - unicos*) - wl='-Wl,' - ;; - uts4*) - ;; - esac -fi - -# Code taken from libtool.m4's _LT_LINKER_SHLIBS. - -hardcode_libdir_flag_spec= -hardcode_libdir_separator= -hardcode_direct=no -hardcode_minus_L=no - -case "$host_os" in - cygwin* | mingw* | pw32* | cegcc*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; -esac - -ld_shlibs=yes -if test "$with_gnu_ld" = yes; then - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - # Unlike libtool, we use -rpath here, not --rpath, since the documented - # option of GNU ld is called -rpath, not --rpath. - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - case "$host_os" in - aix[3-9]*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs=no - fi - ;; - amigaos*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we cannot use - # them. - ld_shlibs=no - ;; - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - : - else - ld_shlibs=no - fi - ;; - cygwin* | mingw* | pw32* | cegcc*) - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec='-L$libdir' - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - : - else - ld_shlibs=no - fi - ;; - interix[3-9]*) - hardcode_direct=no - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - ;; - gnu* | linux* | k*bsd*-gnu) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - : - else - ld_shlibs=no - fi - ;; - netbsd*) - ;; - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs=no - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - : - else - ld_shlibs=no - fi - ;; - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs=no - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - else - ld_shlibs=no - fi - ;; - esac - ;; - sunos4*) - hardcode_direct=yes - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - : - else - ld_shlibs=no - fi - ;; - esac - if test "$ld_shlibs" = no; then - hardcode_libdir_flag_spec= - fi -else - case "$host_os" in - aix3*) - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L=yes - if test "$GCC" = yes; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct=unsupported - fi - ;; - aix[4-9]*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - else - aix_use_runtimelinking=no - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac - fi - hardcode_direct=yes - hardcode_libdir_separator=':' - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - : - else - # We have old collect2 - hardcode_direct=unsupported - hardcode_minus_L=yes - hardcode_libdir_flag_spec='-L$libdir' - hardcode_libdir_separator= - fi - ;; - esac - fi - # Begin _LT_AC_SYS_LIBPATH_AIX. - echo 'int main () { return 0; }' > conftest.c - ${CC} ${LDFLAGS} conftest.c -o conftest - aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` - if test -z "$aix_libpath"; then - aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` - fi - if test -z "$aix_libpath"; then - aix_libpath="/usr/lib:/lib" - fi - rm -f conftest.c conftest - # End _LT_AC_SYS_LIBPATH_AIX. - if test "$aix_use_runtimelinking" = yes; then - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' - else - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - fi - fi - ;; - amigaos*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - # see comment about different semantics on the GNU ld section - ld_shlibs=no - ;; - bsdi[45]*) - ;; - cygwin* | mingw* | pw32* | cegcc*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec=' ' - libext=lib - ;; - darwin* | rhapsody*) - hardcode_direct=no - if test "$GCC" = yes ; then - : - else - case $cc_basename in - xlc*) - ;; - *) - ld_shlibs=no - ;; - esac - fi - ;; - dgux*) - hardcode_libdir_flag_spec='-L$libdir' - ;; - freebsd1*) - ld_shlibs=no - ;; - freebsd2.2*) - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - ;; - freebsd2*) - hardcode_direct=yes - hardcode_minus_L=yes - ;; - freebsd* | dragonfly*) - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - ;; - hpux9*) - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - hpux10*) - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - fi - ;; - hpux11*) - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct=no - ;; - *) - hardcode_direct=yes - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - esac - fi - ;; - irix5* | irix6* | nonstopux*) - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - netbsd*) - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - ;; - newsos6) - hardcode_direct=yes - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - openbsd*) - if test -f /usr/libexec/ld.so; then - hardcode_direct=yes - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - else - case "$host_os" in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - hardcode_libdir_flag_spec='-R$libdir' - ;; - *) - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - ;; - esac - fi - else - ld_shlibs=no - fi - ;; - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - ;; - osf3*) - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - osf4* | osf5*) - if test "$GCC" = yes; then - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - else - # Both cc and cxx compiler support -rpath directly - hardcode_libdir_flag_spec='-rpath $libdir' - fi - hardcode_libdir_separator=: - ;; - solaris*) - hardcode_libdir_flag_spec='-R$libdir' - ;; - sunos4*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_direct=yes - hardcode_minus_L=yes - ;; - sysv4) - case $host_vendor in - sni) - hardcode_direct=yes # is this really true??? - ;; - siemens) - hardcode_direct=no - ;; - motorola) - hardcode_direct=no #Motorola manual says yes, but my tests say they lie - ;; - esac - ;; - sysv4.3*) - ;; - sysv4*MP*) - if test -d /usr/nec; then - ld_shlibs=yes - fi - ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - ;; - sysv5* | sco3.2v5* | sco5v6*) - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator=':' - ;; - uts4*) - hardcode_libdir_flag_spec='-L$libdir' - ;; - *) - ld_shlibs=no - ;; - esac -fi - -# Check dynamic linker characteristics -# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER. -# Unlike libtool.m4, here we don't care about _all_ names of the library, but -# only about the one the linker finds when passed -lNAME. This is the last -# element of library_names_spec in libtool.m4, or possibly two of them if the -# linker has special search rules. -library_names_spec= # the last element of library_names_spec in libtool.m4 -libname_spec='lib$name' -case "$host_os" in - aix3*) - library_names_spec='$libname.a' - ;; - aix[4-9]*) - library_names_spec='$libname$shrext' - ;; - amigaos*) - library_names_spec='$libname.a' - ;; - beos*) - library_names_spec='$libname$shrext' - ;; - bsdi[45]*) - library_names_spec='$libname$shrext' - ;; - cygwin* | mingw* | pw32* | cegcc*) - shrext=.dll - library_names_spec='$libname.dll.a $libname.lib' - ;; - darwin* | rhapsody*) - shrext=.dylib - library_names_spec='$libname$shrext' - ;; - dgux*) - library_names_spec='$libname$shrext' - ;; - freebsd1*) - ;; - freebsd* | dragonfly*) - case "$host_os" in - freebsd[123]*) - library_names_spec='$libname$shrext$versuffix' ;; - *) - library_names_spec='$libname$shrext' ;; - esac - ;; - gnu*) - library_names_spec='$libname$shrext' - ;; - hpux9* | hpux10* | hpux11*) - case $host_cpu in - ia64*) - shrext=.so - ;; - hppa*64*) - shrext=.sl - ;; - *) - shrext=.sl - ;; - esac - library_names_spec='$libname$shrext' - ;; - interix[3-9]*) - library_names_spec='$libname$shrext' - ;; - irix5* | irix6* | nonstopux*) - library_names_spec='$libname$shrext' - case "$host_os" in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; - *) libsuff= shlibsuff= ;; - esac - ;; - esac - ;; - linux*oldld* | linux*aout* | linux*coff*) - ;; - linux* | k*bsd*-gnu) - library_names_spec='$libname$shrext' - ;; - knetbsd*-gnu) - library_names_spec='$libname$shrext' - ;; - netbsd*) - library_names_spec='$libname$shrext' - ;; - newsos6) - library_names_spec='$libname$shrext' - ;; - nto-qnx*) - library_names_spec='$libname$shrext' - ;; - openbsd*) - library_names_spec='$libname$shrext$versuffix' - ;; - os2*) - libname_spec='$name' - shrext=.dll - library_names_spec='$libname.a' - ;; - osf3* | osf4* | osf5*) - library_names_spec='$libname$shrext' - ;; - rdos*) - ;; - solaris*) - library_names_spec='$libname$shrext' - ;; - sunos4*) - library_names_spec='$libname$shrext$versuffix' - ;; - sysv4 | sysv4.3*) - library_names_spec='$libname$shrext' - ;; - sysv4*MP*) - library_names_spec='$libname$shrext' - ;; - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - library_names_spec='$libname$shrext' - ;; - uts4*) - library_names_spec='$libname$shrext' - ;; -esac - -sed_quote_subst='s/\(["`$\\]\)/\\\1/g' -escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` -shlibext=`echo "$shrext" | sed -e 's,^\.,,'` -escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` -escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` -escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` - -LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <]) + ;; +esac + +case "$host_os" in + cygwin | windows* | mingw32* | aros*) + ;; + *) + AC_CHECK_SIZEOF(off_t) + if test x"$ac_cv_sizeof_off_t" != x8 ; then + AC_CHECK_SIZEOF(off64_t) + test x"$ac_cv_sizeof_off64_t" = x8 || AC_MSG_ERROR([Large file support is required]) + fi;; +esac + +if test x$USE_NLS = xno; then + HOST_CFLAGS="$HOST_CFLAGS -fno-builtin-gettext" fi if test "x$cross_compiling" = xyes; then @@ -214,15 +428,108 @@ else AC_PATH_PROG(HELP2MAN, help2man) fi -# Check for functions. -AC_CHECK_FUNCS(posix_memalign memalign asprintf vasprintf) +# Check for functions and headers. +AC_CHECK_FUNCS(posix_memalign memalign getextmntent atexit) +AC_CHECK_HEADERS(sys/param.h sys/mount.h sys/mnttab.h limits.h) -# For grub-mkisofs +# glibc 2.25 still includes sys/sysmacros.h in sys/types.h but emits deprecation +# warning which causes compilation failure later with -Werror. So use -Werror here +# as well to force proper sys/sysmacros.h detection. Used in include/grub/osdep/major.h. +SAVED_CFLAGS="$CFLAGS" +CFLAGS="$HOST_CFLAGS -Werror" AC_HEADER_MAJOR -AC_HEADER_DIRENT -AC_CHECK_FUNCS(memmove sbrk strdup lstat getuid getgid) -AC_CHECK_HEADERS(sys/mkdev.h sys/sysmacros.h malloc.h termios.h sys/types.h) -AC_CHECK_HEADERS(unistd.h string.h strings.h sys/stat.h sys/fcntl.h limits.h) +CFLAGS="$SAVED_CFLAGS" + +AC_CHECK_MEMBERS([struct statfs.f_fstypename],,,[$ac_includes_default +#include +#include ]) + +AC_CHECK_MEMBERS([struct statfs.f_mntfromname],,,[$ac_includes_default +#include +#include ]) + +# For opendisk() and getrawpartition() on NetBSD. +# Used in util/deviceiter.c and in util/hostdisk.c. +AC_CHECK_HEADER([util.h], [ + AC_CHECK_LIB([util], [opendisk], [ + LIBUTIL="-lutil" + AC_DEFINE(HAVE_OPENDISK, 1, [Define if opendisk() in -lutil can be used]) + ]) + AC_CHECK_LIB([util], [getrawpartition], [ + LIBUTIL="-lutil" + AC_DEFINE(HAVE_GETRAWPARTITION, 1, [Define if getrawpartition() in -lutil can be used]) + ]) +]) +AC_SUBST([LIBUTIL]) + +AC_CACHE_CHECK([whether -Wtrampolines work], [grub_cv_host_cc_wtrampolines], [ + SAVED_CFLAGS="$CFLAGS" + CFLAGS="$HOST_CFLAGS -Wtrampolines -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +int va_arg_func (int fixed, va_list args);]], [[]])], + [grub_cv_host_cc_wtrampolines=yes], + [grub_cv_host_cc_wtrampolines=no]) + CFLAGS="$SAVED_CFLAGS" +]) + +if test x"$grub_host_cv_cc_wtrampolines" = xyes ; then + HOST_CFLAGS="$HOST_CFLAGS -Wtrampolines" +fi + +# +# Check for host and build compilers. +# +HOST_CC=$CC +AC_CHECK_PROGS(BUILD_CC, [gcc egcs cc]) +test -z "$BUILD_CC" && AC_MSG_ERROR([none of gcc, egcs and cc is found. set BUILD_CC manually.]) +BUILD_CPP="$BUILD_CC -E" + +case "$build_os" in + haiku*) BUILD_LIBM= ;; + *) BUILD_LIBM=-lm ;; +esac + +dnl FIXME proper test seems to require too deep dive into Autoconf internals. +dnl For now just list known platforms that we support. + +case "$build_os" in + cygwin*|mingw32*|mingw64*) BUILD_EXEEXT=.exe ;; + *) BUILD_EXEEXT= ;; +esac +AC_SUBST(BUILD_EXEEXT) + +# In some build environments like termux /bin/sh is not a valid +# shebang. Use $SHELL instead if it's executable and /bin/sh isn't +BUILD_SHEBANG=/bin/sh +for she in /bin/sh "$SHELL"; do + if test -x "$she" ; then + BUILD_SHEBANG="$she" + fi +done +AC_SUBST(BUILD_SHEBANG) + +# For gnulib. +gl_INIT + +WARN_FLAGS="-Wall -W -Wshadow -Wpointer-arith -Wundef -Wchar-subscripts -Wcomment -Wdeprecated-declarations -Wdisabled-optimization -Wdiv-by-zero -Wfloat-equal -Wformat-extra-args -Wformat-security -Wformat-y2k -Wimplicit -Wimplicit-function-declaration -Wimplicit-int -Wmain -Wmissing-braces -Wmissing-format-attribute -Wmultichar -Wparentheses -Wreturn-type -Wsequence-point -Wshadow -Wsign-compare -Wswitch -Wtrigraphs -Wunknown-pragmas -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-value -Wunused-variable -Wwrite-strings -Wnested-externs -Wstrict-prototypes" +EXTRA_WARN_FLAGS="-Wextra -Wattributes -Wendif-labels -Winit-self -Wint-to-pointer-cast -Winvalid-pch -Wmissing-field-initializers -Wnonnull -Woverflow -Wvla -Wpointer-to-int-cast -Wstrict-aliasing -Wvariadic-macros -Wvolatile-register-var -Wpointer-sign -Wmissing-include-dirs -Wmissing-prototypes -Wmissing-declarations -Wformat=2" + +HOST_CFLAGS="$HOST_CFLAGS $WARN_FLAGS -Wcast-align" + +AC_CACHE_CHECK([which extra warnings work], [grub_cv_cc_w_extra_flags], [ + SAVED_CFLAGS="$CFLAGS" + grub_cv_cc_w_extra_flags= + for x in $EXTRA_WARN_FLAGS; do + CFLAGS="$HOST_CFLAGS $x -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [flag=1], [flag=0]) + if test x$flag = x1 ; then + grub_cv_cc_w_extra_flags="$grub_cv_cc_w_extra_flags $x" + fi + done + CFLAGS="$SAVED_CFLAGS" +]) + +HOST_CFLAGS="$HOST_CFLAGS $grub_cv_cc_w_extra_flags" # # Check for target programs. @@ -235,21 +542,32 @@ if test "x$target_alias" != x && test "x$host_alias" != "x$target_alias"; then AC_CHECK_TOOLS(TARGET_CC, [gcc egcs cc], [AC_MSG_ERROR([none of gcc, egcs and cc is found. set TARGET_CC manually.])]) - AC_CHECK_TOOL(OBJCOPY, objcopy) - AC_CHECK_TOOL(STRIP, strip) - AC_CHECK_TOOL(NM, nm) + AC_CHECK_TOOL(TARGET_OBJCOPY, objcopy) + AC_CHECK_TOOL(TARGET_STRIP, strip) + AC_CHECK_TOOL(TARGET_NM, nm) + AC_CHECK_TOOL(TARGET_RANLIB, ranlib) ac_tool_prefix="$tmp_ac_tool_prefix" else if test "x$TARGET_CC" = x; then TARGET_CC=$CC fi - AC_CHECK_TOOL(OBJCOPY, objcopy) - AC_CHECK_TOOL(STRIP, strip) - AC_CHECK_TOOL(NM, nm) + AC_CHECK_TOOL(TARGET_OBJCOPY, objcopy) + AC_CHECK_TOOL(TARGET_STRIP, strip) + AC_CHECK_TOOL(TARGET_NM, nm) + AC_CHECK_TOOL(TARGET_RANLIB, ranlib) fi -AC_SUBST(TARGET_CC) +AC_SUBST(HOST_CC) +AC_SUBST(BUILD_CC) +AC_SUBST(BUILD_CFLAGS) +AC_SUBST(BUILD_CPPFLAGS) +AC_SUBST(BUILD_LDFLAGS) +AC_SUBST(TARGET_CC) +AC_SUBST(TARGET_NM) +AC_SUBST(TARGET_RANLIB) +AC_SUBST(TARGET_STRIP) +AC_SUBST(TARGET_OBJCOPY) # Test the C compiler for the target environment. tmp_CC="$CC" @@ -263,96 +581,11 @@ CPPFLAGS="$TARGET_CPPFLAGS" LDFLAGS="$TARGET_LDFLAGS" LIBS="" -# debug flags. -TARGET_CFLAGS="$TARGET_CFLAGS -Wall -W -Wshadow -Wpointer-arith -Wmissing-prototypes \ - -Wundef -Wstrict-prototypes -g" - -# Force no alignment to save space on i386. -if test "x$target_cpu" = xi386; then - AC_CACHE_CHECK([whether -falign-loops works], [grub_cv_cc_falign_loop], [ - CFLAGS="$CFLAGS -falign-loops=1" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], - [grub_cv_cc_falign_loop=yes], - [grub_cv_cc_falign_loop=no]) - ]) - - if test "x$grub_cv_cc_falign_loop" = xyes; then - TARGET_CFLAGS="$TARGET_CFLAGS -falign-jumps=1 -falign-loops=1 -falign-functions=1" - else - TARGET_CFLAGS="$TARGET_CFLAGS -malign-jumps=1 -malign-loops=1 -malign-functions=1" - fi - - # Some toolchains enable these features by default, but they need - # registers that aren't set up properly in GRUB. - TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-3dnow" -fi - -# By default, GCC 4.4 generates .eh_frame sections containing unwind -# information in some cases where it previously did not. GRUB doesn't need -# these and they just use up vital space. Restore the old compiler -# behaviour. -AC_CACHE_CHECK([whether -fno-dwarf2-cfi-asm works], [grub_cv_cc_fno_dwarf2_cfi_asm], [ - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -fno-dwarf2-cfi-asm" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], - [grub_cv_cc_fno_dwarf2_cfi_asm=yes], - [grub_cv_cc_fno_dwarf2_cfi_asm=no]) - CFLAGS="$SAVE_CFLAGS" -]) - -if test "x$grub_cv_cc_fno_dwarf2_cfi_asm" = xyes; then - TARGET_CFLAGS="$TARGET_CFLAGS -fno-dwarf2-cfi-asm" -fi - -grub_apple_target_cc -if test x$grub_cv_apple_target_cc = xyes ; then - TARGET_CFLAGS="$TARGET_CFLAGS -DAPPLE_CC=1 -fnested-functions" - CFLAGS="$CFLAGS -DAPPLE_CC=1 -fnested-functions" - TARGET_ASFLAGS="$TARGET_ASFLAGS -DAPPLE_CC=1" - TARGET_APPLE_CC=1 - AC_CHECK_PROG([OBJCONV], [objconv], [objconv], []) - if test "x$OBJCONV" = x ; then - AC_CHECK_PROG([OBJCONV], [objconv], [./objconv], [], [.]) - fi - if test "x$OBJCONV" = x ; then - AC_MSG_ERROR([objconv not found which is required when building with apple compiler]) - fi - TARGET_IMG_LDSCRIPT= - TARGET_IMG_CFLAGS="-static" - TARGET_IMG_LDFLAGS='-nostdlib -static -Wl,-preload -Wl,-segalign,20 -Wl,-image_base,' - TARGET_IMG_LDFLAGS_AC='-nostdlib -static -Wl,-preload -Wl,-segalign,20 -Wl,-image_base,' -else - TARGET_APPLE_CC=0 -# Use linker script if present, otherwise use builtin -N script. -if test -f "${srcdir}/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc"; then - TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc" - TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT} -Wl,-Ttext," - TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/${target_cpu}-${platform}-${host_os}-img-ld.sc" -else - TARGET_IMG_LDSCRIPT= - TARGET_IMG_LDFLAGS='-Wl,-N -Wl,-Ttext,' - TARGET_IMG_LDFLAGS_AC='-Wl,-N -Wl,-Ttext,' -fi -TARGET_IMG_CFLAGS= -fi - -AC_SUBST(TARGET_IMG_LDSCRIPT) -AC_SUBST(TARGET_IMG_LDFLAGS) -AC_SUBST(TARGET_IMG_CFLAGS) - -# For platforms where ELF is not the default link format. -AC_MSG_CHECKING([for command to convert module to ELF format]) -case "${host_os}" in - cygwin) TARGET_OBJ2ELF='grub-pe2elf' ;; - *) ;; -esac -AC_SUBST(TARGET_OBJ2ELF) -AC_MSG_RESULT([$TARGET_OBJ2ELF]) - - if test "x$target_m32" = x1; then # Force 32-bit mode. TARGET_CFLAGS="$TARGET_CFLAGS -m32" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -m32" + TARGET_CPPFLAGS="$TARGET_CPPFLAGS -m32" TARGET_LDFLAGS="$TARGET_LDFLAGS -m32" TARGET_MODULE_FORMAT="elf32" fi @@ -360,30 +593,744 @@ fi if test "x$target_m64" = x1; then # Force 64-bit mode. TARGET_CFLAGS="$TARGET_CFLAGS -m64" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -m64" + TARGET_CPPFLAGS="$TARGET_CPPFLAGS -m64" TARGET_LDFLAGS="$TARGET_LDFLAGS -m64" TARGET_MODULE_FORMAT="elf64" fi -if test "$target_cpu"-"$platform" = x86_64-efi; then +# debug flags. +TARGET_CFLAGS="$TARGET_CFLAGS $WARN_FLAGS -g -Wredundant-decls -Wmissing-prototypes -Wmissing-declarations" +TARGET_CCASFLAGS="$TARGET_CCASFLAGS -g" + +if test "x$target_cpu" != xi386 && test "x$target_cpu" != xx86_64; then +TARGET_CFLAGS="$TARGET_CFLAGS -Wcast-align" +fi + +TARGET_CC_VERSION="$(LC_ALL=C $TARGET_CC --version | head -n1)" + +AC_CACHE_CHECK([which extra warnings work], [grub_cv_target_cc_w_extra_flags], [ + LDFLAGS="$TARGET_LDFLAGS -nostdlib -static" + + grub_cv_target_cc_w_extra_flags= + for x in $EXTRA_WARN_FLAGS; do + CFLAGS="$TARGET_CFLAGS $x -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +asm (".globl start; start:"); +void __main (void); +void __main (void) {} +int main (void); +]], [[]])], [flag=1], [flag=0]) + if test x$flag = x1 ; then + grub_cv_target_cc_w_extra_flags="$grub_cv_target_cc_w_extra_flags $x" + fi + done +]) + +TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_w_extra_flags" + +AC_CACHE_CHECK([if compiling with clang], [grub_cv_cc_target_clang], +[ +CFLAGS="$TARGET_CFLAGS" +AC_COMPILE_IFELSE( +[AC_LANG_PROGRAM([], [[ +#ifdef __clang__ +#error "is clang" +#endif +]])], +[grub_cv_cc_target_clang=no], [grub_cv_cc_target_clang=yes])]) + +if test x$target_cpu = xpowerpc -o x$target_cpu = xmips; then + AC_CACHE_CHECK([for options to get big-endian compilation], grub_cv_target_cc_big_endian, [ + grub_cv_target_cc_big_endian=no + for cand in "-target $target_cpu -Wl,-EB" "-target $target_cpu" \ + "-target $target_cpu-linux-gnu -Wl,-EB" "-target $target_cpu-linux-gnu" \ + "-EB" "-mbig-endian"; do + if test x"$grub_cv_target_cc_big_endian" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__ORDER_BIG_ENDIAN__ != __BYTE_ORDER__) +#error still little endian +#endif +asm (".globl start; start:"); +asm (".globl _start; _start:"); +asm (".globl __start; __start:"); +void __main (void); +void __main (void) {} +int main (void); +]], [[]])], + [grub_cv_target_cc_big_endian="$cand"], []) + done + ]) + + if test x"$grub_cv_target_cc_big_endian" = xno ; then + AC_MSG_ERROR([could not force big-endian]) + fi + + skip_linkflags="$(echo "$grub_cv_target_cc_big_endian"|sed 's@-Wl,-EB@@')" + + TARGET_CFLAGS="$TARGET_CFLAGS $skip_linkflags" + TARGET_CPPFLAGS="$TARGET_CPPFLAGS $skip_linkflags" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $skip_linkflags" + TARGET_LDFLAGS="$TARGET_LDFLAGS $grub_cv_target_cc_big_endian" +elif test x$target_cpu = xmipsel; then + AC_CACHE_CHECK([for options to get little-endian compilation], grub_cv_target_cc_little_endian, [ + grub_cv_target_cc_little_endian=no + for cand in "-target $target_cpu -Wl,-EL" "-target $target_cpu" \ + "-target $target_cpu-linux-gnu -Wl,-EL" "-target $target_cpu-linux-gnu" \ + "-EL"; do + if test x"$grub_cv_target_cc_little_endian" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__ORDER_BIG_ENDIAN__ == __BYTE_ORDER__) +#error still big endian +#endif +asm (".globl start; start:"); +asm (".globl _start; _start:"); +asm (".globl __start; __start:"); +void __main (void); +void __main (void) {} +int main (void); +]], [[]])], + [grub_cv_target_cc_little_endian="$cand"], []) + done + ]) + + if test x"$grub_cv_target_cc_little_endian" = xno ; then + AC_MSG_ERROR([could not force little-endian]) + fi + + skip_linkflags="$(echo "$grub_cv_target_cc_little_endian"|sed 's@-Wl,-EL@@')" + + TARGET_CFLAGS="$TARGET_CFLAGS $skip_linkflags" + TARGET_CPPFLAGS="$TARGET_CPPFLAGS $skip_linkflags" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $skip_linkflags" + TARGET_LDFLAGS="$TARGET_LDFLAGS $grub_cv_target_cc_little_endian" +fi + +# GRUB code is N32-compliant but it's experimental and we would prefer to +# avoid having too much variety when it doesn't result in any real improvement. +# Moreover N64 isn't supported. +if test "x$target_cpu" = xmips || test "x$target_cpu" = xmipsel ; then + AC_CACHE_CHECK([for options to force MIPS o32 ABI], grub_cv_target_cc_mips_o32_abi, [ + grub_cv_target_cc_mips_o32_abi=no + for arg in "" "-mabi=32" "-target $target_cpu -mabi=32" ; do + if test x"$grub_cv_target_cc_mips_o32_abi" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $arg -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#if !defined(_ABIO32) || !defined(_MIPS_SIM) || (_MIPS_SIM != _ABIO32) +#error not o32 ABI +#endif +asm (".globl start; start:"); +asm (".globl _start; _start:"); +asm (".globl __start; __start:"); +void __main (void); +void __main (void) {} +int main (void); +]], [[]])], + [grub_cv_target_cc_mips_o32_abi="$arg"], []) + done + ]) + + if test x"$grub_cv_target_cc_mips_o32_abi" = xno ; then + AC_MSG_ERROR([could not force MIPS o32 ABI]) + fi + + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mips_o32_abi" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_target_cc_mips_o32_abi" +fi + +AC_CACHE_CHECK([for options to compile assembly], [grub_cv_cc_target_asm_compile], [ +test_program= +case "x$target_cpu-$platform" in + xmips-* | xmipsel-*) + test_program=mips + ;; + xi386-pc) + test_program=i386-pc + ;; + xi386-* | xx86_64-*) + test_program=i386 + ;; + xpowerpc-* | xsparc64-* | xarm-*) + test_program=$target_cpu + ;; +esac +if test x"$test_program" = x ; then + grub_cv_cc_target_asm_compile= +else + found=no + for arg in "" "-no-integrated-as"; do + cmdline="$TARGET_CC -c -o /dev/null $TARGET_CCASFLAGS $arg $TARGET_CPPFLAGS $srcdir/asm-tests/$test_program.S" + echo "Running $cmdline" >&AS_MESSAGE_LOG_FD + if $cmdline >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD; then + grub_cv_cc_target_asm_compile="$arg" + found=yes + break + fi + done + if test x"$found" = xno ; then + AC_MSG_ERROR([could not compile assembly]) + fi +fi +]) + +TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_cc_target_asm_compile" + +if test "x$target_cpu" = xi386 && test "x$platform" != xemu; then + TARGET_CFLAGS="$TARGET_CFLAGS -march=i386" +fi + +if test "x$grub_cv_cc_target_clang" = xno && test "x$target_cpu" = xi386 && test "x$platform" != xemu && test "x$platform" != xefi; then + TARGET_CFLAGS="$TARGET_CFLAGS -mrtd -mregparm=3" +fi + +# on mips redirect cache flushing function to non-existant one. +if test "x$target_cpu" = xmips || test "x$target_cpu" = xmipsel ; then + AC_CACHE_CHECK([whether -mflush-func=grub_red_herring works], [grub_cv_cc_mflush_func], [ + CFLAGS="$TARGET_CFLAGS -mflush-func=grub_red_herring -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mflush_func=yes], + [grub_cv_cc_mflush_func=no]) + ]) + + if test "x$grub_cv_cc_mflush_func" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mflush-func=grub_red_herring" + fi + + AC_CACHE_CHECK([whether -mno-gpopt works], [grub_cv_cc_mno_gpopt], [ + CFLAGS="$TARGET_CFLAGS -mno-gpopt -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mno_gpopt=yes], + [grub_cv_cc_mno_gpopt=no]) + ]) + + if test "x$grub_cv_cc_mno_gpopt" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-gpopt" + fi +fi + + +# Force no alignment to save space on i386. +if test "x$target_cpu" = xi386; then + TARGET_CFLAGS="$TARGET_CFLAGS -falign-functions=1" + + AC_CACHE_CHECK([whether -falign-loops works], [grub_cv_cc_falign_loop], [ + CFLAGS="$TARGET_CFLAGS -falign-loops=1 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_falign_loop=yes], + [grub_cv_cc_falign_loop=no]) + ]) + + if test "x$grub_cv_cc_falign_loop" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -falign-loops=1" + fi + + AC_CACHE_CHECK([whether -falign-jumps works], [grub_cv_cc_falign_jumps], [ + CFLAGS="$TARGET_CFLAGS -falign-jumps=1 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_falign_jumps=yes], + [grub_cv_cc_falign_jumps=no]) + ]) + + if test "x$grub_cv_cc_falign_jumps" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -falign-jumps=1" + fi +fi + +AC_CACHE_CHECK([whether -freg-struct-return works], [grub_cv_cc_freg_struct_return], [ + CFLAGS="$TARGET_CFLAGS -freg-struct-return -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_freg_struct_return=yes], + [grub_cv_cc_freg_struct_return=no]) +]) + +if test "x$grub_cv_cc_freg_struct_return" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -freg-struct-return" +fi + +if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$platform" != xemu; then + # Some toolchains enable these features by default, but they need + # registers that aren't set up properly in GRUB. + TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow" +fi + +if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ); then + AC_CACHE_CHECK([whether -Wa,-mx86-used-note works], [grub_cv_cc_mx86_used_note], [ + CFLAGS="$TARGET_CFLAGS -Wa,-mx86-used-note=no -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mx86_used_note=yes], + [grub_cv_cc_mx86_used_note=no]) + ]) + + if test "x$grub_cv_cc_mx86_used_note" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wa,-mx86-used-note=no" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mx86-used-note=no" + fi +fi + +if test "x$target_cpu" = xloongarch64; then + AC_CACHE_CHECK([whether _mno_explicit_relocs works], [grub_cv_cc_mno_explicit_relocs], [ + CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mno_explicit_relocs=yes], + [grub_cv_cc_mno_explicit_relocs=no]) + ]) + if test "x$grub_cv_cc_mno_explicit_relocs" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -fno-plt" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -mno-explicit-relocs -fno-plt" + fi + + AC_CACHE_CHECK([for no-relax options], grub_cv_target_cc_mno_relax, [ + grub_cv_target_cc_mno_relax=no + for cand in "-mno-relax" "-Wa,-mno-relax"; do + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + void __main (void); + void __main (void) {} + int main (void); + ]], [[]])], [grub_cv_target_cc_mno_relax="$cand"], []) + done + ]) + CFLAGS="$TARGET_CFLAGS" + + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mno_relax" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_target_cc_mno_relax" + fi + + TARGET_CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs" +fi + +if test "x$target_cpu" = xriscv64 || test "x$target_cpu" = xriscv32; then + AC_CACHE_CHECK([for no-relax options], grub_cv_target_cc_mno_relax, [ + grub_cv_target_cc_mno_relax=no + for cand in "-mno-relax" "-Wa,-mno-relax"; do + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + void __main (void); + void __main (void) {} + int main (void); + ]], [[]])], [grub_cv_target_cc_mno_relax="$cand"], []) + done + ]) + + CFLAGS="$TARGET_CFLAGS" + + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mno_relax" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_target_cc_mno_relax" + fi +fi + +# GRUB doesn't use float or doubles at all. Yet some toolchains may decide +# that floats are a good fit to run instead of what's written in the code. +# Given that floating point unit is disabled (if present to begin with) +# when GRUB is running which may result in various hard crashes. +if test x"$platform" != xemu ; then + AC_CACHE_CHECK([for options to get soft-float], grub_cv_target_cc_soft_float, [ + grub_cv_target_cc_soft_float=no + if test "x$target_cpu" = xarm64; then + CFLAGS="$TARGET_CFLAGS -mgeneral-regs-only -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-mgeneral-regs-only"], []) + fi + if test "x$target_cpu" = xriscv32; then + CFLAGS="$TARGET_CFLAGS -march=rv32imac -mabi=ilp32 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-march=rv32imac -mabi=ilp32"], []) + # ISA spec version 20191213 factored out extensions Zicsr and Zifencei + CFLAGS="$TARGET_CFLAGS -march=rv32imac_zicsr_zifencei -mabi=ilp32 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-march=rv32imac_zicsr_zifencei -mabi=ilp32"], []) + fi + if test "x$target_cpu" = xriscv64; then + CFLAGS="$TARGET_CFLAGS -march=rv64imac -mabi=lp64 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-march=rv64imac -mabi=lp64"], []) + # ISA spec version 20191213 factored out extensions Zicsr and Zifencei + CFLAGS="$TARGET_CFLAGS -march=rv64imac_zicsr_zifencei -mabi=lp64 -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-march=rv64imac_zicsr_zifencei -mabi=lp64"], []) + fi + if test "x$target_cpu" = xia64; then + CFLAGS="$TARGET_CFLAGS -mno-inline-float-divide -mno-inline-sqrt -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-mno-inline-float-divide -mno-inline-sqrt"], []) + fi + if test "x$target_cpu" = xsh4; then + CFLAGS="$TARGET_CFLAGS -m4-nofpu -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="-m4-nofpu"], []) + fi + for cand in "-msoft-float -Xclang -msoft-float -Xclang -no-implicit-float" \ + "-Xclang -msoft-float -Xclang -no-implicit-float" \ + "-Xclang -msoft-float" "-msoft-float"; do + if test x"$grub_cv_target_cc_soft_float" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_soft_float="$cand"], []) + done + ]) + + if test x"$grub_cv_target_cc_soft_float" = xno ; then + AC_MSG_ERROR([could not force soft-float]) + fi + + case x"$grub_cv_target_cc_soft_float" in + x*"-Xclang"*) + # A trick so that clang doesn't see it on link stаge + TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_soft_float" + ;; + *) + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_soft_float" + ;; + esac + TARGET_CCASFLAGS="$TARGET_CCASFLAGS $grub_cv_target_cc_soft_float" + +fi + +if test x"$target_cpu" = xsparc64 ; then + AC_CACHE_CHECK([for options to reserve application registers], grub_cv_target_cc_mno_app_regs, [ + grub_cv_target_cc_mno_app_regs=no + for cand in "-mllvm -sparc-reserve-app-registers" \ + "-mno-app-regs"; do + if test x"$grub_cv_target_cc_mno_app_regs" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + CPPFLAGS="$TARGET_CPPFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_mno_app_regs="$cand"], []) + done + ]) + + if test x"$grub_cv_target_cc_mno_app_regs" = xno ; then + AC_MSG_ERROR([could not reserve application registers]) + fi + if test x"$grub_cv_target_cc_mno_app_regs" = x"-mllvm -sparc-reserve-app-registers" ; then + # A trick so that clang doesn't see it on link stаge + TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_mno_app_regs" + else + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mno_app_regs" + fi + + AC_CACHE_CHECK([for no-relax options], grub_cv_target_cc_mno_relax, [ + grub_cv_target_cc_mno_relax=no + for cand in "-mno-relax" "-Wl,--no-relax"; do + if test x"$grub_cv_target_cc_mno_relax" != xno ; then + break + fi + LDFLAGS="$TARGET_LDFLAGS $cand -nostdlib -static" + CFLAGS="$TARGET_CFLAGS -Werror" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + void __main (void); + void __main (void) {} + int main (void); + ]], [[]])], [grub_cv_target_cc_mno_relax="$cand"], []) + done + ]) + LDFLAGS="$TARGET_LDFLAGS" + CFLAGS="$TARGET_CFLAGS" + + if test x"$grub_cv_target_cc_mno_relax" = xno ; then + AC_MSG_ERROR([could not find no-relax options]) + fi + TARGET_LDFLAGS="$TARGET_LDFLAGS $grub_cv_target_cc_mno_relax" +fi + +# The backtrace module relies on frame pointers and the default optimization +# level, -Os, omits them. Make sure they are enabled. +AC_CACHE_CHECK([whether -fno-omit-frame-pointer works], [grub_cv_cc_fno_omit_frame_pointer], [ + CFLAGS="$TARGET_CFLAGS -fno-omit-frame-pointer" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_omit_frame_pointer=yes], + [grub_cv_cc_fno_omit_frame_pointer=no]) +]) + +if test "x$grub_cv_cc_fno_omit_frame_pointer" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-omit-frame-pointer" +fi + +# By default, GCC 4.4 generates .eh_frame sections containing unwind +# information in some cases where it previously did not. GRUB doesn't need +# these and they just use up vital space. Restore the old compiler +# behaviour. +AC_CACHE_CHECK([whether -fno-dwarf2-cfi-asm works], [grub_cv_cc_fno_dwarf2_cfi_asm], [ + CFLAGS="$TARGET_CFLAGS -fno-dwarf2-cfi-asm" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_dwarf2_cfi_asm=yes], + [grub_cv_cc_fno_dwarf2_cfi_asm=no]) +]) + +if test "x$grub_cv_cc_fno_dwarf2_cfi_asm" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-dwarf2-cfi-asm" +fi + +if test x"$target_os" = xcygwin; then + AC_CACHE_CHECK([whether option -fno-reorder-functions works], grub_cv_cc_no_reorder_functions, [ + CFLAGS="$TARGET_CFLAGS -fno-reorder-functions" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_no_reorder_functions=yes], + [grub_cv_cc_no_reorder_functions=no]) + ]) +fi + +if test x"$target_os" = xcygwin && test "x$grub_cv_cc_no_reorder_functions" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-reorder-functions" +fi + +AC_CACHE_CHECK([whether -mno-stack-arg-probe works], [grub_cv_cc_mno_stack_arg_probe], [ + CFLAGS="$TARGET_CFLAGS -mno-stack-arg-probe" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mno_stack_arg_probe=yes], + [grub_cv_cc_mno_stack_arg_probe=no]) +]) + +if test "x$grub_cv_cc_mno_stack_arg_probe" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mno-stack-arg-probe" +fi + + +# By default, GCC 4.6 generates .eh_frame sections containing unwind +# information in some cases where it previously did not. GRUB doesn't need +# these and they just use up vital space. Restore the old compiler +# behaviour. +AC_CACHE_CHECK([whether -fno-asynchronous-unwind-tables works], [grub_cv_cc_fno_asynchronous_unwind_tables], [ + CFLAGS="$TARGET_CFLAGS -fno-asynchronous-unwind-tables" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_asynchronous_unwind_tables=yes], + [grub_cv_cc_fno_asynchronous_unwind_tables=no]) +]) + +if test "x$grub_cv_cc_fno_asynchronous_unwind_tables" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-asynchronous-unwind-tables" +fi + +AC_CACHE_CHECK([whether -fno-unwind-tables works], [grub_cv_cc_fno_unwind_tables], [ + CFLAGS="$TARGET_CFLAGS -fno-unwind-tables" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_unwind_tables=yes], + [grub_cv_cc_fno_unwind_tables=no]) +]) + +if test "x$grub_cv_cc_fno_unwind_tables" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-unwind-tables" +fi + +# Do not generate .ident sections. +AC_CACHE_CHECK([whether -fno-ident works], [grub_cv_cc_fno_ident], [ + CFLAGS="$TARGET_CFLAGS -fno-ident" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_fno_ident=yes], + [grub_cv_cc_fno_ident=no]) +]) + +if test "x$grub_cv_cc_fno_ident" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-ident" +fi + +CFLAGS="$TARGET_CFLAGS" + + +if test x"$platform" = xemu ; then + TARGET_OBJ2ELF= + grub_cv_target_cc_link_format= + case "$host_os" in + *darwin* | *mac*) + grub_cv_target_cc_link_format="-arch,${target_cpu}" + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,$grub_cv_target_cc_link_format" + ;; + *windows* | *cygwin* | *mingw*) + if test x${target_cpu} = xi386 ; then + grub_cv_target_cc_link_format=-mi386pe + TARGET_OBJ2ELF='./build-grub-pe2elf$(BUILD_EXEEXT)' + fi + if test x${target_cpu} = xx86_64 ; then + grub_cv_target_cc_link_format=-mi386pep + TARGET_OBJ2ELF='./build-grub-pep2elf$(BUILD_EXEEXT)' + fi + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,$grub_cv_target_cc_link_format" + ;; + esac +elif test x"$target_cpu" = xi386 || test x"$target_cpu" = xx86_64; then + AC_CACHE_CHECK([for target linking format], [grub_cv_target_cc_link_format], [ + grub_cv_target_cc_link_format=unknown + for format in -melf_${target_cpu} -melf_${target_cpu}_fbsd -melf_${target_cpu}_obsd -melf_${target_cpu}_haiku -mi386pe -mi386pep -arch,${target_cpu}; do + if test x${target_cpu} != xi386 && test x$format = x-mi386pe; then + continue + fi + if test x${target_cpu} != xx86_64 && test x$format = x-mi386pep; then + continue + fi + CFLAGS="$TARGET_CFLAGS" + LDFLAGS="$TARGET_LDFLAGS -Wl,$format -nostdlib -static" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + asm (".globl _start; _start:"); + asm (".globl __start; __start:"); + void __main (void); + void __main (void) {} + ]], [[]])], [flag=1], [flag=0]) + if test x"$flag" = x1; then + grub_cv_target_cc_link_format="$format" + break + fi + done]) + if test x"$grub_cv_target_cc_link_format" = xunknown; then + AC_MSG_ERROR([no suitable link format found]) + fi + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,$grub_cv_target_cc_link_format" + if test x"$grub_cv_target_cc_link_format" = x-mi386pe ; then + TARGET_OBJ2ELF='./build-grub-pe2elf$(BUILD_EXEEXT)' + fi + if test x"$grub_cv_target_cc_link_format" = x-mi386pep ; then + TARGET_OBJ2ELF='./build-grub-pep2elf$(BUILD_EXEEXT)' + fi +fi + +if test x$grub_cv_target_cc_link_format = x-arch,i386 || test x$grub_cv_target_cc_link_format = x-arch,x86_64; then + TARGET_APPLE_LINKER=1 + AC_CHECK_PROG([TARGET_OBJCONV], [objconv], [objconv], []) + if test "x$TARGET_OBJCONV" = x ; then + AC_CHECK_PROG([TARGET_OBJCONV], [objconv], [./objconv], [], [.]) + fi + if test "x$TARGET_OBJCONV" = x ; then + AC_MSG_ERROR([objconv not found which is required when building with apple compiler]) + fi + TARGET_IMG_LDSCRIPT= + TARGET_IMG_CFLAGS="-static" + TARGET_IMG_LDFLAGS='-nostdlib -static -Wl,-preload -Wl,-segalign,20' + TARGET_IMG_LDFLAGS_AC='-nostdlib -static -Wl,-preload -Wl,-segalign,20' + TARGET_IMG_BASE_LDOPT="-Wl,-image_base" + TARGET_LDFLAGS_OLDMAGIC="" +elif test x$grub_cv_target_cc_link_format = x-mi386pe || test x$grub_cv_target_cc_link_format = x-mi386pep ; then + TARGET_APPLE_LINKER=0 + TARGET_LDFLAGS_OLDMAGIC="-Wl,-N" + TARGET_IMG_LDSCRIPT='$(top_srcdir)'"/conf/i386-cygwin-img-ld.sc" + TARGET_IMG_LDFLAGS="-Wl,-T${TARGET_IMG_LDSCRIPT}" + TARGET_IMG_LDFLAGS_AC="-Wl,-T${srcdir}/conf/i386-cygwin-img-ld.sc" + TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" + TARGET_IMG_CFLAGS= +else + TARGET_APPLE_LINKER=0 + TARGET_LDFLAGS_OLDMAGIC="-Wl,-N" + TARGET_IMG_LDSCRIPT= + TARGET_IMG_LDFLAGS='-Wl,-N' + TARGET_IMG_LDFLAGS_AC='-Wl,-N' + TARGET_IMG_BASE_LDOPT="-Wl,-Ttext" + TARGET_IMG_CFLAGS= +fi + +CFLAGS="$TARGET_CFLAGS" + +AC_ARG_ENABLE([efiemu], + [AS_HELP_STRING([--enable-efiemu], + [build and install the efiemu runtimes (default=guessed)])]) +if test x"$enable_efiemu" = xno ; then + efiemu_excuse="explicitly disabled" +fi + +if test x"$grub_cv_target_cc_link_format" = x-mi386pe || test x"$grub_cv_target_cc_link_format" = x-mi386pep ; then + efiemu_excuse="not available on cygwin" +fi +if test x"$target_cpu" != xi386 ; then + efiemu_excuse="only available on i386" +fi +if test x"$platform" = xefi ; then + efiemu_excuse="not available on efi" +fi + +if test x"$efiemu_excuse" = x ; then + AC_CACHE_CHECK([whether options required for efiemu work], grub_cv_cc_efiemu, [ + CFLAGS="-m64 -nostdlib -O2 -mcmodel=large -mno-red-zone" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_efiemu=yes], + [grub_cv_cc_efiemu=no]) + ]) + if test x$grub_cv_cc_efiemu = xno; then + efiemu_excuse="cannot compile with -m64 -mcmodel=large -mno-red-zone -nostdlib" + fi +fi +if test x"$efiemu_excuse" = x ; then + AC_CACHE_CHECK([for efiemu64 linking format], [grub_cv_target_cc_efiemu64_link_format], [ + grub_cv_target_cc_efiemu64_link_format=unknown + for format in -melf_x86_64 -melf_x86_64_fbsd -melf_x86_64_obsd -melf_x86_64_haiku -arch,x86_64; do + CFLAGS="-m64 -nostdlib -O2 -mcmodel=large -mno-red-zone" + LDFLAGS="-m64 -Wl,$format -nostdlib -static" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ + asm (".globl start; start:"); + asm (".globl _start; _start:"); + asm (".globl __start; __start:"); + void __main (void); + void __main (void) {} + ]], [[]])], [flag=1], [flag=0]) + if test x"$flag" = x1; then + grub_cv_target_cc_efiemu64_link_format="$format" + break + fi + done]) + if test x"$grub_cv_target_cc_efiemu64_link_format" = xunknown; then + efiemu_excuse="no suitable link format for efiemu64 found" + else + EFIEMU64_LINK_FORMAT="-Wl,$grub_cv_target_cc_efiemu64_link_format" + fi +fi +if test x"$enable_efiemu" = xyes && test x"$efiemu_excuse" != x ; then + AC_MSG_ERROR([efiemu runtime was explicitly requested but can't be compiled ($efiemu_excuse)]) +fi +if test x"$efiemu_excuse" = x ; then +enable_efiemu=yes +else +enable_efiemu=no +fi +AC_SUBST([enable_efiemu]) +AC_SUBST([EFIEMU64_LINK_FORMAT]) + +CFLAGS="$TARGET_CFLAGS" + +AC_SUBST(TARGET_LDFLAGS_OLDMAGIC) + + +LDFLAGS="$TARGET_LDFLAGS" + +if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then # Use large model to support 4G memory AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [ - SAVED_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS -m64 -mcmodel=large" + CFLAGS="$TARGET_CFLAGS -mcmodel=large" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [grub_cv_cc_mcmodel=yes], [grub_cv_cc_mcmodel=no]) ]) - if test "x$grub_cv_cc_mcmodel" = xno; then - CFLAGS="$SAVED_CFLAGS -m64 -DMCMODEL_SMALL=1" - TARGET_CFLAGS="$TARGET_CFLAGS -DMCMODEL_SMALL=1" - AC_MSG_WARN([-mcmodel=large not supported. You won't be able to use the memory over 4GiB. Upgrade your gcc]) - else + if test "x$grub_cv_cc_mcmodel" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large" + elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then + TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany" fi +fi +if test "$target_cpu"-"$platform" = x86_64-efi; then # EFI writes to stack below %rsp, we must not use the red zone AC_CACHE_CHECK([whether option -mno-red-zone works], grub_cv_cc_no_red_zone, [ - CFLAGS="$CFLAGS -m64 -mno-red-zone" + CFLAGS="$TARGET_CFLAGS -mno-red-zone" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [grub_cv_cc_no_red_zone=yes], [grub_cv_cc_no_red_zone=no]) @@ -395,28 +1342,165 @@ if test "$target_cpu"-"$platform" = x86_64-efi; then TARGET_CFLAGS="$TARGET_CFLAGS -mno-red-zone" fi +if test "x$target_cpu" = xarm; then + AC_CACHE_CHECK([for options to disable movt and movw], grub_cv_target_cc_mno_movt, [ + grub_cv_target_cc_mno_movt=no + for cand in "-mno-movt" \ + "-mllvm -arm-use-movt=0" \ + "-mword-relocations"; do + if test x"$grub_cv_target_cc_mno_movt" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + CPPFLAGS="$TARGET_CPPFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_mno_movt="$cand"], []) + done + ]) + + if test x"$grub_cv_target_cc_mno_movt" != xno ; then + # A trick so that clang doesn't see it on link stage + TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_mno_movt" + fi + AC_CACHE_CHECK([whether option -mthumb-interwork works], grub_cv_cc_mthumb_interwork, [ + CFLAGS="$TARGET_CFLAGS -mthumb-interwork -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_cc_mthumb_interwork=yes], + [grub_cv_cc_mthumb_interwork=no]) + ]) + if test "x$grub_cv_cc_mthumb_interwork" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -mthumb-interwork" + # Clang defaults to thumb interworking + elif test "x$grub_cv_cc_target_clang" = xno ; then + AC_MSG_ERROR([your compiler doesn't support -mthumb-interwork]) + fi +fi + +AC_CACHE_CHECK([whether option -Qn works], grub_cv_target_cc_qn, [ + CFLAGS="$TARGET_CFLAGS -Qn -Qunused-arguments -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_qn=yes], + [grub_cv_target_cc_qn=no])]) +if test "x$grub_cv_target_cc_qn" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -Qn -Qunused-arguments" +fi + # # Compiler features. # -# Need __enable_execute_stack() for nested function trampolines? -grub_CHECK_ENABLE_EXECUTE_STACK +CFLAGS="$TARGET_CFLAGS" # Position independent executable. grub_CHECK_PIE +grub_CHECK_NO_PIE +grub_CHECK_NO_PIE_ONEWORD +grub_CHECK_LINK_PIE [# Need that, because some distributions ship compilers that include -# `-fPIE' in the default specs. +# `-fPIE' or '-fpie' and '-pie' in the default specs. if [ x"$pie_possible" = xyes ]; then - TARGET_CFLAGS="$TARGET_CFLAGS -fno-PIE" + TARGET_CFLAGS="$TARGET_CFLAGS -fno-PIE -fno-pie" + TARGET_CCASFLAGS="$TARGET_CCASFLAGS -fno-PIE -fno-pie" +fi + +if [ x"$link_nopie_needed" = xyes ] || [ x"$pie_possible" = xyes ]; then + if [ x"$nopie_possible" = xyes ]; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -no-pie" + fi + if [ x"$nopie_oneword_possible" = xyes ]; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -nopie" + fi fi] -# Smashing stack protector. +CFLAGS="$TARGET_CFLAGS" +LDFLAGS="$TARGET_LDFLAGS" + +# Position independent executable. +grub_CHECK_PIC +[# On most platforms we don't want PIC as it only makes relocations harder +# and code less efficient. On mips we want to have one got table per module +# and reload $gp in every function. +# GCC implements it using symbol __gnu_local_gp in non-PIC as well. +# However with clang we need PIC for this reloading to happen. +# With arm64 we need relocations that are in some way representable in +# PE as we need to support arm64-efi. Without -fPIC clang generates +# movk's which aren't representable. +# Since default varies across dictributions use either -fPIC or -fno-PIC +# explicitly. +if ( test x$target_cpu = xmips || test x$target_cpu = xmipsel || test x$target_cpu = xarm64 ) && test "x$grub_cv_cc_target_clang" = xyes ; then + TARGET_CFLAGS="$TARGET_CFLAGS -fPIC" +elif [ x"$pic_possible" = xyes ]; then + TARGET_CFLAGS="$TARGET_CFLAGS -fno-PIC" +fi] + +CFLAGS="$TARGET_CFLAGS" + +# Stack smashing protector. grub_CHECK_STACK_PROTECTOR -# Need that, because some distributions ship compilers that include -# `-fstack-protector' in the default specs. -if test "x$ssp_possible" = xyes; then - TARGET_CFLAGS="$TARGET_CFLAGS -fno-stack-protector" +AC_ARG_ENABLE([stack-protector], + AS_HELP_STRING([--enable-stack-protector], + [enable the stack protector]), + [], + [enable_stack_protector=no]) +if test "x$enable_stack_protector" = xno; then + if test "x$ssp_possible" = xyes; then + # Need that, because some distributions ship compilers that include + # `-fstack-protector' in the default specs. + TARGET_CFLAGS="$TARGET_CFLAGS -fno-stack-protector" + fi +elif test "x$platform" != xefi; then + if test "$ERROR_PLATFORM_NOT_SUPPORT_SSP" = "yes"; then + AC_MSG_ERROR([--enable-stack-protector is only supported on EFI platforms]) + else + AC_MSG_WARN([--enable-stack-protector is only supported on EFI platforms]) + fi + enable_stack_protector=no +elif test "x$ssp_global_possible" != xyes; then + AC_MSG_ERROR([--enable-stack-protector is not supported (compiler doesn't support -mstack-protector-guard=global)]) +else + TARGET_CFLAGS="$TARGET_CFLAGS -mstack-protector-guard=global" + if test "x$enable_stack_protector" = xyes; then + if test "x$ssp_possible" != xyes; then + AC_MSG_ERROR([--enable-stack-protector is not supported (compiler doesn't support -fstack-protector)]) + fi + TARGET_CFLAGS="$TARGET_CFLAGS -fstack-protector" + elif test "x$enable_stack_protector" = xstrong; then + if test "x$ssp_strong_possible" != xyes; then + AC_MSG_ERROR([--enable-stack-protector=strong is not supported (compiler doesn't support -fstack-protector-strong)]) + fi + TARGET_CFLAGS="$TARGET_CFLAGS -fstack-protector-strong" + else + # Note, -fstack-protector-all requires that the protector is disabled for + # functions that appear in the call stack when the canary is initialized. + AC_MSG_ERROR([invalid value $enable_stack_protector for --enable-stack-protector]) + fi + TARGET_CPPFLAGS="$TARGET_CPPFLAGS -DGRUB_STACK_PROTECTOR=1" + + if test -n "$SOURCE_DATE_EPOCH"; then + GRUB_STACK_PROTECTOR_INIT="0x00f2b7e2$(printf "%x" "$SOURCE_DATE_EPOCH" | sed 's/.*\(........\)$/\1/')" + elif test -r /dev/urandom; then + # Generate the 8 byte stack protector canary at build time if /dev/urandom + # is able to be read. The first byte should be NUL to filter out string + # buffer overflow attacks. + GRUB_STACK_PROTECTOR_INIT="$($PYTHON -c 'import codecs; rf=open("/dev/urandom", "rb"); print("0x00"+codecs.encode(rf.read(7), "hex").decode("ascii"))')" + else + # Some hosts may not have a urandom, e.g. Windows, so use statically + # generated random bytes + GRUB_STACK_PROTECTOR_INIT="0x00f2b7e2f193b25c" + fi + + if test x"$target_m32" = x1 ; then + # Make sure that the canary default value is 24-bits by only using the + # lower 3 bytes on 32 bit systems. This allows the upper byte to be NUL + # to filter out string buffer overflow attacks. + GRUB_STACK_PROTECTOR_INIT="0x00$(echo "$GRUB_STACK_PROTECTOR_INIT" | sed 's/.*\(......\)$/\1/')" + fi + + AC_SUBST([GRUB_STACK_PROTECTOR_INIT]) fi + +CFLAGS="$TARGET_CFLAGS" + grub_CHECK_STACK_ARG_PROBE # Cygwin's GCC uses alloca() to probe the stackframe on static # stack allocations above some threshold. @@ -424,111 +1508,108 @@ if test x"$sap_possible" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -mno-stack-arg-probe" fi -AC_ARG_ENABLE([werror], - [AS_HELP_STRING([--disable-werror], - [do not use -Werror when building GRUB])]) -if test x"$enable_werror" != xno ; then - TARGET_CFLAGS="$TARGET_CFLAGS -Werror" -fi +CFLAGS="$TARGET_CFLAGS" -AC_SUBST(TARGET_CFLAGS) -AC_SUBST(TARGET_MODULE_FORMAT) -AC_SUBST(OBJCONV) -AC_SUBST(TARGET_APPLE_CC) -AC_SUBST(TARGET_ASFLAGS) -AC_SUBST(TARGET_CPPFLAGS) -AC_SUBST(TARGET_LDFLAGS) +# -mno-unaligned-access -mstrict-align +if test "$target_cpu" = arm; then + AC_CACHE_CHECK([for compile options to get strict alignment], [grub_cv_target_cc_strict_align], [ + grub_cv_target_cc_strict_align= + for arg in -mno-unaligned-access "-Xclang -mstrict-align" -mstrict-align; do + CFLAGS="$TARGET_CFLAGS $arg -Werror" + LDFLAGS="$TARGET_LDFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [flag=1], [flag=0]) + if test x"$flag" = x1; then + grub_cv_target_cc_strict_align="$arg" + break + fi + done]) + + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_strict_align" + if test x"$grub_cv_target_cc_strict_align" = x"-Xclang -mstrict-align"; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -Qunused-arguments" + fi + AC_CACHE_CHECK([if compiler generates unaligned accesses], [grub_cv_cc_target_emits_unaligned], + [CFLAGS="$TARGET_CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[ +#ifdef __ARM_FEATURE_UNALIGNED +#error "unaligned" +#endif + ]])], + [grub_cv_cc_target_emits_unaligned=no], [grub_cv_cc_target_emits_unaligned=yes])]) + if test x$grub_cv_cc_target_emits_unaligned = xyes; then + AC_MSG_ERROR([compiler generates unaligned accesses]) + fi +fi # Set them to their new values for the tests below. CC="$TARGET_CC" -if test "x$TARGET_APPLE_CC" = x1 ; then -CFLAGS="$TARGET_CFLAGS -nostdlib -Wno-error" -else -CFLAGS="$TARGET_CFLAGS -nostdlib -Wl,--defsym,___main=0x8100 -Wl,--defsym,abort=main -Wno-error" -fi CPPFLAGS="$TARGET_CPPFLAGS" -LDFLAGS="$TARGET_LDFLAGS" -LIBS=-lgcc # Check for libgcc symbols -AC_CHECK_FUNCS(__bswapsi2 __bswapdi2 __ashldi3 __ashrdi3 __lshrdi3 __trampoline_setup __ucmpdi2 _restgpr_14_x) +if test x"$platform" = xemu; then +CFLAGS="$TARGET_CFLAGS -Wno-error" +AC_CHECK_FUNCS(__udivsi3 __umodsi3 __divsi3 __modsi3 __divdi3 __moddi3 __udivdi3 __umoddi3 __ctzdi2 __ctzsi2 __clzdi2 __aeabi_uidiv __aeabi_uidivmod __aeabi_idiv __aeabi_idivmod __aeabi_ulcmp __muldi3 __aeabi_lmul __aeabi_memcpy __aeabi_memcpy4 __aeabi_memcpy8 __aeabi_memclr __aeabi_memclr4 __aeabi_memclr8 __aeabi_memset __aeabi_lasr __aeabi_llsl __aeabi_llsr _restgpr_14_x __ucmpdi2 __ashldi3 __ashrdi3 __lshrdi3 __bswapsi2 __bswapdi2 __bzero __register_frame_info __deregister_frame_info ___chkstk_ms __chkstk_ms) +fi -if test "x$TARGET_APPLE_CC" = x1 ; then -CFLAGS="$TARGET_CFLAGS -nostdlib" +if test "x$TARGET_APPLE_LINKER" = x1 ; then +CFLAGS="$TARGET_CFLAGS -nostdlib -static" else -CFLAGS="$TARGET_CFLAGS -nostdlib -Wl,--defsym,___main=0x8100" +CFLAGS="$TARGET_CFLAGS -nostdlib" fi LIBS="" -# Defined in aclocal.m4. +# Defined in acinclude.m4. +grub_ASM_USCORE grub_PROG_TARGET_CC -if test "x$TARGET_APPLE_CC" != x1 ; then +if test "x$TARGET_APPLE_LINKER" != x1 ; then grub_PROG_OBJCOPY_ABSOLUTE fi grub_PROG_LD_BUILD_ID_NONE -grub_ASM_USCORE if test "x$target_cpu" = xi386; then - if test ! -z "$TARGET_IMG_LDSCRIPT"; then - # Check symbols provided by linker script. - CFLAGS="$TARGET_CFLAGS -nostdlib $TARGET_IMG_LDFLAGS_AC -Wl,-Ttext,8000,--defsym,___main=0x8100" - fi - if test "x$TARGET_APPLE_CC" != x1 ; then + if test "$platform" != emu && test "x$TARGET_APPLE_LINKER" != x1 ; then + if test ! -z "$TARGET_IMG_LDSCRIPT"; then + # Check symbols provided by linker script. + CFLAGS="$TARGET_CFLAGS -nostdlib ${TARGET_IMG_LDFLAGS_AC} ${TARGET_IMG_BASE_LDOPT},0x8000" + fi grub_CHECK_BSS_START_SYMBOL grub_CHECK_END_SYMBOL fi CFLAGS="$TARGET_CFLAGS" - grub_I386_ASM_PREFIX_REQUIREMENT - grub_I386_ASM_ADDR32 - grub_I386_ASM_ABSOLUTE_WITHOUT_ASTERISK -else - AC_DEFINE([NESTED_FUNC_ATTR], [], [Catch gcc bug]) fi -AH_BOTTOM([#if defined(__i386__) && !defined(GRUB_UTIL) -#define NESTED_FUNC_ATTR __attribute__ ((__regparm__ (1))) -#else -#define NESTED_FUNC_ATTR -#endif]) - -AC_ARG_ENABLE([efiemu], - [AS_HELP_STRING([--enable-efiemu], - [build and install the efiemu runtimes (default=guessed)])]) -if test x"$enable_efiemu" = xno ; then - efiemu_excuse="explicitly disabled" -fi -if test x"$efiemu_excuse" = x ; then - AC_CACHE_CHECK([whether options required for efiemu work], grub_cv_cc_efiemu, [ - CFLAGS="$CFLAGS -m64 -mcmodel=large -mno-red-zone -nostdlib" - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], - [grub_cv_cc_efiemu=yes], - [grub_cv_cc_efiemu=no]) - ]) - if test x$grub_cv_cc_efiemu = xno; then - efiemu_excuse="cannot compile with -m64 -mcmodel=large -mno-red-zone -nostdlib" - fi -fi -if test x"$enable_efiemu" = xyes && test x"$efiemu_excuse" != x ; then - AC_MSG_ERROR([efiemu runtime was explicitly requested but can't be compiled]) -fi -if test x"$efiemu_excuse" = x ; then -enable_efiemu=yes -else -enable_efiemu=no -fi -AC_SUBST([enable_efiemu]) +grub_PROG_NM_WORKS +grub_PROG_NM_MINUS_P +grub_PROG_NM_DEFINED_ONLY +AC_SUBST(TARGET_NMFLAGS_MINUS_P) +AC_SUBST(TARGET_NMFLAGS_DEFINED_ONLY) +if test "$platform" != emu; then AC_CACHE_CHECK([whether -nostdinc -isystem works], [grub_cv_cc_isystem], [ SAVED_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$TARGET_CPPFLAGS -nostdinc -isystem `$TARGET_CC -print-file-name=include`" + CPPFLAGS="$TARGET_CPPFLAGS -nostdlib -nostdinc -isystem `$TARGET_CC -print-file-name=include`" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +#include int va_arg_func (int fixed, va_list args);]], [[]])], [grub_cv_cc_isystem=yes], [grub_cv_cc_isystem=no]) CPPFLAGS="$SAVED_CPPFLAGS" ]) -if test x"$grub_cv_cc_isystem" = xyes ; then - TARGET_CPPFLAGS="$TARGET_CPPFLAGS -nostdinc -isystem `$TARGET_CC -print-file-name=include`" + if test x"$grub_cv_cc_isystem" = xyes ; then + TARGET_CPPFLAGS="$TARGET_CPPFLAGS -nostdinc -isystem `$TARGET_CC -print-file-name=include`" + fi +fi + +AC_CACHE_CHECK([whether -Wtrampolines work], [grub_cv_cc_wtrampolines], [ + CFLAGS="$TARGET_CFLAGS -Wtrampolines -Werror" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +int va_arg_func (int fixed, va_list args);]], [[]])], + [grub_cv_cc_wtrampolines=yes], + [grub_cv_cc_wtrampolines=no]) +]) + +if test x"$grub_cv_cc_wtrampolines" = xyes ; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wtrampolines" fi # Restore the flags. @@ -545,13 +1626,40 @@ LIBS="$tmp_LIBS" # Memory manager debugging. AC_ARG_ENABLE([mm-debug], AS_HELP_STRING([--enable-mm-debug], - [include memory manager debugging]), - [AC_DEFINE([MM_DEBUG], [1], - [Define to 1 if you enable memory manager debugging.])]) + [include memory manager debugging])) +if test x$enable_mm_debug = xyes; then + MM_DEBUG=1 +else + MM_DEBUG=0 +fi +AC_SUBST([MM_DEBUG]) +AM_CONDITIONAL([COND_MM_DEBUG], [test x$MM_DEBUG = x1]) -AC_ARG_ENABLE([grub-emu-usb], - [AS_HELP_STRING([--enable-grub-emu-usb], - [build and install the `grub-emu' debugging utility with USB support (default=guessed)])]) +AC_ARG_ENABLE([cache-stats], + AS_HELP_STRING([--enable-cache-stats], + [enable disk cache statistics collection])) + +if test x$enable_cache_stats = xyes; then + DISK_CACHE_STATS=1 +else + DISK_CACHE_STATS=0 +fi +AC_SUBST([DISK_CACHE_STATS]) + +AC_ARG_ENABLE([boot-time], + AS_HELP_STRING([--enable-boot-time], + [enable boot time statistics collection])) + +if test x$enable_boot_time = xyes; then + BOOT_TIME_STATS=1 +else + BOOT_TIME_STATS=0 +fi +AC_SUBST([BOOT_TIME_STATS]) + +AC_ARG_ENABLE([grub-emu-sdl2], + [AS_HELP_STRING([--enable-grub-emu-sdl2], + [build and install the `grub-emu' debugging utility with SDL2 support (default=guessed)])]) AC_ARG_ENABLE([grub-emu-sdl], [AS_HELP_STRING([--enable-grub-emu-sdl], @@ -562,121 +1670,88 @@ AC_ARG_ENABLE([grub-emu-pci], [build and install the `grub-emu' debugging utility with PCI support (potentially dangerous) (default=no)])]) if test "$platform" = emu; then - missing_ncurses= -[# Check for curses libraries.] - AC_CHECK_LIB([ncurses], [wgetch], [LIBCURSES="-lncurses"], - [AC_CHECK_LIB([curses], [wgetch], [LIBCURSES="-lcurses"], - [missing_ncurses=[true]])]) - AC_SUBST([LIBCURSES]) -[if [ x"$missing_ncurses" = x ]; then ] - [# Check for headers.] - AC_CHECK_HEADERS([ncurses/curses.h], [], - [AC_CHECK_HEADERS([ncurses.h], [], - [AC_CHECK_HEADERS([curses.h], [], - [missing_ncurses=[true]])])]) -[fi] -if test x"$missing_ncurses" = xtrue ; then - AC_MSG_ERROR([grub-emu can't be compiled without ncurses]) -fi + if test x"$enable_grub_emu_sdl2" = xno ; then + grub_emu_sdl2_excuse="explicitly disabled" + fi + [if [ x"$grub_emu_sdl2_excuse" = x ]; then + # Check for libSDL libraries.] + PKG_CHECK_MODULES([SDL2], [sdl2], [ + AC_DEFINE([HAVE_SDL2], [1], [Define to 1 if you have SDL2 library.]) + AC_SUBST(HAVE_SDL2)], + [grub_emu_sdl2_excuse="libSDL2 libraries are required to build \`grub-emu' with SDL2 support"]) + [fi] + if test x"$enable_grub_emu_sdl2" = xyes && test x"$grub_emu_sdl2_excuse" != x ; then + AC_MSG_ERROR([SDL2 support for grub-emu was explicitly requested but can't be compiled ($grub_emu_sdl2_excuse)]) + fi + if test x"$grub_emu_sdl2_excuse" = x ; then + enable_grub_emu_sdl2=yes + else + enable_grub_emu_sdl2=no + fi + if test x"$enable_grub_emu_sdl2" = xyes ; then + grub_emu_sdl_excuse="disabled by sdl2" + fi -if test x"$enable_grub_emu_usb" = xno ; then - grub_emu_usb_excuse="explicitly disabled" -fi -if test x"$enable_grub_emu_pci" = xyes ; then - grub_emu_usb_excuse="conflicts with PCI support" -fi - -[if [ x"$grub_emu_usb_excuse" = x ]; then - # Check for libusb libraries.] -AC_CHECK_LIB([usb], [usb_claim_interface], [LIBUSB="-lusb"], - [grub_emu_usb_excuse=["need libusb library"]]) - AC_SUBST([LIBUSB]) -[fi] -[if [ x"$grub_emu_usb_excuse" = x ]; then - # Check for headers.] - AC_CHECK_HEADERS([usb.h], [], - [grub_emu_usb_excuse=["need libusb headers"]]) -[fi] -if test x"$enable_grub_emu_usb" = xyes && test x"$grub_emu_usb_excuse" != x ; then - AC_MSG_ERROR([USB support for grub-emu was explicitly requested but can't be compiled]) -fi -if test x"$grub_emu_usb_excuse" = x ; then -enable_grub_emu_usb=yes -else -enable_grub_emu_usb=no -fi - -if test x"$enable_grub_emu_sdl" = xno ; then - grub_emu_sdl_excuse="explicitely disabled" -fi -[if [ x"$grub_emu_sdl_excuse" = x ]; then + if test x"$enable_grub_emu_sdl" = xno ; then + grub_emu_sdl_excuse="explicitly disabled" + fi + [if [ x"$grub_emu_sdl_excuse" = x ]; then # Check for libSDL libraries.] AC_CHECK_LIB([SDL], [SDL_Init], [LIBSDL="-lSDL"], [grub_emu_sdl_excuse=["libSDL libraries are required to build \`grub-emu' with SDL support"]]) AC_SUBST([LIBSDL]) -[fi] + [fi] -[if [ x"$grub_emu_sdl_excuse" = x ]; then + [if [ x"$grub_emu_sdl_excuse" = x ]; then # Check for headers.] AC_CHECK_HEADERS([SDL/SDL.h], [], [grub_emu_sdl_excuse=["libSDL header file is required to build \`grub-emu' with SDL support"]]) -[fi] + [fi] -if test x"enable_grub_emu_sdl" = xyes && test x"$grub_emu_sdl_excuse" != x ; then - AC_MSG_ERROR([SDL support for grub-emu was explicitely requested but can't be compiled]) -fi -if test x"$grub_emu_sdl_excuse" = x ; then -enable_grub_emu_sdl=yes -else -enable_grub_emu_sdl=no -fi + if test x"$enable_grub_emu_sdl" = xyes && test x"$grub_emu_sdl_excuse" != x ; then + AC_MSG_ERROR([SDL support for grub-emu was explicitly requested but can't be compiled ($grub_emu_sdl_excuse)]) + fi + if test x"$grub_emu_sdl_excuse" = x ; then + enable_grub_emu_sdl=yes + else + enable_grub_emu_sdl=no + fi -if test x"$enable_grub_emu_pci" != xyes ; then - grub_emu_pci_excuse="not enabled" -fi + if test x"$enable_grub_emu_pci" != xyes ; then + grub_emu_pci_excuse="not enabled" + fi -if test x"$enable_grub_emu_usb" = xyes ; then - grub_emu_pci_excuse="conflicts with USB support" -fi - -[if [ x"$grub_emu_pci_excuse" = x ]; then - # Check for libpci libraries.] - AC_CHECK_LIB([pciaccess], [pci_system_init], [LIBPCIACCESS="-lpciaccess"], + [if [ x"$grub_emu_pci_excuse" = x ]; then + # Check for libpci libraries.] + AC_CHECK_LIB([pciaccess], [pci_system_init], [LIBPCIACCESS="-lpciaccess"], [grub_emu_pci_excuse=["need libpciaccess library"]]) AC_SUBST([LIBPCIACCESS]) -[fi] -[if [ x"$grub_emu_pci_excuse" = x ]; then + [fi] + [if [ x"$grub_emu_pci_excuse" = x ]; then # Check for headers.] - AC_CHECK_HEADERS([pci/pci.h], [], + AC_CHECK_HEADERS([pciaccess.h], [], [grub_emu_pci_excuse=["need libpciaccess headers"]]) -[fi] + [fi] + + if test x"$grub_emu_pci_excuse" = x ; then + enable_grub_emu_pci=yes + else + enable_grub_emu_pci=no + fi + + AC_SUBST([enable_grub_emu_sdl2]) + AC_SUBST([enable_grub_emu_sdl]) + AC_SUBST([enable_grub_emu_pci]) -if test x"$grub_emu_pci_excuse" = x ; then -enable_grub_emu_pci=yes else -enable_grub_emu_pci=no + # Ignore --enable-emu-* if platform is not emu + enable_grub_emu_sdl2=no + enable_grub_emu_sdl=no + enable_grub_emu_pci=no fi -AC_SUBST([enable_grub_emu_sdl]) -AC_SUBST([enable_grub_emu_usb]) -AC_SUBST([enable_grub_emu_pci]) -fi - -AC_ARG_ENABLE([grub-fstest], - [AS_HELP_STRING([--enable-grub-fstest], - [build and install the `grub-fstest' debugging utility (default=guessed)])]) -if test x"$enable_grub_fstest" = xno ; then - grub_fstest_excuse="explicitly disabled" -fi -if test x"$grub_fstest_excuse" = x ; then -enable_grub_fstest=yes -else -enable_grub_fstest=no -fi -AC_SUBST([enable_grub_fstest]) - AC_ARG_ENABLE([grub-mkfont], [AS_HELP_STRING([--enable-grub-mkfont], [build and install the `grub-mkfont' utility (default=guessed)])]) @@ -684,67 +1759,559 @@ if test x"$enable_grub_mkfont" = xno ; then grub_mkfont_excuse="explicitly disabled" fi -if test x"$grub_mkfont_excuse" = x ; then - # Check for freetype libraries. - AC_CHECK_PROGS([FREETYPE], [freetype-config]) - if test "x$FREETYPE" = x ; then - grub_mkfont_excuse=["need freetype2 library"] - fi - freetype_cflags=`freetype-config --cflags` - freetype_libs=`freetype-config --libs` -fi +unset ac_cv_header_ft2build_h if test x"$grub_mkfont_excuse" = x ; then # Check for freetype libraries. - SAVED_CPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $freetype_cflags" - AC_CHECK_HEADERS([ft2build.h], [], - [grub_mkfont_excuse=["need freetype2 headers"]]) - CPPFLAGS="$SAVED_CPPFLAGS" + PKG_CHECK_MODULES([FREETYPE], [freetype2], [ + SAVED_CPPFLAGS="$CPPFLAGS" + SAVED_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $FREETYPE_CFLAGS" + LIBS="$LIBS $FREETYPE_LIBS" + AC_CHECK_HEADERS([ft2build.h], [], + [grub_mkfont_excuse=["need freetype2 headers"]]) + AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], + [grub_mkfont_excuse=["freetype2 library unusable"]]) + CPPFLAGS="$SAVED_CPPFLAGS" + LIBS="$SAVED_LIBS" + ], [grub_mkfont_excuse=["need freetype2 library"]]) + if test x"$grub_mkfont_excuse" = x && test x"$host_kernel" = xnetbsd ; then + FREETYPE_LIBS="$FREETYPE_LIBS -Wl,-R,/usr/pkg/lib" ; + fi fi if test x"$enable_grub_mkfont" = xyes && test x"$grub_mkfont_excuse" != x ; then - AC_MSG_ERROR([grub-mkfont was explicitly requested but can't be compiled]) + AC_MSG_ERROR([grub-mkfont was explicitly requested but can't be compiled ($grub_mkfont_excuse)]) fi if test x"$grub_mkfont_excuse" = x ; then -enable_grub_mkfont=yes + enable_grub_mkfont=yes else -enable_grub_mkfont=no + enable_grub_mkfont=no fi AC_SUBST([enable_grub_mkfont]) -AC_SUBST([freetype_cflags]) -AC_SUBST([freetype_libs]) -AC_SUBST(ASFLAGS) +SAVED_CC="$CC" +SAVED_CPP="$CPP" +SAVED_CFLAGS="$CFLAGS" +SAVED_CPPFLAGS="$CPPFLAGS" +SAVED_LDFLAGS="$LDFLAGS" +CC="$BUILD_CC" +CPP="$BUILD_CPP" +CFLAGS="$BUILD_CFLAGS" +CPPFLAGS="$BUILD_CPPFLAGS" +LDFLAGS="$BUILD_LDFLAGS" -# Output files. -grub_CHECK_LINK_DIR -if test x"$link_dir" = xyes ; then - AC_CONFIG_LINKS([include/grub/cpu:include/grub/$target_cpu]) - if test "$platform" != emu ; then - AC_CONFIG_LINKS([include/grub/machine:include/grub/$target_cpu/$platform]) +unset ac_cv_c_bigendian +unset ac_cv_header_ft2build_h + +AC_COMPUTE_INT([BUILD_SIZEOF_VOID_P], [sizeof (void *)]) +AC_COMPUTE_INT([BUILD_SIZEOF_LONG], [sizeof (long)]) +AC_C_BIGENDIAN([BUILD_WORDS_BIGENDIAN=1], [BUILD_WORDS_BIGENDIAN=0], [BUILD_WORDS_BIGENDIAN=err], [BUILD_WORDS_BIGENDIAN=err]) + +if test x$BUILD_WORDS_BIGENDIAN = xerr ; then + AC_MSG_ERROR([couldnt determine build endianness]) +fi + +AC_SUBST([BUILD_SIZEOF_LONG]) +AC_SUBST([BUILD_SIZEOF_VOID_P]) +AC_SUBST([BUILD_WORDS_BIGENDIAN]) + +if test x"$grub_build_mkfont_excuse" = x ; then + # Check for freetype libraries. + SAVED_PKG_CONFIG="$PKG_CONFIG" + test -z "$BUILD_PKG_CONFIG" || PKG_CONFIG="$BUILD_PKG_CONFIG" + PKG_CHECK_MODULES([BUILD_FREETYPE], [freetype2], [ + SAVED_CPPFLAGS_2="$CPPFLAGS" + SAVED_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $BUILD_FREETYPE_CFLAGS" + LIBS="$LIBS $BUILD_FREETYPE_LIBS" + AC_CHECK_HEADERS([ft2build.h], [], + [grub_build_mkfont_excuse=["need freetype2 headers"]]) + AC_LINK_IFELSE([AC_LANG_CALL([], [FT_Load_Glyph])], [], + [grub_build_mkfont_excuse=["freetype2 library unusable"]]) + LIBS="$SAVED_LIBS" + CPPFLAGS="$SAVED_CPPFLAGS_2" + ], [grub_build_mkfont_excuse=["need freetype2 library"]]) + if test x"$grub_build_mkfont_excuse" = x ; then + case x"$build_os" in + xnetbsd*) BUILD_FREETYPE_LIBS="$BUILD_FREETYPE_LIBS -Wl,-R,/usr/pkg/lib" ;; + esac fi + PKG_CONFIG="$SAVED_PKG_CONFIG" +fi + +if test x"$enable_build_grub_mkfont" = xyes && test x"$grub_build_mkfont_excuse" != x ; then + AC_MSG_ERROR([build-grub-mkfont was explicitly requested but can't be compiled ($grub_build_mkfont_excuse)]) +fi +if test x"$grub_build_mkfont_excuse" = x ; then + enable_build_grub_mkfont=yes else - mkdir -p include/grub 2>/dev/null - rm -rf include/grub/cpu - cp -rp $srcdir/include/grub/$target_cpu include/grub/cpu 2>/dev/null - if test "$platform" != emu ; then - rm -rf include/grub/machine - cp -rp $srcdir/include/grub/$target_cpu/$platform include/grub/machine 2>/dev/null + enable_build_grub_mkfont=no +fi +if test x"$enable_build_grub_mkfont" = xno && ( test "x$platform" = xqemu || test "x$platform" = xloongson || test "x$platform" = xqemu_mips || test "x$platform" = xcoreboot ); then + if test x"$grub_build_mkfont_excuse" = x ; then + AC_MSG_ERROR([qemu, coreboot and loongson ports need build-time grub-mkfont]) + else + AC_MSG_ERROR([qemu, coreboot and loongson ports need build-time grub-mkfont ($grub_build_mkfont_excuse)]) fi fi -AC_CONFIG_FILES([Makefile gensymlist.sh genkernsyms.sh]) + +CC="$SAVED_CC" +CPP="$SAVED_CPP" +CFLAGS="$SAVED_CFLAGS" +CPPFLAGS="$SAVED_CPPFLAGS" +LDFLAGS="$SAVED_LDFLAGS" + + +starfield_excuse= + +AC_ARG_ENABLE([grub-themes], + [AS_HELP_STRING([--enable-grub-themes], + [build and install GRUB themes (default=guessed)])]) +if test x"$enable_grub_themes" = xno ; then + starfield_excuse="explicitly disabled" +fi + +if test x"$starfield_excuse" = x && test x"$enable_build_grub_mkfont" = xno ; then + starfield_excuse="No build-time grub-mkfont" +fi + +AC_ARG_WITH([dejavufont], + AS_HELP_STRING([--with-dejavufont=FILE], + [set the DejeVu source [[guessed]]])) + +if test "x$with_dejavufont" = x; then + # search in well-known directories + if test x"$starfield_excuse" = x; then + for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF /usr/share/fonts/dejavu-sans-fonts /usr/share/fonts/truetype/dejavu; do + if test -f "$dir/DejaVuSans.$ext"; then + DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" + break 2 + fi + done + done + + if test "x$DJVU_FONT_SOURCE" = x; then + starfield_excuse="No DejaVu found" + fi + fi +else + DJVU_FONT_SOURCE="$with_dejavufont" +fi + +if test x"$enable_grub_themes" = xyes && test x"$starfield_excuse" != x; then + AC_MSG_ERROR([themes were explicitly requested but requirements are not satisfied ($starfield_excuse)]) +fi + +AC_SUBST([DJVU_FONT_SOURCE]) + +AC_ARG_WITH([unifont], + AS_HELP_STRING([--with-unifont=FILE], + [set the unifont source [[guessed]]])) + +if test "x$with_unifont" = x; then + # search in well-known directories + for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz otf otf.gz; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/unifont /usr/share/fonts/uni /usr/share/fonts/truetype/unifont /usr/share/fonts/misc /usr/pkg/share/fonts/X11/misc /usr/local/share/fonts/gnu-unifont /usr/local/share/fonts/unifont; do + if test -f "$dir/unifont.$ext"; then + md5="$(md5sum "$dir/unifont.$ext"|awk '{ print $1; }')" + # PCF and BDF from version 6.3 isn't hanled properly by libfreetype. + if test "$md5" = 0a54834d2788c83886a3e1785a6a1e61 || test "$md5" = 28f2565c7a41d8d407e2551159385edb || test "$md5" = dae5e588461b3b92b87b6ffee734f936 || test "$md5" = 4a3d687aa5bb329ed05f4263a1016791 ; then + continue + fi + FONT_SOURCE="$dir/unifont.$ext" + break 2 + fi + done + done +else + FONT_SOURCE="$with_unifont" +fi + +if test x"$enable_build_grub_mkfont" = xno ; then + FONT_SOURCE= +fi + +if test "x$FONT_SOURCE" = x && ( test "x$platform" = xqemu || test "x$platform" = xloongson || test "x$platform" = xqemu_mips || test "x$platform" = xcoreboot ); then + if test x"$grub_build_mkfont_excuse" = x ; then + AC_MSG_ERROR([qemu, coreboot and loongson ports need unifont]) + else + AC_MSG_ERROR([qemu, coreboot and loongson ports need unifont ($grub_build_mkfont_excuse)]) + fi +fi + +AC_SUBST([FONT_SOURCE]) + +if test x"$FONT_SOURCE" = x && test x"$DJVU_FONT_SOURCE" = x && test x"$grub_build_mkfont_excuse" = x; then + grub_build_mkfont_excuse="no fonts" +fi + + +AC_ARG_ENABLE([grub-mount], + [AS_HELP_STRING([--enable-grub-mount], + [build and install the `grub-mount' utility (default=guessed)])]) +if test x"$enable_grub_mount" = xno ; then + grub_mount_excuse="explicitly disabled" +fi + +if test x"$grub_mount_excuse" = x ; then + PKG_CHECK_MODULES([FUSE], [fuse3], [FUSE_CFLAGS="$FUSE_CFLAGS -DFUSE_USE_VERSION=32"], [ + PKG_CHECK_MODULES([FUSE], [fuse], [FUSE_CFLAGS="$FUSE_CFLAGS -DFUSE_USE_VERSION=26"], [ + grub_mount_excuse="need fuse or fuse3 libraries" + ]) + ]) +fi + +if test x"$enable_grub_mount" = xyes && test x"$grub_mount_excuse" != x ; then + AC_MSG_ERROR([grub-mount was explicitly requested but can't be compiled ($grub_mount_excuse)]) +fi +if test x"$grub_mount_excuse" = x ; then +enable_grub_mount=yes +else +enable_grub_mount=no +fi +AC_SUBST([enable_grub_mount]) + +AC_ARG_ENABLE([device-mapper], + [AS_HELP_STRING([--enable-device-mapper], + [enable Linux device-mapper support (default=guessed)])]) +if test x"$enable_device_mapper" = xno ; then + device_mapper_excuse="explicitly disabled" +fi + +if test x"$device_mapper_excuse" = x ; then + # Check for device-mapper header. + AC_CHECK_HEADER([libdevmapper.h], [], + [device_mapper_excuse="need libdevmapper header"]) +fi + +if test x"$device_mapper_excuse" = x ; then + # Check for device-mapper library. + AC_CHECK_LIB([devmapper], [dm_task_create], [], + [device_mapper_excuse="need devmapper library"]) +fi + +if test x"$device_mapper_excuse" = x ; then + # Check for device-mapper library. + AC_CHECK_LIB([devmapper], [dm_log_with_errno_init], + [], + [device_mapper_excuse="need devmapper library"]) +fi + +if test x"$device_mapper_excuse" = x ; then + LIBDEVMAPPER="-ldevmapper" + AC_DEFINE([HAVE_DEVICE_MAPPER], [1], + [Define to 1 if you have the devmapper library.]) +fi + +AC_SUBST([LIBDEVMAPPER]) + +LIBGEOM= +if test x$host_kernel = xkfreebsd; then + AC_CHECK_LIB([geom], [geom_gettree], [], + [AC_MSG_ERROR([Your platform requires libgeom])]) + LIBGEOM="-lgeom" +fi + +AC_SUBST([LIBGEOM]) + +AC_ARG_ENABLE([liblzma], + [AS_HELP_STRING([--enable-liblzma], + [enable liblzma integration (default=guessed)])]) +if test x"$enable_liblzma" = xno ; then + liblzma_excuse="explicitly disabled" +fi + +if test x"$liblzma_excuse" = x ; then +AC_CHECK_LIB([lzma], [lzma_code], + [],[liblzma_excuse="need lzma library"]) +fi +if test x"$liblzma_excuse" = x ; then +AC_CHECK_HEADER([lzma.h], [], [liblzma_excuse="need lzma header"]) +fi + +if test x"$enable_liblzma" = xyes && test x"$liblzma_excuse" != x ; then + AC_MSG_ERROR([liblzma support was explicitly requested but requirements are not satisfied ($liblzma_excuse)]) +fi + + +if test x"$liblzma_excuse" = x ; then + LIBLZMA="-llzma" + AC_DEFINE([USE_LIBLZMA], [1], + [Define to 1 if you have the LZMA library.]) +fi + +AC_SUBST([LIBLZMA]) + +AC_ARG_ENABLE([libzfs], + [AS_HELP_STRING([--enable-libzfs], + [enable libzfs integration (default=guessed)])]) +if test x"$enable_libzfs" = xno ; then + libzfs_excuse="explicitly disabled" +fi + +if test x"$libzfs_excuse" = x ; then + # Only check for system headers if libzfs support has not been disabled. + AC_CHECK_HEADERS(libzfs.h libnvpair.h) +fi + +if test x"$libzfs_excuse" = x ; then + AC_CHECK_LIB([zfs], [libzfs_init], + [], + [libzfs_excuse="need zfs library"]) +fi + +if test x"$libzfs_excuse" = x ; then + AC_CHECK_LIB([nvpair], [nvlist_lookup_string], + [have_normal_nvpair=yes], + [have_normal_nvpair=no]) + if test x"$have_normal_nvpair" = xno ; then + AC_CHECK_LIB([nvpair], [opensolaris_nvlist_lookup_string], + [have_prefixed_nvpair=yes], + [have_prefixed_nvpair=no]) + if test x"$have_prefixed_nvpair" = xyes ; then + AC_DEFINE([GRUB_UTIL_NVPAIR_IS_PREFIXED], [1], + [Define to 1 if libnvpair symbols are prefixed with opensolaris_.]) + else + libzfs_excuse="need nvpair library" + fi + fi +fi + +if test x"$enable_libzfs" = xyes && test x"$libzfs_excuse" != x ; then + AC_MSG_ERROR([libzfs support was explicitly requested but requirements are not satisfied ($libzfs_excuse)]) +fi + +if test x"$libzfs_excuse" = x ; then + # We need both libzfs and libnvpair for a successful build. + LIBZFS="-lzfs" + LIBNVPAIR="-lnvpair" + AC_DEFINE([USE_LIBZFS], [1], + [Define to 1 if ZFS library should be used.]) +fi + +AC_SUBST([LIBZFS]) +AC_SUBST([LIBNVPAIR]) + +AC_ARG_ENABLE([grub-protect], + [AS_HELP_STRING([--enable-grub-protect], + [build and install the `grub-protect' utility (default=guessed)])]) +if test x"$enable_grub_protect" = xno ; then + grub_protect_excuse="explicitly disabled" +fi + +LIBTASN1= +if test x"$grub_protect_excuse" = x ; then + AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], [grub_protect_excuse="need libtasn1 library"]) +fi +AC_SUBST([LIBTASN1]) + +if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x ; then + AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled ($grub_protect_excuse)]) +fi +if test x"$grub_protect_excuse" = x ; then +enable_grub_protect=yes +else +enable_grub_protect=no +fi +AC_SUBST([enable_grub_protect]) + +LIBS="" + +AC_SUBST([FONT_SOURCE]) +AS_IF([test x$target_cpu = xi386 -a x$platform = xqemu], + [AC_SUBST([GRUB_BOOT_MACHINE_LINK_ADDR], 0xffe00)]) + +AC_SUBST(HAVE_ASM_USCORE) +AC_SUBST(BSS_START_SYMBOL) +AC_SUBST(END_SYMBOL) +AC_SUBST(PACKAGE) +AC_SUBST(VERSION) + +AC_ARG_ENABLE([werror], + [AS_HELP_STRING([--disable-werror], + [do not use -Werror when building GRUB])]) +if test x"$enable_werror" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS -Werror" + HOST_CFLAGS="$HOST_CFLAGS -Werror" + if test "x$grub_cv_cc_target_clang" = xyes; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wno-error=vla" + HOST_CFLAGS="$HOST_CFLAGS -Wno-error=vla" + fi +fi + +TARGET_CPP="$TARGET_CC -E" +TARGET_CCAS=$TARGET_CC + +# Includes which include make-time substitutions. They must come last +# as to avoid executing top_builddir in shell. +HOST_CPPFLAGS="$HOST_CPPFLAGS -I\$(top_builddir)/include" +TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_srcdir)/include" +TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_builddir)/include" + +GRUB_TARGET_CPU="${target_cpu}" +GRUB_PLATFORM="${platform}" + +AC_SUBST(GRUB_TARGET_CPU) +AC_SUBST(GRUB_PLATFORM) + +AC_SUBST(TARGET_OBJCONV) +AC_SUBST(TARGET_CPP) +AC_SUBST(TARGET_CCAS) +AC_SUBST(TARGET_OBJ2ELF) +AC_SUBST(TARGET_MODULE_FORMAT) +AC_SUBST(TARGET_CC_VERSION) + +AC_SUBST(TARGET_CFLAGS) +AC_SUBST(TARGET_LDFLAGS) +AC_SUBST(TARGET_CPPFLAGS) +AC_SUBST(TARGET_CCASFLAGS) + +AC_SUBST(TARGET_IMG_LDFLAGS) +AC_SUBST(TARGET_IMG_CFLAGS) +AC_SUBST(TARGET_IMG_BASE_LDOPT) +AC_SUBST(TARGET_APPLE_LINKER) + +AC_SUBST(HOST_CFLAGS) +AC_SUBST(HOST_LDFLAGS) +AC_SUBST(HOST_CPPFLAGS) +AC_SUBST(HOST_CCASFLAGS) + +AC_SUBST(BUILD_LIBM) + +# +# Automake conditionals +# + +AM_CONDITIONAL([COND_real_platform], [test x$platform != xnone]) +AM_CONDITIONAL([COND_emu], [test x$platform = xemu]) +AM_CONDITIONAL([COND_arm], [test x$target_cpu = xarm ]) +AM_CONDITIONAL([COND_arm_uboot], [test x$target_cpu = xarm -a x$platform = xuboot]) +AM_CONDITIONAL([COND_arm_coreboot], [test x$target_cpu = xarm -a x$platform = xcoreboot]) +AM_CONDITIONAL([COND_arm_efi], [test x$target_cpu = xarm -a x$platform = xefi]) +AM_CONDITIONAL([COND_arm64], [test x$target_cpu = xarm64 ]) +AM_CONDITIONAL([COND_arm64_efi], [test x$target_cpu = xarm64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_ia64_efi], [test x$target_cpu = xia64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_i386_pc], [test x$target_cpu = xi386 -a x$platform = xpc]) +AM_CONDITIONAL([COND_i386_efi], [test x$target_cpu = xi386 -a x$platform = xefi]) +AM_CONDITIONAL([COND_i386_qemu], [test x$target_cpu = xi386 -a x$platform = xqemu]) +AM_CONDITIONAL([COND_i386_ieee1275], [test x$target_cpu = xi386 -a x$platform = xieee1275]) +AM_CONDITIONAL([COND_i386_coreboot], [test x$target_cpu = xi386 -a x$platform = xcoreboot]) +AM_CONDITIONAL([COND_i386_multiboot], [test x$target_cpu = xi386 -a x$platform = xmultiboot]) +AM_CONDITIONAL([COND_i386_xen], [test x$target_cpu = xi386 -a x$platform = xxen]) +AM_CONDITIONAL([COND_i386_xen_pvh], [test x$target_cpu = xi386 -a x$platform = xxen_pvh]) +AM_CONDITIONAL([COND_loongarch64], [test x$target_cpu = xloongarch64]) +AM_CONDITIONAL([COND_loongarch64_efi], [test x$target_cpu = xloongarch64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_mips], [test x$target_cpu = xmips -o x$target_cpu = xmipsel]) +AM_CONDITIONAL([COND_mips_arc], [test "(" x$target_cpu = xmips -o x$target_cpu = xmipsel ")" -a x$platform = xarc]) +AM_CONDITIONAL([COND_mips_loongson], [test x$target_cpu = xmipsel -a x$platform = xloongson]) +AM_CONDITIONAL([COND_mips_qemu_mips], [test "(" x$target_cpu = xmips -o x$target_cpu = xmipsel ")" -a x$platform = xqemu_mips]) +AM_CONDITIONAL([COND_mipsel], [test x$target_cpu = xmipsel]) +AM_CONDITIONAL([COND_mipseb], [test x$target_cpu = xmips]) +AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platform = xieee1275]) +AM_CONDITIONAL([COND_riscv32], [test x$target_cpu = xriscv32 ]) +AM_CONDITIONAL([COND_riscv64], [test x$target_cpu = xriscv64 ]) +AM_CONDITIONAL([COND_riscv32_efi], [test x$target_cpu = xriscv32 -a x$platform = xefi]) +AM_CONDITIONAL([COND_riscv64_efi], [test x$target_cpu = xriscv64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_sparc64_ieee1275], [test x$target_cpu = xsparc64 -a x$platform = xieee1275]) +AM_CONDITIONAL([COND_sparc64_emu], [test x$target_cpu = xsparc64 -a x$platform = xemu]) +AM_CONDITIONAL([COND_x86_64_efi], [test x$target_cpu = xx86_64 -a x$platform = xefi]) +AM_CONDITIONAL([COND_x86_64_xen], [test x$target_cpu = xx86_64 -a x$platform = xxen]) + +AM_CONDITIONAL([COND_HOST_HURD], [test x$host_kernel = xhurd]) +AM_CONDITIONAL([COND_HOST_LINUX], [test x$host_kernel = xlinux]) +AM_CONDITIONAL([COND_HOST_NETBSD], [test x$host_kernel = xnetbsd]) +AM_CONDITIONAL([COND_HOST_WINDOWS], [test x$host_kernel = xwindows]) +AM_CONDITIONAL([COND_HOST_KFREEBSD], [test x$host_kernel = xkfreebsd]) +AM_CONDITIONAL([COND_HOST_XNU], [test x$host_kernel = xxnu]) +AM_CONDITIONAL([COND_HOST_ILLUMOS], [test x$host_kernel = xillumos]) + +AM_CONDITIONAL([COND_MAN_PAGES], [test x$cross_compiling = xno -a x$HELP2MAN != x]) +AM_CONDITIONAL([COND_GRUB_EMU_SDL2], [test x$enable_grub_emu_sdl2 = xyes]) +AM_CONDITIONAL([COND_GRUB_EMU_SDL], [test x$enable_grub_emu_sdl = xyes]) +AM_CONDITIONAL([COND_GRUB_EMU_PCI], [test x$enable_grub_emu_pci = xyes]) +AM_CONDITIONAL([COND_GRUB_MKFONT], [test x$enable_grub_mkfont = xyes]) +AM_CONDITIONAL([COND_GRUB_MOUNT], [test x$enable_grub_mount = xyes]) +AM_CONDITIONAL([COND_GRUB_PROTECT], [test x$enable_grub_protect = xyes]) +AM_CONDITIONAL([COND_HAVE_FONT_SOURCE], [test x$FONT_SOURCE != x]) +if test x$FONT_SOURCE != x ; then + HAVE_FONT_SOURCE=1 +else + HAVE_FONT_SOURCE=0 +fi +AC_SUBST(HAVE_FONT_SOURCE) +AM_CONDITIONAL([COND_APPLE_LINKER], [test x$TARGET_APPLE_LINKER = x1]) +AM_CONDITIONAL([COND_ENABLE_EFIEMU], [test x$enable_efiemu = xyes]) +AM_CONDITIONAL([COND_ENABLE_CACHE_STATS], [test x$DISK_CACHE_STATS = x1]) +AM_CONDITIONAL([COND_ENABLE_BOOT_TIME_STATS], [test x$BOOT_TIME_STATS = x1]) + +AM_CONDITIONAL([COND_HAVE_CXX], [test x$HAVE_CXX = xyes]) + +AM_CONDITIONAL([COND_HAVE_ASM_USCORE], [test x$HAVE_ASM_USCORE = x1]) +AM_CONDITIONAL([COND_STARFIELD], [test "x$starfield_excuse" = x]) +AM_CONDITIONAL([COND_HAVE_EXEC], [test "x$have_exec" = xy]) +AM_CONDITIONAL([COND_HAVE_PCI], [test "x$have_pci" = xy]) + +test "x$prefix" = xNONE && prefix="$ac_default_prefix" +test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" +datarootdir="$(eval echo "$datarootdir")" +grub_libdir="$(eval echo "$libdir")" +grub_localedir="$(eval echo "$localedir")" +grub_datadir="$(eval echo "$datadir")" +grub_sysconfdir="$(eval echo "$sysconfdir")" +AC_DEFINE_UNQUOTED(LOCALEDIR, "$grub_localedir", [Locale dir]) +AC_DEFINE_UNQUOTED(GRUB_LIBDIR, "$grub_libdir", [Library dir]) +AC_DEFINE_UNQUOTED(GRUB_DATADIR, "$grub_datadir", [Data dir]) +AC_DEFINE_UNQUOTED(GRUB_SYSCONFDIR, "$grub_sysconfdir", [Configuration dir]) + + +# Output files. +if test "$platform" != none; then + cpudir="${target_cpu}" + if test x${cpudir} = xmipsel; then + cpudir=mips; + fi + grub_CHECK_LINK_DIR + if test x"$link_dir" = xyes ; then + AC_CONFIG_LINKS([include/grub/cpu:include/grub/$cpudir]) + if test "$platform" != emu ; then + AC_CONFIG_LINKS([include/grub/machine:include/grub/$cpudir/$platform]) + fi + else + mkdir -p include/grub 2>/dev/null + rm -rf include/grub/cpu + cp -rp $srcdir/include/grub/$cpudir include/grub/cpu 2>/dev/null + if test "$platform" != emu ; then + rm -rf include/grub/machine + cp -rp $srcdir/include/grub/$cpudir/$platform include/grub/machine 2>/dev/null + fi + fi +else + # Just enough to stop the compiler failing with -I$(srcdir)/include. + mkdir -p include 2>/dev/null + rm -rf include/grub/cpu include/grub/machine +fi + +AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([grub-core/Makefile]) +AC_CONFIG_FILES([grub-core/lib/gnulib/Makefile]) +AC_CONFIG_FILES([po/Makefile.in]) +AC_CONFIG_FILES([docs/Makefile]) +AC_CONFIG_FILES([util/bash-completion.d/Makefile]) AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h]) +AC_CONFIG_FILES([config.h]) + AC_OUTPUT [ echo "*******************************************************" echo GRUB2 will be compiled with following components: echo Platform: "$target_cpu"-"$platform" if [ x"$platform" = xemu ]; then -if [ x"$grub_emu_usb_excuse" = x ]; then -echo USB support for grub-emu: Yes +if [ x"$grub_emu_sdl2_excuse" = x ]; then +echo SDL2 support for grub-emu: Yes else -echo USB support for grub-emu: No "($grub_emu_usb_excuse)" +echo SDL2 support for grub-emu: No "($grub_emu_sdl2_excuse)" fi if [ x"$grub_emu_sdl_excuse" = x ]; then echo SDL support for grub-emu: Yes @@ -757,25 +2324,79 @@ else echo PCI support for grub-emu: No "($grub_emu_pci_excuse)" fi fi +if test x"$device_mapper_excuse" = x ; then +echo With devmapper support: Yes +else +echo With devmapper support: No "($device_mapper_excuse)" +fi if [ x"$enable_mm_debug" = xyes ]; then echo With memory debugging: Yes else echo With memory debugging: No fi +if [ x"$enable_cache_stats" = xyes ]; then +echo With disk cache statistics: Yes +else +echo With disk cache statistics: No +fi + +if [ x"$enable_boot_time" = xyes ]; then +echo With boot time statistics: Yes +else +echo With boot time statistics: No +fi + if [ x"$efiemu_excuse" = x ]; then echo efiemu runtime: Yes else echo efiemu runtime: No "($efiemu_excuse)" fi -if [ x"$grub_fstest_excuse" = x ]; then -echo grub-fstest: Yes -else -echo grub-fstest: No "($grub_fstest_excuse)" -fi if [ x"$grub_mkfont_excuse" = x ]; then echo grub-mkfont: Yes else echo grub-mkfont: No "($grub_mkfont_excuse)" fi +if [ x"$grub_mount_excuse" = x ]; then +echo grub-mount: Yes +else +echo grub-mount: No "($grub_mount_excuse)" +fi +if [ x"$grub_protect_excuse" = x ]; then +echo grub-protect: Yes +else +echo grub-protect: No "($grub_protect_excuse)" +fi +if [ x"$starfield_excuse" = x ]; then +echo starfield theme: Yes +echo With DejaVuSans font from $DJVU_FONT_SOURCE +else +echo starfield theme: No "($starfield_excuse)" +fi +if [ x"$libzfs_excuse" = x ]; then +echo With libzfs support: Yes +else +echo With libzfs support: No "($libzfs_excuse)" +fi +if [ x"$grub_build_mkfont_excuse" = x ]; then + echo Build-time grub-mkfont: Yes + if test "x$FONT_SOURCE" = x ; then + echo "Without unifont" + else + echo "With unifont from $FONT_SOURCE" + fi +else + echo Build-time grub-mkfont: No "($grub_build_mkfont_excuse)" + echo "Without unifont (no build-time grub-mkfont)" +fi +if test x"$liblzma_excuse" != x ; then +echo "Without liblzma (no support for XZ-compressed mips images) ($liblzma_excuse)" +else +echo "With liblzma from $LIBLZMA (support for XZ-compressed mips images)" +fi +if test "x$enable_stack_protector" != xno; then +echo "With stack smashing protector: Yes" +else +echo "With stack smashing protector: No" +fi echo "*******************************************************" ] diff --git a/coreboot.cfg b/coreboot.cfg new file mode 100644 index 000000000..188090d3a --- /dev/null +++ b/coreboot.cfg @@ -0,0 +1,3 @@ +if test -f (cbfsdisk)/etc/grub.cfg; then + source (cbfsdisk)/etc/grub.cfg +fi diff --git a/disk/ata.c b/disk/ata.c deleted file mode 100644 index 687ed9378..000000000 --- a/disk/ata.c +++ /dev/null @@ -1,894 +0,0 @@ -/* ata.c - ATA disk access. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -/* At the moment, only two IDE ports are supported. */ -static const grub_port_t grub_ata_ioaddress[] = { 0x1f0, 0x170 }; -static const grub_port_t grub_ata_ioaddress2[] = { 0x3f6, 0x376 }; - -static struct grub_ata_device *grub_ata_devices; - -/* Wait for !BSY. */ -grub_err_t -grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds) -{ - /* ATA requires 400ns (after a write to CMD register) or - 1 PIO cycle (after a DRQ block transfer) before - first check of BSY. */ - grub_millisleep (1); - - int i = 1; - grub_uint8_t sts; - while ((sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS)) - & GRUB_ATA_STATUS_BUSY) - { - if (i >= milliseconds) - { - grub_dprintf ("ata", "timeout: %dms, status=0x%x\n", - milliseconds, sts); - return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout"); - } - - grub_millisleep (1); - i++; - } - - return GRUB_ERR_NONE; -} - -static inline void -grub_ata_wait (void) -{ - grub_millisleep (50); -} - -/* Wait for !BSY, DRQ. */ -grub_err_t -grub_ata_wait_drq (struct grub_ata_device *dev, int rw, - int milliseconds) -{ - if (grub_ata_wait_not_busy (dev, milliseconds)) - return grub_errno; - - /* !DRQ implies error condition. */ - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - != GRUB_ATA_STATUS_DRQ) - { - grub_dprintf ("ata", "ata error: status=0x%x, error=0x%x\n", - sts, grub_ata_regget (dev, GRUB_ATA_REG_ERROR)); - if (! rw) - return grub_error (GRUB_ERR_READ_ERROR, "ATA read error"); - else - return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error"); - } - - return GRUB_ERR_NONE; -} - -/* Byteorder has to be changed before strings can be read. */ -static void -grub_ata_strncpy (char *dst, char *src, grub_size_t len) -{ - grub_uint16_t *src16 = (grub_uint16_t *) src; - grub_uint16_t *dst16 = (grub_uint16_t *) dst; - unsigned int i; - - for (i = 0; i < len / 2; i++) - *(dst16++) = grub_be_to_cpu16 (*(src16++)); - dst[len] = '\0'; -} - -void -grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size) -{ - grub_uint16_t *buf16 = (grub_uint16_t *) buf; - unsigned int i; - - /* Read in the data, word by word. */ - for (i = 0; i < size / 2; i++) - buf16[i] = grub_le_to_cpu16 (grub_inw(dev->ioaddress + GRUB_ATA_REG_DATA)); -} - -static void -grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size) -{ - grub_uint16_t *buf16 = (grub_uint16_t *) buf; - unsigned int i; - - /* Write the data, word by word. */ - for (i = 0; i < size / 2; i++) - grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA); -} - -static void -grub_ata_dumpinfo (struct grub_ata_device *dev, char *info) -{ - char text[41]; - - /* The device information was read, dump it for debugging. */ - grub_ata_strncpy (text, info + 20, 20); - grub_dprintf ("ata", "Serial: %s\n", text); - grub_ata_strncpy (text, info + 46, 8); - grub_dprintf ("ata", "Firmware: %s\n", text); - grub_ata_strncpy (text, info + 54, 40); - grub_dprintf ("ata", "Model: %s\n", text); - - if (! dev->atapi) - { - grub_dprintf ("ata", "Addressing: %d\n", dev->addr); - grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size); - } -} - -static grub_err_t -grub_atapi_identify (struct grub_ata_device *dev) -{ - char *info; - - info = grub_malloc (GRUB_DISK_SECTOR_SIZE); - if (! info) - return grub_errno; - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4); - grub_ata_wait (); - if (grub_ata_check_ready (dev)) - { - grub_free (info); - return grub_errno; - } - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE); - grub_ata_wait (); - - if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD)) - { - grub_free (info); - return grub_errno; - } - grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE); - - dev->atapi = 1; - - grub_ata_dumpinfo (dev, info); - - grub_free (info); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_atapi_wait_drq (struct grub_ata_device *dev, - grub_uint8_t ireason, - int milliseconds) -{ - /* Wait for !BSY, DRQ, ireason */ - if (grub_ata_wait_not_busy (dev, milliseconds)) - return grub_errno; - - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - grub_uint8_t irs = grub_ata_regget (dev, GRUB_ATAPI_REG_IREASON); - - /* OK if DRQ is asserted and interrupt reason is as expected. */ - if ((sts & GRUB_ATA_STATUS_DRQ) - && (irs & GRUB_ATAPI_IREASON_MASK) == ireason) - return GRUB_ERR_NONE; - - /* !DRQ implies error condition. */ - grub_dprintf ("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n", - sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR)); - - if (! (sts & GRUB_ATA_STATUS_DRQ) - && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR) - { - if (ireason == GRUB_ATAPI_IREASON_CMD_OUT) - return grub_error (GRUB_ERR_READ_ERROR, "ATA PACKET command error"); - else - return grub_error (GRUB_ERR_READ_ERROR, "ATAPI read error"); - } - - return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error"); -} - -static grub_err_t -grub_atapi_packet (struct grub_ata_device *dev, char *packet, - grub_size_t size) -{ - grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4); - if (grub_ata_check_ready (dev)) - return grub_errno; - - /* Send ATA PACKET command. */ - grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0); - grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0); - grub_ata_regset (dev, GRUB_ATAPI_REG_CNTHIGH, size >> 8); - grub_ata_regset (dev, GRUB_ATAPI_REG_CNTLOW, size & 0xFF); - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET); - - /* Wait for !BSY, DRQ, !I/O, C/D. */ - if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_CMD_OUT, GRUB_ATA_TOUT_STD)) - return grub_errno; - - /* Write the packet. */ - grub_ata_pio_write (dev, packet, 12); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_ata_identify (struct grub_ata_device *dev) -{ - char *info; - grub_uint16_t *info16; - - info = grub_malloc (GRUB_DISK_SECTOR_SIZE); - if (! info) - return grub_errno; - - info16 = (grub_uint16_t *) info; - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4); - grub_ata_wait (); - if (grub_ata_check_ready (dev)) - { - grub_free (info); - return grub_errno; - } - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE); - grub_ata_wait (); - - if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD)) - { - grub_free (info); - grub_errno = GRUB_ERR_NONE; - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - - if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ - | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR - && (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04 /* ABRT */)) - /* Device without ATA IDENTIFY, try ATAPI. */ - return grub_atapi_identify (dev); - - else if (sts == 0x00) - /* No device, return error but don't print message. */ - return GRUB_ERR_UNKNOWN_DEVICE; - - else - /* Other Error. */ - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, - "device cannot be identified"); - } - - grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE); - - /* Re-check status to avoid bogus identify data due to stuck DRQ. */ - grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - if (sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - { - grub_dprintf ("ata", "bad status=0x%x\n", sts); - grub_free (info); - /* No device, return error but don't print message. */ - grub_errno = GRUB_ERR_NONE; - return GRUB_ERR_UNKNOWN_DEVICE; - } - - /* Now it is certain that this is not an ATAPI device. */ - dev->atapi = 0; - - /* CHS is always supported. */ - dev->addr = GRUB_ATA_CHS; - - /* Check if LBA is supported. */ - if (info16[49] & (1 << 9)) - { - /* Check if LBA48 is supported. */ - if (info16[83] & (1 << 10)) - dev->addr = GRUB_ATA_LBA48; - else - dev->addr = GRUB_ATA_LBA; - } - - /* Determine the amount of sectors. */ - if (dev->addr != GRUB_ATA_LBA48) - dev->size = grub_le_to_cpu32(*((grub_uint32_t *) &info16[60])); - else - dev->size = grub_le_to_cpu64(*((grub_uint64_t *) &info16[100])); - - /* Read CHS information. */ - dev->cylinders = info16[1]; - dev->heads = info16[3]; - dev->sectors_per_track = info16[6]; - - grub_ata_dumpinfo (dev, info); - - grub_free(info); - - return 0; -} - -static grub_err_t -grub_ata_device_initialize (int port, int device, int addr, int addr2) -{ - struct grub_ata_device *dev; - struct grub_ata_device **devp; - - grub_dprintf ("ata", "detecting device %d,%d (0x%x, 0x%x)\n", - port, device, addr, addr2); - - dev = grub_malloc (sizeof(*dev)); - if (! dev) - return grub_errno; - - /* Setup the device information. */ - dev->port = port; - dev->device = device; - dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE; - dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE; - dev->next = NULL; - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4); - grub_ata_wait (); - - /* Try to detect if the port is in use by writing to it, - waiting for a while and reading it again. If the value - was preserved, there is a device connected. */ - grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A); - grub_ata_wait (); - grub_uint8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS); - grub_dprintf ("ata", "sectors=0x%x\n", sec); - if (sec != 0x5A) - { - grub_free(dev); - return 0; - } - - /* The above test may detect a second (slave) device - connected to a SATA controller which supports only one - (master) device. It is not safe to use the status register - READY bit to check for controller channel existence. Some - ATAPI commands (RESET, DIAGNOSTIC) may clear this bit. */ - - /* Use the IDENTIFY DEVICE command to query the device. */ - if (grub_ata_identify (dev)) - { - grub_free (dev); - return 0; - } - - /* Register the device. */ - for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next); - *devp = dev; - - return 0; -} - -static int NESTED_FUNC_ATTR -grub_ata_pciinit (grub_pci_device_t dev, - grub_pci_id_t pciid) -{ - static int compat_use[2] = { 0 }; - grub_pci_address_t addr; - grub_uint32_t class; - grub_uint32_t bar1; - grub_uint32_t bar2; - int rega; - int regb; - int i; - static int controller = 0; - int cs5536 = 0; - int nports = 2; - - /* Read class. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); - class = grub_pci_read (addr); - - /* AMD CS5536 Southbridge. */ - if (pciid == 0x208f1022) - { - cs5536 = 1; - nports = 1; - } - - /* Check if this class ID matches that of a PCI IDE Controller. */ - if (!cs5536 && (class >> 16 != 0x0101)) - return 0; - - for (i = 0; i < nports; i++) - { - /* Set to 0 when the channel operated in compatibility mode. */ - int compat; - - /* We don't support non-compatibility mode for CS5536. */ - if (cs5536) - compat = 0; - else - compat = (class >> (8 + 2 * i)) & 1; - - rega = 0; - regb = 0; - - /* If the channel is in compatibility mode, just assign the - default registers. */ - if (compat == 0 && !compat_use[i]) - { - rega = grub_ata_ioaddress[i]; - regb = grub_ata_ioaddress2[i]; - compat_use[i] = 1; - } - else if (compat) - { - /* Read the BARs, which either contain a mmapped IO address - or the IO port address. */ - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES - + sizeof (grub_uint64_t) * i); - bar1 = grub_pci_read (addr); - addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES - + sizeof (grub_uint64_t) * i - + sizeof (grub_uint32_t)); - bar2 = grub_pci_read (addr); - - /* Check if the BARs describe an IO region. */ - if ((bar1 & 1) && (bar2 & 1)) - { - rega = bar1 & ~3; - regb = bar2 & ~3; - } - } - - grub_dprintf ("ata", - "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n", - grub_pci_get_bus (dev), grub_pci_get_device (dev), - grub_pci_get_function (dev), compat, rega, regb); - - if (rega && regb) - { - grub_errno = GRUB_ERR_NONE; - grub_ata_device_initialize (controller * 2 + i, 0, rega, regb); - - /* Most errors raised by grub_ata_device_initialize() are harmless. - They just indicate this particular drive is not responding, most - likely because it doesn't exist. We might want to ignore specific - error types here, instead of printing them. */ - if (grub_errno) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - } - - grub_ata_device_initialize (controller * 2 + i, 1, rega, regb); - - /* Likewise. */ - if (grub_errno) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - } - } - } - - controller++; - - return 0; -} - -static grub_err_t -grub_ata_initialize (void) -{ - grub_pci_iterate (grub_ata_pciinit); - return 0; -} - -static void -grub_ata_setlba (struct grub_ata_device *dev, grub_disk_addr_t sector, - grub_size_t size) -{ - grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, size); - grub_ata_regset (dev, GRUB_ATA_REG_LBALOW, sector & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, (sector >> 8) & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, (sector >> 16) & 0xFF); -} - -static grub_err_t -grub_ata_setaddress (struct grub_ata_device *dev, - grub_ata_addressing_t addressing, - grub_disk_addr_t sector, - grub_size_t size) -{ - switch (addressing) - { - case GRUB_ATA_CHS: - { - unsigned int cylinder; - unsigned int head; - unsigned int sect; - - /* Calculate the sector, cylinder and head to use. */ - sect = ((grub_uint32_t) sector % dev->sectors_per_track) + 1; - cylinder = (((grub_uint32_t) sector / dev->sectors_per_track) - / dev->heads); - head = ((grub_uint32_t) sector / dev->sectors_per_track) % dev->heads; - - if (sect > dev->sectors_per_track - || cylinder > dev->cylinders - || head > dev->heads) - return grub_error (GRUB_ERR_OUT_OF_RANGE, - "sector %d cannot be addressed " - "using CHS addressing", sector); - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head); - if (grub_ata_check_ready (dev)) - return grub_errno; - - grub_ata_regset (dev, GRUB_ATA_REG_SECTNUM, sect); - grub_ata_regset (dev, GRUB_ATA_REG_CYLLSB, cylinder & 0xFF); - grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8); - - break; - } - - case GRUB_ATA_LBA: - if (size == 256) - size = 0; - grub_ata_regset (dev, GRUB_ATA_REG_DISK, - 0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F)); - if (grub_ata_check_ready (dev)) - return grub_errno; - - grub_ata_setlba (dev, sector, size); - break; - - case GRUB_ATA_LBA48: - if (size == 65536) - size = 0; - - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4)); - if (grub_ata_check_ready (dev)) - return grub_errno; - - /* Set "Previous". */ - grub_ata_setlba (dev, sector >> 24, size >> 8); - /* Set "Current". */ - grub_ata_setlba (dev, sector, size); - - break; - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf, int rw) -{ - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; - - grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", (unsigned long long) size, rw); - - grub_ata_addressing_t addressing = dev->addr; - grub_size_t batch; - int cmd, cmd_write; - - if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0) - { - batch = 65536; - cmd = GRUB_ATA_CMD_READ_SECTORS_EXT; - cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT; - } - else - { - if (addressing == GRUB_ATA_LBA48) - addressing = GRUB_ATA_LBA; - batch = 256; - cmd = GRUB_ATA_CMD_READ_SECTORS; - cmd_write = GRUB_ATA_CMD_WRITE_SECTORS; - } - - grub_size_t nsectors = 0; - while (nsectors < size) - { - if (size - nsectors < batch) - batch = size - nsectors; - - grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch); - - /* Send read/write command. */ - if (grub_ata_setaddress (dev, addressing, sector, batch)) - return grub_errno; - - grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write)); - - unsigned sect; - for (sect = 0; sect < batch; sect++) - { - /* Wait for !BSY, DRQ. */ - if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Transfer data. */ - if (! rw) - grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE); - else - grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE); - - buf += GRUB_DISK_SECTOR_SIZE; - } - - if (rw) - { - /* Check for write error. */ - if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) - & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error"); - } - - sector += batch; - nsectors += batch; - } - - return GRUB_ERR_NONE; -} - - - -static int -grub_ata_iterate (int (*hook) (const char *name)) -{ - struct grub_ata_device *dev; - - for (dev = grub_ata_devices; dev; dev = dev->next) - { - char devname[10]; - - if (dev->atapi) - continue; - - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - - if (hook (devname)) - return 1; - } - - return 0; -} - -static grub_err_t -grub_ata_open (const char *name, grub_disk_t disk) -{ - struct grub_ata_device *dev; - - for (dev = grub_ata_devices; dev; dev = dev->next) - { - char devname[10]; - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - if (grub_strcmp (name, devname) == 0) - break; - } - - if (! dev) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - - if (dev->atapi) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); - - disk->total_sectors = dev->size; - - disk->id = (unsigned long) dev; - - disk->has_partitions = 1; - disk->data = dev; - - return 0; -} - -static void -grub_ata_close (grub_disk_t disk __attribute__((unused))) -{ - -} - -static grub_err_t -grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - return grub_ata_readwrite (disk, sector, size, buf, 0); -} - -static grub_err_t -grub_ata_write (grub_disk_t disk, - grub_disk_addr_t sector, - grub_size_t size, - const char *buf) -{ - return grub_ata_readwrite (disk, sector, size, (char *) buf, 1); -} - -static struct grub_disk_dev grub_atadisk_dev = - { - .name = "ATA", - .id = GRUB_DISK_DEVICE_ATA_ID, - .iterate = grub_ata_iterate, - .open = grub_ata_open, - .close = grub_ata_close, - .read = grub_ata_read, - .write = grub_ata_write, - .next = 0 - }; - - - -/* ATAPI code. */ - -static int -grub_atapi_iterate (int (*hook) (const char *name, int luns)) -{ - struct grub_ata_device *dev; - - for (dev = grub_ata_devices; dev; dev = dev->next) - { - char devname[10]; - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - - if (! dev->atapi) - continue; - - if (hook (devname, 1)) - return 1; - } - - return 0; - -} - -static grub_err_t -grub_atapi_read (struct grub_scsi *scsi, - grub_size_t cmdsize __attribute__((unused)), - char *cmd, grub_size_t size, char *buf) -{ - struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data; - - grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size); - - if (grub_atapi_packet (dev, cmd, size)) - return grub_errno; - - grub_size_t nread = 0; - while (nread < size) - { - /* Wait for !BSY, DRQ, I/O, !C/D. */ - if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_DATA_IN, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Get byte count for this DRQ assertion. */ - unsigned cnt = grub_ata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8 - | grub_ata_regget (dev, GRUB_ATAPI_REG_CNTLOW); - grub_dprintf("ata", "DRQ count=%u\n", cnt); - - /* Count of last transfer may be uneven. */ - if (! (0 < cnt && cnt <= size - nread && (! (cnt & 1) || cnt == size - nread))) - return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count"); - - /* Read the data. */ - grub_ata_pio_read (dev, buf + nread, cnt); - - if (cnt & 1) - buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA)); - - nread += cnt; - } - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)), - grub_size_t cmdsize __attribute__((unused)), - char *cmd __attribute__((unused)), - grub_size_t size __attribute__((unused)), - char *buf __attribute__((unused))) -{ - // XXX: scsi.mod does not use write yet. - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented"); -} - -static grub_err_t -grub_atapi_open (const char *name, struct grub_scsi *scsi) -{ - struct grub_ata_device *dev; - struct grub_ata_device *devfnd = 0; - - for (dev = grub_ata_devices; dev; dev = dev->next) - { - char devname[10]; - grub_snprintf (devname, sizeof (devname), - "ata%d", dev->port * 2 + dev->device); - - if (!grub_strcmp (devname, name)) - { - devfnd = dev; - break; - } - } - - grub_dprintf ("ata", "opening ATAPI dev `%s'\n", name); - - if (! devfnd) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device"); - - scsi->data = devfnd; - - return GRUB_ERR_NONE; -} - -static void -grub_atapi_close (struct grub_scsi *scsi) -{ - grub_free (scsi->name); -} - -static struct grub_scsi_dev grub_atapi_dev = - { - .name = "ATAPI", - .iterate = grub_atapi_iterate, - .open = grub_atapi_open, - .close = grub_atapi_close, - .read = grub_atapi_read, - .write = grub_atapi_write - }; - - - -GRUB_MOD_INIT(ata) -{ - /* To prevent two drivers operating on the same disks. */ - grub_disk_firmware_is_tainted = 1; - if (grub_disk_firmware_fini) - { - grub_disk_firmware_fini (); - grub_disk_firmware_fini = NULL; - } - - /* ATA initialization. */ - grub_ata_initialize (); - - grub_disk_dev_register (&grub_atadisk_dev); - - /* ATAPI devices are handled by scsi.mod. */ - grub_scsi_dev_register (&grub_atapi_dev); -} - -GRUB_MOD_FINI(ata) -{ - grub_scsi_dev_unregister (&grub_atapi_dev); - grub_disk_dev_unregister (&grub_atadisk_dev); -} diff --git a/disk/ata_pthru.c b/disk/ata_pthru.c deleted file mode 100644 index f52725a49..000000000 --- a/disk/ata_pthru.c +++ /dev/null @@ -1,107 +0,0 @@ -/* ata_pthru.c - ATA pass through for ata.mod. */ -/* - * 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 . - */ - -#include -#include -#include -#include - - -/* ATA pass through support, used by hdparm.mod. */ -static grub_err_t -grub_ata_pass_through (grub_disk_t disk, - struct grub_disk_ata_pass_through_parms *parms) -{ - if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID) - return grub_error (GRUB_ERR_BAD_DEVICE, - "device not accessed via ata.mod"); - - struct grub_ata_device *dev = (struct grub_ata_device *) disk->data; - - if (! (parms->size == 0 || parms->size == GRUB_DISK_SECTOR_SIZE)) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "ATA multi-sector read and DATA OUT not implemented"); - - grub_dprintf ("ata", "ata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n", - parms->taskfile[GRUB_ATA_REG_CMD], - parms->taskfile[GRUB_ATA_REG_FEATURES], - parms->taskfile[GRUB_ATA_REG_SECTORS]); - grub_dprintf ("ata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n", - parms->taskfile[GRUB_ATA_REG_LBAHIGH], - parms->taskfile[GRUB_ATA_REG_LBAMID], - parms->taskfile[GRUB_ATA_REG_LBALOW], parms->size); - - /* Set registers. */ - grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4 - | (parms->taskfile[GRUB_ATA_REG_DISK] & 0xf)); - if (grub_ata_check_ready (dev)) - return grub_errno; - - int i; - for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++) - grub_ata_regset (dev, i, parms->taskfile[i]); - - /* Start command. */ - grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]); - - /* Wait for !BSY. */ - if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) - return grub_errno; - - /* Check status. */ - grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS); - grub_dprintf ("ata", "status=0x%x\n", sts); - - /* Transfer data. */ - if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ) - { - if (parms->size != GRUB_DISK_SECTOR_SIZE) - return grub_error (GRUB_ERR_READ_ERROR, "DRQ unexpected"); - grub_ata_pio_read (dev, parms->buffer, GRUB_DISK_SECTOR_SIZE); - } - - /* Return registers. */ - for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++) - parms->taskfile[i] = grub_ata_regget (dev, i); - - grub_dprintf ("ata", "status=0x%x, error=0x%x, sectors=0x%x\n", - parms->taskfile[GRUB_ATA_REG_STATUS], - parms->taskfile[GRUB_ATA_REG_ERROR], - parms->taskfile[GRUB_ATA_REG_SECTORS]); - - if (parms->taskfile[GRUB_ATA_REG_STATUS] - & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) - return grub_error (GRUB_ERR_READ_ERROR, "ATA passthrough failed"); - - return GRUB_ERR_NONE; -} - - - -GRUB_MOD_INIT(ata_pthru) -{ - /* Register ATA pass through function. */ - grub_disk_ata_pass_through = grub_ata_pass_through; -} - -GRUB_MOD_FINI(ata_pthru) -{ - if (grub_disk_ata_pass_through == grub_ata_pass_through) - grub_disk_ata_pass_through = NULL; -} diff --git a/disk/efi/efidisk.c b/disk/efi/efidisk.c deleted file mode 100644 index f9c6f3153..000000000 --- a/disk/efi/efidisk.c +++ /dev/null @@ -1,851 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct grub_efidisk_data -{ - grub_efi_handle_t handle; - grub_efi_device_path_t *device_path; - grub_efi_device_path_t *last_device_path; - grub_efi_block_io_t *block_io; - grub_efi_disk_io_t *disk_io; - struct grub_efidisk_data *next; -}; - -/* GUIDs. */ -static grub_efi_guid_t disk_io_guid = GRUB_EFI_DISK_IO_GUID; -static grub_efi_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID; - -static struct grub_efidisk_data *fd_devices; -static struct grub_efidisk_data *hd_devices; -static struct grub_efidisk_data *cd_devices; - -/* Duplicate a device path. */ -static grub_efi_device_path_t * -duplicate_device_path (const grub_efi_device_path_t *dp) -{ - grub_efi_device_path_t *p; - grub_size_t total_size = 0; - - for (p = (grub_efi_device_path_t *) dp; - ; - p = GRUB_EFI_NEXT_DEVICE_PATH (p)) - { - total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p); - if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p)) - break; - } - - p = grub_malloc (total_size); - if (! p) - return 0; - - grub_memcpy (p, dp, total_size); - return p; -} - -/* Return the device path node right before the end node. */ -static grub_efi_device_path_t * -find_last_device_path (const grub_efi_device_path_t *dp) -{ - grub_efi_device_path_t *next, *p; - - if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) - return 0; - - for (p = (grub_efi_device_path_t *) dp, next = GRUB_EFI_NEXT_DEVICE_PATH (p); - ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next); - p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next)) - ; - - return p; -} - -/* Compare device paths. */ -static int -compare_device_paths (const grub_efi_device_path_t *dp1, - const grub_efi_device_path_t *dp2) -{ - if (! dp1 || ! dp2) - /* Return non-zero. */ - return 1; - - while (1) - { - grub_efi_uint8_t type1, type2; - grub_efi_uint8_t subtype1, subtype2; - grub_efi_uint16_t len1, len2; - int ret; - - type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1); - type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2); - - if (type1 != type2) - return (int) type2 - (int) type1; - - subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1); - subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2); - - if (subtype1 != subtype2) - return (int) subtype1 - (int) subtype2; - - len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1); - len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2); - - if (len1 != len2) - return (int) len1 - (int) len2; - - ret = grub_memcmp (dp1, dp2, len1); - if (ret != 0) - return ret; - - if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1)) - break; - - dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1); - dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2); - } - - return 0; -} - -static struct grub_efidisk_data * -make_devices (void) -{ - grub_efi_uintn_t num_handles; - grub_efi_handle_t *handles; - grub_efi_handle_t *handle; - struct grub_efidisk_data *devices = 0; - - /* Find handles which support the disk io interface. */ - handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &disk_io_guid, - 0, &num_handles); - if (! handles) - return 0; - - /* Make a linked list of devices. */ - for (handle = handles; num_handles--; handle++) - { - grub_efi_device_path_t *dp; - grub_efi_device_path_t *ldp; - struct grub_efidisk_data *d; - grub_efi_block_io_t *bio; - grub_efi_disk_io_t *dio; - - dp = grub_efi_get_device_path (*handle); - if (! dp) - continue; - - ldp = find_last_device_path (dp); - if (! ldp) - /* This is empty. Why? */ - continue; - - bio = grub_efi_open_protocol (*handle, &block_io_guid, - GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - dio = grub_efi_open_protocol (*handle, &disk_io_guid, - GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (! bio || ! dio) - /* This should not happen... Why? */ - continue; - - d = grub_malloc (sizeof (*d)); - if (! d) - { - /* Uggh. */ - grub_free (handles); - return 0; - } - - d->handle = *handle; - d->device_path = dp; - d->last_device_path = ldp; - d->block_io = bio; - d->disk_io = dio; - d->next = devices; - devices = d; - } - - grub_free (handles); - - return devices; -} - -/* Find the parent device. */ -static struct grub_efidisk_data * -find_parent_device (struct grub_efidisk_data *devices, - struct grub_efidisk_data *d) -{ - grub_efi_device_path_t *dp, *ldp; - struct grub_efidisk_data *parent; - - dp = duplicate_device_path (d->device_path); - if (! dp) - return 0; - - ldp = find_last_device_path (dp); - ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; - ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; - ldp->length[0] = sizeof (*ldp); - ldp->length[1] = 0; - - for (parent = devices; parent; parent = parent->next) - { - /* Ignore itself. */ - if (parent == d) - continue; - - if (compare_device_paths (parent->device_path, dp) == 0) - { - /* Found. */ - if (! parent->last_device_path) - parent = 0; - - break; - } - } - - grub_free (dp); - return parent; -} - -static int -iterate_child_devices (struct grub_efidisk_data *devices, - struct grub_efidisk_data *d, - int (*hook) (struct grub_efidisk_data *child)) -{ - struct grub_efidisk_data *p; - - for (p = devices; p; p = p->next) - { - grub_efi_device_path_t *dp, *ldp; - - dp = duplicate_device_path (p->device_path); - if (! dp) - return 0; - - ldp = find_last_device_path (dp); - ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; - ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; - ldp->length[0] = sizeof (*ldp); - ldp->length[1] = 0; - - if (compare_device_paths (dp, d->device_path) == 0) - if (hook (p)) - { - grub_free (dp); - return 1; - } - - grub_free (dp); - } - - return 0; -} - -/* Add a device into a list of devices in an ascending order. */ -static void -add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d) -{ - struct grub_efidisk_data **p; - struct grub_efidisk_data *n; - - for (p = devices; *p; p = &((*p)->next)) - { - int ret; - - ret = compare_device_paths (find_last_device_path ((*p)->device_path), - find_last_device_path (d->device_path)); - if (ret == 0) - ret = compare_device_paths ((*p)->device_path, - d->device_path); - if (ret == 0) - return; - else if (ret > 0) - break; - } - - n = grub_malloc (sizeof (*n)); - if (! n) - return; - - grub_memcpy (n, d, sizeof (*n)); - n->next = (*p); - (*p) = n; -} - -/* Name the devices. */ -static void -name_devices (struct grub_efidisk_data *devices) -{ - struct grub_efidisk_data *d; - - /* First, identify devices by media device paths. */ - for (d = devices; d; d = d->next) - { - grub_efi_device_path_t *dp; - - dp = d->last_device_path; - if (! dp) - continue; - - if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE) - { - int is_hard_drive = 0; - - switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp)) - { - case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE: - is_hard_drive = 1; - /* Fall through by intention. */ - case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE: - { - struct grub_efidisk_data *parent; - - parent = find_parent_device (devices, d); - if (parent) - { - if (is_hard_drive) - { -#if 0 - grub_printf ("adding a hard drive by a partition: "); - grub_print_device_path (parent->device_path); -#endif - add_device (&hd_devices, parent); - } - else - { -#if 0 - grub_printf ("adding a cdrom by a partition: "); - grub_print_device_path (parent->device_path); -#endif - add_device (&cd_devices, parent); - } - - /* Mark the parent as used. */ - parent->last_device_path = 0; - } - } - /* Mark itself as used. */ - d->last_device_path = 0; - break; - - default: - /* For now, ignore the others. */ - break; - } - } - } - - /* Let's see what can be added more. */ - for (d = devices; d; d = d->next) - { - grub_efi_device_path_t *dp; - grub_efi_block_io_media_t *m; - - dp = d->last_device_path; - if (! dp) - continue; - - m = d->block_io->media; - if (m->logical_partition) - { - /* Only one partition in a non-media device. Assume that this - is a floppy drive. */ -#if 0 - grub_printf ("adding a floppy by guessing: "); - grub_print_device_path (d->device_path); -#endif - add_device (&fd_devices, d); - } - else if (m->read_only && m->block_size > GRUB_DISK_SECTOR_SIZE) - { - /* This check is too heuristic, but assume that this is a - CDROM drive. */ -#if 0 - grub_printf ("adding a cdrom by guessing: "); - grub_print_device_path (d->device_path); -#endif - add_device (&cd_devices, d); - } - else - { - /* The default is a hard drive. */ -#if 0 - grub_printf ("adding a hard drive by guessing: "); - grub_print_device_path (d->device_path); -#endif - add_device (&hd_devices, d); - } - } -} - -static void -free_devices (struct grub_efidisk_data *devices) -{ - struct grub_efidisk_data *p, *q; - - for (p = devices; p; p = q) - { - q = p->next; - grub_free (p); - } -} - -/* Enumerate all disks to name devices. */ -static void -enumerate_disks (void) -{ - struct grub_efidisk_data *devices; - - devices = make_devices (); - if (! devices) - return; - - name_devices (devices); - free_devices (devices); -} - -static int -grub_efidisk_iterate (int (*hook) (const char *name)) -{ - struct grub_efidisk_data *d; - char buf[16]; - int count; - - for (d = fd_devices, count = 0; d; d = d->next, count++) - { - grub_snprintf (buf, sizeof (buf), "fd%d", count); - grub_dprintf ("efidisk", "iterating %s\n", buf); - if (hook (buf)) - return 1; - } - - for (d = hd_devices, count = 0; d; d = d->next, count++) - { - grub_snprintf (buf, sizeof (buf), "hd%d", count); - grub_dprintf ("efidisk", "iterating %s\n", buf); - if (hook (buf)) - return 1; - } - - for (d = cd_devices, count = 0; d; d = d->next, count++) - { - grub_snprintf (buf, sizeof (buf), "cd%d", count); - grub_dprintf ("efidisk", "iterating %s\n", buf); - if (hook (buf)) - return 1; - } - - return 0; -} - -static int -get_drive_number (const char *name) -{ - unsigned long drive; - - if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd') - goto fail; - - drive = grub_strtoul (name + 2, 0, 10); - if (grub_errno != GRUB_ERR_NONE) - goto fail; - - return (int) drive ; - - fail: - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk"); - return -1; -} - -static struct grub_efidisk_data * -get_device (struct grub_efidisk_data *devices, int num) -{ - struct grub_efidisk_data *d; - - for (d = devices; d && num; d = d->next, num--) - ; - - if (num == 0) - return d; - - return 0; -} - -static grub_err_t -grub_efidisk_open (const char *name, struct grub_disk *disk) -{ - int num; - struct grub_efidisk_data *d = 0; - grub_efi_block_io_media_t *m; - - grub_dprintf ("efidisk", "opening %s\n", name); - - num = get_drive_number (name); - if (num < 0) - return grub_errno; - - switch (name[0]) - { - case 'f': - disk->has_partitions = 0; - d = get_device (fd_devices, num); - break; - case 'c': - /* FIXME: a CDROM should have partitions, but not implemented yet. */ - disk->has_partitions = 0; - d = get_device (cd_devices, num); - break; - case 'h': - disk->has_partitions = 1; - d = get_device (hd_devices, num); - break; - default: - /* Never reach here. */ - break; - } - - if (! d) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); - - disk->id = ((num << 8) | name[0]); - m = d->block_io->media; - /* FIXME: Probably it is better to store the block size in the disk, - and total sectors should be replaced with total blocks. */ - grub_dprintf ("efidisk", "m = %p, last block = %llx, block size = %x\n", - m, (unsigned long long) m->last_block, m->block_size); - disk->total_sectors = (m->last_block - * (m->block_size >> GRUB_DISK_SECTOR_BITS)); - disk->data = d; - - grub_dprintf ("efidisk", "opening %s succeeded\n", name); - - return GRUB_ERR_NONE; -} - -static void -grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused))) -{ - /* EFI disks do not allocate extra memory, so nothing to do here. */ - grub_dprintf ("efidisk", "closing %s\n", disk->name); -} - -static grub_err_t -grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - /* For now, use the disk io interface rather than the block io's. */ - struct grub_efidisk_data *d; - grub_efi_disk_io_t *dio; - grub_efi_block_io_t *bio; - grub_efi_status_t status; - - d = disk->data; - dio = d->disk_io; - bio = d->block_io; - - grub_dprintf ("efidisk", - "reading 0x%lx sectors at the sector 0x%llx from %s\n", - (unsigned long) size, (unsigned long long) sector, disk->name); - - status = efi_call_5 (dio->read, dio, bio->media->media_id, - (grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS, - (grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS, - buf); - if (status != GRUB_EFI_SUCCESS) - return grub_error (GRUB_ERR_READ_ERROR, "efidisk read error"); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector, - grub_size_t size, const char *buf) -{ - /* For now, use the disk io interface rather than the block io's. */ - struct grub_efidisk_data *d; - grub_efi_disk_io_t *dio; - grub_efi_block_io_t *bio; - grub_efi_status_t status; - - d = disk->data; - dio = d->disk_io; - bio = d->block_io; - - grub_dprintf ("efidisk", - "writing 0x%lx sectors at the sector 0x%llx to %s\n", - (unsigned long) size, (unsigned long long) sector, disk->name); - - status = efi_call_5 (dio->write, dio, bio->media->media_id, - (grub_efi_uint64_t) sector << GRUB_DISK_SECTOR_BITS, - (grub_efi_uintn_t) size << GRUB_DISK_SECTOR_BITS, - (void *) buf); - if (status != GRUB_EFI_SUCCESS) - return grub_error (GRUB_ERR_WRITE_ERROR, "efidisk write error"); - - return GRUB_ERR_NONE; -} - -static struct grub_disk_dev grub_efidisk_dev = - { - .name = "efidisk", - .id = GRUB_DISK_DEVICE_EFIDISK_ID, - .iterate = grub_efidisk_iterate, - .open = grub_efidisk_open, - .close = grub_efidisk_close, - .read = grub_efidisk_read, - .write = grub_efidisk_write, - .next = 0 - }; - -void -grub_efidisk_init (void) -{ - enumerate_disks (); - grub_disk_dev_register (&grub_efidisk_dev); -} - -void -grub_efidisk_fini (void) -{ - free_devices (fd_devices); - free_devices (hd_devices); - free_devices (cd_devices); - grub_disk_dev_unregister (&grub_efidisk_dev); -} - -/* Some utility functions to map GRUB devices with EFI devices. */ -grub_efi_handle_t -grub_efidisk_get_device_handle (grub_disk_t disk) -{ - struct grub_efidisk_data *d; - char type; - - if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID) - return 0; - - d = disk->data; - type = disk->name[0]; - - switch (type) - { - case 'f': - /* This is the simplest case. */ - return d->handle; - - case 'c': - /* FIXME: probably this is not correct. */ - return d->handle; - - case 'h': - /* If this is the whole disk, just return its own data. */ - if (! disk->partition) - return d->handle; - - /* Otherwise, we must query the corresponding device to the firmware. */ - { - struct grub_efidisk_data *devices; - grub_efi_handle_t handle = 0; - auto int find_partition (struct grub_efidisk_data *c); - - int find_partition (struct grub_efidisk_data *c) - { - grub_efi_hard_drive_device_path_t hd; - - grub_memcpy (&hd, c->last_device_path, sizeof (hd)); - - if ((GRUB_EFI_DEVICE_PATH_TYPE (c->last_device_path) - == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE) - && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c->last_device_path) - == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE) - && (grub_partition_get_start (disk->partition) - == hd.partition_start) - && (grub_partition_get_len (disk->partition) - == hd.partition_size)) - { - handle = c->handle; - return 1; - } - - return 0; - } - - devices = make_devices (); - iterate_child_devices (devices, d, find_partition); - free_devices (devices); - - if (handle != 0) - return handle; - } - break; - - default: - break; - } - - return 0; -} - -char * -grub_efidisk_get_device_name (grub_efi_handle_t *handle) -{ - grub_efi_device_path_t *dp, *ldp; - - dp = grub_efi_get_device_path (handle); - if (! dp) - return 0; - - ldp = find_last_device_path (dp); - if (! ldp) - return 0; - - if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE - && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) - == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE)) - { - /* This is a hard disk partition. */ - grub_disk_t parent = 0; - char *partition_name = 0; - char *device_name; - grub_efi_device_path_t *dup_dp, *dup_ldp; - grub_efi_hard_drive_device_path_t hd; - auto int find_parent_disk (const char *name); - auto int find_partition (grub_disk_t disk, const grub_partition_t part); - - /* Find the disk which is the parent of a given hard disk partition. */ - int find_parent_disk (const char *name) - { - grub_disk_t disk; - - disk = grub_disk_open (name); - if (! disk) - return 1; - - if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID) - { - struct grub_efidisk_data *d; - - d = disk->data; - if (compare_device_paths (d->device_path, dup_dp) == 0) - { - parent = disk; - return 1; - } - } - - grub_disk_close (disk); - return 0; - } - - /* Find the identical partition. */ - int find_partition (grub_disk_t disk __attribute__ ((unused)), - const grub_partition_t part) - { - if (grub_partition_get_start (part) == hd.partition_start - && grub_partition_get_len (part) == hd.partition_size) - { - partition_name = grub_partition_get_name (part); - return 1; - } - - return 0; - } - - /* It is necessary to duplicate the device path so that GRUB - can overwrite it. */ - dup_dp = duplicate_device_path (dp); - if (! dup_dp) - return 0; - - dup_ldp = find_last_device_path (dup_dp); - dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; - dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; - dup_ldp->length[0] = sizeof (*dup_ldp); - dup_ldp->length[1] = 0; - - grub_efidisk_iterate (find_parent_disk); - grub_free (dup_dp); - - if (! parent) - return 0; - - /* Find a partition which matches the hard drive device path. */ - grub_memcpy (&hd, ldp, sizeof (hd)); - grub_partition_iterate (parent, find_partition); - - if (! partition_name) - { - grub_disk_close (parent); - return 0; - } - - device_name = grub_xasprintf ("%s,%s", parent->name, partition_name); - grub_free (partition_name); - grub_disk_close (parent); - - return device_name; - } - else - { - /* This should be an entire disk. */ - auto int find_disk (const char *name); - char *device_name = 0; - - int find_disk (const char *name) - { - grub_disk_t disk; - - disk = grub_disk_open (name); - if (! disk) - return 1; - - if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID) - { - struct grub_efidisk_data *d; - - d = disk->data; - if (compare_device_paths (d->device_path, dp) == 0) - { - device_name = grub_strdup (disk->name); - grub_disk_close (disk); - return 1; - } - } - - grub_disk_close (disk); - return 0; - - } - - grub_efidisk_iterate (find_disk); - return device_name; - } - - return 0; -} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c deleted file mode 100644 index 94d0e3708..000000000 --- a/disk/i386/pc/biosdisk.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int cd_drive = 0; - -static int -grub_biosdisk_get_drive (const char *name) -{ - unsigned long drive; - - if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd') - goto fail; - - drive = grub_strtoul (name + 2, 0, 10); - if (grub_errno != GRUB_ERR_NONE) - goto fail; - - if (name[0] == 'h') - drive += 0x80; - - return (int) drive ; - - fail: - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk"); - return -1; -} - -static int -grub_biosdisk_call_hook (int (*hook) (const char *name), int drive) -{ - char name[10]; - - grub_snprintf (name, sizeof (name), - (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); - return hook (name); -} - -static int -grub_biosdisk_iterate (int (*hook) (const char *name)) -{ - int drive; - int num_floppies; - - /* For hard disks, attempt to read the MBR. */ - for (drive = 0x80; drive < 0x90; drive++) - { - if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1, - GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0) - { - grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive); - break; - } - - if (grub_biosdisk_call_hook (hook, drive)) - return 1; - } - - if (cd_drive) - { - if (grub_biosdisk_call_hook (hook, cd_drive)) - return 1; - } - - /* For floppy disks, we can get the number safely. */ - num_floppies = grub_biosdisk_get_num_floppies (); - for (drive = 0; drive < num_floppies; drive++) - if (grub_biosdisk_call_hook (hook, drive)) - return 1; - - return 0; -} - -static grub_err_t -grub_biosdisk_open (const char *name, grub_disk_t disk) -{ - grub_uint64_t total_sectors = 0; - int drive; - struct grub_biosdisk_data *data; - - drive = grub_biosdisk_get_drive (name); - if (drive < 0) - return grub_errno; - - disk->has_partitions = ((drive & 0x80) && (drive != cd_drive)); - disk->id = drive; - - data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data)); - if (! data) - return grub_errno; - - data->drive = drive; - - if ((cd_drive) && (drive == cd_drive)) - { - data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; - data->sectors = 32; - total_sectors = GRUB_ULONG_MAX; /* TODO: get the correct size. */ - } - else if (drive & 0x80) - { - /* HDD */ - int version; - - version = grub_biosdisk_check_int13_extensions (drive); - if (version) - { - struct grub_biosdisk_drp *drp - = (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; - - /* Clear out the DRP. */ - grub_memset (drp, 0, sizeof (*drp)); - drp->size = sizeof (*drp); - if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp)) - { - data->flags = GRUB_BIOSDISK_FLAG_LBA; - - if (drp->total_sectors) - total_sectors = drp->total_sectors; - else - /* Some buggy BIOSes doesn't return the total sectors - correctly but returns zero. So if it is zero, compute - it by C/H/S returned by the LBA BIOS call. */ - total_sectors = drp->cylinders * drp->heads * drp->sectors; - } - } - } - - if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM)) - { - if (grub_biosdisk_get_diskinfo_standard (drive, - &data->cylinders, - &data->heads, - &data->sectors) != 0) - { - if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA)) - { - data->sectors = 63; - data->heads = 255; - data->cylinders - = grub_divmod64 (total_sectors - + data->heads * data->sectors - 1, - data->heads * data->sectors, 0); - } - else - { - grub_free (data); - return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name); - } - } - - if (! total_sectors) - total_sectors = data->cylinders * data->heads * data->sectors; - } - - disk->total_sectors = total_sectors; - disk->data = data; - - return GRUB_ERR_NONE; -} - -static void -grub_biosdisk_close (grub_disk_t disk) -{ - grub_free (disk->data); -} - -/* For readability. */ -#define GRUB_BIOSDISK_READ 0 -#define GRUB_BIOSDISK_WRITE 1 - -#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3 - -static grub_err_t -grub_biosdisk_rw (int cmd, grub_disk_t disk, - grub_disk_addr_t sector, grub_size_t size, - unsigned segment) -{ - struct grub_biosdisk_data *data = disk->data; - - if (data->flags & GRUB_BIOSDISK_FLAG_LBA) - { - struct grub_biosdisk_dap *dap; - - dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR - + (data->sectors - << GRUB_DISK_SECTOR_BITS)); - dap->length = sizeof (*dap); - dap->reserved = 0; - dap->blocks = size; - dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */ - dap->block = sector; - - if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) - { - int i; - - if (cmd) - return grub_error (GRUB_ERR_WRITE_ERROR, "can\'t write to cdrom"); - - dap->blocks = ALIGN_UP (dap->blocks, 4) >> 2; - dap->block >>= 2; - - for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++) - if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap)) - break; - - if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT) - return grub_error (GRUB_ERR_READ_ERROR, "cdrom read error"); - } - else - if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap)) - { - /* Fall back to the CHS mode. */ - data->flags &= ~GRUB_BIOSDISK_FLAG_LBA; - disk->total_sectors = data->cylinders * data->heads * data->sectors; - return grub_biosdisk_rw (cmd, disk, sector, size, segment); - } - } - else - { - unsigned coff, hoff, soff; - unsigned head; - - /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with - the traditional CHS access. */ - if (sector > - 1024 /* cylinders */ * - 256 /* heads */ * - 63 /* spt */) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name); - - soff = ((grub_uint32_t) sector) % data->sectors + 1; - head = ((grub_uint32_t) sector) / data->sectors; - hoff = head % data->heads; - coff = head / data->heads; - - if (coff >= data->cylinders) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "%s out of disk", disk->name); - - if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive, - coff, hoff, soff, size, segment)) - { - switch (cmd) - { - case GRUB_BIOSDISK_READ: - return grub_error (GRUB_ERR_READ_ERROR, "%s read error", disk->name); - case GRUB_BIOSDISK_WRITE: - return grub_error (GRUB_ERR_WRITE_ERROR, "%s write error", disk->name); - } - } - } - - return GRUB_ERR_NONE; -} - -/* Return the number of sectors which can be read safely at a time. */ -static grub_size_t -get_safe_sectors (grub_disk_addr_t sector, grub_uint32_t sectors) -{ - grub_size_t size; - grub_uint32_t offset; - - /* OFFSET = SECTOR % SECTORS */ - grub_divmod64 (sector, sectors, &offset); - - size = sectors - offset; - - /* Limit the max to 0x7f because of Phoenix EDD. */ - if (size > 0x7f) - size = 0x7f; - - return size; -} - -static grub_err_t -grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - struct grub_biosdisk_data *data = disk->data; - - while (size) - { - grub_size_t len; - grub_size_t cdoff = 0; - - len = get_safe_sectors (sector, data->sectors); - - if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) - { - cdoff = (sector & 3) << GRUB_DISK_SECTOR_BITS; - len = ALIGN_UP (sector + len, 4) - (sector & ~3); - sector &= ~3; - } - - if (len > size) - len = size; - - if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len, - GRUB_MEMORY_MACHINE_SCRATCH_SEG)) - return grub_errno; - - grub_memcpy (buf, (void *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + cdoff), - len << GRUB_DISK_SECTOR_BITS); - buf += len << GRUB_DISK_SECTOR_BITS; - sector += len; - size -= len; - } - - return grub_errno; -} - -static grub_err_t -grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, const char *buf) -{ - struct grub_biosdisk_data *data = disk->data; - - if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) - return grub_error (GRUB_ERR_IO, "can't write to CDROM"); - - while (size) - { - grub_size_t len; - - len = get_safe_sectors (sector, data->sectors); - if (len > size) - len = size; - - grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf, - len << GRUB_DISK_SECTOR_BITS); - - if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len, - GRUB_MEMORY_MACHINE_SCRATCH_SEG)) - return grub_errno; - - buf += len << GRUB_DISK_SECTOR_BITS; - sector += len; - size -= len; - } - - return grub_errno; -} - -static struct grub_disk_dev grub_biosdisk_dev = - { - .name = "biosdisk", - .id = GRUB_DISK_DEVICE_BIOSDISK_ID, - .iterate = grub_biosdisk_iterate, - .open = grub_biosdisk_open, - .close = grub_biosdisk_close, - .read = grub_biosdisk_read, - .write = grub_biosdisk_write, - .next = 0 - }; - -static void -grub_disk_biosdisk_fini (void) -{ - grub_disk_dev_unregister (&grub_biosdisk_dev); -} - -GRUB_MOD_INIT(biosdisk) -{ - struct grub_biosdisk_cdrp *cdrp - = (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; - - if (grub_disk_firmware_is_tainted) - { - grub_printf ("Firmware is marked as tainted, refusing to initialize.\n"); - return; - } - grub_disk_firmware_fini = grub_disk_biosdisk_fini; - - grub_memset (cdrp, 0, sizeof (*cdrp)); - cdrp->size = sizeof (*cdrp); - cdrp->media_type = 0xFF; - if ((! grub_biosdisk_get_cdinfo_int13_extensions (grub_boot_drive, cdrp)) && - ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK) - == GRUB_BIOSDISK_CDTYPE_NO_EMUL)) - cd_drive = cdrp->drive_no; - - grub_disk_dev_register (&grub_biosdisk_dev); -} - -GRUB_MOD_FINI(biosdisk) -{ - grub_disk_biosdisk_fini (); -} diff --git a/disk/ieee1275/ofdisk.c b/disk/ieee1275/ofdisk.c deleted file mode 100644 index e5a4a67fa..000000000 --- a/disk/ieee1275/ofdisk.c +++ /dev/null @@ -1,285 +0,0 @@ -/* ofdisk.c - Open Firmware disk access. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include - -struct ofdisk_hash_ent -{ - char *devpath; - struct ofdisk_hash_ent *next; -}; - -#define OFDISK_HASH_SZ 8 -static struct ofdisk_hash_ent *ofdisk_hash[OFDISK_HASH_SZ]; - -static int -ofdisk_hash_fn (const char *devpath) -{ - int hash = 0; - while (*devpath) - hash ^= *devpath++; - return (hash & (OFDISK_HASH_SZ - 1)); -} - -static struct ofdisk_hash_ent * -ofdisk_hash_find (const char *devpath) -{ - struct ofdisk_hash_ent *p = ofdisk_hash[ofdisk_hash_fn(devpath)]; - - while (p) - { - if (!grub_strcmp (p->devpath, devpath)) - break; - p = p->next; - } - return p; -} - -static struct ofdisk_hash_ent * -ofdisk_hash_add (char *devpath) -{ - struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; - struct ofdisk_hash_ent *p = grub_malloc(sizeof (*p)); - - if (p) - { - p->devpath = devpath; - p->next = *head; - *head = p; - } - return p; -} - -static int -grub_ofdisk_iterate (int (*hook) (const char *name)) -{ - auto int dev_iterate (struct grub_ieee1275_devalias *alias); - - int dev_iterate (struct grub_ieee1275_devalias *alias) - { - int ret = 0; - - grub_dprintf ("disk", "disk name = %s\n", alias->name); - - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY)) - { - grub_ieee1275_phandle_t dev; - char tmp[8]; - - if (grub_ieee1275_finddevice (alias->path, &dev)) - { - grub_dprintf ("disk", "finddevice (%s) failed\n", alias->path); - return 0; - } - - if (grub_ieee1275_get_property (dev, "iconname", tmp, - sizeof tmp, 0)) - { - grub_dprintf ("disk", "get iconname failed\n"); - return 0; - } - - if (grub_strcmp (tmp, "sdmmc")) - { - grub_dprintf ("disk", "device is not an SD card\n"); - return 0; - } - } - - if (! grub_strcmp (alias->type, "block") && - grub_strncmp (alias->name, "cdrom", 5)) - ret = hook (alias->name); - return ret; - } - - return grub_devalias_iterate (dev_iterate); -} - -static char * -compute_dev_path (const char *name) -{ - char *devpath = grub_malloc (grub_strlen (name) + 3); - char *p, c; - - if (!devpath) - return NULL; - - /* Un-escape commas. */ - p = devpath; - while ((c = *name++) != '\0') - { - if (c == '\\' && *name == ',') - { - *p++ = ','; - name++; - } - else - *p++ = c; - } - - if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0)) - { - *p++ = ':'; - *p++ = '0'; - } - *p++ = '\0'; - - return devpath; -} - -static grub_err_t -grub_ofdisk_open (const char *name, grub_disk_t disk) -{ - grub_ieee1275_phandle_t dev; - grub_ieee1275_ihandle_t dev_ihandle = 0; - struct ofdisk_hash_ent *op; - char *devpath; - /* XXX: This should be large enough for any possible case. */ - char prop[64]; - grub_ssize_t actual; - - devpath = compute_dev_path (name); - if (! devpath) - return grub_errno; - - op = ofdisk_hash_find (devpath); - if (!op) - op = ofdisk_hash_add (devpath); - - grub_free (devpath); - if (!op) - return grub_errno; - - grub_dprintf ("disk", "Opening `%s'.\n", op->devpath); - - if (grub_ieee1275_finddevice (op->devpath, &dev)) - { - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read device properties"); - goto fail; - } - - if (grub_ieee1275_get_property (dev, "device_type", prop, sizeof (prop), - &actual)) - { - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read the device type"); - goto fail; - } - - if (grub_strcmp (prop, "block")) - { - grub_error (GRUB_ERR_BAD_DEVICE, "not a block device"); - goto fail; - } - - grub_ieee1275_open (op->devpath, &dev_ihandle); - if (! dev_ihandle) - { - grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - goto fail; - } - - grub_dprintf ("disk", "Opened `%s' as handle %p.\n", op->devpath, - (void *) (unsigned long) dev_ihandle); - - /* XXX: There is no property to read the number of blocks. There - should be a property `#blocks', but it is not there. Perhaps it - is possible to use seek for this. */ - disk->total_sectors = 0xFFFFFFFFUL; - - disk->id = (unsigned long) op; - - /* XXX: Read this, somehow. */ - disk->has_partitions = 1; - disk->data = (void *) (unsigned long) dev_ihandle; - return 0; - - fail: - if (dev_ihandle) - grub_ieee1275_close (dev_ihandle); - return grub_errno; -} - -static void -grub_ofdisk_close (grub_disk_t disk) -{ - grub_dprintf ("disk", "Closing handle %p.\n", - (void *) disk->data); - grub_ieee1275_close ((grub_ieee1275_ihandle_t) (unsigned long) disk->data); -} - -static grub_err_t -grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_ssize_t status, actual; - unsigned long long pos; - - pos = sector * 512UL; - - grub_ieee1275_seek ((grub_ieee1275_ihandle_t) (unsigned long) disk->data, - pos, &status); - if (status < 0) - return grub_error (GRUB_ERR_READ_ERROR, - "seek error, can't seek block %llu", - (long long) sector); - grub_ieee1275_read ((grub_ieee1275_ihandle_t) (unsigned long) disk->data, - buf, size * 512UL, &actual); - if (actual != (grub_ssize_t) (size * 512UL)) - return grub_error (GRUB_ERR_READ_ERROR, "read error on block: %llu", - (long long) sector); - - return 0; -} - -static grub_err_t -grub_ofdisk_write (grub_disk_t disk __attribute ((unused)), - grub_disk_addr_t sector __attribute ((unused)), - grub_size_t size __attribute ((unused)), - const char *buf __attribute ((unused))) -{ - return GRUB_ERR_NOT_IMPLEMENTED_YET; -} - -static struct grub_disk_dev grub_ofdisk_dev = - { - .name = "ofdisk", - .id = GRUB_DISK_DEVICE_OFDISK_ID, - .iterate = grub_ofdisk_iterate, - .open = grub_ofdisk_open, - .close = grub_ofdisk_close, - .read = grub_ofdisk_read, - .write = grub_ofdisk_write, - .next = 0 - }; - -void -grub_ofdisk_init (void) -{ - grub_disk_dev_register (&grub_ofdisk_dev); -} - -void -grub_ofdisk_fini (void) -{ - grub_disk_dev_unregister (&grub_ofdisk_dev); -} diff --git a/disk/lvm.c b/disk/lvm.c deleted file mode 100644 index 2c54ca3b3..000000000 --- a/disk/lvm.c +++ /dev/null @@ -1,617 +0,0 @@ -/* lvm.c - module to read Logical Volumes. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include - -static struct grub_lvm_vg *vg_list; -static int lv_count; - - -/* Go the string STR and return the number after STR. *P will point - at the number. In case STR is not found, *P will be NULL and the - return value will be 0. */ -static int -grub_lvm_getvalue (char **p, char *str) -{ - *p = grub_strstr (*p, str); - if (! *p) - return 0; - *p += grub_strlen (str); - return grub_strtoul (*p, NULL, 10); -} - -static int -grub_lvm_iterate (int (*hook) (const char *name)) -{ - struct grub_lvm_vg *vg; - for (vg = vg_list; vg; vg = vg->next) - { - struct grub_lvm_lv *lv; - if (vg->lvs) - for (lv = vg->lvs; lv; lv = lv->next) - if (hook (lv->name)) - return 1; - } - - return 0; -} - -#ifdef GRUB_UTIL -static grub_disk_memberlist_t -grub_lvm_memberlist (grub_disk_t disk) -{ - struct grub_lvm_lv *lv = disk->data; - grub_disk_memberlist_t list = NULL, tmp; - struct grub_lvm_pv *pv; - - if (lv->vg->pvs) - for (pv = lv->vg->pvs; pv; pv = pv->next) - { - tmp = grub_malloc (sizeof (*tmp)); - tmp->disk = pv->disk; - tmp->next = list; - list = tmp; - } - - return list; -} -#endif - -static grub_err_t -grub_lvm_open (const char *name, grub_disk_t disk) -{ - struct grub_lvm_vg *vg; - struct grub_lvm_lv *lv = NULL; - for (vg = vg_list; vg; vg = vg->next) - { - if (vg->lvs) - for (lv = vg->lvs; lv; lv = lv->next) - if (! grub_strcmp (lv->name, name)) - break; - - if (lv) - break; - } - - if (! lv) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown LVM device %s", name); - - disk->has_partitions = 0; - disk->id = lv->number; - disk->data = lv; - disk->total_sectors = lv->size; - - return 0; -} - -static void -grub_lvm_close (grub_disk_t disk __attribute ((unused))) -{ - return; -} - -static grub_err_t -grub_lvm_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_err_t err = 0; - struct grub_lvm_lv *lv = disk->data; - struct grub_lvm_vg *vg = lv->vg; - struct grub_lvm_segment *seg = lv->segments; - struct grub_lvm_pv *pv; - grub_uint64_t offset; - grub_uint64_t extent; - unsigned int i; - - extent = grub_divmod64 (sector, vg->extent_size, NULL); - - /* Find the right segment. */ - for (i = 0; i < lv->segment_count; i++) - { - if ((seg->start_extent <= extent) - && ((seg->start_extent + seg->extent_count) > extent)) - { - break; - } - - seg++; - } - - if (seg->stripe_count == 1) - { - /* This segment is linear, so that's easy. We just need to find - out the offset in the physical volume and read SIZE bytes - from that. */ - struct grub_lvm_stripe *stripe = seg->stripes; - grub_uint64_t seg_offset; /* Offset of the segment in PV device. */ - - pv = stripe->pv; - seg_offset = ((grub_uint64_t) stripe->start - * (grub_uint64_t) vg->extent_size) + pv->start; - - offset = sector - ((grub_uint64_t) seg->start_extent - * (grub_uint64_t) vg->extent_size) + seg_offset; - } - else - { - /* This is a striped segment. We have to find the right PV - similar to RAID0. */ - struct grub_lvm_stripe *stripe = seg->stripes; - grub_uint32_t a, b; - grub_uint64_t seg_offset; /* Offset of the segment in PV device. */ - unsigned int stripenr; - - offset = sector - ((grub_uint64_t) seg->start_extent - * (grub_uint64_t) vg->extent_size); - - a = grub_divmod64 (offset, seg->stripe_size, NULL); - grub_divmod64 (a, seg->stripe_count, &stripenr); - - a = grub_divmod64 (offset, seg->stripe_size * seg->stripe_count, NULL); - grub_divmod64 (offset, seg->stripe_size, &b); - offset = a * seg->stripe_size + b; - - stripe += stripenr; - pv = stripe->pv; - - seg_offset = ((grub_uint64_t) stripe->start - * (grub_uint64_t) vg->extent_size) + pv->start; - - offset += seg_offset; - } - - /* Check whether we actually know the physical volume we want to - read from. */ - if (pv->disk) - err = grub_disk_read (pv->disk, offset, 0, - size << GRUB_DISK_SECTOR_BITS, buf); - else - err = grub_error (GRUB_ERR_UNKNOWN_DEVICE, - "physical volume %s not found", pv->name); - - return err; -} - -static grub_err_t -grub_lvm_write (grub_disk_t disk __attribute ((unused)), - grub_disk_addr_t sector __attribute ((unused)), - grub_size_t size __attribute ((unused)), - const char *buf __attribute ((unused))) -{ - return GRUB_ERR_NOT_IMPLEMENTED_YET; -} - -static int -grub_lvm_scan_device (const char *name) -{ - grub_err_t err; - grub_disk_t disk; - grub_uint64_t da_offset, da_size, mda_offset, mda_size; - char buf[GRUB_LVM_LABEL_SIZE]; - char vg_id[GRUB_LVM_ID_STRLEN+1]; - char pv_id[GRUB_LVM_ID_STRLEN+1]; - char *metadatabuf, *p, *q, *vgname; - struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf; - struct grub_lvm_pv_header *pvh; - struct grub_lvm_disk_locn *dlocn; - struct grub_lvm_mda_header *mdah; - struct grub_lvm_raw_locn *rlocn; - unsigned int i, j, vgname_len; - struct grub_lvm_vg *vg; - struct grub_lvm_pv *pv; - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* Search for label. */ - for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++) - { - err = grub_disk_read (disk, i, 0, sizeof(buf), buf); - if (err) - goto fail; - - if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID, - sizeof (lh->id))) - && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL, - sizeof (lh->type)))) - break; - } - - /* Return if we didn't find a label. */ - if (i == GRUB_LVM_LABEL_SCAN_SECTORS) - goto fail; - - pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl)); - - for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++) - { - pv_id[j++] = pvh->pv_uuid[i]; - if ((i != 1) && (i != 29) && (i % 4 == 1)) - pv_id[j++] = '-'; - } - pv_id[j] = '\0'; - - dlocn = pvh->disk_areas_xl; - da_offset = grub_le_to_cpu64 (dlocn->offset); - da_size = grub_le_to_cpu64 (dlocn->size); - - dlocn++; - /* Is it possible to have multiple data/metadata areas? I haven't - seen devices that have it. */ - if (dlocn->offset) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "we don't support multiple LVM data areas"); - - goto fail; - } - - dlocn++; - mda_offset = grub_le_to_cpu64 (dlocn->offset); - mda_size = grub_le_to_cpu64 (dlocn->size); - - /* It's possible to have multiple copies of metadata areas, we just use the - first one. */ - - /* Allocate buffer space for the circular worst-case scenario. */ - metadatabuf = grub_malloc (2 * mda_size); - if (! metadatabuf) - goto fail; - - err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf); - if (err) - goto fail2; - - mdah = (struct grub_lvm_mda_header *) metadatabuf; - if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC, - sizeof (mdah->magic))) - || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION)) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unknown LVM metadata header"); - goto fail2; - } - - rlocn = mdah->raw_locns; - if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) > - grub_le_to_cpu64 (mdah->size)) - { - /* Metadata is circular. Copy the wrap in place. */ - grub_memcpy (metadatabuf + mda_size, - metadatabuf + GRUB_LVM_MDA_HEADER_SIZE, - grub_le_to_cpu64 (rlocn->offset) + - grub_le_to_cpu64 (rlocn->size) - - grub_le_to_cpu64 (mdah->size)); - } - p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset); - - while (*q != ' ' && q < metadatabuf + mda_size) - q++; - - if (q == metadatabuf + mda_size) - goto fail2; - - vgname_len = q - p; - vgname = grub_malloc (vgname_len + 1); - if (!vgname) - goto fail2; - - grub_memcpy (vgname, p, vgname_len); - vgname[vgname_len] = '\0'; - - p = grub_strstr (q, "id = \""); - if (p == NULL) - goto fail3; - p += sizeof ("id = \"") - 1; - grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN); - vg_id[GRUB_LVM_ID_STRLEN] = '\0'; - - for (vg = vg_list; vg; vg = vg->next) - { - if (! grub_memcmp(vg_id, vg->id, GRUB_LVM_ID_STRLEN)) - break; - } - - if (! vg) - { - /* First time we see this volume group. We've to create the - whole volume group structure. */ - vg = grub_malloc (sizeof (*vg)); - if (! vg) - goto fail3; - vg->name = vgname; - grub_memcpy (vg->id, vg_id, GRUB_LVM_ID_STRLEN+1); - - vg->extent_size = grub_lvm_getvalue (&p, "extent_size = "); - if (p == NULL) - goto fail4; - - vg->lvs = NULL; - vg->pvs = NULL; - - p = grub_strstr (p, "physical_volumes {"); - if (p) - { - p += sizeof ("physical_volumes {") - 1; - - /* Add all the pvs to the volume group. */ - while (1) - { - int s; - while (grub_isspace (*p)) - p++; - - if (*p == '}') - break; - - pv = grub_malloc (sizeof (*pv)); - q = p; - while (*q != ' ') - q++; - - s = q - p; - pv->name = grub_malloc (s + 1); - grub_memcpy (pv->name, p, s); - pv->name[s] = '\0'; - - p = grub_strstr (p, "id = \""); - if (p == NULL) - goto pvs_fail; - p += sizeof("id = \"") - 1; - - grub_memcpy (pv->id, p, GRUB_LVM_ID_STRLEN); - pv->id[GRUB_LVM_ID_STRLEN] = '\0'; - - pv->start = grub_lvm_getvalue (&p, "pe_start = "); - if (p == NULL) - goto pvs_fail; - - p = grub_strchr (p, '}'); - if (p == NULL) - goto pvs_fail; - p++; - - pv->disk = NULL; - pv->next = vg->pvs; - vg->pvs = pv; - - continue; - pvs_fail: - grub_free (pv->name); - grub_free (pv); - goto fail4; - } - } - - p = grub_strstr (p, "logical_volumes"); - if (p) - { - p += 18; - - /* And add all the lvs to the volume group. */ - while (1) - { - int s; - struct grub_lvm_lv *lv; - struct grub_lvm_segment *seg; - - while (grub_isspace (*p)) - p++; - - if (*p == '}') - break; - - lv = grub_malloc (sizeof (*lv)); - - q = p; - while (*q != ' ') - q++; - - s = q - p; - lv->name = grub_malloc (vgname_len + 1 + s + 1); - grub_memcpy (lv->name, vgname, vgname_len); - lv->name[vgname_len] = '-'; - grub_memcpy (lv->name + vgname_len + 1, p, s); - lv->name[vgname_len + 1 + s] = '\0'; - - lv->size = 0; - - lv->segment_count = grub_lvm_getvalue (&p, "segment_count = "); - if (p == NULL) - goto lvs_fail; - lv->segments = grub_malloc (sizeof (*seg) * lv->segment_count); - seg = lv->segments; - - for (i = 0; i < lv->segment_count; i++) - { - struct grub_lvm_stripe *stripe; - - p = grub_strstr (p, "segment"); - if (p == NULL) - goto lvs_segment_fail; - - seg->start_extent = grub_lvm_getvalue (&p, "start_extent = "); - if (p == NULL) - goto lvs_segment_fail; - seg->extent_count = grub_lvm_getvalue (&p, "extent_count = "); - if (p == NULL) - goto lvs_segment_fail; - seg->stripe_count = grub_lvm_getvalue (&p, "stripe_count = "); - if (p == NULL) - goto lvs_segment_fail; - - lv->size += seg->extent_count * vg->extent_size; - - if (seg->stripe_count != 1) - seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = "); - - seg->stripes = grub_malloc (sizeof (*stripe) - * seg->stripe_count); - stripe = seg->stripes; - - p = grub_strstr (p, "stripes = ["); - if (p == NULL) - goto lvs_segment_fail2; - p += sizeof("stripes = [") - 1; - - for (j = 0; j < seg->stripe_count; j++) - { - char *pvname; - - p = grub_strchr (p, '"'); - if (p == NULL) - continue; - q = ++p; - while (*q != '"') - q++; - - s = q - p; - - pvname = grub_malloc (s + 1); - if (pvname == NULL) - goto lvs_segment_fail2; - - grub_memcpy (pvname, p, s); - pvname[s] = '\0'; - - if (vg->pvs) - for (pv = vg->pvs; pv; pv = pv->next) - { - if (! grub_strcmp (pvname, pv->name)) - { - stripe->pv = pv; - break; - } - } - - grub_free(pvname); - - stripe->start = grub_lvm_getvalue (&p, ","); - if (p == NULL) - continue; - - stripe++; - } - - seg++; - - continue; - lvs_segment_fail2: - grub_free (seg->stripes); - lvs_segment_fail: - goto fail4; - } - - if (p != NULL) - p = grub_strchr (p, '}'); - if (p == NULL) - goto lvs_fail; - p += 3; - - lv->number = lv_count++; - lv->vg = vg; - lv->next = vg->lvs; - vg->lvs = lv; - - continue; - lvs_fail: - grub_free (lv->name); - grub_free (lv); - goto fail4; - } - } - - vg->next = vg_list; - vg_list = vg; - } - else - { - grub_free (vgname); - } - - /* Match the device we are currently reading from with the right - PV. */ - if (vg->pvs) - for (pv = vg->pvs; pv; pv = pv->next) - { - if (! grub_memcmp (pv->id, pv_id, GRUB_LVM_ID_STRLEN)) - { - /* This could happen to LVM on RAID, pv->disk points to the - raid device, we shouldn't change it. */ - if (! pv->disk) - pv->disk = grub_disk_open (name); - break; - } - } - - goto fail2; - - /* Failure path. */ - fail4: - grub_free (vg); - fail3: - grub_free (vgname); - - /* Normal exit path. */ - fail2: - grub_free (metadatabuf); - fail: - grub_disk_close (disk); - return 0; -} - -static struct grub_disk_dev grub_lvm_dev = - { - .name = "lvm", - .id = GRUB_DISK_DEVICE_LVM_ID, - .iterate = grub_lvm_iterate, - .open = grub_lvm_open, - .close = grub_lvm_close, - .read = grub_lvm_read, - .write = grub_lvm_write, -#ifdef GRUB_UTIL - .memberlist = grub_lvm_memberlist, -#endif - .next = 0 - }; - - -GRUB_MOD_INIT(lvm) -{ - grub_device_iterate (&grub_lvm_scan_device); - if (grub_errno) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - } - - grub_disk_dev_register (&grub_lvm_dev); -} - -GRUB_MOD_FINI(lvm) -{ - grub_disk_dev_unregister (&grub_lvm_dev); - /* FIXME: free the lvm list. */ -} diff --git a/disk/raid.c b/disk/raid.c deleted file mode 100644 index 2d544afdc..000000000 --- a/disk/raid.c +++ /dev/null @@ -1,691 +0,0 @@ -/* raid.c - module to read RAID arrays. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include - -/* Linked list of RAID arrays. */ -static struct grub_raid_array *array_list; -grub_raid5_recover_func_t grub_raid5_recover_func; -grub_raid6_recover_func_t grub_raid6_recover_func; - - -static char -grub_is_array_readable (struct grub_raid_array *array) -{ - switch (array->level) - { - case 0: - if (array->nr_devs == array->total_devs) - return 1; - break; - - case 1: - if (array->nr_devs >= 1) - return 1; - break; - - case 4: - case 5: - case 6: - case 10: - { - unsigned int n; - - if (array->level == 10) - { - n = array->layout & 0xFF; - if (n == 1) - n = (array->layout >> 8) & 0xFF; - - n--; - } - else - n = array->level / 3; - - if (array->nr_devs >= array->total_devs - n) - return 1; - - break; - } - } - - return 0; -} - -static int -grub_raid_iterate (int (*hook) (const char *name)) -{ - struct grub_raid_array *array; - - for (array = array_list; array != NULL; array = array->next) - { - if (grub_is_array_readable (array)) - if (hook (array->name)) - return 1; - } - - return 0; -} - -#ifdef GRUB_UTIL -static grub_disk_memberlist_t -grub_raid_memberlist (grub_disk_t disk) -{ - struct grub_raid_array *array = disk->data; - grub_disk_memberlist_t list = NULL, tmp; - unsigned int i; - - for (i = 0; i < array->total_devs; i++) - if (array->device[i]) - { - tmp = grub_malloc (sizeof (*tmp)); - tmp->disk = array->device[i]; - tmp->next = list; - list = tmp; - } - - return list; -} -#endif - -static grub_err_t -grub_raid_open (const char *name, grub_disk_t disk) -{ - struct grub_raid_array *array; - unsigned n; - - for (array = array_list; array != NULL; array = array->next) - { - if (!grub_strcmp (array->name, name)) - if (grub_is_array_readable (array)) - break; - } - - if (!array) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown RAID device %s", - name); - - disk->has_partitions = 1; - disk->id = array->number; - disk->data = array; - - grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name, - array->total_devs, (unsigned long long) array->disk_size); - - switch (array->level) - { - case 1: - disk->total_sectors = array->disk_size; - break; - - case 10: - n = array->layout & 0xFF; - if (n == 1) - n = (array->layout >> 8) & 0xFF; - - disk->total_sectors = grub_divmod64 (array->total_devs * - array->disk_size, - n, 0); - break; - - case 0: - case 4: - case 5: - case 6: - n = array->level / 3; - - disk->total_sectors = (array->total_devs - n) * array->disk_size; - break; - } - - grub_dprintf ("raid", "%s: level=%d, total_sectors=%lld\n", name, - array->level, (unsigned long long) disk->total_sectors); - - return 0; -} - -static void -grub_raid_close (grub_disk_t disk __attribute ((unused))) -{ - return; -} - -void -grub_raid_block_xor (char *buf1, const char *buf2, int size) -{ - grub_size_t *p1; - const grub_size_t *p2; - - p1 = (grub_size_t *) buf1; - p2 = (const grub_size_t *) buf2; - size /= GRUB_CPU_SIZEOF_VOID_P; - - while (size) - { - *(p1++) ^= *(p2++); - size--; - } -} - -static grub_err_t -grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - struct grub_raid_array *array = disk->data; - grub_err_t err = 0; - - switch (array->level) - { - case 0: - case 1: - case 10: - { - grub_disk_addr_t read_sector, far_ofs; - grub_uint32_t disknr, b, near, far, ofs; - - read_sector = grub_divmod64 (sector, array->chunk_size, &b); - far = ofs = near = 1; - far_ofs = 0; - - if (array->level == 1) - near = array->total_devs; - else if (array->level == 10) - { - near = array->layout & 0xFF; - far = (array->layout >> 8) & 0xFF; - if (array->layout >> 16) - { - ofs = far; - far_ofs = 1; - } - else - far_ofs = grub_divmod64 (array->disk_size, - far * array->chunk_size, 0); - - far_ofs *= array->chunk_size; - } - - read_sector = grub_divmod64 (read_sector * near, array->total_devs, - &disknr); - - ofs *= array->chunk_size; - read_sector *= ofs; - - while (1) - { - grub_size_t read_size; - unsigned int i, j; - - read_size = array->chunk_size - b; - if (read_size > size) - read_size = size; - - for (i = 0; i < near; i++) - { - unsigned int k; - - k = disknr; - for (j = 0; j < far; j++) - { - if (array->device[k]) - { - if (grub_errno == GRUB_ERR_READ_ERROR) - grub_errno = GRUB_ERR_NONE; - - err = grub_disk_read (array->device[k], - read_sector + j * far_ofs + b, - 0, - read_size << GRUB_DISK_SECTOR_BITS, - buf); - if (! err) - break; - else if (err != GRUB_ERR_READ_ERROR) - return err; - } - else - err = grub_error (GRUB_ERR_READ_ERROR, - "disk missing"); - - k++; - if (k == array->total_devs) - k = 0; - } - - if (! err) - break; - - disknr++; - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += ofs; - } - } - - if (err) - return err; - - buf += read_size << GRUB_DISK_SECTOR_BITS; - size -= read_size; - if (! size) - break; - - b = 0; - disknr += (near - i); - while (disknr >= array->total_devs) - { - disknr -= array->total_devs; - read_sector += ofs; - } - } - break; - } - - case 4: - case 5: - case 6: - { - grub_disk_addr_t read_sector; - grub_uint32_t b, p, n, disknr, e; - - /* n = 1 for level 4 and 5, 2 for level 6. */ - n = array->level / 3; - - /* Find the first sector to read. */ - read_sector = grub_divmod64 (sector, array->chunk_size, &b); - read_sector = grub_divmod64 (read_sector, array->total_devs - n, - &disknr); - if (array->level >= 5) - { - grub_divmod64 (read_sector, array->total_devs, &p); - - if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) - p = array->total_devs - 1 - p; - - if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) - { - disknr += p + n; - } - else - { - grub_uint32_t q; - - q = p + (n - 1); - if (q >= array->total_devs) - q -= array->total_devs; - - if (disknr >= p) - disknr += n; - else if (disknr >= q) - disknr += q + 1; - } - - if (disknr >= array->total_devs) - disknr -= array->total_devs; - } - else - p = array->total_devs - n; - - read_sector *= array->chunk_size; - - while (1) - { - grub_size_t read_size; - int next_level; - - read_size = array->chunk_size - b; - if (read_size > size) - read_size = size; - - e = 0; - if (array->device[disknr]) - { - /* Reset read error. */ - if (grub_errno == GRUB_ERR_READ_ERROR) - grub_errno = GRUB_ERR_NONE; - - err = grub_disk_read (array->device[disknr], - read_sector + b, 0, - read_size << GRUB_DISK_SECTOR_BITS, - buf); - - if ((err) && (err != GRUB_ERR_READ_ERROR)) - break; - e++; - } - else - err = GRUB_ERR_READ_ERROR; - - if (err) - { - if (array->nr_devs < array->total_devs - n + e) - break; - - grub_errno = GRUB_ERR_NONE; - if (array->level == 6) - { - err = ((grub_raid6_recover_func) ? - (*grub_raid6_recover_func) (array, disknr, p, - buf, read_sector + b, - read_size) : - grub_error (GRUB_ERR_BAD_DEVICE, - "raid6rec is not loaded")); - } - else - { - err = ((grub_raid5_recover_func) ? - (*grub_raid5_recover_func) (array, disknr, - buf, read_sector + b, - read_size) : - grub_error (GRUB_ERR_BAD_DEVICE, - "raid5rec is not loaded")); - } - - if (err) - break; - } - - buf += read_size << GRUB_DISK_SECTOR_BITS; - size -= read_size; - if (! size) - break; - - b = 0; - disknr++; - - if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) - { - if (disknr == array->total_devs) - disknr = 0; - - next_level = (disknr == p); - } - else - { - if (disknr == p) - disknr += n; - - next_level = (disknr >= array->total_devs); - } - - if (next_level) - { - read_sector += array->chunk_size; - - if (array->level >= 5) - { - if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) - p = (p == array->total_devs - 1) ? 0 : p + 1; - else - p = (p == 0) ? array->total_devs - 1 : p - 1; - - if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) - { - disknr = p + n; - if (disknr >= array->total_devs) - disknr -= array->total_devs; - } - else - { - disknr -= array->total_devs; - if (disknr == p) - disknr += n; - } - } - else - disknr = 0; - } - } - } - break; - } - - return err; -} - -static grub_err_t -grub_raid_write (grub_disk_t disk __attribute ((unused)), - grub_disk_addr_t sector __attribute ((unused)), - grub_size_t size __attribute ((unused)), - const char *buf __attribute ((unused))) -{ - return GRUB_ERR_NOT_IMPLEMENTED_YET; -} - -static grub_err_t -insert_array (grub_disk_t disk, struct grub_raid_array *new_array, - const char *scanner_name) -{ - struct grub_raid_array *array = 0, *p; - - /* See whether the device is part of an array we have already seen a - device from. */ - for (p = array_list; p != NULL; p = p->next) - if ((p->uuid_len == new_array->uuid_len) && - (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) - { - grub_free (new_array->uuid); - array = p; - - /* Do some checks before adding the device to the array. */ - - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally. */ - grub_dprintf ("raid", "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - if (array->device[new_array->index] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - new_array->number); - - if (new_array->disk_size < array->disk_size) - array->disk_size = new_array->disk_size; - break; - } - - /* Add an array to the list if we didn't find any. */ - if (!array) - { - array = grub_malloc (sizeof (*array)); - if (!array) - { - grub_free (new_array->uuid); - return grub_errno; - } - - *array = *new_array; - array->nr_devs = 0; - grub_memset (&array->device, 0, sizeof (array->device)); - - /* Check whether we don't have multiple arrays with the same number. */ - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } - - if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } - - array->name = grub_xasprintf ("md%d", array->number); - if (! array->name) - { - grub_free (array->uuid); - grub_free (array); - - return grub_errno; - } - - grub_dprintf ("raid", "Found array %s (%s)\n", array->name, - scanner_name); - - /* Add our new array to the list. */ - array->next = array_list; - array_list = array; - - /* RAID 1 doesn't use a chunksize but code assumes one so set - one. */ - if (array->level == 1) - array->chunk_size = 64; - } - - /* Add the device to the array. */ - array->device[new_array->index] = disk; - array->nr_devs++; - - return 0; -} - -static grub_raid_t grub_raid_list; - -static void -free_array (void) -{ - struct grub_raid_array *array; - - array = array_list; - while (array) - { - struct grub_raid_array *p; - int i; - - p = array; - array = array->next; - - for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) - if (p->device[i]) - grub_disk_close (p->device[i]); - - grub_free (p->uuid); - grub_free (p->name); - grub_free (p); - } - - array_list = 0; -} - -void -grub_raid_register (grub_raid_t raid) -{ - auto int hook (const char *name); - int hook (const char *name) - { - grub_disk_t disk; - struct grub_raid_array array; - - grub_dprintf ("raid", "Scanning for RAID devices on disk %s\n", name); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - if ((disk->total_sectors != GRUB_ULONG_MAX) && - (! grub_raid_list->detect (disk, &array)) && - (! insert_array (disk, &array, grub_raid_list->name))) - return 0; - - /* This error usually means it's not raid, no need to display - it. */ - if (grub_errno != GRUB_ERR_OUT_OF_RANGE) - grub_print_error (); - - grub_errno = GRUB_ERR_NONE; - - grub_disk_close (disk); - - return 0; - } - - raid->next = grub_raid_list; - grub_raid_list = raid; - grub_device_iterate (&hook); -} - -void -grub_raid_unregister (grub_raid_t raid) -{ - grub_raid_t *p, q; - - for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) - if (q == raid) - { - *p = q->next; - break; - } -} - -static struct grub_disk_dev grub_raid_dev = - { - .name = "raid", - .id = GRUB_DISK_DEVICE_RAID_ID, - .iterate = grub_raid_iterate, - .open = grub_raid_open, - .close = grub_raid_close, - .read = grub_raid_read, - .write = grub_raid_write, -#ifdef GRUB_UTIL - .memberlist = grub_raid_memberlist, -#endif - .next = 0 - }; - - -GRUB_MOD_INIT(raid) -{ - grub_disk_dev_register (&grub_raid_dev); -} - -GRUB_MOD_FINI(raid) -{ - grub_disk_dev_unregister (&grub_raid_dev); - free_array (); -} diff --git a/disk/raid6_recover.c b/disk/raid6_recover.c deleted file mode 100644 index 550968ceb..000000000 --- a/disk/raid6_recover.c +++ /dev/null @@ -1,219 +0,0 @@ -/* raid6_recover.c - module to recover from faulty RAID6 arrays. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include - -static grub_uint8_t raid6_table1[256][256]; -static grub_uint8_t raid6_table2[256][256]; - -static void -grub_raid_block_mul (grub_uint8_t mul, char *buf, int size) -{ - int i; - grub_uint8_t *p; - - p = (grub_uint8_t *) buf; - for (i = 0; i < size; i++, p++) - *p = raid6_table1[mul][*p]; -} - -static void -grub_raid6_init_table (void) -{ - int i, j; - - for (i = 0; i < 256; i++) - raid6_table1[i][1] = raid6_table1[1][i] = i; - - for (i = 2; i < 256; i++) - for (j = i; j < 256; j++) - { - int n; - grub_uint8_t c; - - n = i >> 1; - - c = raid6_table1[n][j]; - c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); - if (i & 1) - c ^= j; - - raid6_table1[j][i] = raid6_table1[i][j] = c; - } - - raid6_table2[0][0] = 1; - for (i = 1; i < 256; i++) - raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; - - for (i = 0; i < 254; i++) - for (j = 0; j < 254; j++) - { - grub_uint8_t c, n; - int k; - - if (i == j) - continue; - - k = i - j; - if (k < 0) - k += 255; - - c = n = raid6_table2[k][k] ^ 1; - for (k = 0; k < 253; k++) - c = raid6_table1[c][n]; - - raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c]; - } -} - -static grub_err_t -grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, - char *buf, grub_disk_addr_t sector, int size) -{ - int i, q, pos; - int bad1 = -1, bad2 = -1; - char *pbuf = 0, *qbuf = 0; - - size <<= GRUB_DISK_SECTOR_BITS; - pbuf = grub_zalloc (size); - if (!pbuf) - goto quit; - - qbuf = grub_zalloc (size); - if (!qbuf) - goto quit; - - q = p + 1; - if (q == (int) array->total_devs) - q = 0; - - pos = q + 1; - if (pos == (int) array->total_devs) - pos = 0; - - for (i = 0; i < (int) array->total_devs - 2; i++) - { - if (pos == disknr) - bad1 = i; - else - { - if ((array->device[pos]) && - (! grub_disk_read (array->device[pos], sector, 0, size, buf))) - { - grub_raid_block_xor (pbuf, buf, size); - grub_raid_block_mul (raid6_table2[i][i], buf, size); - grub_raid_block_xor (qbuf, buf, size); - } - else - { - /* Too many bad devices */ - if (bad2 >= 0) - goto quit; - - bad2 = i; - grub_errno = GRUB_ERR_NONE; - } - } - - pos++; - if (pos == (int) array->total_devs) - pos = 0; - } - - /* Invalid disknr or p */ - if (bad1 < 0) - goto quit; - - if (bad2 < 0) - { - /* One bad device */ - if ((array->device[p]) && - (! grub_disk_read (array->device[p], sector, 0, size, buf))) - { - grub_raid_block_xor (buf, pbuf, size); - goto quit; - } - - if (! array->device[q]) - { - grub_error (GRUB_ERR_READ_ERROR, "not enough disk to restore"); - goto quit; - } - - grub_errno = GRUB_ERR_NONE; - if (grub_disk_read (array->device[q], sector, 0, size, buf)) - goto quit; - - grub_raid_block_xor (buf, qbuf, size); - grub_raid_block_mul (raid6_table2[255 - bad1][255 - bad1], buf, - size); - } - else - { - /* Two bad devices */ - grub_uint8_t c; - - if ((! array->device[p]) || (! array->device[q])) - { - grub_error (GRUB_ERR_READ_ERROR, "not enough disk to restore"); - goto quit; - } - - if (grub_disk_read (array->device[p], sector, 0, size, buf)) - goto quit; - - grub_raid_block_xor (pbuf, buf, size); - - if (grub_disk_read (array->device[q], sector, 0, size, buf)) - goto quit; - - grub_raid_block_xor (qbuf, buf, size); - - c = raid6_table2[bad2][bad1]; - grub_raid_block_mul (c, qbuf, size); - - c = raid6_table1[raid6_table2[bad2][bad2]][c]; - grub_raid_block_mul (c, pbuf, size); - - grub_raid_block_xor (pbuf, qbuf, size); - grub_memcpy (buf, pbuf, size); - } - -quit: - grub_free (pbuf); - grub_free (qbuf); - - return grub_errno; -} - -GRUB_MOD_INIT(raid6rec) -{ - grub_raid6_init_table (); - grub_raid6_recover_func = grub_raid6_recover; -} - -GRUB_MOD_FINI(raid6rec) -{ - grub_raid6_recover_func = 0; -} diff --git a/disk/scsi.c b/disk/scsi.c deleted file mode 100644 index eba237287..000000000 --- a/disk/scsi.c +++ /dev/null @@ -1,407 +0,0 @@ -/* scsi.c - scsi support. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -static grub_scsi_dev_t grub_scsi_dev_list; - -void -grub_scsi_dev_register (grub_scsi_dev_t dev) -{ - dev->next = grub_scsi_dev_list; - grub_scsi_dev_list = dev; -} - -void -grub_scsi_dev_unregister (grub_scsi_dev_t dev) -{ - grub_scsi_dev_t *p, q; - - for (p = &grub_scsi_dev_list, q = *p; q; p = &(q->next), q = q->next) - if (q == dev) - { - *p = q->next; - break; - } -} - - -/* Determine the the device is removable and the type of the device - SCSI. */ -static grub_err_t -grub_scsi_inquiry (grub_scsi_t scsi) -{ - struct grub_scsi_inquiry iq; - struct grub_scsi_inquiry_data iqd; - grub_err_t err; - - iq.opcode = grub_scsi_cmd_inquiry; - iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - iq.reserved = 0; - iq.alloc_length = 0x24; /* XXX: Hardcoded for now */ - iq.reserved2 = 0; - - err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq, - sizeof (iqd), (char *) &iqd); - if (err) - return err; - - scsi->devtype = iqd.devtype & GRUB_SCSI_DEVTYPE_MASK; - scsi->removable = iqd.rmb >> GRUB_SCSI_REMOVABLE_BIT; - - return GRUB_ERR_NONE; -} - -/* Read the capacity and block size of SCSI. */ -static grub_err_t -grub_scsi_read_capacity (grub_scsi_t scsi) -{ - struct grub_scsi_read_capacity rc; - struct grub_scsi_read_capacity_data rcd; - grub_err_t err; - - rc.opcode = grub_scsi_cmd_read_capacity; - rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - grub_memset (rc.reserved, 0, sizeof (rc.reserved)); - - err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, - sizeof (rcd), (char *) &rcd); - if (err) - return err; - - scsi->size = grub_be_to_cpu32 (rcd.size); - scsi->blocksize = grub_be_to_cpu32 (rcd.blocksize); - - return GRUB_ERR_NONE; -} - -/* Send a SCSI request for DISK: read SIZE sectors starting with - sector SECTOR to BUF. */ -static grub_err_t -grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_scsi_t scsi; - struct grub_scsi_read10 rd; - - scsi = disk->data; - - rd.opcode = grub_scsi_cmd_read10; - rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - rd.lba = grub_cpu_to_be32 (sector); - rd.reserved = 0; - rd.size = grub_cpu_to_be16 (size); - rd.reserved2 = 0; - rd.pad = 0; - - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); -} - -/* Send a SCSI request for DISK: read SIZE sectors starting with - sector SECTOR to BUF. */ -static grub_err_t -grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_scsi_t scsi; - struct grub_scsi_read12 rd; - - scsi = disk->data; - - rd.opcode = grub_scsi_cmd_read12; - rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - rd.lba = grub_cpu_to_be32 (sector); - rd.size = grub_cpu_to_be32 (size); - rd.reserved = 0; - rd.control = 0; - - return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); -} - -#if 0 -/* Send a SCSI request for DISK: write the data stored in BUF to SIZE - sectors starting with SECTOR. */ -static grub_err_t -grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_scsi_t scsi; - struct grub_scsi_write10 wr; - - scsi = disk->data; - - wr.opcode = grub_scsi_cmd_write10; - wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - wr.lba = grub_cpu_to_be32 (sector); - wr.reserved = 0; - wr.size = grub_cpu_to_be16 (size); - wr.reserved2 = 0; - wr.pad = 0; - - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); -} - -/* Send a SCSI request for DISK: write the data stored in BUF to SIZE - sectors starting with SECTOR. */ -static grub_err_t -grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_scsi_t scsi; - struct grub_scsi_write10 wr; - - scsi = disk->data; - - wr.opcode = grub_scsi_cmd_write12; - wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - wr.lba = grub_cpu_to_be32 (sector); - wr.size = grub_cpu_to_be32 (size); - wr.reserved = 0; - wr.pad = 0; - - return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); -} -#endif - - -static int -grub_scsi_iterate (int (*hook) (const char *name)) -{ - grub_scsi_dev_t p; - - auto int scsi_iterate (const char *name, int luns); - - int scsi_iterate (const char *name, int luns) - { - int i; - - /* In case of a single LUN, just return `usbX'. */ - if (luns == 1) - return hook (name); - - /* In case of multiple LUNs, every LUN will get a prefix to - distinguish it. */ - for (i = 0; i < luns; i++) - { - char *sname; - int ret; - sname = grub_xasprintf ("%s%c", name, 'a' + i); - if (!sname) - return 1; - ret = hook (sname); - grub_free (sname); - if (ret) - return 1; - } - return 0; - } - - for (p = grub_scsi_dev_list; p; p = p->next) - if (p->iterate && (p->iterate) (scsi_iterate)) - return 1; - - return 0; -} - -static grub_err_t -grub_scsi_open (const char *name, grub_disk_t disk) -{ - grub_scsi_dev_t p; - grub_scsi_t scsi; - grub_err_t err; - int len; - int lun; - - scsi = grub_malloc (sizeof (*scsi)); - if (! scsi) - return grub_errno; - - len = grub_strlen (name); - lun = name[len - 1] - 'a'; - - /* Try to detect a LUN ('a'-'z'), otherwise just use the first - LUN. */ - if (lun < 0 || lun > 26) - lun = 0; - - for (p = grub_scsi_dev_list; p; p = p->next) - { - if (p->open (name, scsi)) - continue; - - disk->id = (unsigned long) "scsi"; /* XXX */ - disk->data = scsi; - scsi->dev = p; - scsi->lun = lun; - scsi->name = grub_strdup (name); - if (! scsi->name) - { - grub_free (scsi); - return grub_errno; - } - - grub_dprintf ("scsi", "dev opened\n"); - - err = grub_scsi_inquiry (scsi); - if (err) - { - grub_free (scsi); - grub_dprintf ("scsi", "inquiry failed\n"); - return err; - } - - grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n", - scsi->devtype, scsi->removable); - - /* Try to be conservative about the device types - supported. */ - if (scsi->devtype != grub_scsi_devtype_direct - && scsi->devtype != grub_scsi_devtype_cdrom) - { - grub_free (scsi); - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, - "unknown SCSI device"); - } - - if (scsi->devtype == grub_scsi_devtype_cdrom) - disk->has_partitions = 0; - else - disk->has_partitions = 1; - - err = grub_scsi_read_capacity (scsi); - if (err) - { - grub_free (scsi); - grub_dprintf ("scsi", "READ CAPACITY failed\n"); - return err; - } - - /* SCSI blocks can be something else than 512, although GRUB - wants 512 byte blocks. */ - disk->total_sectors = ((scsi->size * scsi->blocksize) - << GRUB_DISK_SECTOR_BITS); - - grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n", - (unsigned long long) disk->total_sectors, - scsi->blocksize); - - return GRUB_ERR_NONE; - } - - grub_free (scsi); - - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); -} - -static void -grub_scsi_close (grub_disk_t disk) -{ - grub_scsi_t scsi; - - scsi = disk->data; - scsi->dev->close (scsi); - grub_free (scsi); -} - -static grub_err_t -grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) -{ - grub_scsi_t scsi; - - scsi = disk->data; - - /* SCSI sectors are variable in size. GRUB uses 512 byte - sectors. */ - if (scsi->blocksize != GRUB_DISK_SECTOR_SIZE) - { - unsigned spb = scsi->blocksize >> GRUB_DISK_SECTOR_BITS; - if (! (spb != 0 && (scsi->blocksize & GRUB_DISK_SECTOR_SIZE) == 0)) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported SCSI block size"); - - grub_uint32_t sector_mod = 0; - sector = grub_divmod64 (sector, spb, §or_mod); - - if (! (sector_mod == 0 && size % spb == 0)) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unaligned SCSI read not supported"); - - size /= spb; - } - - /* Depending on the type, select a read function. */ - switch (scsi->devtype) - { - case grub_scsi_devtype_direct: - return grub_scsi_read10 (disk, sector, size, buf); - - case grub_scsi_devtype_cdrom: - return grub_scsi_read12 (disk, sector, size, buf); - } - - /* XXX: Never reached. */ - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_scsi_write (grub_disk_t disk __attribute((unused)), - grub_disk_addr_t sector __attribute((unused)), - grub_size_t size __attribute((unused)), - const char *buf __attribute((unused))) -{ -#if 0 - /* XXX: Not tested yet! */ - - /* XXX: This should depend on the device type? */ - return grub_scsi_write10 (disk, sector, size, buf); -#endif - return GRUB_ERR_NOT_IMPLEMENTED_YET; -} - - -static struct grub_disk_dev grub_scsi_dev = - { - .name = "scsi", - .id = GRUB_DISK_DEVICE_SCSI_ID, - .iterate = grub_scsi_iterate, - .open = grub_scsi_open, - .close = grub_scsi_close, - .read = grub_scsi_read, - .write = grub_scsi_write, - .next = 0 - }; - -GRUB_MOD_INIT(scsi) -{ - grub_disk_dev_register (&grub_scsi_dev); -} - -GRUB_MOD_FINI(scsi) -{ - grub_disk_dev_unregister (&grub_scsi_dev); -} diff --git a/disk/usbms.c b/disk/usbms.c deleted file mode 100644 index 8554b224f..000000000 --- a/disk/usbms.c +++ /dev/null @@ -1,394 +0,0 @@ -/* usbms.c - USB Mass Storage 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 . - */ - -#include -#include -#include -#include -#include -#include - -#define GRUB_USBMS_DIRECTION_BIT 7 - -/* The USB Mass Storage Command Block Wrapper. */ -struct grub_usbms_cbw -{ - grub_uint32_t signature; - grub_uint32_t tag; - grub_uint32_t transfer_length; - grub_uint8_t flags; - grub_uint8_t lun; - grub_uint8_t length; - grub_uint8_t cbwcb[16]; -} __attribute__ ((packed)); - -struct grub_usbms_csw -{ - grub_uint32_t signature; - grub_uint32_t tag; - grub_uint32_t residue; - grub_uint8_t status; -} __attribute__ ((packed)); - -struct grub_usbms_dev -{ - struct grub_usb_device *dev; - - int luns; - - int interface; - struct grub_usb_desc_endp *in; - struct grub_usb_desc_endp *out; - - int in_maxsz; - int out_maxsz; - - struct grub_usbms_dev *next; -}; -typedef struct grub_usbms_dev *grub_usbms_dev_t; - -static grub_usbms_dev_t grub_usbms_dev_list; - -static int devcnt; - -static grub_err_t -grub_usbms_reset (grub_usb_device_t dev, int interface) -{ - return grub_usb_control_msg (dev, 0x21, 255, 0, interface, 0, 0); -} - -static void -grub_usbms_finddevs (void) -{ - auto int usb_iterate (grub_usb_device_t dev); - - int usb_iterate (grub_usb_device_t usbdev) - { - grub_usb_err_t err; - struct grub_usb_desc_device *descdev = &usbdev->descdev; - int i; - - if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0) - return 0; - - /* XXX: Just check configuration 0 for now. */ - for (i = 0; i < usbdev->config[0].descconf->numif; i++) - { - struct grub_usbms_dev *usbms; - struct grub_usb_desc_if *interf; - int j; - grub_uint8_t luns; - - interf = usbdev->config[0].interf[i].descif; - - /* If this is not a USB Mass Storage device with a supported - protocol, just skip it. */ - if (interf->class != GRUB_USB_CLASS_MASS_STORAGE - || interf->subclass != GRUB_USBMS_SUBCLASS_BULK - || interf->protocol != GRUB_USBMS_PROTOCOL_BULK) - { - continue; - } - - devcnt++; - usbms = grub_zalloc (sizeof (struct grub_usbms_dev)); - if (! usbms) - return 1; - - usbms->dev = usbdev; - usbms->interface = i; - - /* Iterate over all endpoints of this interface, at least a - IN and OUT bulk endpoint are required. */ - for (j = 0; j < interf->endpointcnt; j++) - { - struct grub_usb_desc_endp *endp; - endp = &usbdev->config[0].interf[i].descendp[j]; - - if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk IN endpoint. */ - usbms->in = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); - usbms->in_maxsz = endp->maxpacket; - } - else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) - { - /* Bulk OUT endpoint. */ - usbms->out = endp; - grub_usb_clear_halt (usbdev, endp->endp_addr & 128); - usbms->out_maxsz = endp->maxpacket; - } - } - - if (!usbms->in || !usbms->out) - { - grub_free (usbms); - return 0; - } - - /* Query the amount of LUNs. */ - err = grub_usb_control_msg (usbdev, 0xA1, 254, - 0, i, 1, (char *) &luns); - if (err) - { - /* In case of a stall, clear the stall. */ - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3); - grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3); - } - - /* Just set the amount of LUNs to one. */ - grub_errno = GRUB_ERR_NONE; - usbms->luns = 1; - } - else - usbms->luns = luns; - - /* XXX: Check the magic values, does this really make - sense? */ - grub_usb_control_msg (usbdev, (1 << 6) | 1, 255, - 0, i, 0, 0); - - /* XXX: To make Qemu work? */ - if (usbms->luns == 0) - usbms->luns = 1; - - usbms->next = grub_usbms_dev_list; - grub_usbms_dev_list = usbms; - - /* XXX: Activate the first configuration. */ - grub_usb_set_configuration (usbdev, 1); - - /* Bulk-Only Mass Storage Reset, after the reset commands - will be accepted. */ - grub_usbms_reset (usbdev, i); - - return 0; - } - - return 0; - } - - grub_usb_iterate (usb_iterate); -} - - - -static int -grub_usbms_iterate (int (*hook) (const char *name, int luns)) -{ - grub_usbms_dev_t p; - int cnt = 0; - - for (p = grub_usbms_dev_list; p; p = p->next) - { - char *devname; - devname = grub_xasprintf ("usb%d", cnt); - - if (hook (devname, p->luns)) - { - grub_free (devname); - return 1; - } - grub_free (devname); - cnt++; - } - - return 0; -} - -static grub_err_t -grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, - grub_size_t size, char *buf, int read_write) -{ - struct grub_usbms_cbw cbw; - grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; - struct grub_usbms_csw status; - static grub_uint32_t tag = 0; - grub_usb_err_t err = GRUB_USB_ERR_NONE; - int retrycnt = 3 + 1; - - retry: - retrycnt--; - if (retrycnt == 0) - return grub_error (GRUB_ERR_IO, "USB Mass Storage stalled"); - - /* Setup the request. */ - grub_memset (&cbw, 0, sizeof (cbw)); - cbw.signature = grub_cpu_to_le32 (0x43425355); - cbw.tag = tag++; - cbw.transfer_length = grub_cpu_to_le32 (size); - cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT; - cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; - cbw.length = cmdsize; - grub_memcpy (cbw.cbwcb, cmd, cmdsize); - - /* Write the request. */ - err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, - sizeof (cbw), (char *) &cbw); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed"); - } - - /* Read/write the data. */ - if (read_write == 0) - { - err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf); - grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read from USB Mass Storage device"); - } - } - else - { - err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf); - grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_WRITE_ERROR, - "can't write to USB Mass Storage device"); - } - } - - /* Read the status. */ - err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, - sizeof (status), (char *) &status); - if (err) - { - if (err == GRUB_USB_ERR_STALL) - { - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - goto retry; - } - return grub_error (GRUB_ERR_READ_ERROR, - "can't read status from USB Mass Storage device"); - } - - /* XXX: Magic and check this code. */ - if (status.status == 2) - { - /* XXX: Phase error, reset device. */ - grub_usbms_reset (dev->dev, dev->interface); - grub_usb_clear_halt (dev->dev, dev->in->endp_addr); - grub_usb_clear_halt (dev->dev, dev->out->endp_addr); - - goto retry; - } - - if (status.status) - return grub_error (GRUB_ERR_READ_ERROR, - "error communication with USB Mass Storage device"); - - return GRUB_ERR_NONE; -} - - -static grub_err_t -grub_usbms_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, - grub_size_t size, char *buf) -{ - return grub_usbms_transfer (scsi, cmdsize, cmd, size, buf, 0); -} - -static grub_err_t -grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, - grub_size_t size, char *buf) -{ - return grub_usbms_transfer (scsi, cmdsize, cmd, size, buf, 1); -} - -static grub_err_t -grub_usbms_open (const char *name, struct grub_scsi *scsi) -{ - grub_usbms_dev_t p; - int devnum; - int i = 0; - - if (grub_strncmp (name, "usb", 3)) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, - "not a USB Mass Storage device"); - - devnum = grub_strtoul (name + 3, NULL, 10); - for (p = grub_usbms_dev_list; p; p = p->next) - { - /* Check if this is the devnumth device. */ - if (devnum == i) - { - scsi->data = p; - scsi->name = grub_strdup (name); - scsi->luns = p->luns; - if (! scsi->name) - return grub_errno; - - return GRUB_ERR_NONE; - } - - i++; - } - - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, - "not a USB Mass Storage device"); -} - -static void -grub_usbms_close (struct grub_scsi *scsi) -{ - grub_free (scsi->name); -} - -static struct grub_scsi_dev grub_usbms_dev = - { - .name = "usb", - .iterate = grub_usbms_iterate, - .open = grub_usbms_open, - .close = grub_usbms_close, - .read = grub_usbms_read, - .write = grub_usbms_write - }; - -GRUB_MOD_INIT(usbms) -{ - grub_usbms_finddevs (); - grub_scsi_dev_register (&grub_usbms_dev); -} - -GRUB_MOD_FINI(usbms) -{ - grub_scsi_dev_unregister (&grub_usbms_dev); -} diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 000000000..93eb39627 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,9 @@ +AUTOMAKE_OPTIONS = subdir-objects + +# AM_MAKEINFOFLAGS = --no-split --no-validate +info_TEXINFOS = grub.texi grub-dev.texi +grub_TEXINFOS = fdl.texi + +EXTRA_DIST = font_char_metrics.png font_char_metrics.txt + + diff --git a/docs/autoiso.cfg b/docs/autoiso.cfg new file mode 100644 index 000000000..9ce51c692 --- /dev/null +++ b/docs/autoiso.cfg @@ -0,0 +1,244 @@ +# Sample GRUB script to autodetect operating systems +# +# Copyright (C) 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 . + +function pathname { regexp -s 2:"$2" '^(\(.*\))?(/.*)$' "$1"; } +function devname { regexp -s "$2" '^(\(.*\)).*$' "$1"; } + +function loopback_iso_entry { + realdev="$1" + isopath="$2" + loopdev="$3" + + if test -f /boot/grub/loopback.cfg; then + cfgpath=/boot/grub/loopback.cfg + elif test -f /grub/loopback.cfg; then + cfgpath=/grub/loopback.cfg + else + return 1; + fi + + echo loopback.cfg $isopath: yes + menuentry "Boot GRUB Loopback Config from ${realdev}${isopath}" "$realdev" "$isopath" "$cfgpath" { + set device="$2" + set iso_path="$3" + set cfg_path="$4" + + export iso_path + loopback loopdev_cfg "${device}${iso_path}" + set root=(loopdev_cfg) + configfile $cfg_path + loopback -d loopdev_cfg + } + return 0 +} + +function grml_iso_entry { + realdev="$1" + isopath="$2" + loopdev="$3" + + result=1 + for dir in /boot/grml /boot/grmlsmall /boot/grmlmedium; do + if ! test -f ${dir}/linux26 -a -f ${dir}/initrd.gz; then continue; fi + + echo grml $isopath: yes + result=0 + menuentry "GRML Linux from ${realdev}${isopath}" \ + "$realdev" "$isopath" "$dir" { + set device="$2" + set isopath="$3" + set grmldir="$4" + + loopback loopdev_grml "${device}${isopath}" + set root=(loopdev_grml) + linux $grmldir/linux26 findiso="$isopath" apm=power-off quiet \ + boot=live nomce + initrd $grmldir/initrd.gz + loopback -d loopdev_grml + } + done + return $result +} + +function pmagic_iso_entry { + realdev="$1" + isopath="$2" + loopdev="$3" + + if ! test -f /pmagic/bzImage -a -f /pmagic/initramfs; then return 1; fi + + echo pmagic $isopath: yes + menuentry "Parted Magic from ${realdev}${isopath}" "$realdev" "$isopath" { + set device="$2" + set isopath="$3" + + loopback loopdev_pmagic "${device}${isopath}" + set root=(loopdev_pmagic) + linux /pmagic/bzImage iso_filename="$isopath" edd=off noapic \ + load_ramdisk=1 prompt_ramdisk=0 rw sleep=10 loglevel=0 \ + keymap=$langcode + initrd /pmagic/initramfs + loopback -d loopdev_pmagic + } + return 0 +} + +function sidux_iso_entry { + realdev="$1" + isopath="$2" + loopdev="$3" + + result=1 + for kernel in /boot/vmlinuz-*-sidux-*; do + if ! test -f "$kernel"; then continue; fi + regexp -s 1:v1 -s 2:v2 '/boot/vmlinuz-(.*)-sidux-(.*)' "$kernel" + + initrd="/boot/initrd.img-$v1-sidux-$v2" + if ! test -f "$initrd"; then continue; fi + + result=0 + echo sidux $isopath: yes + menuentry "Sidux vmlinux-$v1-sidux-$v2 from ${realdev}${isopath}" "$realdev" "$isopath" "$kernel" "$initrd" { + set device="$2" + set isopath="$3" + set kernel="$4" + set initrd="$5" + + loopback loopdev_sidux "${device}${isopath}" + set root=(loopdev_sidux) + linux $kernel fromiso=$isopath boot=fll quiet + initrd $initrd + loopback -d loopdev_sidux + } + done + return $result +} + +function slax_iso_entry { + realdev="$1" + isopath="$2" + loopdev="$3" + + if ! test -f /boot/vmlinuz -a -f /boot/initrd.gz; then return 1; fi + + echo slax $isopath: yes + menuentry "Slax Linux from ${realdev}${isopath}" "$realdev" "$isopath" { + set device="$2" + set isopath="$3" + + loopback loopdev_slax "${device}${isopath}" + set root=(loopdev_slax) + linux /boot/vmlinuz from=$isopath ramdisk_size=6666 root=/dev/ram0 rw + initrd /boot/initrd.gz + loopback -d loopdev_slax + } + return 0 +} + +function tinycore_iso_entry { + realpath="$1" + isopath="$2" + loopdev="$3" + + if ! test -f /boot/bzImage -a -f /boot/tinycore.gz; then return 1; fi + + echo tinycore $isopath: yes + menuentry "Tinycore Linux from ${realdev}${isopath}" "$realdev" "$isopath" { + set device="$2" + set isopath="$3" + + loopback loopdev_tiny "${device}${isopath}" + set root=(loopdev_tiny) + linux /boot/bzImage + initrd /boot/tinycore.gz + loopback -d loopdev_tiny + } + return 0 +} + +function casper_iso_entry { + realpath="$1" + isopath="$2" + loopdev="$3" + + if ! test -f /casper/vmlinuz; then return 1; fi + initrd= + for f in /casper/initrd.*z; do + if ! test -f "$f"; then continue; fi + pathname "$f" initrd + done + if test -z "$initrd"; then return 1; fi + + echo casper $isopath: yes + menuentry "Casper based Linux from ${realdev}${isopath}" "$realdev" "$isopath" "$initrd" { + set device="$2" + set isopath="$3" + set initrd="$4" + + loopback loopdev_casper "${device}${isopath}" + set root=(loopdev_casper) + linux /casper/vmlinuz boot=casper iso-scan/filename="$isopath" quiet splash noprompt keyb="$langcode" \ + debian-installer/language="$langcode" console-setup/layoutcode?="$langcode" -- + initrd $initrd + loopback -d loopdev_casper + } + return 0 +} + +function scan_isos { + isodirs="$1" + + for dev in (*); do + for dir in $isodirs; do + for file in ${dev}${dir}/*.iso ${dev}${dir}/*.ISO; do + if ! test -f "$file"; then continue; fi + + pathname $file isopath + if test -z "$dev" -o -z "$isopath"; then continue; fi + + if ! loopback loopdev_scan "$file"; then continue; fi + saved_root=$root + set root=(loopdev_scan) + + if loopback_iso_entry $dev $isopath (loopdev_scan); then true; + elif grml_iso_entry $dev $isopath (loopdev_scan); then true; + elif pmagic_iso_entry $dev $isopath (loopdev_scan); then true; + elif sidux_iso_entry $dev $isopath (loopdev_scan); then true; + elif slax_iso_entry $dev $isopath (loopdev_scan); then true; + elif tinycore_iso_entry $dev $isopath (loopdev_scan); then true; + elif casper_iso_entry $dev $isopath (loopdev_scan); then true; + else true; fi + + set root=$saved_root + loopback -d loopdev_scan + done + done + done + return 0 +} + +# XXX Remove later +insmod serial +serial +terminal_output --append serial +# terminal_input --append serial + +langcode="$lang" + +insmod regexp +scan_isos /iso /boot/iso + diff --git a/docs/font_char_metrics.png b/docs/font_char_metrics.png new file mode 100644 index 000000000..8d10d1f64 Binary files /dev/null and b/docs/font_char_metrics.png differ diff --git a/docs/font_char_metrics.txt b/docs/font_char_metrics.txt new file mode 100644 index 000000000..92f1371fd --- /dev/null +++ b/docs/font_char_metrics.txt @@ -0,0 +1 @@ +Please fill this in. diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi new file mode 100644 index 000000000..f4367f895 --- /dev/null +++ b/docs/grub-dev.texi @@ -0,0 +1,2448 @@ +\input texinfo +@c -*-texinfo-*- +@c %**start of header +@setfilename grub-dev.info +@include version-dev.texi +@settitle GNU GRUB Developers Manual @value{VERSION} +@c Unify all our little indices for now. +@syncodeindex fn cp +@syncodeindex vr cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp +@c %**end of header + +@footnotestyle separate +@paragraphindent 3 +@finalout + +@copying +This developer manual is for GNU GRUB (version @value{VERSION}, +@value{UPDATED}). + +Copyright @copyright{} 1999,2000,2001,2002,2004,2005,2006,2008,2009,2010,2011 Free Software Foundation, Inc. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 or +any later version published by the Free Software Foundation; with no +Invariant Sections. +@end quotation +@end copying + +@dircategory Kernel +@direntry +* grub-dev: (grub-dev). The GRand Unified Bootloader Dev +@end direntry + +@setchapternewpage odd + +@titlepage +@sp 10 +@title the GNU GRUB developer manual +@subtitle The GRand Unified Bootloader, version @value{VERSION}, @value{UPDATED}. +@author Yoshinori K. Okuji +@author Colin D Bennett +@author Vesa Jääskeläinen +@author Colin Watson +@author Robert Millan +@author Carles Pina +@c The following two commands start the copyright page. +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@c Output the table of contents at the beginning. +@contents + +@finalout +@headings double + +@ifnottex +@node Top +@top GNU GRUB developer manual + +This is the developer documentation of GNU GRUB, the GRand Unified Bootloader, +a flexible and powerful boot loader program for a wide range of +architectures. + +This edition documents version @value{VERSION}. + +@insertcopying +@end ifnottex + +@menu +* Getting the source code:: +* Coding style:: +* Finding your way around:: +* Contributing Changes:: +* Setting up and running test suite:: +* Updating External Code:: +* Debugging:: +* Porting:: +* Error Handling:: +* Stack and heap size:: +* BIOS port memory map:: +* Video Subsystem:: +* PFF2 Font File Format:: +* Graphical Menu Software Design:: +* Verifiers framework:: +* Lockdown framework:: +* Copying This Manual:: Copying This Manual +* Index:: +@end menu + + +@node Getting the source code +@chapter Getting the source code + +GRUB is maintained using the @uref{https://git-scm.com/book/en/v2, +GIT revision control system}. To fetch: + +@example +git clone git://git.sv.gnu.org/grub.git +@end example + +Web access is available under +@example +http://git.savannah.gnu.org/cgit/grub.git/ +@end example + +The branches available are: + +@table @samp +@item master + Main development branch. +@item grub-legacy + GRUB 0.97 codebase. Kept for reference and legal reasons +@item multiboot + Multiboot specfication +@item multiboot2 + Multiboot2 specfication +@item developer branches + Prefixed with developer name. Every developer of a team manages his own branches. + Developer branches do not need changelog entries. +@end table + +Once you have used @kbd{git clone} to fetch an initial copy of a branch, you +can use @kbd{git pull} to keep it up to date. If you have modified your +local version, you may need to resolve conflicts when pulling. + +@node Coding style +@chapter Coding style +@c By YoshinoriOkuji, VesaJääskeläinen and ColinBennett + +Basically we follow the @uref{http://www.gnu.org/prep/standards_toc.html, GNU Coding Standards}. We define additional conventions for GRUB here. + +@menu +* Naming Conventions:: +* Functions:: +* Variables:: +* Types:: +* Macros:: +* Comments:: +* Multi-Line Comments:: +@end menu + +@node Naming Conventions +@section Naming Conventions + +All global symbols (i.e. functions, variables, types, and macros) must have the prefix grub_ or GRUB_. The all capital form is used only by macros. + +@node Functions +@section Functions + +If a function is global, its name must be prefixed with grub_ and must consist of only small letters. If the function belongs to a specific function module, the name must also be prefixed with the module name. For example, if a function is for file systems, its name is prefixed with grub_fs_. If a function is for FAT file system but not for all file systems, its name is prefixed with grub_fs_fat_. The hierarchy is noted this way. + +After a prefix, a function name must start with a verb (such as get or is). It must not be a noun. Some kind of abbreviation is permitted, as long as it wouldn't make code less readable (e.g. init). + +If a function is local, its name may not start with any prefix. It must start with a verb. + +@node Variables +@section Variables + +The rule is mostly the same as functions, as noted above. If a variable is global, its name must be prefixed with grub_ and must consist of only small letters. If the variable belongs to a specific function module, the name must also be prefixed with the module name. For example, if a function is for dynamic loading, its name is prefixed with grub_dl_. If a variable is for ELF but not for all dynamic loading systems, its name is prefixed with grub_dl_elf_. + +After a prefix, a variable name must start with a noun or an adjective (such as name or long) and it should end with a noun. Some kind of abbreviation is permitted, as long as it wouldn't make code less readable (e.g. i18n). + +If a variable is global in the scope of a single file (i.e. it is declared with static), its name may not start with any prefix. It must start with a noun or an adjective. + +If a variable is local, you may choose any shorter name, as long as it wouldn't make code less readable (e.g. i). + +@node Types +@section Types + +The name of a type must be prefixed with grub_ and must consist of only small letters. If the type belongs to a specific function module, the name must also be prefixed with the module name. For example, if a type is for OS loaders, its name is prefixed with grub_loader_. If a type is for Multiboot but not for all OS loaders, its name is prefixed with grub_loader_linux_. + +The name must be suffixed with _t, to emphasize the fact that it is a type but not a variable or a function. + +@node Macros +@section Macros + +If a macro is global, its name must be prefixed with GRUB_ and must consist of only large letters. Other rules are the same as functions or variables, depending on whether a macro is used like a function or a variable. + +@node Comments +@section Comments + +All comments shall be C-style comments, of the form @samp{/* @dots{} */}. +A comment can be placed immediately preceding the entity it describes or it +can be placed together with code, variable declarations, or other non-comment +entities. However, it is recommended to not mix various forms especially in +types/structs descriptions. + +Acceptable: +@example +/* The page # that is the front buffer. */ +int displayed_page; +@end example + +@example +int render_page; /* The page # that is the back buffer. */ +@end example + +@node Multi-Line Comments +@section Multi-Line Comments + +Comments spanning multiple lines shall be formatted with all lines after the +first aligned with the first line. Asterisk characters should be repeated at +the start of each subsequent line. + +Acceptable: +@example +/* + * This is a comment + * which spans multiple lines. + * It is long. + */ +@end example + +Unacceptable: +@example +/* This is a comment + which spans multiple lines. + It is long. */ +@end example + +@example +/* + * This is a comment + * which spans multiple lines. + * It is long. */ +@end example + +@example +/* This is a comment + * which spans multiple lines. + * It is long. + */ +@end example + +In particular first unacceptable form makes comment difficult to distinguish +from the code itself. Especially if it contains the code snippets and/or is +long. So, its usage is disallowed. + +@node Finding your way around +@chapter Finding your way around + +Here is a brief map of the GRUB code base. + +GRUB uses Autoconf and Automake, with most of the Automake input generated +by a Python script. The top-level build rules are in @file{configure.ac}, +@file{grub-core/Makefile.core.def}, and @file{Makefile.util.def}. Each +block in a @file{*.def} file represents a build target, and specifies the +source files used to build it on various platforms. The @file{*.def} files +are processed into Automake input by @file{gentpl.py} (which you only need +to look at if you are extending the build system). If you are adding a new +module which follows an existing pattern, such as a new command or a new +filesystem implementation, it is usually easiest to grep +@file{grub-core/Makefile.core.def} and @file{Makefile.util.def} for an +existing example of that pattern to find out where it should be added. + +In general, code that may be run at boot time is in a subdirectory of +@file{grub-core}, while code that is only run from within a full operating +system is in a subdirectory of the top level. + +Low-level boot code, such as the MBR implementation on PC BIOS systems, is +in the @file{grub-core/boot/} directory. + +The GRUB kernel is in @file{grub-core/kern/}. This contains core facilities +such as the device, disk, and file frameworks, environment variable +handling, list processing, and so on. The kernel should contain enough to +get up to a rescue prompt. Header files for kernel facilities, among +others, are in @file{include/}. + +Terminal implementations are in @file{grub-core/term/}. + +Disk access code is spread across @file{grub-core/disk/} (for accessing the +disk devices themselves), @file{grub-core/partmap/} (for interpreting +partition table data), and @file{grub-core/fs/} (for accessing filesystems). +Note that, with the odd specialised exception, GRUB only contains code to +@emph{read} from filesystems and tries to avoid containing any code to +@emph{write} to filesystems; this lets us confidently assure users that GRUB +cannot be responsible for filesystem corruption. + +PCI and USB bus handling is in @file{grub-core/bus/}. + +Video handling code is in @file{grub-core/video/}. The graphical menu +system uses this heavily, but is in a separate directory, +@file{grub-core/gfxmenu/}. + +Most commands are implemented by files in @file{grub-core/commands/}, with +the following exceptions: + +@itemize +@item +A few core commands live in @file{grub-core/kern/corecmd.c}. + +@item +Commands related to normal mode live under @file{grub-core/normal/}. + +@item +Commands that load and boot kernels live under @file{grub-core/loader/}. + +@item +The @samp{loopback} command is really a disk device, and so lives in +@file{grub-core/disk/loopback.c}. + +@item +The @samp{gettext} command lives under @file{grub-core/gettext/}. + +@item +The @samp{loadfont} and @samp{lsfonts} commands live under +@file{grub-core/font/}. + +@item +The @samp{serial}, @samp{terminfo}, and @samp{background_image} commands +live under @file{grub-core/term/}. + +@item +The @samp{efiemu_*} commands live under @file{grub-core/efiemu/}. + +@item +OS-dependent code should be under @file{grub-core/osdep/} + +@item +Utility programs meant to be run from a full operating system +(except OS-dependent code mentioned previously) are in @file{util/}. + +@end itemize + +There are a few other special-purpose exceptions; grep for them if they +matter to you. + +@node Contributing Changes +@chapter Contributing changes +@c By YoshinoriOkuji, VesaJääskeläinen, ColinWatson + +Contributing changes to GRUB 2 is welcomed activity. However we have a +bit of control what kind of changes will be accepted to GRUB 2. +Therefore it is important to discuss your changes on grub-devel mailing list +(see MailingLists). On this page there are some basic details on the +development process and activities. + +First of all you should come up with the idea yourself what you want to +contribute. If you do not have that beforehand you are advised to study this +manual and try GRUB 2 out to see what you think is missing from there. + +Here are additional pointers: +@itemize +@item @uref{https://savannah.gnu.org/task/?group=grub, GRUB's Task Tracker} +@item @uref{https://savannah.gnu.org/bugs/?group=grub, GRUB's Bug Tracker} +@end itemize + +If you intended to make changes to GRUB Legacy (<=0.97) those are not accepted +anymore. + +@menu +* Getting started:: +* Typical Developer Experience:: +* When you are approved for write access to project's files:: +@end menu + +@node Getting started +@section Getting started + +@itemize +@item Always use latest GRUB 2 source code. So get that first. + +For developers it is recommended always to use the newest development version of GRUB 2. If development takes a long period of time, please remember to keep in sync with newest developments regularly so it is much easier to integrate your change in the future. GRUB 2 is being developed in a GIT repository. + +Please check Savannah's GRUB project page for details how to get newest git: +@uref{https://savannah.gnu.org/git/?group=grub, GRUB 2 git Repository} + +@item Compile it and try it out. + +It is always good idea to first see that things work somehow and after that +to start to implement new features or develop fixes to bugs. + +@item Study the code. + +There are sometimes odd ways to do things in GRUB 2 code base. +This is mainly related to limited environment where GRUB 2 is being executed. +You usually do not need to understand it all so it is better to only try to +look at places that relates to your work. Please do not hesitate to ask for +help if there is something that you do not understand. + +@item Develop a new feature. + +Now that you know what to do and how it should work in GRUB 2 code base, please +be free to develop it. If you have not so far announced your idea on grub-devel +mailing list, please do it now. This is to make sure you are not wasting your +time working on the solution that will not be integrated to GRUB 2 code base. + +You might want to study our coding style before starting development so you +do not need to change much of the code when your patch is being reviewed. +(see @ref{Coding style}) + +For every accepted patch there has to exist a ChangeLog entry. Our ChangeLog +consist of changes within source code and are not describing about what the +change logically does. Please see examples from previous entries. + +Also remember that GRUB 2 is licensed under GPLv3 license and that usually +means that you are not allowed to copy pieces of code from other projects. +Even if the source project's license would be compatible with GPLv3, please +discuss it beforehand on grub-devel mailing list. + +@item Test your change. + +Test that your change works properly. Try it out a couple of times, preferably on different systems, and try to find problems with it. + +@item Publish your change. + +When you are happy with your change, first make sure it is compilable with +latest development version of GRUB 2. After that please send a patch to +grub-devel for review. Please describe in your email why you made the change, +what it changes and so on. Please be prepared to receive even discouraging +comments about your patch. There is usually at least something that needs +to be improved in every patch. + +Please use unified diff to make your patch (good match of arguments for diff is @samp{-pruN}). + +@item Respond to received feedback. + +If you are asked to modify your patch, please do that and resubmit it for +review. If your change is large you are required to submit a copyright +agreement to FSF. Please keep in mind that if you are asked to submit +for copyright agreement, process can take some time and is mandatory +in order to get your changes integrated. + +If you are not on grub-devel to respond to questions, most likely your patch +will not be accepted. Also if problems arise from your changes later on, +it would be preferable that you also fix the problem. So stay around +for a while. + +@item Your patch is accepted. + +Good job! Your patch will now be integrated into GRUB 2 mainline, and if it didn't break anything it will be publicly available in the next release. + +Now you are welcome to do further improvements :) +@end itemize + +@node Typical Developer Experience +@section Typical Developer Experience + +The typical experience for a developer in this project is the following: + +@enumerate +@item You find yourself wanting to do something (e.g. fixing a bug). +@item You show some result in the mailing list or the IRC. +@item You are getting to be known to other developers. +@item You accumulate significant amount of contribution, so copyright assignment is processed. +@item You are free to check in your changes on your own, legally speaking. +@end enumerate + +At this point, it is rather annoying that you ought to ask somebody else every +change to be checked in. For efficiency, it is far better, if you can commit +it yourself. Therefore, our policy is to give you the write permission to our +official repository, once you have shown your skill and will, +and the FSF clerks have dealt with your copyright assignment. + +@node When you are approved for write access to project's files +@section When you are approved for write access to project's files + +As you might know, GRUB is hosted on +@uref{https://savannah.gnu.org/projects/grub, Savannah}, thus the membership +is managed by Savannah. This means that, if you want to be a member of this +project: + +@enumerate +@item You need to create your own account on Savannah. +@item You can submit ``Request for Inclusion'' from ``My Groups'' on Savannah. +@end enumerate + +Then, one of the admins can approve your request, and you will be a member. +If you don't want to use the Savannah interface to submit a request, you can +simply notify the admins by email or something else, alternatively. But you +still need to create an account beforehand. + +NOTE: we sometimes receive a ``Request for Inclusion'' from an unknown person. +In this case, the request would be just discarded, since it is too dangerous +to allow a stranger to be a member, which automatically gives him a commit +right to the repository, both for a legal reason and for a technical reason. + +If your intention is to just get started, please do not submit a inclusion +request. Instead, please subscribe to the mailing list, and communicate first +(e.g. sending a patch, asking a question, commenting on another message...). + +@node Setting up and running test suite +@chapter Setting up and running test suite + +GRUB is basically a tiny operating system with read support for many file +systems and which has been ported to a variety of architectures. As such, its +test suite has quite a few dependencies required to fully run the suite. +These dependencies are currently documented in the +@uref{https://git.savannah.gnu.org/cgit/grub.git/tree/INSTALL, INSTALL} +file in the source repository. Once installed, the test suite can be started +by running the @command{make check} command from the GRUB build directory. + +@node Updating External Code +@chapter Updating external code + +GRUB includes some code from other projects, and it is sometimes necessary +to update it. + +@menu +* Gnulib:: +* jsmn:: +* minilzo:: +* libtasn1:: +@end menu + +@node Gnulib +@section Gnulib + +Gnulib is a source code library that provides basic functionality to +programs and libraries. Many software packages make use of Gnulib +to avoid reinventing the portability wheel. + +GRUB imports Gnulib using its @command{bootstrap} utility, identifying a +particular Git commit in @file{bootstrap.conf}. To upgrade to a new Gnulib +commit, set @code{GNULIB_REVISION} in @file{bootstrap.conf} to the new commit +ID, then run @kbd{./bootstrap} and whatever else you need to make sure it +works. Check for changes to Gnulib's @file{NEWS} file between the old and new +commits; in some cases it will be necessary to adjust GRUB to match. You may +also need to update the patches in @file{grub-core/lib/gnulib-patches/}. + +To add a new Gnulib module or remove one that is no longer needed, change +@code{gnulib_modules} in @file{bootstrap.conf}. Again, run @kbd{./bootstrap} +and whatever else you need to make sure it works. + +Bootstrapping from an older distribution containing gettext version < 0.18.3, +will require a patch similar to this to be applied first before running the +@command{./bootstrap} utility: + +@example +diff --git a/bootstrap.conf b/bootstrap.conf +index 988dda0..a3193a9 100644 +--- a/bootstrap.conf ++++ b/bootstrap.conf +@@ -67,7 +67,7 @@ SKIP_PO=t +buildreq="\ +autoconf 2.63 +automake 1.11 +-gettext 0.18.3 ++gettext 0.17 +git 1.5.5 +tar - +" +diff --git a/configure.ac b/configure.ac +index 08b518f..99f5b36 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -362,7 +362,7 @@ AC_CHECK_PROG(HAVE_CXX, $CXX, yes, no) + +AC_GNU_SOURCE +AM_GNU_GETTEXT([external]) +-AM_GNU_GETTEXT_VERSION([0.18.3]) ++AM_GNU_GETTEXT_VERSION([0.17]) +AC_SYS_LARGEFILE + +# Identify characteristics of the host architecture. + +@end example + +It will also be necessary to adjust the patches in +@file{po/gettext-patches/} to apply to an older version of gettext. + +@node jsmn +@section jsmn + +jsmn is a minimalistic JSON parser which is implemented in a single header file +@file{jsmn.h}. To import a different version of the jsmn parser, you may simply +download the @file{jsmn.h} header from the desired tag or commit to the target +directory: + +@example +curl -L https://raw.githubusercontent.com/zserge/jsmn/v1.1.0/jsmn.h \ + -o grub-core/lib/json/jsmn.h +@end example + +@node minilzo +@section minilzo + +miniLZO is a very lightweight subset of the LZO library intended for easy +inclusion in other projects. It is generated automatically from the LZO +source code and contains the most important LZO functions. + +To upgrade to a new version of the miniLZO library, download the release +tarball and copy the files into the target directory: + +@example +curl -L -O https://www.oberhumer.com/opensource/lzo/download/minilzo-2.10.tar.gz +tar -zxf minilzo-2.10.tar.gz +rm minilzo-2.10/testmini.c +rm -r grub-core/lib/minilzo/* +cp minilzo-2.10/*.[hc] grub-core/lib/minilzo +rm -r minilzo-2.10* +@end example + +@node libtasn1 +@section libtasn1 + +libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as +specified by the X.680 ITU-T recommendation) parsing and structures management, +and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding +functions. + +To upgrade to a new version of the libtasn1 library, download the release +tarball and copy the files into the target directory: + +@example +curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz +tar xvzf libtasn1-4.19.0.tar.gz +rm -rf grub-core/lib/libtasn1 +mkdir -p grub-core/lib/libtasn1/lib +mkdir -p grub-core/lib/libtasn1/tests +cp libtasn1-4.19.0/@{README.md,COPYING@} grub-core/lib/libtasn1 +cp libtasn1-4.19.0/lib/@{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@} grub-core/lib/libtasn1/lib +cp libtasn1-4.19.0/lib/includes/libtasn1.h grub-core/lib/libtasn1 +cp libtasn1-4.19.0/tests/@{CVE-2018-1000654-1_asn1_tab.h,CVE-2018-1000654-2_asn1_tab.h,CVE-2018-1000654.c,object-id-decoding.c,object-id-encoding.c,octet-string.c,reproducers.c,Test_overflow.c,Test_simple.c,Test_strings.c@} grub-core/lib/libtasn1/tests +rm -rf libtasn1-4.19.0* +@end example + +After upgrading the library, it may be necessary to apply the patches in +@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with +GRUB. These patches were needed to use the current version of libtasn1. The +existing patches may not apply cleanly, apply at all, or even be needed for a +newer version of the library, and other patches may be needed due to changes in +the newer version. If existing patches need to be refreshed to apply cleanly, +please include updated patches as part of the a patch set sent to the list. +If new patches are needed or existing patches are not needed, also please send +additions or removals as part of any patch set upgrading libtasn1. + +@node Debugging +@chapter Debugging + +GRUB2 can be difficult to debug because it runs on the bare-metal and thus +does not have the debugging facilities normally provided by an operating +system. This chapter aims to provide useful information on some ways to +debug GRUB2 for some architectures. It by no means intends to be exhaustive. +The focus will be one x86_64 and i386 architectures. Luckily for some issues +virtual machines have made the ability to debug GRUB2 much easier, and this +chapter will focus debugging via the QEMU virtual machine. We will not be +going over debugging of the userland tools (eg. grub-install), there are +many tutorials on debugging programs in userland. + +You will need GDB and the QEMU binaries for your system, on Debian these +can be installed with the @samp{gdb} and @samp{qemu-system-x86} packages. +Also it is assumed that you have already successfully compiled GRUB2 from +source for the target specified in the section below and have some +familiarity with GDB. When GRUB2 is built it will create many different +binaries. The ones of concern will be in the @file{grub-core} +directory of the GRUB2 build dir. To aide in debugging we will want the +debugging symbols generated during the build because these symbols are not +kept in the binaries which get installed to the boot location. The build +process outputs two sets of binaries, one without symbols which gets executed +at boot, and another set of ELF images with debugging symbols. The built +images with debugging symbols will have a @file{.image} suffix, and the ones +without a @file{.img} suffix. Similarly, loadable modules with debugging +symbols will have a @file{.module} suffix, and ones without a @file{.mod} +suffix. In the case of the kernel the binary with symbols is named +@file{kernel.exec}. + +In the following sections, information will be provided on debugging on +various targets using @command{gdb} and the @samp{gdb_grub} GDB script. + +@menu +* i386-pc:: +* x86_64-efi:: +@end menu + +@node i386-pc +@section i386-pc + +The i386-pc target is a good place to start when first debugging GRUB2 +because in some respects it's easier than EFI platforms. The reason being +that the initial load address is always known in advance. To start +debugging GRUB2 first QEMU must be started in GDB stub mode. The following +command is a simple illustration: + +@example +qemu-system-i386 -drive file=disk.img,format=raw \ + -device virtio-scsi-pci,id=scsi0 -S -s +@end example + +This will start a QEMU instance booting from @file{disk.img}. It will pause +at start waiting for a GDB instance to attach to it. You should change +@file{disk.img} to something more appropriate. A block device can be used, +but you may need to run QEMU as a privileged user. + +To connect to this QEMU instance with GDB, the @code{target remote} GDB +command must be used. We also need to load a binary image, preferably with +symbols. This can be done using the GDB command @code{file kernel.exec}, if +GDB is started from the @file{grub-core} directory in the GRUB2 build +directory. GRUB2 developers have made this more simple by including a GDB +script which does much of the setup. This file is at @file{grub-core/gdb_grub} +in the build directory and is also installed via @command{make install}. +When using a pre-built GRUB, the distribution may have a package which installs +this GDB script along with debug symbol binaries, such as Debian's +@samp{grub-pc-dbg} package. The GDB script is intended to be used +like so, assuming that @samp{/path/to/script} is the path to the directory +containing the gdb_grub script and debug symbol files: + +@example +cd $(dirname /path/to/script/gdb_grub) +gdb -x gdb_grub +@end example + +Once GDB has been started with the @file{gdb_grub} script it will +automatically connect to the QEMU instance. You can then do things you +normally would in GDB like set a break point on @var{grub_main}. + +Setting breakpoints in modules is trickier since they haven't been loaded +yet and are loaded at addresses determined at runtime. The module could be +loaded to different addresses in different QEMU instances. The debug symbols +in the modules @file{.module} binary, thus are always wrong, and GDB needs +to be told where to load the symbols to. But this must happen at runtime +after GRUB2 has determined where the module will get loaded. Luckily the +@file{gdb_grub} script takes care of this with the @command{runtime_load_module} +command, which configures GDB to watch for GRUB2 module loading and when +it does add the module symbols with the appropriate offset. + +@node x86_64-efi +@section x86_64-efi + +Using GDB to debug GRUB2 for the x86_64-efi target has some similarities with +the i386-pc target. Please read and familiarize yourself with the @ref{i386-pc} +section when reading this one. Extra care must be used to run QEMU such that it +boots a UEFI firmware. This usually involves either using the @samp{-bios} +option with a UEFI firmware blob (eg. @file{OVMF.fd}) or loading the firmware +via pflash. This document will not go further into how to do this as there are +ample resource on the web. + +Like all EFI implementations, on x86_64-efi the (U)EFI firmware that loads +the GRUB2 EFI application determines at runtime where the application will +be loaded. This means that we do not know where to tell GDB to load the +symbols for the GRUB2 core until the (U)EFI firmware determines it. There are +two good ways of figuring this out when running in QEMU: use a @ref{OVMF debug log, +debug build of OVMF} and check the debug log, or have GRUB2 say where it is +loaded. Neither of these are ideal because they both generally give the +information after GRUB2 is already running, which makes debugging early boot +infeasible. Technically, the first method does give the load address before +GRUB2 is run, but without debugging the EFI firmware with symbols, the author +currently does not know how to cause the OVMF firmware to pause at that point +to use the load address before GRUB2 is run. + +Even after getting the application load address, the loading of core symbols +is complicated by the fact that the debugging symbols for the kernel are in +an ELF binary named @file{kernel.exec} while what is in memory are sections +for the PE32+ EFI binary. When @command{grub-mkimage} creates the PE32+ +binary it condenses several segments from the ELF kernel binary into one +.data section in the PE32+ binary. This must be taken into account to +properly load the other non-text sections. Otherwise, GDB will work as +expected when breaking on functions, but, for instance, global variables +will point to the wrong address in memory and thus give incorrect values +(which can be difficult to debug). + +Calculating the correct offsets for sections is taken care of automatically +when loading the kernel symbols via the user-defined GDB command +@command{dynamic_load_kernel_exec_symbols}, which takes one argument, the +address where the text section is loaded as determined by one of the methods +above. Alternatively, the command @command{dynamic_load_symbols} with the text +section address as an agrument can be called to load the kernel symbols and set +up loading the module symbols as they are loaded at runtime. + +In the author's experience, when debugging with QEMU and OVMF, to have +debugging symbols loaded at the start of GRUB2 execution the GRUB2 EFI +application must be run via QEMU at least once prior in order to get the +load address. Two methods for obtaining the load address are described in +two subsections below. Generally speaking, the load address does not change +between QEMU runs. There are exceptions to this, namely that different +GRUB2 EFI applications can be run at different addresses. Also, it has been +observed that after running the EFI application for the first time, the +second run will sometimes have a different load address, but subsequent +runs of the same EFI application will have the same load address as the +second run. And it's a near certainty that if the GRUB EFI binary has changed, +eg. been recompiled, the load address will also be different. + +This ability to predict what the load address will be allows one to assume +the load address on subsequent runs and thus load the symbols before GRUB2 +starts. The following command illustrates this, assuming that QEMU is +running and waiting for a debugger connection and the current working +directory is where @file{gdb_grub} resides: + +@example +gdb -x gdb_grub -ex 'dynamic_load_symbols @var{address of .text section}' +@end example + +If you load the symbols in this manner and, after continuing execution, do +not see output showing the module symbols loading, then it is very likely +that the load address was incorrect. + +Another thing to be aware of is how the loading of the GRUB image by the +firmware affects previously set software breakpoints. On x86 platforms, +software breakpoints are implemented by GDB by writing a special processor +instruction at the location of the desired breakpoint. This special instruction +when executed will stop the program execution and hand control to the +debugger, GDB. GDB will first save the instruction bytes that are +overwritten at the breakpoint and will put them back when the breakpoint +is hit. If GRUB is being run for the first time in QEMU, the firmware will +be loading the GRUB image into memory where every byte is already set to 0. +This means that if a breakpoint is set before GRUB is loaded, GDB will save +the 0-byte(s) where the the special instruction will go. Then when the firmware +loads the GRUB image and because it is unaware of the debugger, it will +write the GRUB image to memory, overwriting anything that was there previously --- +notably in this case the instruction that implements the software breakpoint. +This will be confusing for the person using GDB because GDB will show the +breakpoint as set, but the brekapoint will never be hit. Furthermore, GDB +then becomes confused, such that even deleting an recreating the breakpoint +will not create usable breakpoints. The @file{gdb_grub} script takes care of +this by saving the breakpoints just before they are overwritten, and then +restores them at the start of GRUB execution. So breakpoints for GRUB can be +set before GRUB is loaded, but be mindful of this effect if you are confused +as to why breakpoints are not getting hit. + +Also note, that hardware breakpoints do not suffer this problem. They are +implemented by having the breakpoint address in special debug registers on +the CPU. So they can always be set freely without regard to whether GRUB has +been loaded or not. The reason that hardware breakpoints aren't always used +is because there are a limited number of them, usually around 4 on various +CPUs, and specifically exactly 4 for x86 CPUs. The @file{gdb_grub} script goes +out of its way to avoid using hardware breakpoints internally and uses them as +briefly as possible when needed, thus allowing the user to have a maximal +number at their disposal. + +@menu +* OVMF debug log:: +* Using the gdbinfo command:: +@end menu + +@node OVMF debug log +@subsection OVMF debug log + +In order to get the GRUB2 load address from OVMF, first, a debug build +of OVMF must be obtained (@uref{https://github.com/retrage/edk2-nightly/raw/master/bin/DEBUGX64_OVMF.fd, +here is one} which is not officially recommended). OVMF will output debug +messages to a special serial device, which we must add to QEMU. The following +QEMU command will run the debug OVMF and write the debug messages to a +file named @file{debug.log}. It is assumed that @file{disk.img} is a disk +image or block device that is set up to boot GRUB2 EFI. + +@example +qemu-system-x86_64 -bios /path/to/debug/OVMF.fd \ + -drive file=disk.img,format=raw \ + -device virtio-scsi-pci,id=scsi0 \ + -debugcon file:debug.log -global isa-debugcon.iobase=0x402 +@end example + +If GRUB2 was started by the (U)EFI firmware, then in the @file{debug.log} +file one of the last lines should be a log message like: +@samp{Loading driver at 0x00006AEE000 EntryPoint=0x00006AEE756}. This +means that the GRUB2 EFI application was loaded at @samp{0x00006AEE000} and +its .text section is at @samp{0x00006AEE756}. + +@node Using the gdbinfo command +@subsection Using the gdbinfo command + +On EFI platforms the command @command{gdbinfo} will output a string that +is to be run in a GDB session running with the @file{gdb_grub} GDB script. + + +@node Porting +@chapter Porting + +GRUB2 is designed to be easily portable accross platforms. But because of the +nature of bootloader every new port must be done separately. Here is how I did +MIPS (loongson and ARC) and Xen ports. Note than this is more of suggestions, +not absolute truth. + +First of all grab any architecture specifications you can find in public +(please avoid NDA). + +First stage is ``Hello world''. I've done it outside of GRUB for simplicity. +Your task is to have a small program which is loadable as bootloader and +clearly shows its presence to you. If you have easily accessible console +you can just print a message. If you have a mapped framebuffer you know address +of, you can draw a square. If you have a debug facility, just hanging without +crashing might be enough. For the first stage you can choose to load the +bootloader across the network since format for network image is often easier +than for local boot and it skips the need of small intermediary stages and +nvram handling. Additionally you can often have a good idea of the needed +format by running ``file'' on any netbootable executable for given platform. + +This program should probably have 2 parts: an assembler and C one. Assembler one +handles BSS cleaning and other needed setup (on some platforms you may need +to switch modes or copy the executable to its definitive position). So your code +may look like (x86 assembly for illustration purposes) + +@example + .globl _start +_start: + movl $_bss_start, %edi + movl $_end, %ecx + subl %edi, %ecx + xorl %eax, %eax + cld + rep + stosb + call main +@end example + +@example + +static const char msg[] = "Hello, world"; + +void +putchar (int c) +@{ + ... +@} + +void +main (void) +@{ + const char *ptr = msg; + while (*ptr) + putchar (*ptr++); + while (1); +@} +@end example + +Sometimes you need a third file: assembly stubs for ABI-compatibility. + +Once this file is functional it's time to move it into GRUB2. The startup +assembly file goes to grub-core/kern/$cpu/$platform/startup.S. You should also +include grub/symbol.h and replace call to entry point with call to +EXT_C(grub_main). The C file goes to grub-core/kern/$cpu/$platform/init.c +and its entry point is renamed to void grub_machine_init (void). Keep final +infinite loop for now. Stubs file if any goes to +grub-core/kern/$cpu/$platform/callwrap.S. Sometimes either $cpu or $platform +is dropped if file is used on several cpus respectivelyplatforms. +Check those locations if they already have what you're looking for. + +Then modify in configure.ac the following parts: + +CPU names: + +@example +case "$target_cpu" in + i[[3456]]86) target_cpu=i386 ;; + amd64) target_cpu=x86_64 ;; + sparc) target_cpu=sparc64 ;; + s390x) target_cpu=s390 ;; + ... +esac +@end example + +Sometimes CPU have additional architecture names which don't influence booting. +You might want to have some canonical name to avoid having bunch of identical +platforms with different names. + +NOTE: it doesn't influence compile optimisations which depend solely on +chosen compiler and compile options. + +@example +if test "x$with_platform" = x; then + case "$target_cpu"-"$target_vendor" in + i386-apple) platform=efi ;; + i386-*) platform=pc ;; + x86_64-apple) platform=efi ;; + x86_64-*) platform=pc ;; + powerpc-*) platform=ieee1275 ;; + ... + esac +else + ... +fi +@end example + +This part deals with guessing the platform from CPU and vendor. Sometimes you +need to use 32-bit mode for booting even if OS runs in 64-bit one. If so add +your platform to: + +@example +case "$target_cpu"-"$platform" in + x86_64-efi) ;; + x86_64-emu) ;; + x86_64-*) target_cpu=i386 ;; + powerpc64-ieee1275) target_cpu=powerpc ;; +esac +@end example + +Add your platform to the list of supported ones: + +@example +case "$target_cpu"-"$platform" in + i386-efi) ;; + x86_64-efi) ;; + i386-pc) ;; + i386-multiboot) ;; + i386-coreboot) ;; + ... +esac +@end example + +If explicit -m32 or -m64 is needed add it to: + +@example +case "$target_cpu" in + i386 | powerpc) target_m32=1 ;; + x86_64 | sparc64) target_m64=1 ;; +esac +@end example + +Finally you need to add a conditional to the following block: + +@example +AM_CONDITIONAL([COND_mips_arc], [test x$target_cpu = xmips -a x$platform = xarc]) +AM_CONDITIONAL([COND_sparc64_ieee1275], [test x$target_cpu = xsparc64 -a x$platform = xieee1275]) +AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platform = xieee1275]) +@end example + +Next stop is gentpl.py. You need to add your platform to the list of supported +ones (sorry that this list is duplicated): + +@example +GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", + "i386_multiboot", "i386_ieee1275", "x86_64_efi", + "mips_loongson", "sparc64_ieee1275", + "powerpc_ieee1275", "mips_arc", "ia64_efi", + "mips_qemu_mips", "s390_mainframe" ] +@end example + +You may also want already to add new platform to one or several of available +groups. In particular we always have a group for each CPU even when only +one platform for given CPU is available. + +Then comes grub-core/Makefile.core.def. In the block ``kernel'' you'll need +to define ldflags for your platform ($cpu_$platform_ldflags). You also need to +declare startup asm file ($cpu_$platform_startup) as well as any other files +(e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c). +At this stage you will also need to add dummy dl.c and cache.S with functions +grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and +void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They +won't be used for now. + +You will need to create directory include/$cpu/$platform and a file +include/$cpu/types.h. The latter following this template: + +@example +#ifndef GRUB_TYPES_CPU_HEADER +#define GRUB_TYPES_CPU_HEADER 1 + +/* The size of void *. */ +#define GRUB_TARGET_SIZEOF_VOID_P 4 + +/* The size of long. */ +#define GRUB_TARGET_SIZEOF_LONG 4 + +/* mycpu is big-endian. */ +#define GRUB_TARGET_WORDS_BIGENDIAN 1 +/* Alternatively: mycpu is little-endian. */ +#undef GRUB_TARGET_WORDS_BIGENDIAN + +#endif /* ! GRUB_TYPES_CPU_HEADER */ +@end example + +You will also need to add a dummy file to datetime and setjmp modules to +avoid any of it having no files. It can be just completely empty at this stage. + +You'll need to make grub-mkimage.c (util/grub_mkimage.c) aware of the needed +format. For most commonly used formats like ELF, PE, aout or raw the support +is already present and you'll need to make it follow the existant code paths +for your platform adding adjustments if necessary. When done compile: + +@example +./bootstrap +./configure --target=$cpu --with-platform=$platform TARGET_CC=.. OBJCOPY=... STRIP=... +make > /dev/null +@end example + +And create image + +@example +./grub-mkimage -d grub-core -O $format_id -o test.img +@end example + +And it's time to test your test.img. + +If it works next stage is to have heap, console and timer. + +To have the heap working you need to determine which regions are suitable for +heap usage, allocate them from firmware and map (if applicable). Then call +grub_mm_init_region (void *start, grub_size_t s) for every of this region. +As a shortcut for early port you can allocate right after _end or have +a big static array for heap. If you do you'll probably need to come back to +this later. As for output console you should distinguish between an array of +text, terminfo or graphics-based console. Many of real-world examples don't +fit perfectly into any of these categories but one of the models is easier +to be used as base. In second and third case you should add your platform to +terminfokernel respectively videoinkernel group. A good example of array of +text is i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c). +Of terminfo is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c). +Of video is loongson (kern/mips/loongson/init.c). Note that terminfo has +to be inited in 2 stages: one before (to get at least rudimentary console +as early as possible) and another after the heap (to get full-featured console). +For the input there are string of keys, terminfo and direct hardware. For string +of keys look at i386-pc (same files), for terminfo ieee1275 (same files) and for +hardware loongson (kern/mips/loongson/init.c and term/at_keyboard.c). + +For the timer you'll need to call grub_install_get_time_ms (...) with as sole +argument a function returning a grub_uint64_t of a number of milliseconds +elapsed since arbitrary point in the past. + +Once these steps accomplished you can remove the inifinite loop and you should +be able to get to the minimal console. Next step is to have module loading +working. For this you'll need to fill kern/$cpu/dl.c and kern/$cpu/cache.S +with real handling of relocations and respectively the real sync of I and D +caches. Also you'll need to decide where in the image to store the modules. +Usual way is to have it concatenated at the end. In this case you'll need to +modify startup.S to copy modules out of bss to let's say ALIGN_UP (_end, 8) +before cleaning out bss. You'll probably find useful to add total_module_size +field to startup.S. In init.c you need to set grub_modbase to the address +where modules can be found. You may need grub_modules_get_end () to avoid +declaring the space occupied by modules as usable for heap. You can test modules +with: + +@example +./grub-mkimage -d grub-core -O $format_id -o test.img hello +@end example + +and then running ``hello'' in the shell. + +Once this works, you should think of implementing disk access. Look around +disk/ for examples. + +Then, very importantly, you probably need to implement the actual loader +(examples available in loader/) + +Last step to have minimally usable port is to add support to grub-install to +put GRUB in a place where firmware or platform will pick it up. + +Next steps are: filling datetime.c, setjmp.S, network (net/drivers), +video (video/), halt (lib/), reboot (lib/). + +Please add your platform to Platform limitations and Supported kernels chapter +in user documentation and mention any steps you skipped which result in reduced +features or performance. Here is the quick checklist of features. Some of them +are less important than others and skipping them is completely ok, just needs +to be mentioned in user documentation. + +Checklist: +@itemize +@item Is heap big enough? +@item Which charset is supported by console? +@item Does platform have disk driver? +@item Do you have network card support? +@item Are you able to retrieve datetime (with date)? +@item Are you able to set datetime (with date)? +@item Is serial supported? +@item Do you have direct disk support? +@item Do you have direct keyboard support? +@item Do you have USB support? +@item Do you support loading through network? +@item Do you support loading from disk? +@item Do you support chainloading? +@item Do you support network chainloading? +@item Does cpuid command supports checking all +CPU features that the user might want conditionalise on +(64-bit mode, hypervisor,...) +@item Do you support hints? How reliable are they? +@item Does platform have ACPI? If so do ``acpi'' and ``lsacpi'' modules work? +@item Do any of platform-specific operations mentioned in the relevant section of +user manual makes sense on your platform? +@item Does your platform support PCI? If so is there an appropriate driver for +GRUB? +@item Do you support badram? +@end itemize + +@node Error Handling +@chapter Error Handling + +Error handling in GRUB 2 is based on exception handling model. As C language +doesn't directly support exceptions, exception handling behavior is emulated +in software. + +When exception is raised, function must return to calling function. If calling +function does not provide handling of the exception it must return back to its +calling function and so on, until exception is handled. If exception is not +handled before prompt is displayed, error message will be shown to user. + +Exception information is stored on @code{grub_errno} global variable. If +@code{grub_errno} variable contains value @code{GRUB_ERR_NONE}, there is no active +exception and application can continue normal processing. When @code{grub_errno} has +other value, it is required that application code either handles this error or +returns instantly to caller. If function is with return type @code{grub_err_t} is +about to return @code{GRUB_ERR_NONE}, it should not set @code{grub_errno} to that +value. Only set @code{grub_errno} in cases where there is error situation. + +Simple exception forwarder. +@example +grub_err_t +forwarding_example (void) +@{ + /* Call function that might cause exception. */ + foobar (); + + /* No special exception handler, just forward possible exceptions. */ + if (grub_errno != GRUB_ERR_NONE) + @{ + return grub_errno; + @} + + /* All is OK, do more processing. */ + + /* Return OK signal, to caller. */ + return GRUB_ERR_NONE; +@} +@end example + +Error reporting has two components, the actual error code (of type +@code{grub_err_t}) and textual message that will be displayed to user. List of +valid error codes is listed in header file @file{include/grub/err.h}. Textual +error message can contain any textual data. At time of writing, error message +can contain up to 256 characters (including terminating NUL). To ease error +reporting there is a helper function @code{grub_error} that allows easier +formatting of error messages and should be used instead of writing directly to +global variables. + +Example of error reporting. +@example +grub_err_t +failing_example () +@{ + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Failed to read %s, tried %d times.", + "test.txt", + 10); +@} +@end example + +If there is a special reason that error code does not need to be taken account, +@code{grub_errno} can be zeroed back to @code{GRUB_ERR_NONE}. In cases like this all +previous error codes should have been handled correctly. This makes sure that +there are no unhandled exceptions. + +Example of zeroing @code{grub_errno}. +@example +grub_err_t +probe_example () +@{ + /* Try to probe device type 1. */ + probe_for_device (); + if (grub_errno == GRUB_ERR_NONE) + @{ + /* Device type 1 was found on system. */ + register_device (); + return GRUB_ERR_NONE; + @} + /* Zero out error code. */ + grub_errno = GRUB_ERR_NONE; + + /* No device type 1 found, try to probe device type 2. */ + probe_for_device2 (); + if (grub_errno == GRUB_ERR_NONE) + @{ + /* Device type 2 was found on system. */ + register_device2 (); + return GRUB_ERR_NONE; + @} + /* Zero out error code. */ + grub_errno = GRUB_ERR_NONE; + + /* Return custom error message. */ + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No device type 1 or 2 found."); +@} +@end example + +Some times there is a need to continue processing even if there is a error +state in application. In situations like this, there is a needed to save old +error state and then call other functions that might fail. To aid in this, +there is a error stack implemented. Error state can be pushed to error stack +by calling function @code{grub_error_push ()}. When processing has been completed, +@code{grub_error_pop ()} can be used to pop error state from stack. Error stack +contains predefined amount of error stack items. Error stack is protected for +overflow and marks these situations so overflow error does not get unseen. +If there is no space available to store error message, it is simply discarded +and overflow will be marked as happened. When overflow happens, it most likely +will corrupt error stack consistency as for pushed error there is no matching +pop, but overflow message will be shown to inform user about the situation. +Overflow message will be shown at time when prompt is about to be drawn. + +Example usage of error stack. +@example +/* Save possible old error message. */ +grub_error_push (); + +/* Do your stuff here. */ +call_possibly_failing_function (); + +if (grub_errno != GRUB_ERR_NONE) + @{ + /* Inform rest of the code that there is error (grub_errno + is set). There is no pop here as we want both error states + to be displayed. */ + return; + @} + +/* Restore old error state by popping previous item from stack. */ +grub_error_pop (); +@end example + +@node Stack and heap size +@chapter Stack and heap size + +On emu stack and heap are just normal host OS stack and heap. Stack is typically +8 MiB although it's OS-dependent. + +On i386-pc, i386-coreboot, i386-qemu and i386-multiboot the stack is 60KiB. +All available space between 1MiB and 4GiB marks is part of heap. + +On *-xen stack is 4MiB. If compiled for x86-64 with GCC 4.4 or later addressable +space is unlimited. When compiled for x86-64 with older GCC version addressable +space is limited to 2GiB. When compiling for i386 addressable space is limited +to 4GiB. All addressable pages except the ones for stack, GRUB binary, special +pages and page table are in the heap. + +On *-efi GRUB uses same stack as EFI. If compiled for x86-64 with GCC 4.4 or +later addressable space is unlimited. When compiled for x86-64 with older GCC +version addressable space is limited to 2GiB. For all other platforms addressable +space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most +1.6 GiB. + +On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275. + +On i386-ieee1275 and powerpc-ieee1275, GRUB will allocate 32MiB for its heap on +startup. It may allocate more at runtime, as long as at least 128MiB remain free +in OpenFirmware. + +On sparc64-ieee1275 stack is 256KiB and heap is 2MiB. + +On mips(el)-qemu_mips and mipsel-loongson stack is 2MiB (everything below +GRUB image) and everything above GRUB image (from 2MiB + kernel size) +until 256MiB is part of heap. + +On mips-arc stack is 2MiB (everything below GRUB image) and everything above +GRUB image(from 2MiB + kernel size) until 128MiB is part of heap. + +On mipsel-arc stack is 2MiB (everything below GRUB image which is not part +of ARC) and everything above GRUB image (from 7MiB + kernel size) +until 256MiB is part of heap. + +On arm-uboot stack is 256KiB and heap is 2MiB. + +In short: + +@multitable @columnfractions .15 .25 .5 +@headitem Platform @tab Stack @tab Heap +@item emu @tab 8 MiB @tab ? +@item i386-pc @tab 60 KiB @tab < 4 GiB +@item i386-coreboot @tab 60 KiB @tab < 4 GiB +@item i386-multiboot @tab 60 KiB @tab < 4 GiB +@item i386-qemu @tab 60 KiB @tab < 4 GiB +@item *-efi @tab ? @tab < 1.6 GiB +@item i386-ieee1275 @tab ? @tab < 32 MiB +@item powerpc-ieee1275 @tab ? @tab available memory - 128MiB +@item sparc64-ieee1275 @tab 256KiB @tab 2 MiB +@item arm-uboot @tab 256KiB @tab 2 MiB +@item mips(el)-qemu_mips @tab 2MiB @tab 253 MiB +@item mipsel-loongson @tab 2MiB @tab 253 MiB +@item mips-arc @tab 2MiB @tab 125 MiB +@item mipsel-arc @tab 2MiB @tab 248 MiB +@item x86_64-xen (GCC >= 4.4) @tab 4MiB @tab unlimited +@item x86_64-xen (GCC < 4.4) @tab 4MiB @tab < 2GiB +@item i386-xen @tab 4MiB @tab < 4GiB +@end multitable + + +@node BIOS port memory map +@chapter BIOS port memory map +@c By Yoshinori K Okuji + +@multitable @columnfractions .15 .25 .5 +@headitem Start @tab End @tab Usage +@item 0 @tab 0x1000 - 1 @tab BIOS and real mode interrupts +@item 0x07BE @tab 0x07FF @tab Partition table passed to another boot loader +@item ? @tab 0x2000 - 1 @tab Real mode stack +@item 0x7C00 @tab 0x7D00 - 1 @tab Boot sector +@item 0x8000 @tab ? @tab GRUB kernel +@item 0x68000 @tab 0x71000 - 1 @tab Disk buffer +@item ? @tab 0x80000 - 1 @tab Protected mode stack +@item ? @tab 0xA0000 - 1 @tab Extended BIOS Data Area +@item 0xA0000 @tab 0xC0000 - 1 @tab Video RAM +@item 0xC0000 @tab 0x100000 - 1 @tab BIOS +@item 0x100000 @tab ? @tab Heap and module code +@end multitable + +@node Video Subsystem +@chapter Video Subsystem +@c By VesaJääskeläinen +This document contains specification for Video Subsystem for GRUB2. +Currently only the usage interface is described in this document. +Internal structure of how video drivers are registering and how video +driver manager works are not included here. + +@menu +* Video API:: +* Example usage of Video API:: +* Bitmap API:: +@end menu + +@node Video API +@section Video API + +@subsection grub_video_setup + +@itemize +@item Prototype: +@example +grub_err_t +grub_video_setup (unsigned int width, unsigned int height, unsigned int mode_type); +@end example +@item Description: + +Driver will use information provided to it to select best possible video mode and switch to it. Supported values for @code{mode_type} are @code{GRUB_VIDEO_MODE_TYPE_INDEX_COLOR} for index color modes, @code{GRUB_VIDEO_MODE_TYPE_RGB} for direct RGB color modes and @code{GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED} for double buffering. When requesting RGB mode, highest bits per pixel mode will be selected. When requesting Index color mode, mode with highest number of colors will be selected. If all parameters are specified as zero, video adapter will try to figure out best possible mode and initialize it, platform specific differences are allowed here. If there is no mode matching request, error X will be returned. If there are no problems, function returns @code{GRUB_ERR_NONE}. + +This function also performs following task upon succesful mode switch. Active rendering target is changed to screen and viewport is maximized to allow whole screen to be used when performing graphics operations. In RGB modes, emulated palette gets 16 entries containing default values for VGA palette, other colors are defined as black. When switching to Indexed Color mode, driver may set default VGA palette to screen if the video card allows the operation. + +@end itemize + +@subsection grub_video_restore +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_restore (void); +@end example +@item Description: + +Video subsystem will deinitialize activated video driver to restore old state of video device. This can be used to switch back to text mode. +@end itemize + +@subsection grub_video_get_info +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_get_info (struct grub_video_mode_info *mode_info); +@end example +@example +struct grub_video_mode_info +@{ + /* Width of the screen. */ + unsigned int width; + /* Height of the screen. */ + unsigned int height; + /* Mode type bitmask. Contains information like is it Index color or + RGB mode. */ + unsigned int mode_type; + /* Bits per pixel. */ + unsigned int bpp; + /* Bytes per pixel. */ + unsigned int bytes_per_pixel; + /* Pitch of one scanline. How many bytes there are for scanline. */ + unsigned int pitch; + /* In index color mode, number of colors. In RGB mode this is 256. */ + unsigned int number_of_colors; + /* Optimization hint how binary data is coded. */ + enum grub_video_blit_format blit_format; + /* How many bits are reserved for red color. */ + unsigned int red_mask_size; + /* What is location of red color bits. In Index Color mode, this is 0. */ + unsigned int red_field_pos; + /* How many bits are reserved for green color. */ + unsigned int green_mask_size; + /* What is location of green color bits. In Index Color mode, this is 0. */ + unsigned int green_field_pos; + /* How many bits are reserved for blue color. */ + unsigned int blue_mask_size; + /* What is location of blue color bits. In Index Color mode, this is 0. */ + unsigned int blue_field_pos; + /* How many bits are reserved in color. */ + unsigned int reserved_mask_size; + /* What is location of reserved color bits. In Index Color mode, + this is 0. */ + unsigned int reserved_field_pos; +@}; +@end example +@item Description: + +Software developer can use this function to query properties of active rendering taget. Information provided here can be used by other parts of GRUB, like image loaders to convert loaded images to correct screen format to allow more optimized blitters to be used. If there there is no configured video driver with active screen, error @code{GRUB_ERR_BAD_DEVICE} is returned, otherwise @code{mode_info} is filled with valid information and @code{GRUB_ERR_NONE} is returned. +@end itemize + +@subsection grub_video_get_blit_format +@itemize +@item Prototype: + +@example +enum grub_video_blit_format +grub_video_get_blit_format (struct grub_video_mode_info *mode_info); +@end example +@example +enum grub_video_blit_format + @{ + /* Follow exactly field & mask information. */ + GRUB_VIDEO_BLIT_FORMAT_RGBA, + /* Make optimization assumption. */ + GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8, + /* Follow exactly field & mask information. */ + GRUB_VIDEO_BLIT_FORMAT_RGB, + /* Make optimization assumption. */ + GRUB_VIDEO_BLIT_FORMAT_R8G8B8, + /* When needed, decode color or just use value as is. */ + GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR + @}; +@end example +@item Description: + +Used to query how data could be optimized to suit specified video mode. Returns exact video format type, or a generic one if there is no definition for the type. For generic formats, use @code{grub_video_get_info} to query video color coding settings. +@end itemize + +@subsection grub_video_set_palette +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_set_palette (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); +@end example +@example +struct grub_video_palette_data +@{ + grub_uint8_t r; /* Red color value (0-255). */ + grub_uint8_t g; /* Green color value (0-255). */ + grub_uint8_t b; /* Blue color value (0-255). */ + grub_uint8_t a; /* Reserved bits value (0-255). */ +@}; +@end example +@item Description: + +Used to setup indexed color palettes. If mode is RGB mode, colors will be set to emulated palette data. In Indexed Color modes, palettes will be set to hardware. Color values will be converted to suit requirements of the video mode. @code{start} will tell what hardware color index (or emulated color index) will be set to according information in first indice of @code{palette_data}, after that both hardware color index and @code{palette_data} index will be incremented until @code{count} number of colors have been set. +@end itemize + +@subsection grub_video_get_palette +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_get_palette (unsigned int start, unsigned int count, struct grub_video_palette_data *palette_data); +@end example +@example +struct grub_video_palette_data +@{ + grub_uint8_t r; /* Red color value (0-255). */ + grub_uint8_t g; /* Green color value (0-255). */ + grub_uint8_t b; /* Blue color value (0-255). */ + grub_uint8_t a; /* Reserved bits value (0-255). */ +@}; +@end example +@item Description: + +Used to query indexed color palettes. If mode is RGB mode, colors will be copied from emulated palette data. In Indexed Color modes, palettes will be read from hardware. Color values will be converted to suit structure format. @code{start} will tell what hardware color index (or emulated color index) will be used as a source for first indice of @code{palette_data}, after that both hardware color index and @code{palette_data} index will be incremented until @code{count} number of colors have been read. +@end itemize + +@subsection grub_video_set_area_status +@itemize + +@item Prototype: +@example +grub_err_t +grub_video_set_area_status (grub_video_area_status_t area_status); +@end example +@example +enum grub_video_area_status_t + @{ + GRUB_VIDEO_AREA_DISABLED, + GRUB_VIDEO_AREA_ENABLED + @}; +@end example + +@item Description: + +Used to set area drawing mode for redrawing the specified region. Draw commands +are performed in the intersection of the viewport and the region called area. +Coordinates remain related to the viewport. If draw commands try to draw over +the area, they are clipped. +Set status to DISABLED if you need to draw everything. +Set status to ENABLED and region to the desired rectangle to redraw everything +inside the region leaving everything else intact. +Should be used for redrawing of active elements. +@end itemize + +@subsection grub_video_get_area_status +@itemize + +@item Prototype: +@example +grub_err_r +grub_video_get_area_status (grub_video_area_status_t *area_status); +@end example + +@item Description: +Used to query the area status. +@end itemize + +@subsection grub_video_set_viewport +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_set_viewport (unsigned int x, unsigned int y, unsigned int width, unsigned int height); +@end example +@item Description: + +Used to specify viewport where draw commands are performed. When viewport is set, all draw commands coordinates relate to those specified by @code{x} and @code{y}. If draw commands try to draw over viewport, they are clipped. If developer requests larger than possible viewport, width and height will be clamped to fit screen. If @code{x} and @code{y} are out of bounds, all functions drawing to screen will not be displayed. In order to maximize viewport, use @code{grub_video_get_info} to query actual screen dimensions and provide that information to this function. +@end itemize + +@subsection grub_video_get_viewport +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_get_viewport (unsigned int *x, unsigned int *y, unsigned int *width, unsigned int *height); +@end example +@item Description: + +Used to query current viewport dimensions. Software developer can use this to choose best way to render contents of the viewport. +@end itemize + +@subsection grub_video_set_region +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_set_region (unsigned int x, unsigned int y, unsigned int width, unsigned int height); +@end example +@item Description: + +Used to specify the region of the screen which should be redrawn. Use absolute +values. When the region is set and area status is ENABLE all draw commands will +be performed inside the interseption of region and viewport named area. +If draw commands try to draw over viewport, they are clipped. If developer +requests larger than possible region, width and height will be clamped to fit +screen. Should be used for redrawing of active elements. +@end itemize + +@subsection grub_video_get_region +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_get_region (unsigned int *x, unsigned int *y, unsigned int *width, unsigned int *height); +@end example +@item Description: + +Used to query current region dimensions. +@end itemize + +@subsection grub_video_map_color +@itemize +@item Prototype: + +@example +grub_video_color_t +grub_video_map_color (grub_uint32_t color_name); +@end example +@item Description: + +Map color can be used to support color themes in GRUB. There will be collection of color names that can be used to query actual screen mapped color data. Examples could be @code{GRUB_COLOR_CONSOLE_BACKGROUND}, @code{GRUB_COLOR_CONSOLE_TEXT}. The actual color defines are not specified at this point. +@end itemize + +@subsection grub_video_map_rgb +@itemize +@item Prototype: + +@example +grub_video_color_t +grub_video_map_rgb (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue); +@end example +@item Description: + +Map RGB values to compatible screen color data. Values are expected to be in range 0-255 and in RGB modes they will be converted to screen color data. In index color modes, index color palette will be searched for specified color and then index is returned. +@end itemize + +@subsection grub_video_map_rgba +@itemize +@item Prototype: + +@example +grub_video_color_t +grub_video_map_rgba (grub_uint8_t red, grub_uint8_t green, grub_uint8_t blue, grub_uint8_t alpha); +@end example +@item Description: + +Map RGBA values to compatible screen color data. Values are expected to be in range 0-255. In RGBA modes they will be converted to screen color data. In index color modes, index color palette will be searched for best matching color and its index is returned. +@end itemize + +@subsection grub_video_unmap_color +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_unmap_color (grub_video_color_t color, grub_uint8_t *red, grub_uint8_t *green, grub_uint8_t *blue, grub_uint8_t *alpha); +@end example +@item Description: + +Unmap color value from @code{color} to color channels in @code{red}, @code{green}, @code{blue} and @code{alpha}. Values will be in range 0-255. Active rendering target will be used for color domain. In case alpha information is not available in rendering target, it is assumed to be opaque (having value 255). +@end itemize + +@subsection grub_video_fill_rect +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_fill_rect (grub_video_color_t color, int x, int y, unsigned int width, unsigned int height); +@end example +@item Description: + +Fill specified area limited by given coordinates within specified viewport. Negative coordinates are accepted in order to allow easy moving of rectangle within viewport. If coordinates are negative, area of the rectangle will be shrinken to follow size limits of the viewport. + +Software developer should use either @code{grub_video_map_color}, @code{grub_video_map_rgb} or @code{grub_video_map_rgba} to map requested color to @code{color} parameter. +@end itemize + +@subsection grub_video_blit_glyph +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_blit_glyph (struct grub_font_glyph *glyph, grub_video_color_t color, int x, int y); +@end example +@example +struct grub_font_glyph @{ + /* TBD. */ +@}; +@end example +@item Description: + +Used to blit glyph to viewport in specified coodinates. If glyph is at edge of viewport, pixels outside of viewport will be clipped out. Software developer should use either @code{grub_video_map_rgb} or @code{grub_video_map_rgba} to map requested color to @code{color} parameter. +@end itemize + +@subsection grub_video_blit_bitmap +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, unsigned int width, unsigned int height); +@end example +@example +struct grub_video_bitmap +@{ + /* TBD. */ +@}; + +enum grub_video_blit_operators + @{ + GRUB_VIDEO_BLIT_REPLACE, + GRUB_VIDEO_BLIT_BLEND + @}; +@end example +@item Description: + +Used to blit bitmap to viewport in specified coordinates. If part of bitmap is outside of viewport region, it will be clipped out. Offsets affect bitmap position where data will be copied from. Negative values for both viewport coordinates and bitmap offset coordinates are allowed. If data is looked out of bounds of bitmap, color value will be assumed to be transparent. If viewport coordinates are negative, area of the blitted rectangle will be shrinken to follow size limits of the viewport and bitmap. Blitting operator @code{oper} specifies should source pixel replace data in screen or blend with pixel alpha value. + +Software developer should use @code{grub_video_bitmap_create} or @code{grub_video_bitmap_load} to create or load bitmap data. +@end itemize + +@subsection grub_video_blit_render_target +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_blit_render_target (struct grub_video_render_target *source, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, unsigned int width, unsigned int height); +@end example +@example +struct grub_video_render_target @{ + /* This is private data for video driver. Should not be accessed from elsewhere directly. */ +@}; + +enum grub_video_blit_operators + @{ + GRUB_VIDEO_BLIT_REPLACE, + GRUB_VIDEO_BLIT_BLEND + @}; +@end example +@item Description: + +Used to blit source render target to viewport in specified coordinates. If part of source render target is outside of viewport region, it will be clipped out. If blitting operator is specified and source contains alpha values, resulting pixel color components will be calculated using formula ((src_color * src_alpha) + (dst_color * (255 - src_alpha)) / 255, if target buffer has alpha, it will be set to src_alpha. Offsets affect render target position where data will be copied from. If data is looked out of bounds of render target, color value will be assumed to be transparent. Blitting operator @code{oper} specifies should source pixel replace data in screen or blend with pixel alpha value. +@end itemize + +@subsection grub_video_scroll +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_scroll (grub_video_color_t color, int dx, int dy); +@end example +@item Description: + +Used to scroll viewport to specified direction. New areas are filled with specified color. This function is used when screen is scroller up in video terminal. +@end itemize + +@subsection grub_video_swap_buffers +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_swap_buffers (void); +@end example +@item Description: + +If double buffering is enabled, this swaps frontbuffer and backbuffer, in order to show values drawn to back buffer. Video driver is free to choose how this operation is techincally done. +@end itemize + +@subsection grub_video_create_render_target +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_create_render_target (struct grub_video_render_target **result, unsigned int width, unsigned int height, unsigned int mode_type); +@end example +@example +struct grub_video_render_target @{ + /* This is private data for video driver. Should not be accessed from elsewhere directly. */ +@}; +@end example +@item Description: + +Driver will use information provided to it to create best fitting render target. @code{mode_type} will be used to guide on selecting what features are wanted for render target. Supported values for @code{mode_type} are @code{GRUB_VIDEO_MODE_TYPE_INDEX_COLOR} for index color modes, @code{GRUB_VIDEO_MODE_TYPE_RGB} for direct RGB color modes and @code{GRUB_VIDEO_MODE_TYPE_ALPHA} for alpha component. +@end itemize + +@subsection grub_video_delete_render_target +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_delete_render_target (struct grub_video_render_target *target); +@end example +@item Description: + +Used to delete previously created render target. If @code{target} contains @code{NULL} pointer, nothing will be done. If render target is correctly destroyed, GRUB_ERR_NONE is returned. +@end itemize + +@subsection grub_video_set_active_render_target +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_set_active_render_target (struct grub_video_render_target *target); +@end example +@item Description: + +Sets active render target. If this comand is successful all drawing commands will be done to specified @code{target}. There is also special values for target, @code{GRUB_VIDEO_RENDER_TARGET_DISPLAY} used to reference screen's front buffer, @code{GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER} used to reference screen's front buffer (alias for @code{GRUB_VIDEO_RENDER_TARGET_DISPLAY}) and @code{GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER} used to reference back buffer (if double buffering is enabled). If render target is correclty switched GRUB_ERR_NONE is returned. In no any event shall there be non drawable active render target. + +@end itemize +@subsection grub_video_get_active_render_target +@itemize +@item Prototype: + +@example +grub_err_t +grub_video_get_active_render_target (struct grub_video_render_target **target); +@end example +@item Description: + +Returns currently active render target. It returns value in @code{target} that can be subsequently issued back to @code{grub_video_set_active_render_target}. +@end itemize + +@node Example usage of Video API +@section Example usage of Video API +@subsection Example of screen setup +@example +grub_err_t rc; +/* Try to initialize video mode 1024 x 768 with direct RGB. */ +rc = grub_video_setup (1024, 768, GRUB_VIDEO_MODE_TYPE_RGB); +if (rc != GRUB_ERR_NONE) +@{ + /* Fall back to standard VGA Index Color mode. */ + rc = grub_video_setup (640, 480, GRUB_VIDEO_MODE_TYPE_INDEX); + if (rc != GRUB_ERR_NONE) + @{ + /* Handle error. */ + @} +@} +@end example +@subsection Example of setting up console viewport +@example +grub_uint32_t x, y, width, height; +grub_video_color_t color; +struct grub_font_glyph glyph; +grub_err_t rc; +/* Query existing viewport. */ +grub_video_get_viewport (&x, &y, &width, &height); +/* Fill background. */ +color = grub_video_map_color (GRUB_COLOR_BACKGROUND); +grub_video_fill_rect (color, 0, 0, width, height); +/* Setup console viewport. */ +grub_video_set_viewport (x + 10, y + 10, width - 20, height - 20); +grub_video_get_viewport (&x, &y, &width, &height); +color = grub_video_map_color (GRUB_COLOR_CONSOLE_BACKGROUND); +grub_video_fill_rect (color, 0, 0, width, height); +/* Draw text to viewport. */ +color = grub_video_map_color (GRUB_COLOR_CONSOLE_TEXT); +grub_font_get_glyph ('X', &glyph); +grub_video_blit_glyph (&glyph, color, 0, 0); +@end example + +@node Bitmap API +@section Bitmap API +@subsection grub_video_bitmap_create +@itemize +@item Prototype: +@example +grub_err_t grub_video_bitmap_create (struct grub_video_bitmap **bitmap, unsigned int width, unsigned int height, enum grub_video_blit_format blit_format) +@end example + +@item Description: + +Creates a new bitmap with given dimensions and blitting format. Allocated bitmap data can then be modified freely and finally blitted with @code{grub_video_blit_bitmap} to rendering target. +@end itemize + +@subsection grub_video_bitmap_destroy +@itemize +@item Prototype: +@example +grub_err_t grub_video_bitmap_destroy (struct grub_video_bitmap *bitmap); +@end example + +@item Description: + +When bitmap is no longer needed, it can be freed from memory using this command. @code{bitmap} is previously allocated bitmap with @code{grub_video_bitmap_create} or loaded with @code{grub_video_bitmap_load}. +@end itemize + +@subsection grub_video_bitmap_load +@itemize +@item Prototype: +@example +grub_err_t grub_video_bitmap_load (struct grub_video_bitmap **bitmap, const char *filename); +@end example + +@item Description: + +Tries to load given bitmap (@code{filename}) using registered bitmap loaders. In case bitmap format is not recognized or supported error @code{GRUB_ERR_BAD_FILE_TYPE} is returned. +@end itemize + +@subsection grub_video_bitmap_get_width +@itemize +@item Prototype: +@example +unsigned int grub_video_bitmap_get_width (struct grub_video_bitmap *bitmap); +@end example + +@item Description: + +Returns bitmap width. +@end itemize + +@subsection grub_video_bitmap_get_height +@itemize +@item Prototype: +@example +unsigned int grub_video_bitmap_get_height (struct grub_video_bitmap *bitmap); +@end example + +@item Description: + +Return bitmap height. +@end itemize + +@subsection grub_video_bitmap_get_mode_info +@itemize +@item Prototype: +@example +void grub_video_bitmap_get_mode_info (struct grub_video_bitmap *bitmap, struct grub_video_mode_info *mode_info); +@end example + +@item Description: + +Returns bitmap format details in form of @code{grub_video_mode_info}. +@end itemize + +@subsection grub_video_bitmap_get_data +@itemize +@item Prototype: +@example +void *grub_video_bitmap_get_data (struct grub_video_bitmap *bitmap); +@end example + +@item Description: + +Return pointer to bitmap data. Contents of the pointed data can be freely modified. There is no extra protection against going off the bounds so you have to be carefull how to access the data. +@end itemize + +@node PFF2 Font File Format +@chapter PFF2 Font File Format + +@c Author: Colin D. Bennett +@c Date: 8 January 2009 + +@menu +* Introduction:: +* File Structure:: +* Font Metrics:: +@end menu + + +@node Introduction +@section Introduction + +The goal of this format is to provide a bitmap font format that is simple to +use, compact, and cleanly supports Unicode. + + +@subsection Goals of the GRUB Font Format + +@itemize +@item Simple to read and use. +Since GRUB will only be reading the font files, +we are more concerned with making the code to read the font simple than we +are with writing the font. + +@item Compact storage. +The fonts will generally be stored in a small boot +partition where GRUB is located, and this may be on a removable storage +device such as a CD or USB flash drive where space is more limited than it +is on most hard drives. + +@item Unicode. +GRUB should not have to deal with multiple character +encodings. The font should always use Unicode character codes for simple +internationalization. +@end itemize + +@subsection Why Another Font Format? + +There are many existing bitmap font formats that GRUB could use. However, +there are aspects of these formats that may make them less than suitable for +use in GRUB at this time: + +@table @samp +@item BDF +Inefficient storage; uses ASCII to describe properties and +hexadecimal numbers in ASCII for the bitmap rows. +@item PCF +Many format variations such as byte order and bitmap padding (rows +padded to byte, word, etc.) would result in more complex code to +handle the font format. +@end table + +@node File Structure +@section File Structure + +A file @strong{section} consists of a 4-byte name, a 32-bit big-endian length (not +including the name or length), and then @var{length} more section-type-specific +bytes. + +The standard file extension for PFF2 font files is @file{.pf2}. + + +@subsection Section Types + +@table @samp +@item FILE +@strong{File type ID} (ASCII string). This must be the first section in the file. It has length 4 +and the contents are the four bytes of the ASCII string @samp{PFF2}. + +@item NAME +@strong{Font name} (ASCII string). This is the full font name including family, +weight, style, and point size. For instance, "Helvetica Bold Italic 14". + +@item FAMI +@strong{Font family name} (ASCII string). For instance, "Helvetica". This should +be included so that intelligent font substitution can take place. + +@item WEIG +@strong{Font weight} (ASCII string). Valid values are @samp{bold} and @samp{normal}. +This should be included so that intelligent font substitution can take +place. + +@item SLAN +@strong{Font slant} (ASCII string). Valid values are @samp{italic} and @samp{normal}. +This should be included so that intelligent font substitution can take +place. + +@item PTSZ +@strong{Font point size} (uint16be). + +@item MAXW +@strong{Maximum character width in pixels} (uint16be). + +@item MAXH +@strong{Maximum character height in pixels} (uint16be). + +@item ASCE +@strong{Ascent in pixels} (uint16be). @xref{Font Metrics}, for details. + +@item DESC +@strong{Descent in pixels} (uint16be). @xref{Font Metrics}, for details. + +@item CHIX +@strong{Character index.} +The character index begins with a 32-bit big-endian unsigned integer +indicating the total size of the section, not including this size value. +For each character, there is an instance of the following entry structure: + +@itemize +@item @strong{Unicode code point.} (32-bit big-endian integer.) + +@item @strong{Storage flags.} (byte.) + +@itemize +@item Bits 2..0: + +If equal to 000 binary, then the character data is stored +uncompressed beginning at the offset indicated by the character's +@strong{offset} value. + +If equal to 001 binary, then the character data is stored within a +compressed character definition block that begins at the offset +within the file indicated by the character's @strong{offset} value. +@end itemize + +@item @strong{Offset.} (32-bit big-endian integer.) + +A marker that indicates the remainder of the file is data accessed via +the character index (CHIX) section. When reading this font file, the rest +of the file can be ignored when scanning the sections. The length should +be set to -1 (0xFFFFFFFF). + +Supported data structures: + +Character definition +Each character definition consists of: + +@itemize +@item @strong{Width.} +Width of the bitmap in pixels. The bitmap's extents +represent the glyph's bounding box. @code{uint16be}. + +@item @strong{Height.} +Height of the bitmap in pixels. The bitmap's extents +represent the glyph's bounding box. @code{uint16be}. + +@item @strong{X offset.} +The number of pixels to shift the bitmap by +horizontally before drawing the character. @code{int16be}. + +@item @strong{Y offset.} +The number of pixels to shift the bitmap by +vertically before drawing the character. @code{int16be}. + +@item @strong{Device width.} +The number of pixels to advance horizontally from +this character's origin to the origin of the next character. +@code{int16be}. + +@item @strong{Bitmap data.} +This is encoded as a string of bits. It is +organized as a row-major, top-down, left-to-right bitmap. The most +significant bit of each byte is taken to be the leftmost or uppermost +bit in the byte. For the sake of compact storage, rows are not padded +to byte boundaries (i.e., a single byte may contain bits belonging to +multiple rows). The last byte of the bitmap @strong{is} padded with zero +bits in the bits positions to the right of the last used bit if the +bitmap data does not fill the last byte. + +The length of the @strong{bitmap data} field is (@var{width} * @var{height} + 7) / 8 +using integer arithmetic, which is equivalent to ceil(@var{width} * +@var{height} / 8) using real number arithmetic. + +It remains to be determined whether bitmap fonts usually make all +glyph bitmaps the same height, or if smaller glyphs are stored with +bitmaps having a lesser height. In the latter case, the baseline +would have to be used to calculate the location the bitmap should be +anchored at on screen. +@end itemize + +@end itemize +@end table + +@node Font Metrics +@section Font Metrics + +@itemize +@item Ascent. +The distance from the baseline to the top of most characters. +Note that in some cases characters may extend above the ascent. + +@item Descent. +The distance from the baseline to the bottom of most characters. Note that +in some cases characters may extend below the descent. + +@item Leading. +The amount of space, in pixels, to leave between the descent of one line of +text and the ascent of the next line. This metrics is not specified in the +current file format; instead, the font rendering engine calculates a +reasonable leading value based on the other font metrics. + +@item Horizonal leading. +The amount of space, in pixels, to leave horizontally between the left and +right edges of two adjacent glyphs. The @strong{device width} field determines +the effective leading value that is used to render the font. + +@end itemize +@ifnottex +@image{font_char_metrics,,,,.png} +@end ifnottex + +An illustration of how the various font metrics apply to characters. + + + +@node Graphical Menu Software Design +@chapter Graphical Menu Software Design + +@c By Colin D. Bennett +@c Date: 17 August 2008 + +@menu +* Introduction_2:: +* Startup Sequence:: +* GUI Components:: +* Command Line Window:: +@end menu + +@node Introduction_2 +@section Introduction + +The @samp{gfxmenu} module provides a graphical menu interface for GRUB 2. It +functions as an alternative to the menu interface provided by the @samp{normal} +module, which uses the grub terminal interface to display a menu on a +character-oriented terminal. + +The graphical menu uses the GRUB video API, which is currently for the VESA +BIOS extensions (VBE) 2.0+. This is supported on the i386-pc platform. +However, the graphical menu itself does not depend on using VBE, so if another +GRUB video driver were implemented, the @samp{gfxmenu} graphical menu would work +on the new video driver as well. + + +@node Startup Sequence +@section Startup Sequence + +@itemize +@item grub_enter_normal_mode [normal/main.c] +@item grub_normal_execute [normal/main.c] +@item read_config_file [normal/main.c] +@item (When @file{gfxmenu.mod} is loaded with @command{insmod}, it will call @code{grub_menu_viewer_register()} to register itself.) +@item GRUB_MOD_INIT (gfxmenu) [gfxmenu/gfxmenu.c] +@item grub_menu_viewer_register [kern/menu_viewer.c] +@item grub_menu_viewer_show_menu [kern/menu_viewer.c] +@item get_current_menu_viewer() [kern/menu_viewer.c] +@item show_menu() [gfxmenu/gfxmenu.c] +@item grub_gfxmenu_model_new [gfxmenu/model.c] +@item grub_gfxmenu_view_new [gfxmenu/view.c] +@item set_graphics_mode [gfxmenu/view.c] +@item grub_gfxmenu_view_load_theme [gfxmenu/theme_loader.c] +@end itemize + + +@node GUI Components +@section GUI Components + +The graphical menu implements a GUI component system that supports a +container-based layout system. Components can be added to containers, and +containers (which are a type of component) can then be added to other +containers, to form a tree of components. Currently, the root component of +this tree is a @samp{canvas} component, which allows manual layout of its child +components. + +Components (non-container): + +@itemize +@item label +@item image +@item progress_bar +@item circular_progress +@item list (currently hard coded to be a boot menu list) +@end itemize + +Containers: + +@itemize +@item canvas +@item hbox +@item vbox +@end itemize + +The GUI component instances are created by the theme loader in +@file{gfxmenu/theme_loader.c} when a theme is loaded. Theme files specify +statements such as @samp{+vbox@{ +label @{ text="Hello" @} +label@{ text="World" @} @}} +to add components to the component tree root. By nesting the component +creation statements in the theme file, the instantiated components are nested +the same way. + +When a component is added to a container, that new child is considered @strong{owned} +by the container. Great care should be taken if the caller retains a +reference to the child component, since it will be destroyed if its parent +container is destroyed. A better choice instead of storing a pointer to the +child component is to use the component ID to find the desired component. +Component IDs do not have to be unique (it is often useful to have multiple +components with an ID of "__timeout__", for instance). + +In order to access and use components in the component tree, there are two +functions (defined in @file{gfxmenu/gui_util.c}) that are particularly useful: + +@itemize + +@item @code{grub_gui_find_by_id (root, id, callback, userdata)}: + +This function recursively traverses the component tree rooted at @var{root}, and +for every component that has an ID equal to @var{id}, calls the function pointed +to by @var{callback} with the matching component and the void pointer @var{userdata} +as arguments. The callback function can do whatever is desired to use the +component passed in. + +@item @code{grub_gui_iterate_recursively (root, callback, userdata)}: + +This function calls the function pointed to by @var{callback} for every +component that is a descendant of @var{root} in the component tree. When the +callback function is called, the component and the void pointer @var{userdata} +as arguments. The callback function can do whatever is desired to use the +component passed in. +@end itemize + +@node Command Line Window +@section Command Line Window + +The terminal window used to provide command line access within the graphical +menu is managed by @file{gfxmenu/view.c}. The @samp{gfxterm} terminal is used, and +it has been modified to allow rendering to an offscreen render target to allow +it to be composed into the double buffering system that the graphical menu +view uses. This is bad for performance, however, so it would probably be a +good idea to make it possible to temporarily disable double buffering as long +as the terminal window is visible. There are still unresolved problems that +occur when commands are executed from the terminal window that change the +graphics mode. It's possible that making @code{grub_video_restore()} return to +the graphics mode that was in use before @code{grub_video_setup()} was called +might fix some of the problems. + + +@node Verifiers framework +@chapter Verifiers framework + +To register your own verifier call @samp{grub_verifier_register} with a structure +pointing to your functions. + +The interface is inspired by the hash interface with @samp{init}/@samp{write}/@samp{fini}. + +There are essentially 2 ways of using it, hashing and whole-file verification. + +With the hashing approach: +During @samp{init} you decide whether you want to check the given file and init context. +In @samp{write} you update your hashing state. +In @samp{fini} you check that the hash matches the expected value/passes some check/... + +With whole-file verification: +During @samp{init} you decide whether you want to check the given file and init context. +In @samp{write} you verify the file and return an error if it fails. +You don't have @samp{fini}. + +Additional @samp{verify_string} receives various strings like kernel parameters +to verify. Returning no error means successful verification and an error stops +the current action. + +Detailed description of the API: + +Every time a file is opened your @samp{init} function is called with file descriptor +and file type. Your function can have the following outcomes: + +@itemize + +@item returning no error and setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_DEFER_AUTH}. +In this case verification is deferred to other active verifiers. Verification +fails if nobody cares or selected verifier fails. + +@item returning no error and setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_SKIP_VERIFICATION}. +In this case your verifier will not be called anymore and it is assumed to have +skipped verification. + +@item returning no error and not setting @samp{*flags} to @samp{GRUB_VERIFY_FLAGS_SKIP_VERIFICATION} +In this case verification is done as described in the following section. + +@item returning an error. Then opening of the file will fail due to failed verification. + +@end itemize + +In the third case your @samp{write} will be called with chunks of the file. If +you need the whole file in a single chunk then during @samp{init} set the bit +@samp{GRUB_VERIFY_FLAGS_SINGLE_CHUNK} in @samp{*flags}. During @samp{init} you +may set @samp{*context} if you need additional context. At every iteration you +may return an error and the file will be considered as having failed the +verification. If you return no error then verification continues. + +Optionally at the end of the file @samp{fini}, if it exists, is called with just +the context. If you return no error during any of @samp{init}, @samp{write} and +@samp{fini} then the file is considered as having succeded verification. + +@node Lockdown framework +@chapter Lockdown framework + +The GRUB can be locked down, which is a restricted mode where some operations +are not allowed. For instance, some commands cannot be used when the GRUB is +locked down. + +The function +@code{grub_lockdown()} is used to lockdown GRUB and the function +@code{grub_is_lockdown()} function can be used to check whether lockdown is +enabled or not. When enabled, the function returns @samp{GRUB_LOCKDOWN_ENABLED} +and @samp{GRUB_LOCKDOWN_DISABLED} when is not enabled. + +The following functions can be used to register the commands that can only be +used when lockdown is disabled: + +@itemize + +@item @code{grub_cmd_lockdown()} registers command which should not run when the +GRUB is in lockdown mode. + +@item @code{grub_cmd_lockdown()} registers extended command which should not run +when the GRUB is in lockdown mode. + +@end itemize + +@node Copying This Manual +@appendix Copying This Manual + +@menu +* GNU Free Documentation License:: License for copying this manual. +@end menu + +@include fdl.texi + + +@node Index +@unnumbered Index + +@c Currently, we use only the Concept Index. +@printindex cp + +@bye diff --git a/docs/grub.cfg b/docs/grub.cfg index e14cdb776..dc184d7d9 100644 --- a/docs/grub.cfg +++ b/docs/grub.cfg @@ -5,15 +5,22 @@ # Boot automatically after 30 secs. set timeout=30 -# By default, boot the first entry. -set default=0 +# By default, boot the GNU/Linux +set default=gnulinux -# Fallback to the second entry. -set fallback=1 +# Fallback to GNU/Hurd. +set fallback=gnuhurd + +# For booting GNU/Linux +menuentry "GNU/Linux" --id gnulinux { + set root=(hd0,msdos1) + linux /vmlinuz root=/dev/sda1 + initrd /initrd.img +} # For booting GNU/Hurd -menuentry "GNU (aka GNU/Hurd)" { - set root=(hd0,1) +menuentry "GNU (aka GNU/Hurd)" --id gnuhurd { + set root=(hd0,msdos1) multiboot /boot/gnumach.gz root=device:hd0s1 module /hurd/ext2fs.static ext2fs --readonly \ --multiboot-command-line='${kernel-command-line}' \ @@ -24,41 +31,35 @@ menuentry "GNU (aka GNU/Hurd)" { module /lib/ld.so.1 exec /hurd/exec '$(exec-task=task-create)' } -# For booting GNU/Linux -menuentry "GNU/Linux" { - set root=(hd0,1) - linux /vmlinuz root=/dev/sda1 - initrd /initrd.img -} - # For booting FreeBSD menuentry "FreeBSD (or GNU/kFreeBSD), direct boot" { - set root=(hd0,1,a) + set root=(hd0,msdos1,bsd1) kfreebsd /boot/kernel/kernel kfreebsd_loadenv /boot/device.hints kfreebsd_module /boot/splash.bmp type=splash_image_data set kFreeBSD.vfs.root.mountfrom=ufs:ad0s1a } + menuentry "FreeBSD (or GNU/kFreeBSD), via /boot/loader" { - set root=(hd0,1,a) + set root=(hd0,msdos1,bsd1) kfreebsd /boot/loader } # For booting NetBSD menuentry "NetBSD" { - set root=(hd0,1,a) + set root=(hd0,netbsd1) knetbsd /netbsd } # For booting OpenBSD menuentry "OpenBSD" { - set root=(hd0,1,a) + set root=(hd0,openbsd1) kopenbsd /bsd } # For booting Microsoft Windows menuentry "Microsoft Windows" { - set root=(hd0,1) + set root=(hd0,msdos1) chainloader +1 } diff --git a/docs/grub.texi b/docs/grub.texi index f8a9bc414..34b3484dc 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -20,7 +20,7 @@ This manual is for GNU GRUB (version @value{VERSION}, @value{UPDATED}). -Copyright @copyright{} 1999,2000,2001,2002,2004,2006,2008,2009 Free Software Foundation, Inc. +Copyright @copyright{} 1999,2000,2001,2002,2004,2006,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc. @quotation Permission is granted to copy, distribute and/or modify this document @@ -34,9 +34,13 @@ Invariant Sections. @direntry * GRUB: (grub). The GRand Unified Bootloader * grub-install: (grub)Invoking grub-install. Install GRUB on your drive -* grub-terminfo: (grub)Invoking grub-terminfo. Generate a terminfo - command from a - terminfo name +* grub-mkconfig: (grub)Invoking grub-mkconfig. Generate GRUB configuration +* grub-mkpasswd-pbkdf2: (grub)Invoking grub-mkpasswd-pbkdf2. +* grub-mkrelpath: (grub)Invoking grub-mkrelpath. +* grub-mkrescue: (grub)Invoking grub-mkrescue. Make a GRUB rescue image +* grub-mount: (grub)Invoking grub-mount. Mount a file system using GRUB +* grub-probe: (grub)Invoking grub-probe. Probe device information +* grub-script-check: (grub)Invoking grub-script-check. @end direntry @setchapternewpage odd @@ -47,6 +51,8 @@ Invariant Sections. @subtitle The GRand Unified Bootloader, version @value{VERSION}, @value{UPDATED}. @author Gordon Matzigkeit @author Yoshinori K. Okuji +@author Colin Watson +@author Colin D. Bennett @c The following two commands start the copyright page. @page @vskip 0pt plus 1filll @@ -75,24 +81,33 @@ This edition documents version @value{VERSION}. @menu * Introduction:: Capturing the spirit of GRUB * Naming convention:: Names of your drives in GRUB +* OS-specific notes about grub tools:: + Some notes about OS-specific behaviour of GRUB + tools * Installation:: Installing GRUB on your drive * Booting:: How to boot different operating systems * Configuration:: Writing your own configuration file +* Theme file format:: Format of GRUB theme files * Network:: Downloading OS images from a network * Serial terminal:: Using GRUB via a serial line -* Preset Menu:: Embedding a configuration file into GRUB +* Vendor power-on keys:: Changing GRUB behaviour on vendor power-on keys * Images:: GRUB image files +* Core image size limitation:: GRUB image files size limitations * Filesystem:: Filesystem syntax and semantics * Interface:: The menu and the command-line -* Commands:: The list of available builtin commands +* Environment:: GRUB environment variables +* Modules:: Available modules +* Commands:: Available builtin commands +* Internationalisation:: Topics relating to language support +* Security:: Authentication, authorisation, and signatures +* Platform limitations:: Platform-specific limitations +* Platform-specific operations:: Platform-specific operations +* Supported kernels:: Supported kernels * Troubleshooting:: Error messages produced by GRUB -* Invoking the grub shell:: How to use the grub shell -* Invoking grub-install:: How to use the GRUB installer -* Invoking grub-terminfo:: How to generate a terminfo command +* User-space utilities:: Usage of user-space utilities * Obtaining and Building GRUB:: How to obtain and build GRUB * Reporting bugs:: Where you should send a bug report * Future:: Some future plans on GRUB -* Internals:: Hacking GRUB * Copying This Manual:: Copying This Manual * Index:: @end menu @@ -104,6 +119,7 @@ This edition documents version @value{VERSION}. @menu * Overview:: What exactly GRUB is and how to use it * History:: From maggot to house fly +* Changes from GRUB Legacy:: Differences from previous versions * Features:: GRUB features * Role of a boot loader:: The role of a boot loader @end menu @@ -172,6 +188,91 @@ official GNU package, and opened its development by making the latest sources available via anonymous CVS. @xref{Obtaining and Building GRUB}, for more information. +Over the next few years, GRUB was extended to meet many needs, but it +quickly became clear that its design was not keeping up with the extensions +being made to it, and we reached the point where it was very difficult to +make any further changes without breaking existing features. Around 2002, +Yoshinori K. Okuji started work on PUPA (Preliminary Universal Programming +Architecture for GNU GRUB), aiming to rewrite the core of GRUB to make it +cleaner, safer, more robust, and more powerful. PUPA was eventually renamed +to GRUB 2, and the original version of GRUB was renamed to GRUB Legacy. +Small amounts of maintenance continued to be done on GRUB Legacy, but the +last release (0.97) was made in 2005 and at the time of writing it seems +unlikely that there will be another. + +By around 2007, GNU/Linux distributions started to use GRUB 2 to limited +extents, and by the end of 2009 multiple major distributions were installing +it by default. + + +@node Changes from GRUB Legacy +@section Differences from previous versions + +GRUB 2 is a rewrite of GRUB (@pxref{History}), although it shares many +characteristics with the previous version, now known as GRUB Legacy. Users +of GRUB Legacy may need some guidance to find their way around this new +version. + +@itemize @bullet +@item +The configuration file has a new name (@file{grub.cfg} rather than +@file{menu.lst} or @file{grub.conf}), new syntax (@pxref{Configuration}) and +many new commands (@pxref{Commands}). Configuration cannot be copied over +directly, although most GRUB Legacy users should not find the syntax too +surprising. + +@item +@file{grub.cfg} is typically automatically generated by +@command{grub-mkconfig} (@pxref{Simple configuration}). This makes it +easier to handle versioned kernel upgrades. + +@item +Partition numbers in GRUB device names now start at 1, not 0 (@pxref{Naming +convention}). + +@item +The configuration file is now written in something closer to a full +scripting language: variables, conditionals, and loops are available. + +@item +A small amount of persistent storage is available across reboots, using the +@command{save_env} and @command{load_env} commands in GRUB and the +@command{grub-editenv} utility. This is not available in all configurations +(@pxref{Environment block}). + +@item +GRUB 2 has more reliable ways to find its own files and those of target +kernels on multiple-disk systems, and has commands (@pxref{search}) to find +devices using file system labels or Universally Unique Identifiers (UUIDs). + +@item +GRUB 2 is available for several other types of system in addition to the PC +BIOS systems supported by GRUB Legacy: PC EFI, PC coreboot, PowerPC, SPARC, +and MIPS Lemote Yeeloong are all supported. + +@item +Many more file systems are supported, including but not limited to ext4, +HFS+, and NTFS. + +@item +GRUB 2 can read files directly from LVM and RAID devices. + +@item +A graphical terminal and a graphical menu system are available. + +@item +GRUB 2's interface can be translated, including menu entry names. + +@item +The image files (@pxref{Images}) that make up GRUB have been reorganised; +Stage 1, Stage 1.5, and Stage 2 are no more. + +@item +GRUB 2 puts many facilities in dynamically loaded modules, allowing the core +image to be smaller, and allowing the core image to be built in more +flexible ways. +@end itemize + @node Features @section GRUB features @@ -213,8 +314,10 @@ tables are also loaded. @item Support non-Multiboot kernels Support many of the various free 32-bit kernels that lack Multiboot -compliance (primarily FreeBSD, NetBSD, OpenBSD, and -Linux). Chain-loading of other boot loaders is also supported. +compliance (primarily FreeBSD, NetBSD@footnote{The NetBSD/i386 kernel +is Multiboot-compliant, but lacks support for Multiboot modules.}, +OpenBSD, and Linux). Chain-loading of other boot loaders is also +supported. @item Load multiples modules Fully support the Multiboot feature of loading multiple modules. @@ -246,15 +349,29 @@ devices, partitions, and files in a directory depending on context. @item Support multiple filesystem types Support multiple filesystem types transparently, plus a useful explicit -blocklist notation. The currently supported filesystem types are -@dfn{BSD FFS}, @dfn{DOS FAT16 and FAT32}, @dfn{Minix fs}, @dfn{Linux -ext2fs}, @dfn{ReiserFS}, @dfn{JFS}, @dfn{XFS}, and @dfn{VSTa -fs}. @xref{Filesystem}, for more information. +blocklist notation. The currently supported filesystem types are @dfn{Amiga +Fast FileSystem (AFFS)}, @dfn{AtheOS fs}, @dfn{BeFS}, +@dfn{BtrFS} (including raid0, raid1, raid10, gzip and lzo), +@dfn{cpio} (little- and big-endian bin, odc and newc variants), +@dfn{EROFS} (only uncompressed support for now), +@dfn{Linux ext2/ext3/ext4}, @dfn{DOS FAT12/FAT16/FAT32}, +@dfn{exFAT}, @dfn{F2FS}, @dfn{HFS}, @dfn{HFS+}, +@dfn{ISO9660} (including Joliet, Rock-ridge and multi-chunk files), +@dfn{JFS}, @dfn{Minix fs} (versions 1, 2 and 3), @dfn{nilfs2}, +@dfn{NTFS} (including compression), @dfn{ReiserFS}, @dfn{ROMFS}, +@dfn{Amiga Smart FileSystem (SFS)}, @dfn{Squash4}, @dfn{tar}, @dfn{UDF}, +@dfn{BSD UFS/UFS2}, @dfn{XFS}, and @dfn{ZFS} (including lzjb, gzip, +zle, mirror, stripe, raidz1/2/3 and encryption in AES-CCM and AES-GCM). +@xref{Filesystem}, for more information. +Note: Only a subset of filesystems are supported in lockdown mode (such +as when secure boot is enabled, @pxref{Lockdown} for more information). @item Support automatic decompression -Can decompress files which were compressed by @command{gzip}. This -function is both automatic and transparent to the user (i.e. all -functions operate upon the uncompressed contents of the specified +Can decompress files which were compressed by @command{gzip} or +@command{xz}@footnote{Only CRC32 data integrity check is supported (xz default +is CRC64 so one should use --check=crc32 option). LZMA BCJ filters are +supported.}. This function is both automatic and transparent to the user +(i.e. all functions operate upon the uncompressed contents of the specified files). This greatly reduces a file size and loading time, a particularly great benefit for floppies.@footnote{There are a few pathological cases where loading a very badly organized ELF kernel might @@ -356,12 +473,13 @@ disk. The number @samp{0} is the drive number, which is counted from disk. @example -(hd0,2) +(hd0,msdos2) @end example Here, @samp{hd} means it is a hard disk drive. The first integer -@samp{0} indicates the drive number, that is, the first hard disk, while -the second integer, @samp{1}, indicates the partition number (or the +@samp{0} indicates the drive number, that is, the first hard disk, +the string @samp{msdos} indicates the partition scheme, while +the second integer, @samp{2}, indicates the partition number (or the @sc{pc} slice number in the BSD terminology). The partition numbers are counted from @emph{one}, not from zero (as was the case in previous versions of GRUB). This expression means the second partition of the @@ -369,7 +487,7 @@ first hard disk drive. In this case, GRUB uses one partition of the disk, instead of the whole disk. @example -(hd0,5) +(hd0,msdos5) @end example This specifies the first @dfn{extended partition} of the first hard disk @@ -378,24 +496,21 @@ counted from @samp{5}, regardless of the actual number of primary partitions on your hard disk. @example -(hd1,a) +(hd1,msdos1,bsd1) @end example -This means the BSD @samp{a} partition of the second hard disk. If you -need to specify which @sc{pc} slice number should be used, use something -like this: @samp{(hd1,1,a)}. If the @sc{pc} slice number is omitted, -GRUB searches for the first @sc{pc} slice which has a BSD @samp{a} -partition. +This means the BSD @samp{a} partition on first @sc{pc} slice number +of the second hard disk. Of course, to actually access the disks or partitions with GRUB, you -need to use the device specification in a command, like @samp{root -(fd0)} or @samp{unhide (hd0,3)}. To help you find out which number -specifies a partition you want, the GRUB command-line +need to use the device specification in a command, like @samp{set +root=(fd0)} or @samp{parttool (hd0,msdos3) hidden-}. To help you find out +which number specifies a partition you want, the GRUB command-line (@pxref{Command-line interface}) options have argument completion. This means that, for example, you only need to type @example -root ( +set root=( @end example followed by a @key{TAB}, and GRUB will display the list of drives, @@ -413,7 +528,7 @@ Now the question is, how to specify a file? Again, consider an example: @example -(hd0,1)/vmlinuz +(hd0,msdos1)/vmlinuz @end example This specifies the file named @samp{vmlinuz}, found on the first @@ -423,6 +538,75 @@ completion works with file names, too. That was easy, admit it. Now read the next chapter, to find out how to actually install GRUB on your drive. +@node OS-specific notes about grub tools +@chapter OS-specific notes about grub tools + +On OS which have device nodes similar to Unix-like OS GRUB tools use the +OS name. E.g. for GNU/Linux: + +@example +# @kbd{grub-install /dev/sda} +@end example + +On AROS we use another syntax. For volumes: + +@example +//: +@end example + +E.g. + +@example +//:DH0 +@end example + +For disks we use syntax: +@example +//:/unit/flags +@end example + +E.g. + +@example +# @kbd{grub-install //:ata.device/0/0} +@end example + +On Windows we use UNC path. For volumes it's typically + +@example +\\?\Volume@{@} +\\?\: +@end example + +E.g. + +@example +\\?\Volume@{17f34d50-cf64-4b02-800e-51d79c3aa2ff@} +\\?\C: +@end example + + +For disks it's + +@example +\\?\PhysicalDrive +@end example + +E.g. + +@example +# @kbd{grub-install \\?\PhysicalDrive0} +@end example + +Beware that you may need to further escape the backslashes depending on your +shell. + +When compiled with cygwin support then cygwin drive names are automatically +when needed. E.g. + +@example +# @kbd{grub-install /dev/sda} +@end example @node Installation @chapter Installation @@ -433,52 +617,43 @@ system (@pxref{Obtaining and Building GRUB}). You can do this either from the source tarball, or as a package for your OS. After you have done that, you need to install the boot loader on a -drive (floppy or hard disk). There are two ways of doing that - either -using the utility @command{grub-install} (@pxref{Invoking -grub-install}) on a UNIX-like OS, or by running GRUB itself from a -floppy. These are quite similar, however the utility might probe a -wrong BIOS drive, so you should be careful. - -Also, if you install GRUB on a UNIX-like OS, please make sure that you -have an emergency boot disk ready, so that you can rescue your computer -if, by any chance, your hard drive becomes unusable (unbootable). +drive (floppy or hard disk) by using the utility +@command{grub-install} (@pxref{Invoking grub-install}) on a UNIX-like OS. GRUB comes with boot images, which are normally put in the directory -@file{/usr/lib/grub/i386-pc}. Hereafter, the directory where GRUB images are -initially placed (normally @file{/usr/lib/grub/i386-pc}) will be +@file{/usr/lib/grub/-} (for BIOS-based machines +@file{/usr/lib/grub/i386-pc}). Hereafter, the directory where GRUB images are +initially placed (normally @file{/usr/lib/grub/-}) will be called the @dfn{image directory}, and the directory where the boot -loader needs to find them (usually @file{/boot/grub}) will be called +loader needs to find them (usually @file{/boot}) will be called the @dfn{boot directory}. @menu * Installing GRUB using grub-install:: +* Making a GRUB bootable CD-ROM:: +* Device map:: +* BIOS installation:: @end menu @node Installing GRUB using grub-install @section Installing GRUB using grub-install -@strong{Caution:} This procedure is definitely less safe, because -there are several ways in which your computer can become -unbootable. For example, most operating systems don't tell GRUB how to -map BIOS drives to OS devices correctly---GRUB merely @dfn{guesses} -the mapping. This will succeed in most cases, but not -always. Therefore, GRUB provides you with a map file called the -@dfn{device map}, which you must fix if it is wrong. @xref{Device -map}, for more details. +For information on where GRUB should be installed on PC BIOS platforms, +@pxref{BIOS installation}. -If you still do want to install GRUB under a UNIX-like OS (such +In order to install GRUB under a UNIX-like OS (such as @sc{gnu}), invoke the program @command{grub-install} (@pxref{Invoking grub-install}) as the superuser (@dfn{root}). The usage is basically very simple. You only need to specify one argument to the program, namely, where to install the boot loader. The -argument can be either a device file (like @samp{/dev/hda}) or a -partition specified in GRUB's notation. For example, under Linux the -following will install GRUB into the MBR of the first IDE disk: +argument has to be either a device file (like @samp{/dev/hda}). +For example, under Linux the following will install GRUB into the MBR +of the first IDE disk: @example -# @kbd{grub-install /dev/hda} +# @kbd{grub-install /dev/sda} @end example Likewise, under GNU/Hurd, this has the same effect: @@ -487,56 +662,54 @@ Likewise, under GNU/Hurd, this has the same effect: # @kbd{grub-install /dev/hd0} @end example -If it is the first BIOS drive, this is the same as well: - -@example -# @kbd{grub-install '(hd0)'} -@end example - -Or you can omit the parentheses: - -@example -# @kbd{grub-install hd0} -@end example - -But all the above examples assume that GRUB should use images under -the root directory. If you want GRUB to use images under a directory -other than the root directory, you need to specify the option -@option{--root-directory}. The typical usage is that you create a GRUB +But all the above examples assume that GRUB should put images under +the @file{/boot} directory. If you want GRUB to put images under a directory +other than @file{/boot}, you need to specify the option +@option{--boot-directory}. The typical usage is that you create a GRUB boot floppy with a filesystem. Here is an example: @example @group # @kbd{mke2fs /dev/fd0} # @kbd{mount -t ext2 /dev/fd0 /mnt} -# @kbd{grub-install --root-directory=/mnt fd0} +# @kbd{mkdir /mnt/boot} +# @kbd{grub-install --boot-directory=/mnt/boot /dev/fd0} # @kbd{umount /mnt} @end group @end example -Another example is when you have a separate boot partition -which is mounted at @file{/boot}. Since GRUB is a boot loader, it -doesn't know anything about mountpoints at all. Thus, you need to run -@command{grub-install} like this: +Some BIOSes have a bug of exposing the first partition of a USB drive as a +floppy instead of exposing the USB drive as a hard disk (they call it +``USB-FDD'' boot). In such cases, you need to install like this: @example -# @kbd{grub-install --root-directory=/boot /dev/hda} +# @kbd{losetup /dev/loop0 /dev/sdb1} +# @kbd{mount /dev/loop0 /mnt/usb} +# @kbd{grub-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0} @end example -By the way, as noted above, it is quite difficult to guess BIOS drives -correctly under a UNIX-like OS. Thus, @command{grub-install} will prompt -you to check if it could really guess the correct mappings, after the -installation. The format is defined in @ref{Device map}. Please be -quite careful. If the output is wrong, it is unlikely that your -computer will be able to boot with no problem. +This install doesn't conflict with standard install as long as they are in +separate directories. -Note that @command{grub-install} is actually just a shell script and the -real task is done by the grub shell @command{grub} (@pxref{Invoking the -grub shell}). Therefore, you may run @command{grub} directly to install -GRUB, without using @command{grub-install}. Don't do that, however, -unless you are very familiar with the internals of GRUB. Installing a -boot loader on a running OS may be extremely dangerous. +On EFI systems for fixed disk install you have to mount EFI System Partition. +If you mount it at @file{/boot/efi} then you don't need any special arguments: +@example +# @kbd{grub-install} +@end example + +Otherwise you need to specify where your EFI System partition is mounted: + +@example +# @kbd{grub-install --efi-directory=/mnt/efi} +@end example + +For removable installs you have to use @option{--removable} and specify both +@option{--boot-directory} and @option{--efi-directory}: + +@example +# @kbd{grub-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable} +@end example @node Making a GRUB bootable CD-ROM @section Making a GRUB bootable CD-ROM @@ -547,15 +720,22 @@ using BIOS functions.}. This means that you can use the whole CD-ROM from GRUB and you don't have to make a floppy or hard disk image file, which can cause compatibility problems. -For booting from a CD-ROM, GRUB uses a special Stage 2 called -@file{stage2_eltorito}. The only GRUB files you need to have in your -bootable CD-ROM are this @file{stage2_eltorito} and optionally a config file -@file{grub.cfg}. You don't need to use @file{stage1} or @file{stage2}, -because El Torito is quite different from the standard boot process. +For booting from a CD-ROM, GRUB uses a special image called +@file{cdboot.img}, which is concatenated with @file{core.img}. The +@file{core.img} used for this should be built with at least the +@samp{iso9660} and @samp{biosdisk} modules. Your bootable CD-ROM will +usually also need to include a configuration file @file{grub.cfg} and some +other GRUB modules. -Here is an example of procedures to make a bootable CD-ROM -image. First, make a top directory for the bootable image, say, -@samp{iso}: +To make a simple generic GRUB rescue CD, you can use the +@command{grub-mkrescue} program (@pxref{Invoking grub-mkrescue}): + +@example +$ @kbd{grub-mkrescue -o grub.iso} +@end example + +You will often need to include other files in your image. To do this, first +make a top directory for the bootable image, say, @samp{iso}: @example $ @kbd{mkdir iso} @@ -567,33 +747,160 @@ Make a directory for GRUB: $ @kbd{mkdir -p iso/boot/grub} @end example -Copy the file @file{stage2_eltorito}: - -@example -$ @kbd{cp /usr/lib/grub/i386-pc/stage2_eltorito iso/boot/grub} -@end example - If desired, make the config file @file{grub.cfg} under @file{iso/boot/grub} (@pxref{Configuration}), and copy any files and directories for the disc to the directory @file{iso/}. -Finally, make a ISO9660 image file like this: +Finally, make the image: @example -$ @kbd{mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot \ - -boot-load-size 4 -boot-info-table -o grub.iso iso} +$ @kbd{grub-mkrescue -o grub.iso iso} @end example This produces a file named @file{grub.iso}, which then can be burned -into a CD (or a DVD). @kbd{mkisofs} has already set up the disc to boot -from the @kbd{boot/grub/stage2_eltorito} file, so there is no need to -setup GRUB on the disc. (Note that the @kbd{-boot-load-size 4} bit is -required for compatibility with the BIOS on many older machines.) +into a CD (or a DVD), or written to a USB mass storage device. -You can use the device @samp{(cd)} to access a CD-ROM in your -config file. This is not required; GRUB automatically sets the root device -to @samp{(cd)} when booted from a CD-ROM. It is only necessary to refer to -@samp{(cd)} if you want to access other drives as well. +The root device will be set up appropriately on entering your +@file{grub.cfg} configuration file, so you can refer to file names on the CD +without needing to use an explicit device name. This makes it easier to +produce rescue images that will work on both optical drives and USB mass +storage devices. + + +@node Device map +@section The map between BIOS drives and OS devices + +If the device map file exists, the GRUB utilities (@command{grub-probe}, +etc.) read it to map BIOS drives to OS devices. This file consists of lines +like this: + +@example +(@var{device}) @var{file} +@end example + +@var{device} is a drive specified in the GRUB syntax (@pxref{Device +syntax}), and @var{file} is an OS file, which is normally a device file. + +Historically, the device map file was used because GRUB device names had to +be used in the configuration file, and they were derived from BIOS drive +numbers. The map between BIOS drives and OS devices cannot always be +guessed correctly: for example, GRUB will get the order wrong if you +exchange the boot sequence between IDE and SCSI in your BIOS. + +Unfortunately, even OS device names are not always stable. Modern versions +of the Linux kernel may probe drives in a different order from boot to boot, +and the prefix (@file{/dev/hd*} versus @file{/dev/sd*}) may change depending +on the driver subsystem in use. As a result, the device map file required +frequent editing on some systems. + +GRUB avoids this problem nowadays by using UUIDs or file system labels when +generating @file{grub.cfg}, and we advise that you do the same for any +custom menu entries you write. If the device map file does not exist, then +the GRUB utilities will assume a temporary device map on the fly. This is +often good enough, particularly in the common case of single-disk systems. + +However, the device map file is not entirely obsolete yet, and it is +used for overriding when current environment is different from the one on boot. +Most common case is if you use a partition or logical volume as a disk for +virtual machine. You can put any comments in the file if needed, +as the GRUB utilities assume that a line is just a comment if +the first character is @samp{#}. + + +@node BIOS installation +@section BIOS installation + +@heading MBR + +The partition table format traditionally used on PC BIOS platforms is called +the Master Boot Record (MBR) format; this is the format that allows up to +four primary partitions and additional logical partitions. With this +partition table format, there are two ways to install GRUB: it can be +embedded in the area between the MBR and the first partition (called by +various names, such as the "boot track", "MBR gap", or "embedding area", and +which is usually at least 1000 KiB), or the core image can be installed in a +file system and a list of the blocks that make it up can be stored in the +first sector of that partition. + +Modern tools usually leave MBR gap of at least 1023 KiB. This amount is +sufficient to cover most configurations. Hence this value is recommended +by the GRUB team. + +Historically many tools left only 31 KiB of space. This is not enough to +parse reliably difficult structures like Btrfs, ZFS, RAID or LVM, or to +use difficult disk access methods like ahci. Hence GRUB will warn if attempted +to install into small MBR gap except in a small number of configurations +that were grandfathered. The grandfathered config must: + +@itemize @bullet +@item +use biosdisk as disk access module for @file{/boot} + +@item +not use any additional partition maps to access @file{/boot} + +@item +@file{/boot} must be on one of following filesystems: + AFFS, AFS, BFS, cpio, newc, odc, ext2/3/4, FAT, exFAT, + F2FS, HFS, uncompressed HFS+, ISO9660, JFS, Minix, Minix2, Minix3, NILFS2, + NTFS, ReiserFS, ROMFS, SFS, tar, UDF, UFS1, UFS2, XFS +@end itemize +Note: Only a subset of filesystems are supported in lockdown mode (such +as when secure boot is enabled, @pxref{Lockdown} for more information). + +MBR gap has few technical problems. There is no way to reserve space in +the embedding area with complete safety, and some proprietary software is +known to use it to make it difficult for users to work around licensing +restrictions. GRUB works around it by detecting sectors by other software and +avoiding them and protecting its own sectors using Reed-Solomon encoding. + +GRUB team recommends having MBR gap of at least 1000 KiB. + +Should it not be possible, GRUB has support for a fallback solution which is +heavily recommended against. Installing to a filesystem means that GRUB is +vulnerable to its blocks being moved around by filesystem features such as +tail packing, or even by aggressive fsck implementations, so this approach +is quite fragile; and this approach can only be used if the @file{/boot} +filesystem is on the same disk that the BIOS boots from, so that GRUB does +not have to rely on guessing BIOS drive numbers. + +The GRUB development team generally recommends embedding GRUB before the +first partition, unless you have special requirements. You must ensure that +the first partition starts at least 1000 KiB (2000 sectors) from the start of +the disk; on modern disks, it is often a performance advantage to align +partitions on larger boundaries anyway, so the first partition might start 1 +MiB from the start of the disk. + +@heading GPT + +Some newer systems use the GUID Partition Table (GPT) format. This was +specified as part of the Extensible Firmware Interface (EFI), but it can +also be used on BIOS platforms if system software supports it; for example, +GRUB and GNU/Linux can be used in this configuration. With this format, it +is possible to reserve a whole partition for GRUB, called the BIOS Boot +Partition. GRUB can then be embedded into that partition without the risk +of being overwritten by other software and without being contained in a +filesystem which might move its blocks around. + +When creating a BIOS Boot Partition on a GPT system, you should make sure +that it is at least 31 KiB in size. (GPT-formatted disks are not usually +particularly small, so we recommend that you make it larger than the bare +minimum, such as 1 MiB, to allow plenty of room for growth.) You must also +make sure that it has the proper partition type. Using GNU Parted, you can +set this using a command such as the following: + +@example +# @kbd{parted /dev/@var{disk} set @var{partition-number} bios_grub on} +@end example + +If you are using gdisk, set the partition type to @samp{0xEF02}. With +partitioning programs that require setting the GUID directly, it should be +@samp{21686148-6449-6e6f-744e656564454649}. + +@strong{Caution:} Be very careful which partition you select! When GRUB +finds a BIOS Boot Partition during installation, it will automatically +overwrite part of it. Make sure that the partition does not contain any +other data. @node Booting @@ -605,6 +912,8 @@ magic. @menu * General boot methods:: How to boot OSes with GRUB generally +* Loopback booting:: Notes on booting from loopbacks +* LVM cache booting:: Notes on booting from LVM cache logical volume * OS-specific notes:: Notes on some operating systems @end menu @@ -612,17 +921,17 @@ magic. @node General boot methods @section How to boot operating systems -GRUB has two distinct boot methods. One of the two is to load an -operating system directly, and the other is to chain-load another boot -loader which then will load an operating system actually. Generally -speaking, the former is more desirable, because you don't need to -install or maintain other boot loaders and GRUB is flexible enough to -load an operating system from an arbitrary disk/partition. However, -the latter is sometimes required, since GRUB doesn't support all the -existing operating systems natively. +GRUB has three distinct boot methods: loading an operating system +directly, using kexec from userspace, and chainloading another +bootloader. Generally speaking, the first two are more desirable +because you don't need to install or maintain other boot loaders and +GRUB is flexible enough to load an operating system from an arbitrary +disk/partition. However, chainloading is sometimes required, as GRUB +doesn't support all existing operating systems natively. @menu * Loading an operating system directly:: +* Kexec:: * Chain-loading:: @end menu @@ -648,6 +957,95 @@ use more complicated instructions. @xref{DOS/Windows}, for more information. +@node Kexec +@subsection Kexec with grub2-emu + +GRUB can be run in userspace by invoking the grub2-emu tool. It will +read all configuration scripts as if booting directly (see @ref{Loading +an operating system directly}). With the @code{--kexec} flag, and +kexec(8) support from the operating system, the @command{linux} command +will directly boot the target image. For systems that lack working +systemctl(1) support for kexec, passing the @code{--kexec} flag twice +will fallback to invoking kexec(8) directly; note however that this +fallback may be unsafe outside read-only environments, as it does not +invoke shutdown machinery. + + +@node Chain-loading +@subsection Chain-loading an OS + +Operating systems that do not support Multiboot and do not have specific +support in GRUB (specific support is available for Linux, FreeBSD, NetBSD +and OpenBSD) must be chain-loaded, which involves loading another boot +loader and jumping to it in real mode or via the firmware. + +The @command{chainloader} command (@pxref{chainloader}) is used to set this +up. It is normally also necessary to load some GRUB modules and set the +appropriate root device. Putting this together, we get something like this, +for a Windows system on the first partition of the first hard disk: + +@verbatim +menuentry "Windows" { + insmod chain + insmod ntfs + set root=(hd0,1) + chainloader +1 +} +@end verbatim +@c FIXME: document UUIDs. + +On systems with multiple hard disks, an additional workaround may be +required. @xref{DOS/Windows}. + +Chain-loading is only supported on PC BIOS and EFI platforms. + +@node Loopback booting +@section Loopback booting +GRUB is able to read from an image (be it one of CD or HDD) stored on +any of its accessible storages (refer to @pxref{loopback} command). +However the OS itself should be able to find its root. This usually +involves running a userspace program running before the real root +is discovered. This is achieved by GRUB loading a specially made +small image and passing it as ramdisk to the kernel. This is achieved +by commands @command{kfreebsd_module}, @command{knetbsd_module_elf}, +@command{kopenbsd_ramdisk}, @command{initrd} (@pxref{initrd}), +@command{initrd16} (@pxref{initrd16}), @command{multiboot_module}, +@command{multiboot2_module} or @command{xnu_ramdisk} +depending on the loader. Note that for knetbsd the image must be put +inside miniroot.kmod and the whole miniroot.kmod has to be loaded. In +kopenbsd payload this is disabled by default. Additionally, behaviour of +initial ramdisk depends on command line options. Several distributors provide +the image for this purpose or it's integrated in their standard ramdisk and +activated by special option. Consult your kernel and distribution manual for +more details. Other loaders like @command{appleloader}, @command{chainloader} +(BIOS, EFI, coreboot), @command{freedos}, @command{ntldr}, @command{plan9} +and @command{truecrypt} provide no possibility of loading initial ramdisk and +as far as author is aware the payloads in question don't support either initial +ramdisk or discovering loopback boot in other way and as such not bootable this +way. Please consider alternative boot methods like copying all files +from the image to actual partition. Consult your OS documentation for +more details. + +@node LVM cache booting +@section Booting from LVM cache logical volume + +The LVM cache logical volume is the logical volume consisting of the original +and the cache pool logical volume. The original is usually on a larger and +slower storage device while the cache pool is on a smaller and faster one. The +performance of the original volume can be improved by storing the frequently +used data on the cache pool to utilize the greater performance of faster +device. + +GRUB boots from LVM cache logical volume merely by reading it's original +logical volume so that dirty data in cache pool volume is disregarded. This is +not a problem for "writethrough" cache mode as it ensures that any data written +will be stored both on the cache and the origin LV. For the other cache mode +"writeback", which delays writing from the cache pool back to the origin LV to +boost performance, GRUB may fail to boot in the wake of accidental power outage +due to it's inability to assemble the cache device for reading the required +dirty data left behind. The situation will be improved after adding full +support to the LVM cache logical volume in the future. + @node OS-specific notes @section Some caveats on OS-specific issues @@ -656,6 +1054,8 @@ Here, we describe some caveats on several operating systems. @menu * GNU/Hurd:: * GNU/Linux:: +* NetBSD:: +* DOS/Windows:: @end menu @@ -666,11 +1066,30 @@ Since GNU/Hurd is Multiboot-compliant, it is easy to boot it; there is nothing special about it. But do not forget that you have to specify a root partition to the kernel. -FIXME: this section is incomplete. - @enumerate @item -Run the command @command{boot} (@pxref{boot}). +Set GRUB's root device to the same drive as GNU/Hurd's. The command +@code{search --set=root --file /boot/gnumach.gz} or similar may help you +(@pxref{search}). + +@item +Load the kernel and the modules, like this: + +@example +@group +grub> @kbd{multiboot /boot/gnumach.gz root=device:hd0s1} +grub> @kbd{module /hurd/ext2fs.static ext2fs --readonly \ + --multiboot-command-line='$@{kernel-command-line@}' \ + --host-priv-port='$@{host-port@}' \ + --device-master-port='$@{device-port@}' \ + --exec-server-task='$@{exec-task@}' -T typed '$@{root@}' \ + '$(task-create)' '$(task-resume)'} +grub> @kbd{module /lib/ld.so.1 exec /hurd/exec '$(exec-task=task-create)'} +@end group +@end example + +@item +Finally, run the command @command{boot} (@pxref{boot}). @end enumerate @@ -680,21 +1099,1598 @@ Run the command @command{boot} (@pxref{boot}). It is relatively easy to boot GNU/Linux from GRUB, because it somewhat resembles to boot a Multiboot-compliant OS. -FIXME: this section is incomplete. - @enumerate @item -Set GRUB's root device to the same drive as GNU/Linux's. +Set GRUB's root device to the same drive as GNU/Linux's. The command +@code{search --set=root --file /vmlinuz} or similar may help you +(@pxref{search}). + +@item +Load the kernel using the command @command{linux} (@pxref{linux}): + +@example +grub> @kbd{linux /vmlinuz root=/dev/sda1} +@end example + +If you need to specify some kernel parameters, just append them to the +command. For example, to set @option{acpi} to @samp{off}, do this: + +@example +grub> @kbd{linux /vmlinuz root=/dev/sda1 acpi=off} +@end example + +See the documentation in the Linux source tree for complete information on +the available options. + +With @command{linux} GRUB uses 32-bit protocol. Some BIOS services like APM +or EDD aren't available with this protocol. In this case you need to use +@command{linux16} + +@example +grub> @kbd{linux16 /vmlinuz root=/dev/sda1 acpi=off} +@end example + +@item +If you use an initrd, execute the command @command{initrd} (@pxref{initrd}) +after @command{linux}: + +@example +grub> @kbd{initrd /initrd} +@end example + +If you used @command{linux16} you need to use @command{initrd16}: + +@example +grub> @kbd{initrd16 /initrd} +@end example @item Finally, run the command @command{boot} (@pxref{boot}). @end enumerate -@strong{Caution:} If you use an initrd and specify the @samp{mem=} -option to the kernel to let it use less than actual memory size, you -will also have to specify the same memory size to GRUB. To let GRUB know -the size, run the command @command{uppermem} @emph{before} loading the -kernel. @xref{uppermem}, for more information. + +@node NetBSD +@subsection NetBSD + +Booting a NetBSD kernel from GRUB is also relatively easy: first set +GRUB's root device, then load the kernel and the modules, and finally +run @command{boot}. + +@enumerate +@item +Set GRUB's root device to the partition holding the NetBSD root file +system. For a disk with a NetBSD disk label, this is usually the first +partition (a:). In that case, and assuming that the partition is on the +first hard disk, set GRUB's root device as follows: + +@example +grub> @kbd{insmod part_bsd} +grub> @kbd{set root=(hd0,netbsd1)} +@end example + +For a disk with a GUID Partition Table (GPT), and assuming that the +NetBSD root partition is the third GPT partition, do this: + +@example +grub> @kbd{insmod part_gpt} +grub> @kbd{set root=(hd0,gpt3)} +@end example + +@item +Load the kernel using the command @command{knetbsd}: + +@example +grub> @kbd{knetbsd /netbsd} +@end example + +Various options may be given to @command{knetbsd}. These options are, +for the most part, the same as in the NetBSD boot loader. For instance, +to boot the system in single-user mode and with verbose messages, do +this: + +@example +grub> @kbd{knetbsd /netbsd -s -v} +@end example + +@item +If needed, load kernel modules with the command +@command{knetbsd_module_elf}. A typical example is the module for the +root file system: + +@example +grub> @kbd{knetbsd_module_elf /stand/amd64/6.0/modules/ffs/ffs.kmod} +@end example + +@item +Finally, run the command @command{boot} (@pxref{boot}). +@end enumerate + + +@node DOS/Windows +@subsection DOS/Windows + +GRUB cannot boot DOS or Windows directly, so you must chain-load them +(@pxref{Chain-loading}). However, their boot loaders have some critical +deficiencies, so it may not work to just chain-load them. To overcome +the problems, GRUB provides you with two helper functions. + +If you have installed DOS (or Windows) on a non-first hard disk, you +have to use the disk swapping technique, because that OS cannot boot +from any disks but the first one. The workaround used in GRUB is the +command @command{drivemap} (@pxref{drivemap}), like this: + +@example +drivemap -s (hd0) (hd1) +@end example + +This performs a @dfn{virtual} swap between your first and second hard +drive. + +@strong{Caution:} This is effective only if DOS (or Windows) uses BIOS +to access the swapped disks. If that OS uses a special driver for the +disks, this probably won't work. + +Another problem arises if you installed more than one set of DOS/Windows +onto one disk, because they could be confused if there are more than one +primary partitions for DOS/Windows. Certainly you should avoid doing +this, but there is a solution if you do want to do so. Use the partition +hiding/unhiding technique. + +If GRUB @dfn{hides} a DOS (or Windows) partition (@pxref{parttool}), DOS (or +Windows) will ignore the partition. If GRUB @dfn{unhides} a DOS (or Windows) +partition, DOS (or Windows) will detect the partition. Thus, if you have +installed DOS (or Windows) on the first and the second partition of the +first hard disk, and you want to boot the copy on the first partition, do +the following: + +@example +@group +parttool (hd0,1) hidden- +parttool (hd0,2) hidden+ +set root=(hd0,1) +chainloader +1 +parttool @verb{'${root}'} boot+ +boot +@end group +@end example + + +@node Configuration +@chapter Writing your own configuration file + +GRUB is configured using @file{grub.cfg}, usually located under +@file{/boot/grub}. This file is quite flexible, but most users will not +need to write the whole thing by hand. + +@menu +* Simple configuration:: Recommended for most users +* Root Identification Heuristics:: Summary on how the root file system is identified. +* Shell-like scripting:: For power users and developers +* Multi-boot manual config:: For non-standard multi-OS scenarios +* Embedded configuration:: Embedding a configuration file into GRUB +@end menu + + +@node Simple configuration +@section Simple configuration handling + +The program @command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}) +generates @file{grub.cfg} files suitable for most cases. It is suitable for +use when upgrading a distribution, and will discover available kernels and +attempt to generate menu entries for them. + +@command{grub-mkconfig} does have some limitations. While adding extra +custom menu entries to the end of the list can be done by editing +@file{/etc/grub.d/40_custom} or creating @file{/boot/grub/custom.cfg}, +changing the order of menu entries or changing their titles may require +making complex changes to shell scripts stored in @file{/etc/grub.d/}. This +may be improved in the future. In the meantime, those who feel that it +would be easier to write @file{grub.cfg} directly are encouraged to do so +(@pxref{Booting}, and @ref{Shell-like scripting}), and to disable any system +provided by their distribution to automatically run @command{grub-mkconfig}. + +The file @file{/etc/default/grub} controls the operation of +@command{grub-mkconfig}. It is sourced by a shell script, and so must be +valid POSIX shell input; normally, it will just be a sequence of +@samp{KEY=value} lines, but if the value contains spaces or other special +characters then it must be quoted. For example: + +@example +GRUB_TERMINAL_INPUT="console serial" +@end example + +Valid keys in @file{/etc/default/grub} are as follows: + +@table @samp +@item GRUB_DEFAULT +The default menu entry. This may be a number, in which case it identifies +the Nth entry in the generated menu counted from zero, or the title of a +menu entry, or the special string @samp{saved}. Using the id may be +useful if you want to set a menu entry as the default even though there may +be a variable number of entries before it. + +For example, if you have: + +@verbatim +menuentry 'Example GNU/Linux distribution' --class gnu-linux --id example-gnu-linux { + ... +} +@end verbatim + +then you can make this the default using: + +@example +GRUB_DEFAULT=example-gnu-linux +@end example + +Previously it was documented the way to use entry title. While this still +works it's not recommended since titles often contain unstable device names +and may be translated + +If you set this to @samp{saved}, then the default menu entry will be that +saved by @samp{GRUB_SAVEDEFAULT} or @command{grub-set-default}. This relies on +the environment block, which may not be available in all situations +(@pxref{Environment block}). + +The default is @samp{0}. + +@item GRUB_SAVEDEFAULT +If this option is set to @samp{true}, then, when an entry is selected, save +it as a new default entry for use by future runs of GRUB. This is only +useful if @samp{GRUB_DEFAULT=saved}; it is a separate option because +@samp{GRUB_DEFAULT=saved} is useful without this option, in conjunction with +@command{grub-set-default}. Unset by default. +This option relies on the environment block, which may not be available in +all situations (@pxref{Environment block}). + +@item GRUB_TIMEOUT +Boot the default entry this many seconds after the menu is displayed, unless +a key is pressed. The default is @samp{5}. Set to @samp{0} to boot +immediately without displaying the menu, or to @samp{-1} to wait +indefinitely. + +If @samp{GRUB_TIMEOUT_STYLE} is set to @samp{countdown} or @samp{hidden}, +the timeout is instead counted before the menu is displayed. + +@item GRUB_TIMEOUT_STYLE +If this option is unset or set to @samp{menu}, then GRUB will display the +menu and then wait for the timeout set by @samp{GRUB_TIMEOUT} to expire +before booting the default entry. Pressing a key interrupts the timeout. + +If this option is set to @samp{countdown} or @samp{hidden}, then, before +displaying the menu, GRUB will wait for the timeout set by @samp{GRUB_TIMEOUT} +to expire. If @key{ESC} or @key{F4} are pressed, or @key{SHIFT} is held down +during that time, it will display the menu and wait for input. If a hotkey +associated with a menu entry is pressed, it will boot the associated menu entry +immediately. If the timeout expires before either of these happens, it will +boot the default entry. In the @samp{countdown} case, it will show a one-line +indication of the remaining time. + +@item GRUB_DEFAULT_BUTTON +@itemx GRUB_TIMEOUT_BUTTON +@itemx GRUB_TIMEOUT_STYLE_BUTTON +@itemx GRUB_BUTTON_CMOS_ADDRESS +Variants of the corresponding variables without the @samp{_BUTTON} suffix, +used to support vendor-specific power buttons. @xref{Vendor power-on keys}. + +@item GRUB_DISTRIBUTOR +Set by distributors of GRUB to their identifying name. This is used to +generate more informative menu entry titles. + +@item GRUB_TERMINAL_INPUT +Select the terminal input device. You may select multiple devices here, +separated by spaces. + +Valid terminal input names depend on the platform, but may include +@samp{console} (native platform console), @samp{serial} (serial terminal), +@samp{serial_} (serial terminal with explicit port selection), +@samp{at_keyboard} (PC AT keyboard), or @samp{usb_keyboard} (USB keyboard +using the HID Boot Protocol, for cases where the firmware does not handle +this). + +The default is to use the platform's native terminal input. + +@item GRUB_TERMINAL_OUTPUT +Select the terminal output device. You may select multiple devices here, +separated by spaces. + +Valid terminal output names depend on the platform, but may include +@samp{console} (native platform console), @samp{serial} (serial terminal), +@samp{serial_} (serial terminal with explicit port selection), +@samp{gfxterm} (graphics-mode output), @samp{vga_text} (VGA text output), +@samp{mda_text} (MDA text output), @samp{morse} (Morse-coding using system +beeper) or @samp{spkmodem} (simple data protocol using system speaker). + +@samp{spkmodem} is useful when no serial port is available. Connect the output +of sending system (where GRUB is running) to line-in of receiving system +(usually developer machine). +On receiving system compile @samp{spkmodem-recv} from +@samp{util/spkmodem-recv.c} and run: + +@example +parecord --channels=1 --rate=48000 --format=s16le | ./spkmodem-recv +@end example + +The default is to use the platform's native terminal output. + +@item GRUB_TERMINAL +If this option is set, it overrides both @samp{GRUB_TERMINAL_INPUT} and +@samp{GRUB_TERMINAL_OUTPUT} to the same value. + +@item GRUB_SERIAL_COMMAND +A command to configure the serial port when using the serial console. +@xref{serial}. Defaults to @samp{serial}. + +@item GRUB_CMDLINE_LINUX +Command-line arguments to add to menu entries for the Linux kernel. + +@item GRUB_CMDLINE_LINUX_DEFAULT +Unless @samp{GRUB_DISABLE_RECOVERY} is set to @samp{true}, two menu +entries will be generated for each Linux kernel: one default entry and one +entry for recovery mode. This option lists command-line arguments to add +only to the default menu entry, after those listed in +@samp{GRUB_CMDLINE_LINUX}. + +@item GRUB_CMDLINE_LINUX_RECOVERY +Unless @samp{GRUB_DISABLE_RECOVERY} is set to @samp{true}, two menu +entries will be generated for each Linux kernel: one default entry and one +entry for recovery mode. This option lists command-line arguments to add +only to the recovery menu entry, before those listed in @samp{GRUB_CMDLINE_LINUX}. +The default is @samp{single}. + +@item GRUB_CMDLINE_NETBSD +@itemx GRUB_CMDLINE_NETBSD_DEFAULT +As @samp{GRUB_CMDLINE_LINUX} and @samp{GRUB_CMDLINE_LINUX_DEFAULT}, but for +NetBSD. + +@item GRUB_CMDLINE_GNUMACH +As @samp{GRUB_CMDLINE_LINUX}, but for GNU Mach. + +@item GRUB_CMDLINE_XEN +@itemx GRUB_CMDLINE_XEN_DEFAULT +The values of these options are passed to Xen hypervisor Xen menu entries, +for all respectively normal entries. + +@item GRUB_CMDLINE_LINUX_XEN_REPLACE +@item GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT +The values of these options replace the values of @samp{GRUB_CMDLINE_LINUX} +and @samp{GRUB_CMDLINE_LINUX_DEFAULT} for Linux and Xen menu entries. + +@item GRUB_TOP_LEVEL +@item GRUB_TOP_LEVEL_XEN +This option should be an absolute path to a kernel image. If provided, the +image specified will be made the top-level entry if it is found in the scan. + +@item GRUB_TOP_LEVEL_OS_PROBER +This option should be a line of output from @command{os-prober}. As +@samp{GRUB_TOP_LEVEL}, if provided, the image specified will be made the +top-level entry if it is found in the scan. + +@item GRUB_EARLY_INITRD_LINUX_CUSTOM +@itemx GRUB_EARLY_INITRD_LINUX_STOCK +List of space-separated early initrd images to be loaded from @samp{/boot}. +This is for loading things like CPU microcode, firmware, ACPI tables, crypto +keys, and so on. These early images will be loaded in the order declared, +and all will be loaded before the actual functional initrd image. + +@samp{GRUB_EARLY_INITRD_LINUX_STOCK} is for your distribution to declare +images that are provided by the distribution. It should not be modified +without understanding the consequences. They will be loaded first. + +@samp{GRUB_EARLY_INITRD_LINUX_CUSTOM} is for your custom created images. + +The default stock images are as follows, though they may be overridden by +your distribution: +@example +intel-uc.img intel-ucode.img amd-uc.img amd-ucode.img early_ucode.cpio microcode.cpio +@end example + +@item GRUB_DISABLE_LINUX_UUID +Normally, @command{grub-mkconfig} will generate menu entries that use +universally-unique identifiers (UUIDs) to identify the root filesystem to +the Linux kernel, using a @samp{root=UUID=...} kernel parameter. This is +usually more reliable, but in some cases it may not be appropriate. To +disable the use of UUIDs, set this option to @samp{true}. + +@item GRUB_DISABLE_LINUX_PARTUUID +If @command{grub-mkconfig} cannot identify the root filesystem via its +universally-unique indentifier (UUID), @command{grub-mkconfig} can use the UUID +of the partition containing the filesystem to identify the root filesystem to +the Linux kernel via a @samp{root=PARTUUID=...} kernel parameter. This is not +as reliable as using the filesystem UUID, but is more reliable than using the +Linux device names. When @samp{GRUB_DISABLE_LINUX_PARTUUID} is set to +@samp{false}, the Linux kernel version must be 2.6.37 (3.10 for systems using +the MSDOS partition scheme) or newer. This option defaults to @samp{true}. To +enable the use of partition UUIDs, set this option to @samp{false}. + +@item GRUB_DISABLE_RECOVERY +If this option is set to @samp{true}, disable the generation of recovery +mode menu entries. + +@item GRUB_DISABLE_UUID +Normally, @command{grub-mkconfig} will generate menu entries that use +universally-unique identifiers (UUIDs) to identify various filesystems to +search for files. This is usually more reliable, but in some cases it may +not be appropriate. To disable this use of UUIDs, set this option to +@samp{true}. Setting this option to @samp{true}, will also set the options +@samp{GRUB_DISABLE_LINUX_UUID} and @samp{GRUB_DISABLE_LINUX_PARTUUID} to +@samp{true}, unless they have been explicitly set to @samp{false}. + +@item GRUB_VIDEO_BACKEND +If graphical video support is required, either because the @samp{gfxterm} +graphical terminal is in use or because @samp{GRUB_GFXPAYLOAD_LINUX} is set, +then @command{grub-mkconfig} will normally load all available GRUB video +drivers and use the one most appropriate for your hardware. If you need to +override this for some reason, then you can set this option. + +After @command{grub-install} has been run, the available video drivers are +listed in @file{/boot/grub/video.lst}. + +@item GRUB_GFXMODE +Set the resolution used on the @samp{gfxterm} graphical terminal. Note that +you can only use modes which your graphics card supports via VESA BIOS +Extensions (VBE), so for example native LCD panel resolutions may not be +available. The default is @samp{auto}, which tries to select a preferred +resolution. @xref{gfxmode}. + +@item GRUB_BACKGROUND +Set a background image for use with the @samp{gfxterm} graphical terminal. +The value of this option must be a file readable by GRUB at boot time, and +it must end with @file{.png}, @file{.tga}, @file{.jpg}, or @file{.jpeg}. +The image will be scaled if necessary to fit the screen. Image height and +width will be restricted by an artificial limit of 16384. + +@item GRUB_THEME +Set a theme for use with the @samp{gfxterm} graphical terminal. + +@item GRUB_GFXPAYLOAD_LINUX +Set to @samp{text} to force the Linux kernel to boot in normal text mode, +@samp{keep} to preserve the graphics mode set using @samp{GRUB_GFXMODE}, +@samp{@var{width}x@var{height}}[@samp{x@var{depth}}] to set a particular +graphics mode, or a sequence of these separated by commas or semicolons to +try several modes in sequence. @xref{gfxpayload}. + +Depending on your kernel, your distribution, your graphics card, and the +phase of the moon, note that using this option may cause GNU/Linux to suffer +from various display problems, particularly during the early part of the +boot sequence. If you have problems, set this option to @samp{text} and +GRUB will tell Linux to boot in normal text mode. + +@item GRUB_DISABLE_OS_PROBER +The @command{grub-mkconfig} has a feature to use the external +@command{os-prober} program to discover other operating systems installed on +the same machine and generate appropriate menu entries for them. It is disabled +by default since automatic and silent execution of @command{os-prober}, and +creating boot entries based on that data, is a potential attack vector. Set +this option to @samp{false} to enable this feature in the +@command{grub-mkconfig} command. + +@item GRUB_OS_PROBER_SKIP_LIST +List of space-separated case insensitive UUIDs of filesystems to be ignored +from os-prober output. For EFI chainloaders it's @@. For +backward compatibility with previous behaviour, @@/dev/* is also accepted +for non-EFI chainloaders even if the device does not match, and comma and +semicolon are also accepted as separator. + +@item GRUB_DISABLE_SUBMENU +Normally, @command{grub-mkconfig} will generate top level menu entry for +the kernel with highest version number and put all other found kernels +or alternative menu entries for recovery mode in submenu. For entries returned +by @command{os-prober} first entry will be put on top level and all others +in submenu. If this option is set to @samp{true}, flat menu with all entries +on top level will be generated instead. Changing this option will require +changing existing values of @samp{GRUB_DEFAULT}, @samp{fallback} (@pxref{fallback}) +and @samp{default} (@pxref{default}) environment variables as well as saved +default entry using @command{grub-set-default} and value used with +@command{grub-reboot}. + +@item GRUB_ENABLE_CRYPTODISK +If set to @samp{y}, @command{grub-mkconfig} and @command{grub-install} will +check for encrypted disks and generate additional commands needed to access +them during boot. Note that in this case unattended boot is not possible +because GRUB will wait for passphrase to unlock encrypted container. + +@item GRUB_INIT_TUNE +Play a tune on the speaker when GRUB starts. This is particularly useful +for users unable to see the screen. The value of this option is passed +directly to @ref{play}. + +@item GRUB_BADRAM +If this option is set, GRUB will issue a @ref{badram} command to filter +out specified regions of RAM. + +@item GRUB_PRELOAD_MODULES +This option may be set to a list of GRUB module names separated by spaces. +Each module will be loaded as early as possible, at the start of +@file{grub.cfg}. + +@end table + +The following options are still accepted for compatibility with existing +configurations, but have better replacements: + +@table @samp +@item GRUB_HIDDEN_TIMEOUT +Wait this many seconds before displaying the menu. If @key{ESC} or @key{F4} are +pressed, or @key{SHIFT} is held down during that time, display the menu and wait +for input according to @samp{GRUB_TIMEOUT}. If a hotkey associated with a menu +entry is pressed, boot the associated menu entry immediately. If the timeout +expires before either of these happens, display the menu for the number of +seconds specified in @samp{GRUB_TIMEOUT} before booting the default entry. + +If you set @samp{GRUB_HIDDEN_TIMEOUT}, you should also set +@samp{GRUB_TIMEOUT=0} so that the menu is not displayed at all unless +@key{ESC} or @key{F4} are pressed, or @key{SHIFT} is held down. + +This option is unset by default, and is deprecated in favour of the less +confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or +@samp{GRUB_TIMEOUT_STYLE=hidden}. + +@item GRUB_HIDDEN_TIMEOUT_QUIET +In conjunction with @samp{GRUB_HIDDEN_TIMEOUT}, set this to @samp{true} to +suppress the verbose countdown while waiting for a key to be pressed before +displaying the menu. + +This option is unset by default, and is deprecated in favour of the less +confusing @samp{GRUB_TIMEOUT_STYLE=countdown}. + +@item GRUB_HIDDEN_TIMEOUT_BUTTON +Variant of @samp{GRUB_HIDDEN_TIMEOUT}, used to support vendor-specific power +buttons. @xref{Vendor power-on keys}. + +This option is unset by default, and is deprecated in favour of the less +confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or +@samp{GRUB_TIMEOUT_STYLE=hidden}. + +@end table + +For more detailed customisation of @command{grub-mkconfig}'s output, you may +edit the scripts in @file{/etc/grub.d} directly. +@file{/etc/grub.d/40_custom} is particularly useful for adding entire custom +menu entries; simply type the menu entries you want to add at the end of +that file, making sure to leave at least the first two lines intact. + +@node Root Identification Heuristics +@section Root Identification Heuristics +If the target operating system uses the Linux kernel, @command{grub-mkconfig} +attempts to identify the root file system via a heuristic algoirthm. This +algorithm selects the identification method of the root file system by +considering three factors. The first is if an initrd for the target operating +system is also present. The second is @samp{GRUB_DISABLE_LINUX_UUID} and if set +to @samp{true}, prevents @command{grub-mkconfig} from identifying the root file +system by its UUID. The third is @samp{GRUB_DISABLE_LINUX_PARTUUID} and if set +to @samp{true}, prevents @command{grub-mkconfig} from identifying the root file +system via the UUID of its enclosing partition. If the variables are assigned +any other value, that value is considered equivalent to @samp{false}. The +variables are also considered to be set to @samp{false} if they are not set. + +When booting, the Linux kernel will delegate the task of mounting the root +filesystem to the initrd. Most initrd images determine the root file system by +checking the Linux kernel's command-line for the @samp{root} key and use its +value as the identification method of the root file system. To improve the +reliability of booting, most initrd images also allow the root file system to be +identified by its UUID. Because of this behavior, the @command{grub-mkconfig} +command will set @samp{root} to @samp{root=UUID=...} to provide the initrd with +the filesystem UUID of the root file system. + +If no initrd is detected or @samp{GRUB_DISABLE_LINUX_UUID} is set to @samp{true} +then @command{grub-command} will identify the root filesystem by setting the +kernel command-line variable @samp{root} to @samp{root=PARTUUID=...} unless +@samp{GRUB_DISABLE_LINUX_PARTUUID} is also set to @samp{true}. If +@samp{GRUB_DISABLE_LINUX_PARTUUID} is also set to @samp{true}, +@command{grub-command} will identify by its Linux device name. + +The following table summarizes the behavior of the @command{grub-mkconfig} +command. + +@multitable {detected} {GRUB_DISABLE_LINUX_PARTUUID} {GRUB_DISABLE_LINUX_UUID} {Linux Root} +@headitem Initrd detected @tab GRUB_DISABLE_LINUX_PARTUUID Set To @tab GRUB_DISABLE_LINUX_UUID Set To @tab Linux Root ID Method +@item false @tab false @tab false @tab part UUID +@item false @tab false @tab true @tab part UUID +@item false @tab true @tab false @tab dev name +@item false @tab true @tab true @tab dev name +@item true @tab false @tab false @tab fs UUID +@item true @tab false @tab true @tab part UUID +@item true @tab true @tab false @tab fs UUID +@item true @tab true @tab true @tab dev name +@end multitable + +Remember, @samp{GRUB_DISABLE_LINUX_PARTUUID} and @samp{GRUB_DISABLE_LINUX_UUID} +are also considered to be set to @samp{true} and @samp{false}, respectively, +when they are unset. + +@node Shell-like scripting +@section Writing full configuration files directly + +@c Some of this section is derived from the GNU Bash manual page, also +@c copyrighted by the FSF. + +@file{grub.cfg} is written in GRUB's built-in scripting language, which has +a syntax quite similar to that of GNU Bash and other Bourne shell +derivatives. + +@heading Words + +A @dfn{word} is a sequence of characters considered as a single unit by +GRUB. Words are separated by @dfn{metacharacters}, which are the following +plus space, tab, and newline: + +@example +@{ @} | & $ ; < > +@end example + +Quoting may be used to include metacharacters in words; see below. + +@heading Reserved words + +Reserved words have a special meaning to GRUB. The following words are +recognised as reserved when unquoted and either the first word of a simple +command or the third word of a @code{for} command: + +@example +! [[ ]] @{ @} +case do done elif else esac fi for function +if in menuentry select then time until while +@end example + +Not all of these reserved words have a useful purpose yet; some are reserved +for future expansion. + +@heading Quoting + +Quoting is used to remove the special meaning of certain characters or +words. It can be used to treat metacharacters as part of a word, to prevent +reserved words from being recognised as such, and to prevent variable +expansion. + +There are three quoting mechanisms: the escape character, single quotes, and +double quotes. + +A non-quoted backslash (\) is the @dfn{escape character}. It preserves the +literal value of the next character that follows, with the exception of +newline. + +Enclosing characters in single quotes preserves the literal value of each +character within the quotes. A single quote may not occur between single +quotes, even when preceded by a backslash. + +Enclosing characters in double quotes preserves the literal value of all +characters within the quotes, with the exception of @samp{$} and @samp{\}. +The @samp{$} character retains its special meaning within double quotes. +The backslash retains its special meaning only when followed by one of the +following characters: @samp{$}, @samp{"}, @samp{\}, or newline. A +backslash-newline pair is treated as a line continuation (that is, it is +removed from the input stream and effectively ignored@footnote{Currently a +backslash-newline pair within a variable name is not handled properly, so +use this feature with some care.}). A double quote may be quoted within +double quotes by preceding it with a backslash. + +@heading Variable expansion + +The @samp{$} character introduces variable expansion. The variable name to +be expanded may be enclosed in braces, which are optional but serve to +protect the variable to be expanded from characters immediately following it +which could be interpreted as part of the name. + +Normal variable names begin with an alphabetic character, followed by zero +or more alphanumeric characters. These names refer to entries in the GRUB +environment (@pxref{Environment}). + +Positional variable names consist of one or more digits. They represent +parameters passed to function calls, with @samp{$1} representing the first +parameter, and so on. + +The special variable name @samp{?} expands to the exit status of the most +recently executed command. When positional variable names are active, other +special variable names @samp{@@}, @samp{*} and @samp{#} are defined and they +expand to all positional parameters with necessary quoting, positional +parameters without any quoting, and positional parameter count respectively. + +@heading Comments + +A word beginning with @samp{#} causes that word and all remaining characters +on that line to be ignored. + +@heading Simple commands + +A @dfn{simple command} is a sequence of words separated by spaces or tabs +and terminated by a semicolon or a newline. The first word specifies the +command to be executed. The remaining words are passed as arguments to the +invoked command. + +The return value of a simple command is its exit status. If the reserved +word @code{!} precedes the command, then the return value is instead the +logical negation of the command's exit status. + +@heading Compound commands + +A @dfn{compound command} is one of the following: + +@table @asis +@item for @var{name} in @var{word} @dots{}; do @var{list}; done +The list of words following @code{in} is expanded, generating a list of +items. The variable @var{name} is set to each element of this list in turn, +and @var{list} is executed each time. The return value is the exit status +of the last command that executes. If the expansion of the items following +@code{in} results in an empty list, no commands are executed, and the return +status is 0. + +@item if @var{list}; then @var{list}; [elif @var{list}; then @var{list};] @dots{} [else @var{list};] fi +The @code{if} @var{list} is executed, where @var{list} is a series of +@dfn{simple command}s separated by a ";". If its exit status of the last +command is zero, the @code{then} @var{list} is executed. Otherwise, each +@code{elif} @var{list} is executed in turn, and if its last command's exit +status is zero, the corresponding @code{then} @var{list} is executed and the +command completes. Otherwise, the @code{else} @var{list} is executed, if +present. The exit status is the exit status of the last command executed, or +zero if no condition tested true. + +@item while @var{cond}; do @var{list}; done +@itemx until @var{cond}; do @var{list}; done +The @code{while} command continuously executes the @code{do} @var{list} as +long as the last command in @var{cond} returns an exit status of zero, where +@var{cond} is a list of @dfn{simple command}s separated by a ";". The +@code{until} command is identical to the @code{while} command, except that +the test is negated; the @code{do} @var{list} is executed as long as the +last command in @var{cond} returns a non-zero exit status. The exit status +of the @code{while} and @code{until} commands is the exit status of the last +@code{do} @var{list} command executed, or zero if none was executed. + +@item function @var{name} @{ @var{command}; @dots{} @} +This defines a function named @var{name}. The @dfn{body} of the function is +the list of commands within braces, each of which must be terminated with a +semicolon or a newline. This list of commands will be executed whenever +@var{name} is specified as the name of a simple command. Function +definitions do not affect the exit status in @code{$?}. When executed, the +exit status of a function is the exit status of the last command executed in +the body. + +@item menuentry @var{title} [@option{--class=class} @dots{}] [@option{--users=users}] [@option{--unrestricted}] [@option{--hotkey=key}] [@option{--id=id}] @{ @var{command}; @dots{} @} +@xref{menuentry}. +@end table + +@heading Built-in Commands + +Some built-in commands are also provided by GRUB script to help script +writers perform actions that are otherwise not possible. For example, these +include commands to jump out of a loop without fully completing it, etc. + +@table @asis +@item break [@code{n}] +Exit from within a @code{for}, @code{while}, or @code{until} loop. If +@code{n} is specified, break @code{n} levels. @code{n} must be greater than +or equal to 1. If @code{n} is greater than the number of enclosing loops, +all enclosing loops are exited. The return value is 0 unless @code{n} is +not greater than or equal to 1. + +@item continue [@code{n}] +Resume the next iteration of the enclosing @code{for}, @code{while} or +@code{until} loop. If @code{n} is specified, resume at the @code{n}th +enclosing loop. @code{n} must be greater than or equal to 1. If @code{n} +is greater than the number of enclosing loops, the last enclosing loop (the +@dfn{top-level} loop) is resumed. The return value is 0 unless @code{n} is +not greater than or equal to 1. + +@item return [@code{n}] +Causes a function to exit with the return value specified by @code{n}. If +@code{n} is omitted, the return status is that of the last command executed +in the function body. If used outside a function the return status is +false. + +@item setparams [@code{arg}] @dots{} +Replace positional parameters starting with @code{$1} with arguments to +@command{setparams}. + +@item shift [@code{n}] +The positional parameters from @code{n}+1 @dots{} are renamed to +@code{$1}@dots{}. Parameters represented by the numbers @code{$#} down to +@code{$#}-@code{n}+1 are unset. @code{n} must be a non-negative number less +than or equal to @code{$#}. If @code{n} is 0, no parameters are changed. +If @code{n} is not given, it is assumed to be 1. If @code{n} is greater +than @code{$#}, the positional parameters are not changed. The return +status is greater than zero if @code{n} is greater than @code{$#} or less +than zero; otherwise 0. + +@end table + +@node Multi-boot manual config +@section Multi-boot manual config + +Currently autogenerating config files for multi-boot environments depends on +os-prober and has several shortcomings. Due to that it is disabled by default. +It is advised to use the power of GRUB syntax and do it yourself. A possible +configuration is detailed here, feel free to adjust to your needs. + +First create a separate GRUB partition, big enough to hold GRUB. Some of the +following entries show how to load OS installer images from this same partition, +for that you obviously need to make the partition large enough to hold those +images as well. +Mount this partition on/mnt/boot and disable GRUB in all OSes and manually +install self-compiled latest GRUB with: + +@code{grub-install --boot-directory=/mnt/boot /dev/sda} + +In all the OSes install GRUB tools but disable installing GRUB in bootsector, +so you'll have menu.lst and grub.cfg available for use. Also disable os-prober +use by setting: + +@code{GRUB_DISABLE_OS_PROBER=true} + +in /etc/default/grub + +Then write a grub.cfg (/mnt/boot/grub/grub.cfg): + +@example + +menuentry "OS using grub2" @{ + insmod xfs + search --set=root --label OS1 --hint hd0,msdos8 + configfile /boot/grub/grub.cfg +@} + +menuentry "OS using grub2-legacy" @{ + insmod ext2 + search --set=root --label OS2 --hint hd0,msdos6 + legacy_configfile /boot/grub/menu.lst +@} + +menuentry "Windows XP" @{ + insmod ntfs + search --set=root --label WINDOWS_XP --hint hd0,msdos1 + ntldr /ntldr +@} + +menuentry "Windows 7" @{ + insmod ntfs + search --set=root --label WINDOWS_7 --hint hd0,msdos2 + ntldr /bootmgr +@} + +menuentry "FreeBSD" @{ + insmod zfs + search --set=root --label freepool --hint hd0,msdos7 + kfreebsd /freebsd@@/boot/kernel/kernel + kfreebsd_module_elf /freebsd@@/boot/kernel/opensolaris.ko + kfreebsd_module_elf /freebsd@@/boot/kernel/zfs.ko + kfreebsd_module /freebsd@@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + set kFreeBSD.vfs.root.mountfrom=zfs:freepool/freebsd + set kFreeBSD.hw.psm.synaptics_support=1 +@} + +menuentry "experimental GRUB" @{ + search --set=root --label GRUB --hint hd0,msdos5 + multiboot /experimental/grub/i386-pc/core.img +@} + +menuentry "Fedora 16 installer" @{ + search --set=root --label GRUB --hint hd0,msdos5 + linux /fedora/vmlinuz lang=en_US keymap=sg resolution=1280x800 + initrd /fedora/initrd.img +@} + +menuentry "Fedora rawhide installer" @{ + search --set=root --label GRUB --hint hd0,msdos5 + linux /fedora/vmlinuz repo=ftp://mirror.switch.ch/mirror/fedora/linux/development/rawhide/x86_64 lang=en_US keymap=sg resolution=1280x800 + initrd /fedora/initrd.img +@} + +menuentry "Debian sid installer" @{ + search --set=root --label GRUB --hint hd0,msdos5 + linux /debian/dists/sid/main/installer-amd64/current/images/hd-media/vmlinuz + initrd /debian/dists/sid/main/installer-amd64/current/images/hd-media/initrd.gz +@} + +@end example + +Notes: +@itemize +@item Argument to search after --label is FS LABEL. You can also use UUIDs with --fs-uuid UUID instead of --label LABEL. You could also use direct @code{root=hd0,msdosX} but this is not recommended due to device name instability. +@end itemize + +@node Embedded configuration +@section Embedding a configuration file into GRUB + +GRUB supports embedding a configuration file directly into the core image, +so that it is loaded before entering normal mode. This is useful, for +example, when it is not straightforward to find the real configuration file, +or when you need to debug problems with loading that file. +@command{grub-install} uses this feature when it is not using BIOS disk +functions or when installing to a different disk from the one containing +@file{/boot/grub}, in which case it needs to use the @command{search} +command (@pxref{search}) to find @file{/boot/grub}. + +To embed a configuration file, use the @option{-c} option to +@command{grub-mkimage}. The file is copied into the core image, so it may +reside anywhere on the file system, and may be removed after running +@command{grub-mkimage}. + +After the embedded configuration file (if any) is executed, GRUB will load +the @samp{normal} module (@pxref{normal}), which will then read the real +configuration file from @file{$prefix/grub.cfg}. By this point, the +@code{root} variable will also have been set to the root device name. For +example, @code{prefix} might be set to @samp{(hd0,1)/boot/grub}, and +@code{root} might be set to @samp{hd0,1}. Thus, in most cases, the embedded +configuration file only needs to set the @code{prefix} and @code{root} +variables, and then drop through to GRUB's normal processing. A typical +example of this might look like this: + +@example +@group +search.fs_uuid 01234567-89ab-cdef-0123-456789abcdef root +set prefix=($root)/boot/grub +@end group +@end example + +(The @samp{search_fs_uuid} module must be included in the core image for this +example to work.) + +In more complex cases, it may be useful to read other configuration files +directly from the embedded configuration file. This allows such things as +reading files not called @file{grub.cfg}, or reading files from a directory +other than that where GRUB's loadable modules are installed. To do this, +include the @samp{configfile} and @samp{normal} modules in the core image, +and embed a configuration file that uses the @command{configfile} command to +load another file. The following example of this also requires the +@command{echo}, @command{search_label}, and @command{test} modules to be +included in the core image: + +@example +@group +search.fs_label grub root +if [ -e /boot/grub/example/test1.cfg ]; then + set prefix=($root)/boot/grub + configfile /boot/grub/example/test1.cfg +else + if [ -e /boot/grub/example/test2.cfg ]; then + set prefix=($root)/boot/grub + configfile /boot/grub/example/test2.cfg + else + echo "Could not find an example configuration file!" + fi +fi +@end group +@end example + +The embedded configuration file may not contain menu entries directly, but +may only read them from elsewhere using @command{configfile}. + +@node Theme file format +@chapter Theme file format +@section Introduction +The GRUB graphical menu supports themes that can customize the layout and +appearance of the GRUB boot menu. The theme is configured through a plain +text file that specifies the layout of the various GUI components (including +the boot menu, timeout progress bar, and text messages) as well as the +appearance using colors, fonts, and images. Example is available in docs/example_theme.txt + +@section Theme Elements +@subsection Colors + +Colors can be specified in several ways: + +@itemize +@item HTML-style ``#RRGGBB'' or ``#RGB'' format, where *R*, *G*, and *B* are hexadecimal digits (e.g., ``#8899FF'') +@item as comma-separated decimal RGB values (e.g., ``128, 128, 255'') +@item with ``SVG 1.0 color names'' (e.g., ``cornflowerblue'') which must be specified in lowercase. +@end itemize +@subsection Fonts +The fonts GRUB uses ``PFF2 font format'' bitmap fonts. Fonts are specified +with full font names. Currently there is no +provision for a preference list of fonts, or deriving one font from another. +Fonts are loaded with the ``loadfont'' command in GRUB (@ref{loadfont}). To see the list of +loaded fonts, execute the ``lsfonts'' command (@ref{lsfonts}). If there are too many fonts to +fit on screen, do ``set pager=1'' before executing ``lsfonts''. + + +@subsection Progress Bar + +@float Figure, Pixmap-styled progress bar +@c @image{Theme_progress_bar,,,,png} +@end float + +@float Figure, Plain progress bar, drawn with solid color. +@c @image{Theme_progress_bar_filled,,,,png} +@end float + +Progress bars are used to display the remaining time before GRUB boots the +default menu entry. To create a progress bar that will display the remaining +time before automatic boot, simply create a ``progress_bar'' component with +the id ``__timeout__''. This indicates to GRUB that the progress bar should +be updated as time passes, and it should be made invisible if the countdown to +automatic boot is interrupted by the user. + +Progress bars may optionally have text displayed on them. This text is +controlled by variable ``text'' which contains a printf template with the +only argument %d is the number of seconds remaining. Additionally special +values ``@@TIMEOUT_NOTIFICATION_SHORT@@'', ``@@TIMEOUT_NOTIFICATION_MIDDLE@@'', +``@@TIMEOUT_NOTIFICATION_LONG@@'' are replaced with standard and translated +templates. + +@subsection Circular Progress Indicator + +@c @image{Theme_circular_progress,,,,.png} + +The circular progress indicator functions similarly to the progress bar. When +given an id of ``__timeout__'', GRUB updates the circular progress indicator's +value to indicate the time remaining. For the circular progress indicator, +there are two images used to render it: the *center* image, and the *tick* +image. The center image is rendered in the center of the component, while the +tick image is used to render each mark along the circumference of the +indicator. + + +@subsection Labels + +Text labels can be placed on the boot screen. The font, color, and horizontal +alignment can be specified for labels. If a label is given the id +``__timeout__'', then the ``text'' property for that label is also updated +with a message informing the user of the number of seconds remaining until +automatic boot. This is useful in case you want the text displayed somewhere +else instead of directly on the progress bar. + + +@subsection Boot Menu + +@c @image{Theme_boot_menu,,,,.png} + +The boot menu where GRUB displays the menu entries from the ``grub.cfg'' file. +It is a list of items, where each item has a title and an optional icon. The +icon is selected based on the *classes* specified for the menu entry. If +there is a PNG file named ``myclass.png'' in the ``grub/themes/icons'' +directory, it will be displayed for items which have the class *myclass*. The +boot menu can be customized in several ways, such as the font and color used +for the menu entry title, and by specifying styled boxes for the menu itself +and for the selected item highlight. + + +@subsection Styled Boxes + +One of the most important features for customizing the layout is the use of + *styled boxes*. A styled box is composed of 9 rectangular (and potentially +empty) regions, which are used to seamlessly draw the styled box on screen: + +@multitable @columnfractions 0.3 0.3 0.3 +@item Northwest (nw) @tab North (n) @tab Northeast (ne) +@item West (w) @tab Center (c) @tab East (e) +@item Southwest (sw) @tab South (s) @tab Southeast (se) +@end multitable + +To support any size of box on screen, the center slice and the slices for the +top, bottom, and sides are all scaled to the correct size for the component on +screen, using the following rules: + +@enumerate +@item The edge slices (north, south, east, and west) are scaled in the direction of the edge they are adjacent to. For instance, the west slice is scaled vertically. +@item The corner slices (northwest, northeast, southeast, and southwest) are not scaled. +@item The center slice is scaled to fill the remaining space in the middle. +@end enumerate + +As an example of how an image might be sliced up, consider the styled box +used for a terminal view. + +@float Figure, An example of the slices (in red) used for a terminal window. This drawing was created and sliced in Inkscape_, as the next section explains. +@c @image{Box_slice_example_terminal,,,,.png} +@end float + +@subsection Creating Styled Box Images + +The Inkscape_ scalable vector graphics editor is a very useful tool for +creating styled box images. One process that works well for slicing a drawing +into the necessary image slices is: + +@enumerate +@item Create or open the drawing you'd like use. +@item Create a new layer on the top of the layer stack. Make it visible. Select this layer as the current layer. +@item Draw 9 rectangles on your drawing where you'd like the slices to be. Clear the fill option, and set the stroke to 1 pixel wide solid stroke. The corners of the slices must meet precisely; if it is off by a single pixel, it will probably be evident when the styled box is rendered in the GRUB menu. You should probably go to File | Document Properties | Grids and enable a grid or create a guide (click on one of the rulers next to the drawing and drag over the drawing; release the mouse button to place the guide) to help place the rectangles precisely. +@item Right click on the center slice rectangle and choose Object Properties. Change the "Id" to ``slice_c`` and click Set. Repeat this for the remaining 8 rectangles, giving them Id values of ``slice_n``, ``slice_ne``, ``slice_e``, and so on according to the location. +@item Save the drawing. +@item Select all the slice rectangles. With the slice layer selected, you can simply press Ctrl+A to select all rectangles. The status bar should indicate that 9 rectangles are selected. +@item Click the layer hide icon for the slice layer in the layer palette. The rectangles will remain selected, even though they are hidden. +@item Choose File | Export Bitmap and check the *Batch export 9 selected objects* box. Make sure that *Hide all except selected* is unchecked. click *Export*. This will create PNG files in the same directory as the drawing, named after the slices. These can now be used for a styled box in a GRUB theme. +@end enumerate + +@section Theme File Manual + +The theme file is a plain text file. Lines that begin with ``#`` are ignored +and considered comments. (Note: This may not be the case if the previous line +ended where a value was expected.) + +The theme file contains two types of statements: +@enumerate +@item Global properties. +@item Component construction. +@end enumerate + +@subsection Global Properties + +@subsection Format + +Global properties are specified with the simple format: +@itemize +@item name1: value1 +@item name2: "value which may contain spaces" +@item name3: #88F +@end itemize + +In this example, name3 is assigned a color value. + + +@subsection Global Property List + +@multitable @columnfractions 0.3 0.6 +@item title-text + @tab Specifies the text to display at the top center of the screen as a title. +@item title-font + @tab Defines the font used for the title message at the top of the screen. +@item title-color + @tab Defines the color of the title message. +@item message-font + @tab Currently unused. Left for backward compatibility. +@item message-color + @tab Currently unused. Left for backward compatibility. +@item message-bg-color + @tab Currently unused. Left for backward compatibility. +@item desktop-image + @tab Specifies the image to use as the background. It will be scaled + to fit the screen size or proportionally scaled depending on the scale + method. +@item desktop-image-scale-method + @tab Specifies the scaling method for the *desktop-image*. Options are + ``stretch``, ``crop``, ``padding``, ``fitwidth``, ``fitheight``. + ``stretch`` for fitting the screen size. Otherwise it is proportional + scaling of a part of *desktop-image* to the part of the screen. + ``crop`` part of the *desktop-image* will be proportionally scaled to + fit the screen sizes. ``padding`` the entire *desktop-image* will be + contained on the screen. ``fitwidth`` for fitting the *desktop-image*'s + width with screen width. ``fitheight`` for fitting the *desktop-image*'s + height with the screen height. Default is ``stretch``. +@item desktop-image-h-align + @tab Specifies the horizontal alignment of the *desktop-image* if + *desktop-image-scale-method* isn't equeal to ``stretch``. Options are + ``left``, ``center``, ``right``. Default is ``center``. +@item desktop-image-v-align + @tab Specifies the vertical alignment of the *desktop-image* if + *desktop-image-scale-method* isn't equeal to ``stretch``. Options are + ``top``, ``center``, ``bottom``. Default is ``center``. +@item desktop-color + @tab Specifies the color for the background if *desktop-image* is not + specified. +@item terminal-box + @tab Specifies the file name pattern for the styled box slices used for the + command line terminal window. For example, ``terminal-box: terminal_*.png`` + will use the images ``terminal_c.png`` as the center area, ``terminal_n.png`` + as the north (top) edge, ``terminal_nw.png`` as the northwest (upper left) + corner, and so on. If the image for any slice is not found, it will simply + be left empty. +@item terminal-border + @tab Specifies the border width of the terminal window. +@item terminal-left + @tab Specifies the left coordinate of the terminal window. +@item terminal-top + @tab Specifies the top coordinate of the terminal window. +@item terminal-width + @tab Specifies the width of the terminal window. +@item terminal-height + @tab Specifies the height of the terminal window. +@end multitable + + +@subsection Component Construction + +Greater customizability comes is provided by components. A tree of components +forms the user interface. *Containers* are components that can contain other +components, and there is always a single root component which is an instance +of a *canvas* container. + +Components are created in the theme file by prefixing the type of component +with a '+' sign: + +@code{ + label @{ text="GRUB" font="aqui 11" color="#8FF" @} } + +properties of a component are specified as "name = value" (whitespace +surrounding tokens is optional and is ignored) where *value* may be: +@itemize +@item a single word (e.g., ``align = center``, ``color = #FF8080``), +@item a quoted string (e.g., ``text = "Hello, World!"``), or +@item a tuple (e.g., ``preferred_size = (120, 80)``). +@end itemize + +@subsection Component List + +The following is a list of the components and the properties they support. + +@itemize +@item label + A label displays a line of text. + + Properties: + @multitable @columnfractions 0.2 0.7 + @item id + @tab Set to ``__timeout__`` to display the time elapsed to an automatical + boot of the default entry. + @item text + @tab The text to display. If ``id`` is set to ``__timeout__`` and no + ``text`` property is set then the amount of seconds will be shown. + If set to ``@@KEYMAP_SHORT@@``, ``@@KEYMAP_MIDDLE@@`` or + ``@@KEYMAP_LONG@@`` then predefined hotkey information will be shown. + @item font + @tab The font to use for text display. + @item color + @tab The color of the text. + @item align + @tab The horizontal alignment of the text within the component. + Options are ``left``, ``center`` and ``right``. + @item visible + @tab Set to ``false`` to hide the label. + @end multitable + +@item image + A component that displays an image. The image is scaled to fit + the component. + + Properties: + + @multitable @columnfractions 0.2 0.7 + @item file + @tab The full path to the image file to load. + @end multitable + +@item progress_bar + Displays a horizontally oriented progress bar. It can be rendered using + simple solid filled rectangles, or using a pair of pixmap styled boxes. + + Properties: + + @multitable @columnfractions 0.2 0.7 + @item id + @tab Set to ``__timeout__`` to display the time elapsed to an automatical + boot of the default entry. + @item fg_color + @tab The foreground color for plain solid color rendering. + @item bg_color + @tab The background color for plain solid color rendering. + @item border_color + @tab The border color for plain solid color rendering. + @item text_color + @tab The text color. + @item bar_style + @tab The styled box specification for the frame of the progress bar. + Example: ``progress_frame_*.png`` + If the value is equal to ``highlight_style`` then no styled boxes + will be shown. + @item highlight_style + @tab The styled box specification for the highlighted region of the + progress bar. This box will be used to paint just the highlighted region + of the bar, and will be increased in size as the bar nears completion. + Example: ``progress_hl_*.png``. + If the value is equal to ``bar_style`` then no styled boxes + will be shown. + @item highlight_overlay + @tab If this option is set to ``true`` then the highlight box + side slices (every slice except the center slice) will overlay the + frame box side slices. And the center slice of the highlight box + can move all the way (from top to bottom), being drawn on the center + slice of the frame box. That way we can make a progress bar with + round-shaped edges so there won't be a free space from the highlight to + the frame in top and bottom scrollbar positions. Default is ``false``. + @item font + @tab The font to use for progress bar. + @item text + @tab The text to display on the progress bar. If the progress bar's ID + is set to ``__timeout__`` and the value of this property is set to + ``@@TIMEOUT_NOTIFICATION_SHORT@@``, ``@@TIMEOUT_NOTIFICATION_MIDDLE@@`` + or ``@@TIMEOUT_NOTIFICATION_LONG@@``, then GRUB will update this + property with an informative message as the timeout approaches. + @end multitable + +@item circular_progress + Displays a circular progress indicator. The appearance of this component + is determined by two images: the *center* image and the *tick* image. The + center image is generally larger and will be drawn in the center of the + component. Around the circumference of a circle within the component, the + tick image will be drawn a certain number of times, depending on the + properties of the component. + + Properties: + + @multitable @columnfractions 0.3 0.6 + @item id + @tab Set to ``__timeout__`` to display the time elapsed to an automatical + boot of the default entry. + @item center_bitmap + @tab The file name of the image to draw in the center of the component. + @item tick_bitmap + @tab The file name of the image to draw for the tick marks. + @item num_ticks + @tab The number of ticks that make up a full circle. + @item ticks_disappear + @tab Boolean value indicating whether tick marks should progressively appear, + or progressively disappear as *value* approaches *end*. Specify + ``true`` or ``false``. Default is ``false``. + @item start_angle + @tab The position of the first tick mark to appear or disappear. + Measured in "parrots", 1 "parrot" = 1 / 256 of the full circle. + Use values ``xxx deg`` or ``xxx \xc2\xb0`` to set the angle in degrees. + @end multitable + +@item boot_menu + Displays the GRUB boot menu. It allows selecting items and executing them. + + Properties: + + @multitable @columnfractions 0.4 0.5 + @item item_font + @tab The font to use for the menu item titles. + @item selected_item_font + @tab The font to use for the selected menu item, or ``inherit`` (the default) + to use ``item_font`` for the selected menu item as well. + @item item_color + @tab The color to use for the menu item titles. + @item selected_item_color + @tab The color to use for the selected menu item, or ``inherit`` (the default) + to use ``item_color`` for the selected menu item as well. + @item icon_width + @tab The width of menu item icons. Icons are scaled to the specified size. + @item icon_height + @tab The height of menu item icons. + @item item_height + @tab The height of each menu item in pixels. + @item item_padding + @tab The amount of space in pixels to leave on each side of the menu item + contents. + @item item_icon_space + @tab The space between an item's icon and the title text, in pixels. + @item item_spacing + @tab The amount of space to leave between menu items, in pixels. + @item menu_pixmap_style + @tab The image file pattern for the menu frame styled box. + Example: ``menu_*.png`` (this will use images such as ``menu_c.png``, + ``menu_w.png``, `menu_nw.png``, etc.) + @item item_pixmap_style + @tab The image file pattern for the item styled box. + @item selected_item_pixmap_style + @tab The image file pattern for the selected item highlight styled box. + @item scrollbar + @tab Boolean value indicating whether the scroll bar should be drawn if the + frame and thumb styled boxes are configured. + @item scrollbar_frame + @tab The image file pattern for the entire scroll bar. + Example: ``scrollbar_*.png`` + @item scrollbar_thumb + @tab The image file pattern for the scroll bar thumb (the part of the scroll + bar that moves as scrolling occurs). + Example: ``scrollbar_thumb_*.png`` + @item scrollbar_thumb_overlay + @tab If this option is set to ``true`` then the scrollbar thumb + side slices (every slice except the center slice) will overlay the + scrollbar frame side slices. And the center slice of the scrollbar_thumb + can move all the way (from top to bottom), being drawn on the center + slice of the scrollbar frame. That way we can make a scrollbar with + round-shaped edges so there won't be a free space from the thumb to + the frame in top and bottom scrollbar positions. Default is ``false``. + @item scrollbar_slice + @tab The menu frame styled box's slice in which the scrollbar will be + drawn. Possible values are ``west``, ``center``, ``east`` (default). + ``west`` - the scrollbar will be drawn in the west slice (right-aligned). + ``east`` - the scrollbar will be drawn in the east slice (left-aligned). + ``center`` - the scrollbar will be drawn in the center slice. + Note: in case of ``center`` slice: + a) If the scrollbar should be drawn then boot menu entry's width is + decreased by the scrollbar's width and the scrollbar is drawn at the + right side of the center slice. + b) If the scrollbar won't be drawn then the boot menu entry's width + is the width of the center slice. + c) We don't necessary need the menu pixmap box to display the scrollbar. + @item scrollbar_left_pad + @tab The left scrollbar padding in pixels. + Unused if ``scrollbar_slice`` is ``west``. + @item scrollbar_right_pad + @tab The right scrollbar padding in pixels. + Unused if ``scrollbar_slice`` is ``east``. + @item scrollbar_top_pad + @tab The top scrollbar padding in pixels. + @item scrollbar_bottom_pad + @tab The bottom scrollbar padding in pixels. + @item visible + @tab Set to ``false`` to hide the boot menu. + @end multitable + +@item canvas + Canvas is a container that allows manual placement of components within it. + It does not alter the positions of its child components. It assigns all + child components their preferred sizes. + +@item hbox + The *hbox* container lays out its children from left to right, giving each + one its preferred width. The height of each child is set to the maximum of + the preferred heights of all children. + +@item vbox + The *vbox* container lays out its children from top to bottom, giving each + one its preferred height. The width of each child is set to the maximum of + the preferred widths of all children. +@end itemize + + +@subsection Common properties + +The following properties are supported by all components: +@table @samp +@item left + The distance from the left border of container to left border of the object in either of three formats: + @multitable @columnfractions 0.2 0.7 + @item x @tab Value in pixels + @item p% @tab Percentage + @item p%+x @tab mixture of both + @end multitable +@item top + The distance from the left border of container to left border of the object in same format. +@item width + The width of object in same format. +@item height + The height of object in same format. +@item id + The identifier for the component. This can be any arbitrary string. + The ID can be used by scripts to refer to various components in the GUI + component tree. Currently, there is one special ID value that GRUB + recognizes: + + @multitable @columnfractions 0.2 0.7 + @item ``__timeout__`` + @tab Component with this ID will be updated by GRUB and will indicate + time elapsed to an automatical boot of the default entry. + Affected components: ``label``, ``circular_progress``, ``progress_bar``. + @end multitable +@end table + + + +@node Network +@chapter Booting GRUB from the network + +The following instructions don't work for *-emu, i386-qemu, i386-coreboot, +i386-multiboot, mips_loongson, mips-arc and mips_qemu_mips + +To generate a netbootable directory, run: + +@example +@group +grub-mknetdir --net-directory=/srv/tftp --subdir=/boot/grub -d /usr/lib/grub/ +@end group +@end example + +E.g. for i386-pc: + +@example +@group +grub-mknetdir --net-directory=/srv/tftp --subdir=/boot/grub -d /usr/lib/grub/i386-pc +@end group +@end example + +Then follow instructions printed out by grub-mknetdir on configuring your DHCP +server. + +The grub.cfg file is placed in the same directory as the path output by +grub-mknetdir hereafter referred to as FWPATH. GRUB will search for its +configuration files in order using the following rules where the appended +value corresponds to a value on the client machine. + +@example +@group +@samp{(FWPATH)}/grub.cfg-@samp{(UUID OF MACHINE)} +@samp{(FWPATH)}/grub.cfg-01-@samp{(MAC ADDRESS OF NIC)} +@samp{(FWPATH)}/grub.cfg-@samp{(IPv4 OR IPv6 ADDRESS)} +@samp{(FWPATH)}/grub.cfg +@end group +@end example + +The UUID is the Client Machine Identifier Option Definition as specified in +RFC 4578. The client will only attempt to look up a UUID config file if it +was provided by the DHCP server. + +The client will only attempt to look up an IPv6 address config once, however, +it will try the IPv4 multiple times. The concrete example below shows what +would happen under the IPv4 case. + +@example +@group +UUID: 7726a678-7fc0-4853-a4f6-c85ac36a120a +MAC: 52:54:00:ec:33:81 +IPV4: 10.0.0.130 (0A000082) +@end group +@end example + +@example +@group +@samp{(FWPATH)}/grub.cfg-7726a678-7fc0-4853-a4f6-c85ac36a120a +@samp{(FWPATH)}/grub.cfg-01-52-54-00-ec-33-81 +@samp{(FWPATH)}/grub.cfg-0A000082 +@samp{(FWPATH)}/grub.cfg-0A00008 +@samp{(FWPATH)}/grub.cfg-0A0000 +@samp{(FWPATH)}/grub.cfg-0A000 +@samp{(FWPATH)}/grub.cfg-0A00 +@samp{(FWPATH)}/grub.cfg-0A0 +@samp{(FWPATH)}/grub.cfg-0A +@samp{(FWPATH)}/grub.cfg-0 +@samp{(FWPATH)}/grub.cfg +@end group +@end example + +This feature is enabled by default but it can be disabled by setting the +@samp{feature_net_search_cfg} to @samp{n}. Since this happens before the +configuration file is read by GRUB, this option has to be disabled in an +embedded configuration file (@pxref{Embedded configuration}). + +After GRUB has started, files on the TFTP server will be accessible via the +@samp{(tftp)} device. + +The server IP address can be controlled by changing the +@samp{(tftp)} device name to @samp{(tftp,@var{server-ip})}. Note that +this should be changed both in the prefix and in any references to the +device name in the configuration file. + +GRUB provides several environment variables which may be used to inspect or +change the behaviour of the PXE device. In the following description +@var{} is placeholder for the name of network interface (platform +dependent): + +@table @samp +@item net_@var{}_ip +The network interface's IP address. Read-only. + +@item net_@var{}_mac +The network interface's MAC address. Read-only. + +@item net_@var{}_clientid +The client id provided by DHCP. Read-only. + +@item net_@var{}_clientuuid +The client uuid provided by DHCP. Read-only. + +@item net_@var{}_hostname +The client host name provided by DHCP. Read-only. + +@item net_@var{}_domain +The client domain name provided by DHCP. Read-only. + +@item net_@var{}_rootpath +The path to the client's root disk provided by DHCP. Read-only. + +@item net_@var{}_extensionspath +The path to additional DHCP vendor extensions provided by DHCP. Read-only. + +@item net_@var{}_boot_file +The boot file name provided by DHCP. Read-only. + +@item net_@var{}_dhcp_server_name +The name of the DHCP server responsible for these boot parameters. +Read-only. + +@item net_@var{}_next_server +The IP address of the next (usually, TFTP) server provided by DHCP. +Read-only. + +@item net_default_interface +Initially set to name of network interface that was used to load grub. +Read-write, although setting it affects only interpretation of +@samp{net_default_ip} and @samp{net_default_mac} + +@item net_default_ip +The IP address of default interface. Read-only. This is alias for the +@samp{net_$@{net_default_interface@}_ip}. + +@item net_default_mac +The default interface's MAC address. Read-only. This is alias for the +@samp{net_$@{net_default_interface@}_mac}. + +@item net_default_server +The default server used by network drives (@pxref{Device syntax}). Read-write, +although setting this is only useful before opening a network device. + +@item pxe_default_server +This performs the same function as @samp{net_default_server}. + +@end table @node Serial terminal @@ -712,42 +2708,265 @@ minicom. Refer to a manual of your operating system, for more information. As for GRUB, the instruction to set up a serial terminal is quite -simple. First of all, make sure that you haven't specified the option -@option{--disable-serial} to the configure script when you built your -GRUB images. If you get them in binary form, probably they have serial -terminal support already. - -Then, initialize your serial terminal after GRUB starts up. Here is an -example: +simple. Here is an example: @example @group grub> @kbd{serial --unit=0 --speed=9600} -grub> @kbd{terminal serial} +grub> @kbd{terminal_input serial; terminal_output serial} @end group @end example The command @command{serial} initializes the serial unit 0 with the speed 9600bps. The serial unit 0 is usually called @samp{COM1}, so, if you want to use COM2, you must specify @samp{--unit=1} instead. This -command accepts many other options, so please refer to @ref{serial}, -for more details. +command accepts many other options, @pxref{serial} for more details. -The command @command{terminal} (@pxref{terminal}) chooses which type of +Without argument or with @samp{--port=auto}, GRUB will attempt to use +ACPI when available to auto-detect the default serial port and its +configuration. + +The commands @command{terminal_input} (@pxref{terminal_input}) and +@command{terminal_output} (@pxref{terminal_output}) choose which type of terminal you want to use. In the case above, the terminal will be a serial terminal, but you can also pass @code{console} to the command, -as @samp{terminal serial console}. In this case, a terminal in which -you press any key will be selected as a GRUB terminal. +as @samp{terminal_input serial console}. In this case, a terminal in which +you press any key will be selected as a GRUB terminal. In the example above, +note that you need to put both commands on the same command line, as you +will lose the ability to type commands on the console after the first +command. However, note that GRUB assumes that your terminal emulator is compatible with VT100 by default. This is true for most terminal -emulators nowadays, but you should pass the option @option{--dumb} to -the command if your terminal emulator is not VT100-compatible or -implements few VT100 escape sequences. If you specify this option then -GRUB provides you with an alternative menu interface, because the normal -menu requires several fancy features of your terminal. +emulators nowadays. However if your terminal emulator is not VT100-compatible +or implements few VT100 escape sequences, you shoud tell GRUB that the +terminal is dumb using the @command{terminfo} (@pxref{terminfo}) command. +This will have GRUB provide you with an alternative menu interface, because +the normal menu requires several fancy features of your terminal. +@node Vendor power-on keys +@chapter Using GRUB with vendor power-on keys + +Some laptop vendors provide an additional power-on button which boots +another OS. GRUB supports such buttons with the @samp{GRUB_TIMEOUT_BUTTON}, +@samp{GRUB_TIMEOUT_STYLE_BUTTON}, @samp{GRUB_DEFAULT_BUTTON}, and +@samp{GRUB_BUTTON_CMOS_ADDRESS} variables in default/grub (@pxref{Simple +configuration}). @samp{GRUB_TIMEOUT_BUTTON}, +@samp{GRUB_TIMEOUT_STYLE_BUTTON}, and @samp{GRUB_DEFAULT_BUTTON} are used +instead of the corresponding variables without the @samp{_BUTTON} suffix +when powered on using the special button. @samp{GRUB_BUTTON_CMOS_ADDRESS} +is vendor-specific and partially model-specific. Values known to the GRUB +team are: + +@table @key +@item Dell XPS M1330M +121:3 +@item Dell XPS M1530 +85:3 +@item Dell Latitude E4300 +85:3 +@item Asus EeePC 1005PE +84:1 (unconfirmed) +@item LENOVO ThinkPad T410s (2912W1C) +101:3 +@end table + +To take full advantage of this function, install GRUB into the MBR +(@pxref{Installing GRUB using grub-install}). + +If you have a laptop which has a similar feature and not in the above list +could you figure your address and contribute? +To discover the address do the following: +@itemize +@item boot normally +@item +@example +sudo modprobe nvram +sudo cat /dev/nvram | xxd > normal_button.txt +@end example +@item boot using vendor button +@item +@example +sudo modprobe nvram +sudo cat /dev/nvram | xxd > normal_vendor.txt +@end example +@end itemize + +Then compare these text files and find where a bit was toggled. E.g. in +case of Dell XPS it was: +@example +byte 0x47: 20 --> 28 +@end example +It's a bit number 3 as seen from following table: +@multitable @columnfractions .2 .2 +@item 0 @tab 01 +@item 1 @tab 02 +@item 2 @tab 04 +@item 3 @tab 08 +@item 4 @tab 10 +@item 5 @tab 20 +@item 6 @tab 40 +@item 7 @tab 80 +@end multitable + +0x47 is decimal 71. Linux nvram implementation cuts first 14 bytes of +CMOS. So the real byte address in CMOS is 71+14=85 +So complete address is 85:3 + +@node Images +@chapter GRUB image files + +@c FIXME: parts of this section are specific to PC BIOS right now. + +GRUB consists of several images: a variety of bootstrap images for starting +GRUB in various ways, a kernel image, and a set of modules which are +combined with the kernel image to form a core image. Here is a short +overview of them. + +@table @file +@item boot.img +On PC BIOS systems, this image is the first part of GRUB to start. It is +written to a master boot record (MBR) or to the boot sector of a partition. +Because a PC boot sector is 512 bytes, the size of this image is exactly 512 +bytes. + +The sole function of @file{boot.img} is to read the first sector of the core +image from a local disk and jump to it. Because of the size restriction, +@file{boot.img} cannot understand any file system structure, so +@command{grub-install} hardcodes the location of the first sector of the +core image into @file{boot.img} when installing GRUB. + +@item diskboot.img +This image is used as the first sector of the core image when booting from a +hard disk. It reads the rest of the core image into memory and starts the +kernel. Since file system handling is not yet available, it encodes the +location of the core image using a block list format. + +@item cdboot.img +This image is used as the first sector of the core image when booting from a +CD-ROM drive. It performs a similar function to @file{diskboot.img}. + +@item pxeboot.img +This image is used as the start of the core image when booting from the +network using PXE. @xref{Network}. + +@item lnxboot.img +This image may be placed at the start of the core image in order to make +GRUB look enough like a Linux kernel that it can be booted by LILO using an +@samp{image=} section. + +@item kernel.img +This image contains GRUB's basic run-time facilities: frameworks for device +and file handling, environment variables, the rescue mode command-line +parser, and so on. It is rarely used directly, but is built into all core +images. + +@item core.img +This is the core image of GRUB. It is built dynamically from the kernel +image and an arbitrary list of modules by the @command{grub-mkimage} +program. Usually, it contains enough modules to access @file{/boot/grub}, +and loads everything else (including menu handling, the ability to load +target operating systems, and so on) from the file system at run-time. The +modular design allows the core image to be kept small, since the areas of +disk where it must be installed are often as small as 32KB. + +@xref{BIOS installation}, for details on where the core image can be +installed on PC systems. + +@item *.mod +Everything else in GRUB resides in dynamically loadable modules. These are +often loaded automatically, or built into the core image if they are +essential, but may also be loaded manually using the @command{insmod} +command (@pxref{insmod}). +@end table + +@heading For GRUB Legacy users + +GRUB 2 has a different design from GRUB Legacy, and so correspondences with +the images it used cannot be exact. Nevertheless, GRUB Legacy users often +ask questions in the terms they are familiar with, and so here is a brief +guide to how GRUB 2's images relate to that. + +@table @file +@item stage1 +Stage 1 from GRUB Legacy was very similar to @file{boot.img} in GRUB 2, and +they serve the same function. + +@item *_stage1_5 +In GRUB Legacy, Stage 1.5's function was to include enough filesystem code +to allow the much larger Stage 2 to be read from an ordinary filesystem. In +this respect, its function was similar to @file{core.img} in GRUB 2. +However, @file{core.img} is much more capable than Stage 1.5 was; since it +offers a rescue shell, it is sometimes possible to recover manually in the +event that it is unable to load any other modules, for example if partition +numbers have changed. @file{core.img} is built in a more flexible way, +allowing GRUB 2 to support reading modules from advanced disk types such as +LVM and RAID. + +GRUB Legacy could run with only Stage 1 and Stage 2 in some limited +configurations, while GRUB 2 requires @file{core.img} and cannot work +without it. + +@item stage2 +GRUB 2 has no single Stage 2 image. Instead, it loads modules from +@file{/boot/grub} at run-time. + +@item stage2_eltorito +In GRUB 2, images for booting from CD-ROM drives are now constructed using +@file{cdboot.img} and @file{core.img}, making sure that the core image +contains the @samp{iso9660} module. It is usually best to use the +@command{grub-mkrescue} program for this. + +@item nbgrub +There is as yet no equivalent for @file{nbgrub} in GRUB 2; it was used by +Etherboot and some other network boot loaders. + +@item pxegrub +In GRUB 2, images for PXE network booting are now constructed using +@file{pxeboot.img} and @file{core.img}, making sure that the core image +contains the @samp{pxe} and @samp{pxecmd} modules. @xref{Network}. +@end table + +@node Core image size limitation +@chapter Core image size limitation + +Heavily limited platforms: +@itemize +@item i386-pc (normal and PXE): the core image size (compressed) is limited by 458240 bytes. + kernel.img (.text + .data + .bss, uncompressed) is limited by 392704 bytes. + module size (uncompressed) + kernel.img (.text + .data, uncompressed) is limited by the size of contiguous chunk at 1M address. +@item sparc64-ieee1275: kernel.img (.text + .data + .bss) + modules + 256K (stack) + 2M (heap) is limited by space available at 0x4400. On most platforms it's just 3 or 4M since ieee1275 maps only so much. +@item i386-ieee1275: kernel.img (.text + .data + .bss) + modules is limited by memory available at 0x10000, at most 596K +@end itemize + +Lightly limited platforms: + +@itemize +@item *-xen: limited only by adress space and RAM size. +@item i386-qemu: kernel.img (.text + .data + .bss) is limited by 392704 bytes. + (core.img would be limited by ROM size but it's unlimited on qemu +@item All EFI platforms: limited by contiguous RAM size and possibly firmware bugs +@item Coreboot and multiboot. kernel.img (.text + .data + .bss) is limited by 392704 bytes. + module size is limited by the size of contiguous chunk at 1M address. +@item mipsel-loongson (ELF), mips(el)-qemu_mips (ELF): if uncompressed: + kernel.img (.text + .data) + modules is limited by the space from 80200000 forward + if compressed: + kernel.img (.text + .data, uncompressed) + modules (uncompressed) + + (modules + kernel.img (.text + .data)) (compressed) + + decompressor is limited by the space from 80200000 forward +@item mipsel-loongson (Flash), mips(el)-qemu_mips (Flash): kernel.img (.text + .data) + modules is limited by the space from 80200000 forward + core.img (final) is limited by flash size (512K on yeeloong and fulooong) +@item mips-arc: if uncompressed: + kernel.img (.text + .data) is limited by the space from 8bd00000 forward + modules + dummy decompressor is limited by the space from 8bd00000 backward + if compressed: + kernel.img (.text + .data, uncompressed) is limited by the space from 8bd00000 forward + modules (uncompressed) + (modules + kernel.img (.text + .data)) (compressed, aligned to 1M) + + 1M (decompressor + scratch space) is limited by the space from 8bd00000 backward +@item powerpc-ieee1275: kernel.img (.text + .data + .bss) + modules is limited by space available at 0x200000 +@end itemize + @node Filesystem @chapter Filesystem syntax and semantics @@ -755,8 +2974,8 @@ GRUB uses a special syntax for specifying disk drives which can be accessed by BIOS. Because of BIOS limitations, GRUB cannot distinguish between IDE, ESDI, SCSI, or others. You must know yourself which BIOS device is equivalent to which OS device. Normally, that will be clear if -you see the files in a device or use the command @command{find} -(@pxref{find}). +you see the files in a device or use the command @command{search} +(@pxref{search}). @menu * Device syntax:: How to specify devices @@ -771,42 +2990,87 @@ you see the files in a device or use the command @command{find} The device syntax is like this: @example -@code{(@var{device}[,@var{part-num}][,@var{bsd-subpart-letter}])} +@code{(@var{device}[,@var{partmap-name1}@var{part-num1}[,@var{partmap-name2}@var{part-num2}[,...]]])} @end example -@samp{[]} means the parameter is optional. @var{device} should be -either @samp{fd} or @samp{hd} followed by a digit, like @samp{fd0}. -But you can also set @var{device} to a hexadecimal or a decimal number -which is a BIOS drive number, so the following are equivalent: +@samp{[]} means the parameter is optional. @var{device} depends on the disk +driver in use. BIOS and EFI disks use either @samp{fd} or @samp{hd} followed +by a digit, like @samp{fd0}, or @samp{cd}. +AHCI, PATA (ata), crypto, USB use the name of driver followed by a number. +Memdisk and host are limited to one disk and so it's referred just by driver +name. +RAID (md), ofdisk (ieee1275 and nand), LVM (lvm), LDM, virtio (vdsk) +and arcdisk (arc) use intrinsic name of disk prefixed by driver name. +Additionally just ``nand'' refers to the disk aliased as ``nand''. +Conflicts are solved by suffixing a number if necessary. +Commas need to be escaped. +Loopback uses whatever name specified to @command{loopback} command. +Hostdisk uses names specified in device.map as long as it's of the form +[fhc]d[0-9]* or hostdisk/. +For crypto and RAID (md) additionally you can use the syntax +uuid/. For LVM additionally you can use the syntax +lvmid//. @example +(fd0) (hd0) -(0x80) -(128) +(cd) +(ahci0) +(ata0) +(crypto0) +(usb0) +(cryptouuid/123456789abcdef0123456789abcdef0) +(mduuid/123456789abcdef0123456789abcdef0) +(lvm/system-root) +(lvmid/F1ikgD-2RES-306G-il9M-7iwa-4NKW-EbV1NV/eLGuCQ-L4Ka-XUgR-sjtJ-ffch-bajr-fCNfz5) +(md/myraid) +(md/0) +(ieee1275/disk2) +(ieee1275//pci@@1f\,0/ide@@d/disk@@2) +(nand) +(memdisk) +(host) +(myloop) +(hostdisk//dev/sda) @end example @var{part-num} represents the partition number of @var{device}, starting -from one for primary partitions and from five for extended partitions, -and @var{bsd-subpart-letter} represents the BSD disklabel subpartition, -such as @samp{a} or @samp{e}. - -A shortcut for specifying BSD subpartitions is -@code{(@var{device},@var{bsd-subpart-letter})}, in this case, GRUB -searches for the first PC partition containing a BSD disklabel, then -finds the subpartition @var{bsd-subpart-letter}. Here is an example: - -@example -(hd0,a) -@end example +from one. @var{partname} is optional but is recommended since disk may have +several top-level partmaps. Specifying third and later component you can access +to subpartitions. The syntax @samp{(hd0)} represents using the entire disk (or the MBR when installing GRUB), while the syntax @samp{(hd0,1)} represents using the first partition of the disk (or the boot sector of the partition when installing GRUB). -If you enabled the network support, the special drive, @samp{(nd)}, is -also available. Before using the network drive, you must initialize the -network. @xref{Network}, for more information. +@example +(hd0,msdos1) +(hd0,msdos1,msdos5) +(hd0,msdos1,bsd3) +(hd0,netbsd1) +(hd0,gpt1) +(hd0,1,3) +@end example + +If you enabled the network support, the special drives +@code{(@var{protocol}[,@var{server}])} are also available. Supported protocols +are @samp{http} and @samp{tftp}. If @var{server} is omitted, value of +environment variable @samp{net_default_server} is used. +Before using the network drive, you must initialize the network. +@xref{Network}, for more information. + +When using @samp{http} or @samp{tftp}, ports other than @samp{80} can be +specified using a colon (@samp{:}) after the address. To avoid parsing +conflicts, when using IPv6 addresses with custom ports, the addresses +must be enclosed with square brackets (@samp{[]}), as is standard +practice. + +@example +(http,grub.example.com:31337) +(http,192.0.2.1:339) +(http,[2001:db8::1]:11235) +@end example If you boot GRUB from a CD-ROM, @samp{(cd)} is available. @xref{Making a GRUB bootable CD-ROM}, for details. @@ -824,8 +3088,15 @@ example is @samp{(hd0,1)/boot/grub/grub.cfg}. This means the file @file{/boot/grub/grub.cfg} in the first partition of the first hard disk. If you omit the device name in an absolute file name, GRUB uses GRUB's @dfn{root device} implicitly. So if you set the root device to, -say, @samp{(hd1,1)} by the command @command{root} (@pxref{root}), then -@code{/boot/kernel} is the same as @code{(hd1,1)/boot/kernel}. +say, @samp{(hd1,1)} by the command @samp{set root=(hd1,1)} (@pxref{set}), +then @code{/boot/kernel} is the same as @code{(hd1,1)/boot/kernel}. + +On ZFS filesystem the first path component must be +@var{volume}@samp{@@}[@var{snapshot}]. +So @samp{/rootvol@@snap-129/boot/grub/grub.cfg} refers to file +@samp{/boot/grub/grub.cfg} in snapshot of volume @samp{rootvol} with name +@samp{snap-129}. Trailing @samp{@@} after volume name is mandatory even if +snapshot name is omitted. @node Block list syntax @@ -833,16 +3104,18 @@ say, @samp{(hd1,1)} by the command @command{root} (@pxref{root}), then A block list is used for specifying a file that doesn't appear in the filesystem, like a chainloader. The syntax is -@code{[@var{offset}]+@var{length}[,[@var{offset}]+@var{length}]@dots{}}. +@code{[@var{offset}]+[@var{length}][,[@var{offset}]+[@var{length}]]@dots{}}. Here is an example: @example -@code{0+100,200+1,300+300} +@code{0+100,200+1,300+300,800+} @end example This represents that GRUB should read blocks 0 through 99, block 200, -and blocks 300 through 599. If you omit an offset, then GRUB assumes -the offset is zero. +blocks 300 through 599, and blocks 800 until the end of the device. +If you omit an offset, then GRUB assumes the offset is zero. If the +length is omitted, then GRUB assumes the block list extends until the +end of the device. Like the file name syntax (@pxref{File name syntax}), if a blocklist does not contain a device name, then GRUB uses GRUB's @dfn{root @@ -866,6 +3139,7 @@ the command-line interface. @menu * Command-line interface:: The flexible command-line interface * Menu interface:: The simple menu interface +* Menu entry editor:: Editing a menu entry @end menu @@ -876,9 +3150,8 @@ The command-line interface provides a prompt and after it an editable text area much like a command-line in Unix or DOS. Each command is immediately executed after it is entered@footnote{However, this behavior will be changed in the future version, in a user-invisible -way.}. The commands (@pxref{Command-line and menu entry commands}) are a -subset of those available in the configuration file, used with exactly -the same syntax. +way.}. The commands (@pxref{Commands}) are a subset of those available +in the configuration file, used with exactly the same syntax. Cursor movement and editing of the text on the line can be done via a subset of the functions available in the Bash shell: @@ -960,6 +3233,9 @@ If you protect the menu interface with a password (@pxref{Security}), all you can do is choose an entry by pressing @key{RET}, or press @key{p} to enter the password. +Pressing @key{Ctrl-l} will refresh the menu, which can be useful when +connecting via serial after the menu has been drawn. + @node Menu entry editor @section Editing a menu entry @@ -971,23 +3247,2735 @@ of entry names. If an @key{ESC} is pressed in the editor, it aborts all the changes made to the configuration entry and returns to the main menu interface. -When a particular line is selected, the editor places the user in a -special version of the GRUB command-line to edit that line. When the -user hits @key{RET}, GRUB replaces the line in question in the boot -entry with the changes (unless it was aborted via @key{ESC}, -in which case the changes are thrown away). +Each line in the menu entry can be edited freely, and you can add new lines +by pressing @key{RET} at the end of a line. To boot the edited entry, press +@key{Ctrl-x}. -If you want to add a new line to the menu entry, press @key{o} if adding -a line after the current line or press @key{O} if before the current -line. +Although GRUB unfortunately does not support @dfn{undo}, you can do almost +the same thing by just returning to the main menu using @key{ESC}. -To delete a line, hit the key @key{d}. Although GRUB unfortunately -does not support @dfn{undo}, you can do almost the same thing by just -returning to the main menu. +@node Environment +@chapter GRUB environment variables + +GRUB supports environment variables which are rather like those offered by +all Unix-like systems. Environment variables have a name, which is unique +and is usually a short identifier, and a value, which is an arbitrary string +of characters. They may be set (@pxref{set}), unset (@pxref{unset}), or +looked up (@pxref{Shell-like scripting}) by name. + +A number of environment variables have special meanings to various parts of +GRUB. Others may be used freely in GRUB configuration files. + + +@menu +* Special environment variables:: +* Environment block:: +@end menu + + +@node Special environment variables +@section Special environment variables + +These variables have special meaning to GRUB. + +@menu +* biosnum:: +* check_signatures:: +* chosen:: +* cmdpath:: +* color_highlight:: +* color_normal:: +* config_directory:: +* config_file:: +* cryptodisk_passphrase_tries:: +* debug:: +* default:: +* fallback:: +* gfxmode:: +* gfxpayload:: +* gfxterm_font:: +* grub_cpu:: +* grub_platform:: +* icondir:: +* lang:: +* locale_dir:: +* lockdown:: +* menu_color_highlight:: +* menu_color_normal:: +* net_@var{}_boot_file:: +* net_@var{}_clientid:: +* net_@var{}_clientuuid:: +* net_@var{}_dhcp_server_name:: +* net_@var{}_domain:: +* net_@var{}_extensionspath:: +* net_@var{}_hostname:: +* net_@var{}_ip:: +* net_@var{}_mac:: +* net_@var{}_next_server:: +* net_@var{}_rootpath:: +* net_default_interface:: +* net_default_ip:: +* net_default_mac:: +* net_default_server:: +* pager:: +* prefix:: +* pxe_default_server:: +* root:: +* shim_lock:: +* superusers:: +* theme:: +* timeout:: +* timeout_style:: +* tpm_fail_fatal:: +@end menu + + +@node biosnum +@subsection biosnum + +When chain-loading another boot loader (@pxref{Chain-loading}), GRUB may +need to know what BIOS drive number corresponds to the root device +(@pxref{root}) so that it can set up registers properly. If the +@var{biosnum} variable is set, it overrides GRUB's own means of guessing +this. + +For an alternative approach which also changes BIOS drive mappings for the +chain-loaded system, @pxref{drivemap}. + + +@node check_signatures +@subsection check_signatures + +This variable controls whether GRUB enforces digital signature +validation on loaded files. @xref{Using digital signatures}. + +@node chosen +@subsection chosen + +When executing a menu entry, GRUB sets the @var{chosen} variable to the +title of the entry being executed. + +If the menu entry is in one or more submenus, then @var{chosen} is set to +the titles of each of the submenus starting from the top level followed by +the title of the menu entry itself, separated by @samp{>}. + + +@node cmdpath +@subsection cmdpath + +The location from which @file{core.img} was loaded as an absolute +directory name (@pxref{File name syntax}). This is set by GRUB at +startup based on information returned by platform firmware. Not every +platform provides this information and some may return only device +without path name. + + +@node color_highlight +@subsection color_highlight + +This variable contains the ``highlight'' foreground and background terminal +colors, separated by a slash (@samp{/}). Setting this variable changes +those colors. For the available color names, @pxref{color_normal}. + +The default is @samp{black/light-gray}. + + +@node color_normal +@subsection color_normal + +This variable contains the ``normal'' foreground and background terminal +colors, separated by a slash (@samp{/}). Setting this variable changes +those colors. Each color must be a name from the following list: + +@itemize @bullet +@item black +@item blue +@item green +@item cyan +@item red +@item magenta +@item brown +@item light-gray +@item dark-gray +@item light-blue +@item light-green +@item light-cyan +@item light-red +@item light-magenta +@item yellow +@item white +@end itemize + +The default is @samp{light-gray/black}. + +The color support support varies from terminal to terminal. + +@samp{morse} has no color support at all. + +@samp{mda_text} color support is limited to highlighting by +black/white reversal. + +@samp{console} on ARC, EMU and IEEE1275, @samp{serial_*} and +@samp{spkmodem} are governed by terminfo and support +only 8 colors if in modes @samp{vt100-color} (default for console on emu), +@samp{arc} (default for console on ARC), @samp{ieee1275} (default +for console on IEEE1275). When in mode @samp{vt100} +then the color support is limited to highlighting by black/white +reversal. When in mode @samp{dumb} there is no color support. + +When console supports no colors this setting is ignored. +When console supports 8 colors, then the colors from the +second half of the previous list are mapped to the +matching colors of first half. + +@samp{console} on EFI and BIOS and @samp{vga_text} support all 16 colors. + +@samp{gfxterm} supports all 16 colors and would be theoretically extendable +to support whole rgb24 palette but currently there is no compelling reason +to go beyond the current 16 colors. + + +@node config_directory +@subsection config_directory + +This variable is automatically set by GRUB to the directory part of +current configuration file name (@pxref{config_file}). + + +@node config_file +@subsection config_file + +This variable is automatically set by GRUB to the name of configuration file that is being +processed by commands @command{configfile} (@pxref{configfile}) or @command{normal} +(@pxref{normal}). It is restored to the previous value when command completes. + + +@node cryptodisk_passphrase_tries +@subsection cryptodisk_passphrase_tries + +When prompting the user for a cryptodisk passphrase, allow this many attempts +before giving up. Defaults to @samp{3} if unset or set to an invalid value. +(The user can give up early by entering an empty passphrase.) + + +@node debug +@subsection debug + +This variable may be set to enable debugging output from various components +of GRUB. The value is an ordered list of debug facility names separated by +whitespace or @samp{,}. If the special facility named @samp{all} is present +then debugging output of all facility names is enabled at the start of +processing the value of this variable. A facility's debug output can then be +disabled by prefixing its name with a @samp{-}. The last occurence facility +name with or without a leading @samp{-} takes precendent over any previous +occurence. This allows the easy enabling or disabling of facilities by +appending a @samp{,} and then the facility name with or without the leading +@samp{-}, which will preserve the state of the rest of the facilities. +The facility names are the first argument to grub_dprintf. Consult the +source for more details. + + +@node default +@subsection default + +If this variable is set, it identifies a menu entry that should be +selected by default, possibly after a timeout (@pxref{timeout}). The +entry may be identified by number (starting from 0 at each level of +the hierarchy), by title, or by id. + +For example, if you have: + +@verbatim +menuentry 'Example GNU/Linux distribution' --class gnu-linux --id example-gnu-linux { + ... +} +@end verbatim + +then you can make this the default using: + +@example +default=example-gnu-linux +@end example + +If the entry is in a submenu, then it must be identified using the +number, title, or id of each of the submenus starting from the top +level, followed by the number, title, or id of the menu entry itself, +with each element separated by @samp{>}. For example, take the +following menu structure: + +@example +GNU/Hurd --id gnu-hurd + Standard Boot --id=gnu-hurd-std + Rescue shell --id=gnu-hurd-rescue +Other platforms --id=other + Minix --id=minix + Version 3.4.0 --id=minix-3.4.0 + Version 3.3.0 --id=minix-3.3.0 + GRUB Invaders --id=grub-invaders +@end example + +The more recent release of Minix would then be identified as +@samp{Other platforms>Minix>Version 3.4.0}, or as @samp{1>0>0}, or as +@samp{other>minix>minix-3.4.0}. + +This variable is often set by @samp{GRUB_DEFAULT} (@pxref{Simple +configuration}), @command{grub-set-default}, or @command{grub-reboot}. + + +@node fallback +@subsection fallback + +If this variable is set, it identifies a menu entry that should be selected +if the default menu entry fails to boot. Entries are identified in the same +way as for @samp{default} (@pxref{default}). + + +@node gfxmode +@subsection gfxmode + +If this variable is set, it sets the resolution used on the @samp{gfxterm} +graphical terminal. Note that you can only use modes which your graphics +card supports via VESA BIOS Extensions (VBE), so for example native LCD +panel resolutions may not be available. The default is @samp{auto}, which +selects a platform-specific default that should look reasonable. Supported +modes can be listed by @samp{videoinfo} command in GRUB. + +The resolution may be specified as a sequence of one or more modes, +separated by commas (@samp{,}) or semicolons (@samp{;}); each will be tried +in turn until one is found. Each mode should be either @samp{auto}, +@samp{@var{width}x@var{height}}, or +@samp{@var{width}x@var{height}x@var{depth}}. + + +@node gfxpayload +@subsection gfxpayload + +If this variable is set, it controls the video mode in which the Linux +kernel starts up, replacing the @samp{vga=} boot option (@pxref{linux}). It +may be set to @samp{text} to force the Linux kernel to boot in normal text +mode, @samp{keep} to preserve the graphics mode set using @samp{gfxmode}, or +any of the permitted values for @samp{gfxmode} to set a particular graphics +mode (@pxref{gfxmode}). + +Depending on your kernel, your distribution, your graphics card, and the +phase of the moon, note that using this option may cause GNU/Linux to suffer +from various display problems, particularly during the early part of the +boot sequence. If you have problems, set this variable to @samp{text} and +GRUB will tell Linux to boot in normal text mode. + +The default is platform-specific. On platforms with a native text mode +(such as PC BIOS platforms), the default is @samp{text}. Otherwise the +default may be @samp{auto} or a specific video mode. + +This variable is often set by @samp{GRUB_GFXPAYLOAD_LINUX} (@pxref{Simple +configuration}). + + +@node gfxterm_font +@subsection gfxterm_font + +If this variable is set, it names a font to use for text on the +@samp{gfxterm} graphical terminal. Otherwise, @samp{gfxterm} may use any +available font. + + +@node grub_cpu +@subsection grub_cpu + +In normal mode (@pxref{normal}), GRUB sets the @samp{grub_cpu} variable to +the CPU type for which GRUB was built (e.g. @samp{i386} or @samp{powerpc}). + + +@node grub_platform +@subsection grub_platform + +In normal mode (@pxref{normal}), GRUB sets the @samp{grub_platform} variable +to the platform for which GRUB was built (e.g. @samp{pc} or @samp{efi}). + + +@node icondir +@subsection icondir + +If this variable is set, it names a directory in which the GRUB graphical +menu should look for icons after looking in the theme's @samp{icons} +directory. @xref{Theme file format}. + + +@node lang +@subsection lang + +If this variable is set, it names the language code that the +@command{gettext} command (@pxref{gettext}) uses to translate strings. For +example, French would be named as @samp{fr}, and Simplified Chinese as +@samp{zh_CN}. + +@command{grub-mkconfig} (@pxref{Simple configuration}) will try to set a +reasonable default for this variable based on the system locale. + + +@node locale_dir +@subsection locale_dir + +If this variable is set, it names the directory where translation files may +be found (@pxref{gettext}), usually @file{/boot/grub/locale}. Otherwise, +internationalization is disabled. + +@command{grub-mkconfig} (@pxref{Simple configuration}) will set a reasonable +default for this variable if internationalization is needed and any +translation files are available. + + +@node lockdown +@subsection lockdown + +If this variable is set to @samp{y}, it means that GRUB has entered +@pxref{Lockdown} mode. + + +@node menu_color_highlight +@subsection menu_color_highlight + +This variable contains the foreground and background colors to be used for +the highlighted menu entry, separated by a slash (@samp{/}). Setting this +variable changes those colors. For the available color names, +@pxref{color_normal}. + +The default is the value of @samp{color_highlight} +(@pxref{color_highlight}). + + +@node menu_color_normal +@subsection menu_color_normal + +This variable contains the foreground and background colors to be used for +non-highlighted menu entries, separated by a slash (@samp{/}). Setting this +variable changes those colors. For the available color names, +@pxref{color_normal}. + +The default is the value of @samp{color_normal} (@pxref{color_normal}). + + +@node net_@var{}_boot_file +@subsection net_@var{}_boot_file + +@xref{Network}. + + +@node net_@var{}_clientid +@subsection net_@var{}_clientid + +@xref{Network}. + + +@node net_@var{}_clientuuid +@subsection net_@var{}_clientuuid + +@xref{Network}. + + +@node net_@var{}_dhcp_server_name +@subsection net_@var{}_dhcp_server_name + +@xref{Network}. + + +@node net_@var{}_domain +@subsection net_@var{}_domain + +@xref{Network}. + + +@node net_@var{}_extensionspath +@subsection net_@var{}_extensionspath + +@xref{Network}. + + +@node net_@var{}_hostname +@subsection net_@var{}_hostname + +@xref{Network}. + + +@node net_@var{}_ip +@subsection net_@var{}_ip + +@xref{Network}. + + +@node net_@var{}_mac +@subsection net_@var{}_mac + +@xref{Network}. + + +@node net_@var{}_next_server +@subsection net_@var{}_next_server + +@xref{Network}. + + +@node net_@var{}_rootpath +@subsection net_@var{}_rootpath + +@xref{Network}. + + +@node net_default_interface +@subsection net_default_interface + +@xref{Network}. + + +@node net_default_ip +@subsection net_default_ip + +@xref{Network}. + + +@node net_default_mac +@subsection net_default_mac + +@xref{Network}. + + +@node net_default_server +@subsection net_default_server + +@xref{Network}. + + +@node pager +@subsection pager + +If set to @samp{1}, pause output after each screenful and wait for keyboard +input. The default is not to pause output. + + +@node prefix +@subsection prefix + +The location of the @samp{/boot/grub} directory as an absolute file name +(@pxref{File name syntax}). This is normally set by GRUB at startup based +on information provided by @command{grub-install}. GRUB modules are +dynamically loaded from this directory, so it must be set correctly in order +for many parts of GRUB to work. + + +@node pxe_default_server +@subsection pxe_default_server + +@xref{Network}. + + +@node root +@subsection root + +The root device name (@pxref{Device syntax}). Any file names that do not +specify an explicit device name are read from this device. The default is +normally set by GRUB at startup based on the value of @samp{prefix} +(@pxref{prefix}). + +For example, if GRUB was installed to the first partition of the first hard +disk, then @samp{prefix} might be set to @samp{(hd0,msdos1)/boot/grub} and +@samp{root} to @samp{hd0,msdos1}. + + +@node shim_lock +@subsection shim_lock + +If this variable is set to @samp{y}, it means that the shim_lock verifier +is registered (see @pxref{UEFI secure boot and shim}). + + +@node superusers +@subsection superusers + +This variable may be set to a list of superuser names to enable +authentication support. @xref{Security}. + + +@node theme +@subsection theme + +This variable may be set to a directory containing a GRUB graphical menu +theme. @xref{Theme file format}. + +This variable is often set by @samp{GRUB_THEME} (@pxref{Simple +configuration}). + + +@node timeout +@subsection timeout + +If this variable is set, it specifies the time in seconds to wait for +keyboard input before booting the default menu entry. A timeout of @samp{0} +means to boot the default entry immediately without displaying the menu; a +timeout of @samp{-1} (or unset) means to wait indefinitely. + +If @samp{timeout_style} (@pxref{timeout_style}) is set to @samp{countdown} +or @samp{hidden}, the timeout is instead counted before the menu is +displayed. + +This variable is often set by @samp{GRUB_TIMEOUT} (@pxref{Simple +configuration}). + + +@node timeout_style +@subsection timeout_style + +This variable may be set to @samp{menu}, @samp{countdown}, or @samp{hidden} +to control the way in which the timeout (@pxref{timeout}) interacts with +displaying the menu. See the documentation of @samp{GRUB_TIMEOUT_STYLE} +(@pxref{Simple configuration}) for details. + + +@node tpm_fail_fatal +@subsection tpm_fail_fatal + +If this variable is set and true (i.e., not set to ``0'', ``false'', +``disable'', or ``no''), TPM measurements that fail will be treated as +fatal. Otherwise, they will merely be debug-logged and boot will +continue. + +Call to EFI firmware, like hash_log_extend_event(), can return an unknown +error, i.e. due to bug present in firmware. When this variable is set and +true (same values as with TPM measurements) this situation will be considered +to be fatal and error-logged as ``unknown TPM error''. If not set, booting +the OS will be enabled. + +@node Environment block +@section The GRUB environment block + +It is often useful to be able to remember a small amount of information from +one boot to the next. For example, you might want to set the default menu +entry based on what was selected the last time. GRUB deliberately does not +implement support for writing files in order to minimise the possibility of +the boot loader being responsible for file system corruption, so a GRUB +configuration file cannot just create a file in the ordinary way. However, +GRUB provides an ``environment block'' which can be used to save a small +amount of state. + +The environment block is a preallocated 1024-byte file, which normally lives +in @file{/boot/grub/grubenv} (although you should not assume this). At boot +time, the @command{load_env} command (@pxref{load_env}) loads environment +variables from it, and the @command{save_env} (@pxref{save_env}) command +saves environment variables to it. From a running system, the +@command{grub-editenv} utility can be used to edit the environment block. + +For safety reasons, this storage is only available when installed on a plain +disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and +using BIOS or EFI functions (no ATA, USB or IEEE1275). + +@command{grub-mkconfig} uses this facility to implement +@samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). + +@node Modules +@chapter Modules + +In this chapter, we list all modules that are available in GRUB. + +Modules can be loaded via the @command{insmod} (@pxref{insmod}) command. + +@menu +* acpi_module:: +* adler32_module:: +* affs_module:: +* afs_module:: +* afsplitter_module:: +* ahci_module:: +* all_video_module:: +* aout_module:: +* appleldr_module:: +* archelp_module:: +* at_keyboard_module:: +* ata_module:: +* backtrace_module:: +* bfs_module:: +* biosdisk_module:: +* bitmap_module:: +* bitmap_scale_module:: +* bli_module:: +* blocklist_module:: +* boot_module:: +* boottime_module:: +* bsd_module:: +* bswap_test_module:: +* btrfs_module:: +* bufio_module:: +* cacheinfo_module:: +* cat_module:: +* cbfs_module:: +* cbls_module:: +* cbmemc_module:: +* cbtable_module:: +* cbtime_module:: +* chain_module:: +* cmdline_cat_test_module:: +* cmosdump_module:: +* cmostest_module:: +* cmp_module:: +* cmp_test_module:: +* configfile_module:: +* cpio_module:: +* cpio_be_module:: +* cpuid_module:: +* crc64_module:: +* crypto_module:: +* cryptodisk_module:: +* cs5536_module:: +* ctz_test_module:: +* date_module:: +* datehook_module:: +* datetime_module:: +* disk_module:: +* diskfilter_module:: +* div_module:: +* div_test_module:: +* dm_nv_module:: +* drivemap_module:: +* echo_module:: +* efi_gop_module:: +* efi_uga_module:: +* efiemu_module:: +* efifwsetup_module:: +* efinet_module:: +* efitextmode_module:: +* ehci_module:: +* elf_module:: +* emunet_module:: +* emupci_module:: +* erofs_module:: +* escc_module:: +* eval_module:: +* exfat_module:: +* exfctest_module:: +* ext2_module:: +* extcmd_module:: +* f2fs_module:: +* fat_module:: +* fdt_module:: +* file_module:: +* fixvideo_module:: +* font_module:: +* freedos_module:: +* fshelp_module:: +* functional_test_module:: +* gcry_arcfour_module:: +* gcry_blowfish_module:: +* gcry_camellia_module:: +* gcry_cast5_module:: +* gcry_crc_module:: +* gcry_des_module:: +* gcry_dsa_module:: +* gcry_idea_module:: +* gcry_md4_module:: +* gcry_md5_module:: +* gcry_rfc2268_module:: +* gcry_rijndael_module:: +* gcry_rmd160_module:: +* gcry_rsa_module:: +* gcry_seed_module:: +* gcry_serpent_module:: +* gcry_sha1_module:: +* gcry_sha256_module:: +* gcry_sha512_module:: +* gcry_tiger_module:: +* gcry_twofish_module:: +* gcry_whirlpool_module:: +* gdb_module:: +* geli_module:: +* gettext_module:: +* gfxmenu_module:: +* gfxterm_module:: +* gfxterm_background_module:: +* gfxterm_menu_module:: +* gptsync_module:: +* gzio_module:: +* halt_module:: +* hashsum_module:: +* hdparm_module:: +* hello_module:: +* help_module:: +* hexdump_module:: +* hfs_module:: +* hfsplus_module:: +* hfspluscomp_module:: +* http_module:: +* ieee1275_fb_module:: +* iorw_module:: +* iso9660_module:: +* jfs_module:: +* jpeg_module:: +* json_module:: +* keylayouts_module:: +* keystatus_module:: +* ldm_module:: +* legacy_password_test_module:: +* legacycfg_module:: +* linux_module:: +* linux16_module:: +* loadbios_module:: +* loadenv_module:: +* loopback_module:: +* ls_module:: +* lsacpi_module:: +* lsapm_module:: +* lsdev_module:: +* lsefi_module:: +* lsefimmap_module:: +* lsefisystab_module:: +* lsmmap_module:: +* lspci_module:: +* lssal_module:: +* lsspd_module:: +* lsxen_module:: +* luks_module:: +* luks2_module:: +* lvm_module:: +* lzopio_module:: +* macbless_module:: +* macho_module:: +* mda_text_module:: +* mdraid09_module:: +* mdraid09_be_module:: +* mdraid1x_module:: +* memdisk_module:: +* memrw_module:: +* memtools_module:: +* minicmd_module:: +* minix_module:: +* minix2_module:: +* minix2_be_module:: +* minix3_module:: +* minix3_be_module:: +* minix_be_module:: +* mmap_module:: +* morse_module:: +* mpi_module:: +* msdospart_module:: +* mul_test_module:: +* multiboot_module:: +* multiboot2_module:: +* nand_module:: +* nativedisk_module:: +* net_module:: +* newc_module:: +* nilfs2_module:: +* normal_module:: +* ntfs_module:: +* ntfscomp_module:: +* ntldr_module:: +* odc_module:: +* offsetio_module:: +* ofnet_module:: +* ohci_module:: +* part_acorn_module:: +* part_amiga_module:: +* part_apple_module:: +* part_bsd_module:: +* part_dfly_module:: +* part_dvh_module:: +* part_gpt_module:: +* part_msdos_module:: +* part_plan_module:: +* part_sun_module:: +* part_sunpc_module:: +* parttool_module:: +* password_module:: +* password_pbkdf2_module:: +* pata_module:: +* pbkdf2_module:: +* pbkdf2_test_module:: +* pci_module:: +* pcidump_module:: +* pgp_module:: +* plainmount_module:: +* plan9_module:: +* play_module:: +* png_module:: +* priority_queue_module:: +* probe_module:: +* procfs_module:: +* progress_module:: +* pxe_module:: +* pxechain_module:: +* raid5rec_module:: +* raid6rec_module:: +* random_module:: +* rdmsr_module:: +* read_module:: +* reboot_module:: +* regexp_module:: +* reiserfs_module:: +* relocator_module:: +* romfs_module:: +* scsi_module:: +* sdl_module:: +* search_module:: +* search_fs_file_module:: +* search_fs_uuid_module:: +* search_label_module:: +* sendkey_module:: +* serial_module:: +* setjmp_module:: +* setjmp_test_module:: +* setpci_module:: +* sfs_module:: +* shift_test_module:: +* signature_test_module:: +* sleep_module:: +* sleep_test_module:: +* smbios_module:: +* spkmodem_module:: +* squash4_module:: +* strtoull_test_module:: +* suspend_module:: +* syslinuxcfg_module:: +* tar_module:: +* terminal_module:: +* terminfo_module:: +* test_module:: +* test_blockarg_module:: +* testload_module:: +* testspeed_module:: +* tftp_module:: +* tga_module:: +* time_module:: +* tpm_module:: +* tr_module:: +* trig_module:: +* true_module:: +* truecrypt_module:: +* ubootnet_module:: +* udf_module:: +* ufs1_module:: +* ufs1_be_module:: +* ufs2_module:: +* uhci_module:: +* usb_module:: +* usb_keyboard_module:: +* usbms_module:: +* usbserial_common_module:: +* usbserial_ftdi_module:: +* usbserial_pl2303_module:: +* usbserial_usbdebug_module:: +* usbtest_module:: +* vbe_module:: +* verifiers_module:: +* vga_module:: +* vga_text_module:: +* video_module:: +* video_bochs_module:: +* video_cirrus_module:: +* video_colors_module:: +* video_fb_module:: +* videoinfo_module:: +* videotest_module:: +* videotest_checksum_module:: +* wrmsr_module:: +* xen_boot_module:: +* xfs_module:: +* xnu_module:: +* xnu_uuid_module:: +* xnu_uuid_test_module:: +* xzio_module:: +* zfs_module:: +* zfscrypt_module:: +* zfsinfo_module:: +* zstd_module:: + +@end menu + +@node acpi_module +@section acpi +This module provides the command @command{acpi} for loading / replacing Advanced +Configuration and Power Interface (ACPI) tables. Please @pxref{acpi} for more +information. + +@node adler32_module +@section adler32 +This module provides the library implementation for the adler32 checksum. +This is used as part of LZO decompression / compression. + +@node affs_module +@section affs +This module provides support for the Amiga Fast FileSystem (AFFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node afs_module +@section afs +This module provides support for the AtheOS File System (AFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node afsplitter_module +@section afsplitter +This module provides library support for the Anti forensic information splitter +(AFS) operation @code{AF_merge}. This is used by LUKS and LUKS2. + +@node ahci_module +@section ahci +This module provides support for the Advanced Host Controller Interface protocol +to access disks supporting this standard. AHCI is often an option for Serial +ATA (SATA) controllers (meant to replace the older IDE protocol). + +@node all_video_module +@section all_video +This is a "dummy module" with no actual function except to load all other video +modules as dependencies (a convenient way to load all video modules). + +@node aout_module +@section aout +This module provides support for loading files packaged in the "a.out" format. +The "a.out" format is considered to be an older format than some alternatives +such as "ELF", for example support for the "a.out" format was removed from the +Linux kernel in 5.18. + +@node appleldr_module +@section appleldr +This module provides support for loading files on a BIOS / EFI based Apple Mac +computer (Intel based Macs). + +@node archelp_module +@section archelp +This module provides Archive Helper functions for archive based file systems +such as TAR and CPIO archives. + +@node at_keyboard_module +@section at_keyboard +This module provides support for the AT keyboard input for the GRUB terminal. + +@node ata_module +@section ata +This modules provides support for direct ATA and ATAPI access to compatible +disks. + +@node backtrace_module +@section backtrace +This module provides the command @command{backtrace} for printing a backtrace +to the terminal for the current call stack. + +@node bfs_module +@section bfs +This module provides support for the BeOS "Be File System" (BFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node biosdisk_module +@section biosdisk +This module provides support for booting from a bootable removable disk such +as a CD-ROM, BD-ROM, etc. + +@node bitmap_module +@section bitmap +This module provides support for reading and interacting with bitmap image +files. + +@node bitmap_scale_module +@section bitmap_scale +This module provides support for scaling bitmap image files. + +@node bli_module +@section bli +This module provides basic support for the Boot Loader Interface. The Boot +Loader Interface specifies a set of EFI variables that are used to communicate +boot-time information between the bootloader and the operating system. + +The following variables are placed under the vendor UUID +@code{4a67b082-0a4c-41cf-b6c7-440b29bb8c4f} when the module is loaded: + +The GPT partition UUID of the EFI System Partition used during boot is +published via the @code{LoaderDevicePartUUID} variable. The Boot Loader +Interface specification requires GPT formatted drives. The bli module +ignores drives/partitions in any other format. If GRUB is loaded from +a non-GPT partition, e.g. from an MSDOS formatted drive or network, +this variable will not be set. + +A string identifying GRUB as the active bootloader including the version +number is stored in @code{LoaderInfo}. + +This module is only available on UEFI platforms. + +@node blocklist_module +@section blocklist +This module provides support for the command @command{blocklist} to list +blocks for a given file. Please @pxref{blocklist} for more information. + +@node boot_module +@section boot +This module provides support for the command @command{boot} to boot an +operating system. Please @pxref{boot} for more information. + +@node boottime_module +@section boottime +This module provides support for the command @command{boottime} to display +time taken to perform various GRUB operations. This module is only available +when GRUB is built with the conditional compile option @code{BOOT_TIME_STATS}. + +@node bsd_module +@section bsd +This module provides support for loading BSD operating system images via +commands such as: @command{kfreebsd_loadenv}, @command{kfreebsd_module_elf}, +@command{kfreebsd_module}, @command{kfreebsd}, @command{knetbsd_module_elf}, +@command{knetbsd_module}, @command{knetbsd}, @command{kopenbsd}, and +@command{kopenbsd_ramdisk}. Please @pxref{Loader commands} for more info. + +@node bswap_test_module +@section bswap_test +This module is intended for performing a functional test of the byte swapping +functionality of GRUB. + +@node btrfs_module +@section btrfs +This module provides support for the B-Tree File System (BTRFS). + +@node bufio_module +@section bufio +This module is a library module for support buffered I/O of files to support +file reads performed in other modules. + +@node cacheinfo_module +@section cacheinfo +This module provides support for the command @command{cacheinfo} which provides +statistics on disk cache accesses. This module is only built if +@code{DISK_CACHE_STATS} is enabled. + +@node cat_module +@section cat +This module provides support for the command @command{cat} which outputs the +content of a file to the terminal. Please @pxref{cat} for more info. + +@node cbfs_module +@section cbfs +This module provides support for the Coreboot File System (CBFS) which is an +archive based file system. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node cbls_module +@section cbls +This module provides support for the command @command{lscoreboot} to list the +Coreboot tables. + +@node cbmemc_module +@section cbmemc +This module provides support for the command @command{cbmemc} to show the +content of the Coreboot Memory console. + +@node cbtable_module +@section cbtable +This module provides support for accessing the Coreboot tables. + +@node cbtime_module +@section cbtime +This module provides support for the command @command{coreboot_boottime} to show +the Coreboot boot time statistics. + +@node chain_module +@section chain +This module provides support for the command @command{chainloader} to boot +another bootloader. Please @pxref{chainloader} for more information. + +@node cmdline_cat_test_module +@section cmdline_cat_test +This module is intended for performing a functional test of the @command{cat} +command of GRUB. + +@node cmosdump_module +@section cmosdump +This module provides support for the command @command{cmosdump} to show a raw +dump of the CMOS contents. Please @pxref{cmosdump} for more information. + +@node cmostest_module +@section cmostest +This module provides support for the commands @command{cmostest}, +@command{cmosclean}, and @command{cmosset} to interact with a CMOS. +@xref{cmostest} / @pxref{cmosclean} for more information. + +@node cmp_module +@section cmp +This module provides support for the command @command{cmp} to compare the +content of two files. @xref{cmp} for more information. + +@node cmp_test_module +@section cmp_test +This module is intended for performing a functional test of relational +operations in GRUB. Note that this module is *not* associated with the +@command{cmp} command and does not test the @command{cmp} command. + +@node configfile_module +@section configfile +This module provides support for the commands: @command{configfile}, +@command{source}, @command{extract_entries_source}, +@command{extract_entries_configfile}, @command{.} (dot command). +@xref{configfile} / @pxref{source}. + +@node cpio_module +@section cpio +This module provides support for the CPIO archive file format. This module is +for the "bin" version of CPIO (default of GNU CPIO) supporting around 2GB. + +@node cpio_be_module +@section cpio_be +This module provides support for the CPIO archive file format in big-endian +format. This module is for the "bin" version of CPIO (default of GNU CPIO) +supporting around 2GB. + +@node cpuid_module +@section cpuid +This module provides support for the command @command{cpuid} to test for +various CPU features. @xref{cpuid} for more information. + +@node crc64_module +@section crc64 +This module provides support for the CRC64 operation. + +@node crypto_module +@section crypto +This module provides library support for various base cryptography operations +in GRUB. + +@node cryptodisk_module +@section cryptodisk +This module provides support for the command @command{cryptomount} to interact +with encrypted file systems. @xref{cryptomount} for more information. + +@node cs5536_module +@section cs5536 +This module provides support for the AMD Geode CS5536 companion device. + +@node ctz_test_module +@section ctz_test +This module is intended for performing a functional test of the ctz functions +in GRUB used to Count Trailing Zeros. + +@node date_module +@section date +This module provides support for the command @command{date} to get the date/time +or set the date/time. @xref{date} for more information. + +@node datehook_module +@section datehook +This module provides support for populating / providing the environment +variables @code{YEAR}, @code{MONTH}, @code{DAY}, @code{HOUR}, @code{MINUTE}, +@code{SECOND}, @code{WEEKDAY}. + +@node datetime_module +@section datetime +This module provides library support for getting and setting the date / time +from / to a hardware clock device. + +@node disk_module +@section disk +This module provides library support for writing to a storage disk. + +@node diskfilter_module +@section diskfilter +This module provides library support for reading a disk RAID array. +It also provides support for the command @command{cryptocheck}. +@xref{cryptocheck} for more information. + +@node div_module +@section div +This module provides library support for some operations such as divmod. + +@node div_test_module +@section div_test +This module is intended for performing a functional test of the divmod function +in GRUB. + +@node dm_nv_module +@section dm_nv +This module provides support for handling some Nvidia "fakeraid" disk devices. + +@node drivemap_module +@section drivemap +This module provides support for the @command{drivemap} to manage BIOS drive +mappings. @xref{drivemap} for more information. + +@node echo_module +@section echo +This module provides support for the @command{echo} to display a line of text. +@xref{echo} for more information. + +@node efi_gop_module +@section efi_gop +This module provides support for the UEFI video output protocol "Graphics +Output Protocol" (GOP). + +@node efi_uga_module +@section efi_uga +This module provides support for the EFI video protocol "Universal Graphic +Adapter" (UGA). + +@node efiemu_module +@section efiemu +This module provides support for the commands @command{efiemu_loadcore}, +@command{efiemu_prepare}, and @command{efiemu_unload}. This provides an EFI +emulation. + +@node efifwsetup_module +@section efifwsetup +This modules provides support for the command @command{fwsetup} to reboot into +the firmware setup menu. @xref{fwsetup} for more information. + +@node efinet_module +@section efinet +This module provides support for UEFI Network Booting for loading images and +data from the network. + +@node efitextmode_module +@section efitextmode +This module provides support for command @command{efitextmode} to get and set +output mode resolution. @xref{efitextmode} for more information. + +@node ehci_module +@section ehci +This module provides support for the USB Enhanced Host Controller Interface +(EHCI) specification (USB 2.0). + +@node elf_module +@section elf +This module provides support for loading Executable and Linkable Format (ELF) +files. + +@node emunet_module +@section emunet +This module provides support for networking in GRUB on the emu platform. + +@node emupci_module +@section emupci +This module provides support for accessing the PCI bus in GRUB on the emu +platform. + +@node erofs_module +@section erofs +This module provides support for the Enhanced Read Only File System (EROFS). + +@node escc_module +@section escc +This module provides support for the "mac-io" terminal device on PowerPC. + +@node eval_module +@section eval +This module provides support for command @command{eval} to evaluate the provided +input as a sequence of GRUB commands. @xref{eval} for more information. + +@node exfat_module +@section exfat +This module provides support for the Extensible File Allocation Table (exFAT) +file system in GRUB. + +@node exfctest_module +@section exfctest +This module is intended to provide an Example Functional Test of GRUB functions +to use as a template for developing other GRUB functional tests. + +@node ext2_module +@section ext2 +This module provides support for the Extended File System versions 2, 3, and 4 +(ext2, ext3, and ext4) file systems in GRUB. + +@node extcmd_module +@section extcmd +This module is a support module to provide wrapper functions for registering +other module commands depending on the state of the lockdown variable. + +@node f2fs_module +@section f2fs +This module provides support for the Flash-Friendly File System (F2FS) in GRUB. + +@node fat_module +@section fat +This module provides support for the File Allocation Table 12-bit, 16-bit, and +32-bit (FAT12, FAT16, and FAT32) file systems in GRUB. + +@node fdt_module +@section fdt +This module provides support for the commands @command{fdtdump} and +@command{devicetree} to dump the contents of a device tree blob (.dtb) to the +console and to load a device tree blob (.dtb) from a filesystem, for +later use by a Linux kernel, respectively. @xref{devicetree} and +@pxref{fdtdump} for more information. + +@node file_module +@section file +This module provides support for the command @command{file} to test if the +provided filename is of the specified type. @xref{file} for more information. + +@node fixvideo_module +@section fixvideo +This module provides support for the command @command{fix_video} to fix video +problems in specific PCIe video devices by "patching" specific device register +settings. Currently supports Intel 945GM (PCI ID @code{0x27a28086}) and Intel +965GM (PCI ID @code{0x2a028086}). + +@node font_module +@section font +This module provides support for the commands @command{loadfont} and +@command{lsfonts} to load a given font or list the loaded fonts. @xref{loadfont} +and @pxref{lsfonts} for more information. + +@node freedos_module +@section freedos +This module provides support for command @command{freedos} for loading a FreeDOS +kernel. + +@node fshelp_module +@section fshelp +This module provides support functions (helper functions) for file systems. + +@node functional_test_module +@section functional_test +This module provides support for running the GRUB functional tests using +commands @command{functional_test} and @command{all_functional_test}. + +@node gcry_arcfour_module +@section gcry_arcfour +This module provides support for the arcfour stream cipher also known as RC4. +If security is a concern, RC4 / arcfour cipher is consider broken (multiple +known vulnerabilities make this insecure). +This GRUB module is based on libgcrypt. + +@node gcry_blowfish_module +@section gcry_blowfish +This module provides support for the Blowfish cipher. +This GRUB module is based on libgcrypt. + +@node gcry_camellia_module +@section gcry_camellia +This module provides support for the Camellia cipher. +This GRUB module is based on libgcrypt. + +@node gcry_cast5_module +@section gcry_cast5 +This module provides support for the CAST5 (RFC2144, also known as CAST-128) +cipher. This GRUB module is based on libgcrypt. + +@node gcry_crc_module +@section gcry_crc +This module provides support for the CRC32, CRC32 RFC1510, and CRC24 RFC2440 +cyclic redundancy checks. +This GRUB module is based on libgcrypt. + +@node gcry_des_module +@section gcry_des +This module provides support for the Data Encryption Standard (DES) and +Triple-DES ciphers. +If security is a concern, DES has known vulnerabilities and is not recommended, +and Triple-DES is no longer recommended by NIST. +This GRUB module is based on libgcrypt. + +@node gcry_dsa_module +@section gcry_dsa +This module provides support for the Digital Signature Algorithm (DSA) cipher. +This GRUB module is based on libgcrypt. + +@node gcry_idea_module +@section gcry_idea +This module provides support for the International Data Encryption Algorithm +(IDEA) cipher. +This GRUB module is based on libgcrypt. + +@node gcry_md4_module +@section gcry_md4 +This module provides support for the Message Digest 4 (MD4) message digest. +If security is a concern, MD4 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_md5_module +@section gcry_md5 +This module provides support for the Message Digest 5 (MD5) message digest. +If security is a concern, MD5 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_rfc2268_module +@section gcry_rfc2268 +This module provides support for the RFC2268 (RC2 / Ron's Cipher 2) cipher. +If security is a concern, RC2 has known vulnerabilities and is not recommended. +This GRUB module is based on libgcrypt. + +@node gcry_rijndael_module +@section gcry_rijndael +This module provides support for the Advanced Encryption Standard (AES-128, +AES-192, and AES-256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_rmd160_module +@section gcry_rmd160 +This module provides support for the RIPEMD-160 message digest. +This GRUB module is based on libgcrypt. + +@node gcry_rsa_module +@section gcry_rsa +This module provides support for the Rivest–Shamir–Adleman (RSA) cipher. +This GRUB module is based on libgcrypt. + +@node gcry_seed_module +@section gcry_seed +This module provides support for the SEED cipher. +This GRUB module is based on libgcrypt. + +@node gcry_serpent_module +@section gcry_serpent +This module provides support for the Serpent (128, 192, and 256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_sha1_module +@section gcry_sha1 +This module provides support for the Secure Hash Algorithm 1 (SHA-1) message +digest. +If security is a concern, SHA-1 has known vulnerabilities and is not +recommended. +This GRUB module is based on libgcrypt. + +@node gcry_sha256_module +@section gcry_sha256 +This module provides support for the Secure Hash Algorithm 2 (224 and 256 bit) +(SHA-224 / SHA-256) message digests. +This GRUB module is based on libgcrypt. + +@node gcry_sha512_module +@section gcry_sha512 +This module provides support for the Secure Hash Algorithm 2 (384 and 512 bit) +(SHA-384 / SHA-512) message digests. +This GRUB module is based on libgcrypt. + +@node gcry_tiger_module +@section gcry_tiger +This module provides support for the Tiger, Tiger 1, and Tiger 2 message +digests. +This GRUB module is based on libgcrypt. + +@node gcry_twofish_module +@section gcry_twofish +This module provides support for the Twofish (128 and 256) ciphers. +This GRUB module is based on libgcrypt. + +@node gcry_whirlpool_module +@section gcry_whirlpool +This module provides support for the Whirlpool message digest. +This GRUB module is based on libgcrypt. + +@node gdb_module +@section gdb +This module provides support for remotely debugging GRUB using the GNU +Debugger (GDB) over serial. This is typically done when troubleshooting GRUB +during development and not required for normal GRUB operation. This module adds +support for commands required by the GDB remote debug function including +@command{gdbstub} to start GDB stub on given serial port, +@command{gdbstub_break} to break into GDB, @command{gdbstub_stop} to stop the +GDB stub. + +@node geli_module +@section geli +This module provides support for the GEOM ELI (GELI) disk encryption / +decryption protocol used by FreeBSD. This module supports the following ciphers +using the associated "gcry" modules: DES, Triple-DES, Blowfish, CAST5, AES, and +Camellia 128. + +@node gettext_module +@section gettext +This module provides support for the @command{gettext} command to support +translating information displayed / output by GRUB. @xref{gettext} for more +information. + +@node gfxmenu_module +@section gfxmenu +This module provides support for displaying a graphical menu / user interface +from GRUB. This includes features such as graphical font support, theme support, +image support, and icon support. + +@node gfxterm_module +@section gfxterm +This module provides support for displaying a terminal and menu interface from +GRUB using graphics mode. + +@node gfxterm_background_module +@section gfxterm_background +This module provides support for setting the gfxterm background color and +background image using commands @command{background_color} and +@command{background_image}. @xref{background_color} and @pxref{background_image} +for more information. + +@node gfxterm_menu_module +@section gfxterm_menu +This module is intended for performing a functional test of the gfxmenu function +in GRUB. + +@node gptsync_module +@section gptsync +This module provides support for the @command{gptsync} command.. @xref{gptsync} +for more information. + +@node gzio_module +@section gzio +This module provides support for decompression (inflate) of files compressed +with the GZ compression algorithm. This supports only the "DEFLATE" method for +GZIP. Unsupported flags (will result in failure to inflate) include: +@code{GRUB_GZ_CONTINUATION}, @code{GRUB_GZ_ENCRYPTED}, +@code{GRUB_GZ_RESERVED}, and @code{GRUB_GZ_EXTRA_FIELD}. + +@node halt_module +@section halt +This module provides support for the @command{halt} command to shutdown / halt +the system. @xref{halt} for more information. + +@node hashsum_module +@section hashsum +This module provide support for the commands @command{hashsum}, +@command{md5sum}, @command{sha1sum}, @command{sha256sum}, @command{sha512sum}, +and @command{crc} to calculate or check hashes of files using various methods. +@xref{hashsum}, @pxref{md5sum} @pxref{sha1sum}, @pxref{sha256sum}, +@pxref{sha512sum}, and @pxref{crc}. + +@node hdparm_module +@section hdparm +This module provides support for the @command{hdparm} command to get or set +various ATA disk parameters. This includes controlling Advanced Power Management +(APM), displaying power mode, freezing ATA security settings until reset, +displaying SMART status, controlling automatic acoustic management, setting +standby timeout, setting the drive to standby mode, setting the drive to sleep +mode, displaying the drive identification and settings, and enable/disable +SMART. + +@node hello_module +@section hello +This provides support for the @command{hello} command to simply output +"Hello World". This is intended for testing GRUB module loading / functionality. + +@node help_module +@section help +This module provides support for the @command{help} command to output help +text. @xref{help} for more information. + +@node hexdump_module +@section hexdump +This module provides support for the @command{hexdump} command to dump the +contents of a file in hexadecimal. @xref{hexdump} for more information. + +@node hfs_module +@section hfs +This module provides support for the Hierarchical File System (HFS) file system +in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node hfsplus_module +@section hfsplus +This module provides support for the Hierarchical File System Plus (HFS+) file +system in GRUB. + +@node hfspluscomp_module +@section hfspluscomp +This module provides support for the Hierarchical File System Plus Compressed +(HFS+ Compressed) file system in GRUB. + +@node http_module +@section http +This module provides support for getting data over the HTTP network protocol in +GRUB (using the HTTP GET method). This may be used, for example, to obtain +an operating system over HTTP (network boot). + +@node ieee1275_fb_module +@section ieee1275_fb +This module provides support for the IEEE1275 video driver output for PowerPC +with a IEEE-1275 platform. + +@node iorw_module +@section iorw +This module provides support for commands @command{inb}, @command{inw}, +@command{inl}, @command{outb}, @command{outw}, and @command{outl} to read / +write data to physical I/O ports. The "in" commands accept one +parameter to specify the source port. The "out" commands require either two +or three parameters, with the order: port, value, . + +@node iso9660_module +@section iso9660 +This module provides support for the ISO9660 file system (often associated with +optical disks such as CD-ROMs and DVD-ROMs, with extensions: +System Use Sharing Protocol (SUSP), Rock Ridge (UNIX style permissions and +longer names) + +@node jfs_module +@section jfs +This module provides support for the Journaled File System (JFS) file system. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node jpeg_module +@section jpeg +This module provides support for reading JPEG image files in GRUB, such as +to support displaying a JPEG image as a background image of the gfxmenu. + +@node json_module +@section json +This module provides library support for parsing / processing JavaScript Object +Notation (JSON) formatted data. This is used, for example, to support LUKS2 +disk encryption / decryption as metadata is encoded in JSON. + +@node keylayouts_module +@section keylayouts +This module provides support for the @command{keymap} command. This command +accepts one parameter to specify either the @var{layout_name} or the +@var{filename}. +When specifying the @var{layout_name}, this command will attempt to open the +GRUB keymap file based on the following logic: + +Get the "prefix" from environment variable @var{prefix} + +Open keymap file @var{prefix}/layouts/@var{layout_name}.gkb + +When specifying the @var{filename}, the full path to the ".gkb" file should be +provided. The ".gkb" file can be generated by grub-kbdcomp. + +@node keystatus_module +@section keystatus +This module provides support for the @command{keystatus} command to check key +modifier status. @xref{keystatus} for more information. + +@node ldm_module +@section ldm +This module provides support for the Logical Disk Manager (LDM) disk format. +LDM is used to add support for logical volumes most often with Microsoft +Windows systems. A logical volume can be defined to span more than one physical +disk. + +@node legacy_password_test_module +@section legacy_password_test +This module is intended for performing a functional test of the legacy password +function in GRUB. + +@node legacycfg_module +@section legacycfg +This module provides support for commands @command{legacy_source}, +@command{legacy_configfile}, @command{extract_legacy_entries_source}, +@command{extract_legacy_entries_configfile}, @command{legacy_kernel}, +@command{legacy_initrd}, @command{legacy_initrd_nounzip}, +@command{legacy_password}, and @command{legacy_check_password}. For new uses / +configurations of GRUB other commands / modules offer the modern equivalents. + +@node linux_module +@section linux +This module provides support for the commands @command{linux} and +@command{initrd} to load Linux and an Initial RAM Disk respectively. +@xref{linux} and @pxref{initrd} for more information. + +@node linux16_module +@section linux16 +This module provides support for the commands @command{linux16} and +@command{initrd16} to load Linux in 16-bit mode and an Initial RAM Disk +in 16-bit mode respectively. +@xref{linux16} and @pxref{initrd16} for more information. + +@node loadbios_module +@section loadbios +This module provides support for the commands @command{fakebios} and +@command{loadbios}. These commands may only be useful on platforms with +issues requiring work-arounds. Command @command{fakebios} is used to create +BIOS-like structures for backward compatibility with existing OS. Command +@command{loadbios} is used to load a BIOS dump. + +@node loadenv_module +@section loadenv +This module provides support for commands @command{load_env}, +@command{list_env}, and @command{save_env}. These commands can be used to +load environment variables from a file, list environment variables in a file, +and save environment variables to a file. @xref{load_env}, @pxref{list_env}, and +@pxref{save_env}. + +@node loopback_module +@section loopback +This module provides support for the @command{loopback} command. +@xref{loopback} for more information. + +@node ls_module +@section ls +This module provides support for the @command{ls} command. +@xref{ls} for more information. + +@node lsacpi_module +@section lsacpi +This module provides support for the @command{lsacpi} command. This command +can be used to display Advanced Configuration and Power Interface (ACPI) tables. + +@node lsapm_module +@section lsapm +This module provides support for the @command{lsapm} command. This command +can be used to display Advanced power management (APM) information. + +@node lsdev_module +@section lsdev +This module provides support for the @command{lsdev} command. This command +can be used on MIPS Advanced RISC Computing (ARC) platforms to display devices. + +@node lsefi_module +@section lsefi +This module provides support for the @command{lsefi} command. This command +can be used on EFI platforms to display EFI handles. + +@node lsefimmap_module +@section lsefimmap +This module provides support for the @command{lsefimmap} command. This command +can be used on EFI platforms to display the EFI memory map. + +@node lsefisystab_module +@section lsefisystab +This module provides support for the @command{lsefisystab} command. This +command can be used on EFI platforms to display the EFI system tables. + +@node lsmmap_module +@section lsmmap +This module provides support for the @command{lsmmap} command. This +command can be used to display the memory map provided by firmware. + +@node lspci_module +@section lspci +This module provides support for the @command{lspci} command. This +command can be used to display the PCI / PCIe devices. + +@node lssal_module +@section lssal +This module provides support for the @command{lsefisystab} command. This +command can be used on Itanium (IA-64) EFI platforms to display the EFI +System Abstraction Layer system table. + +@node lsspd_module +@section lsspd +This module provides support for the @command{lsspd} command. This +command can be used on MIPS Loongson platforms to display the DDR RAM Serial +Presence Detect (SPD) EEPROM data. + +@node lsxen_module +@section lsxen +This module provides support for the commands @command{xen_ls} and +@command{xen_cat} on Xen platforms to list Xen storage. + +@node luks_module +@section luks +This module provides support for the Linux Unified Key Setup (LUKS) (version 1) +disk encryption / decryption protocol. + +@node luks2_module +@section luks2 +This module provides support for the Linux Unified Key Setup 2 (LUKS2) +disk encryption / decryption protocol. + +@node lvm_module +@section lvm +This module provides support for reading Logical Volume Management "logical" +disks. For example, a single "logical" disk may be mapped to span more than one +physical disk. This would be used when booting from a LVM formatted disk as may +be setup in Linux. + +@node lzopio_module +@section lzopio +This module provides support for decompressing LZO / LZOP compressed files / +archives. + +@node macbless_module +@section macbless +This module provides support for commands @command{mactelbless} and +@command{macppcbless} for "blessing" a bootloader on Intel / PPC based MACs +using the HFS or HFS+ file system. On HFS / HFS+ - "blessing" makes a file +run as the bootloader. + +@node macho_module +@section macho +This module provides support for Mach Object (Mach-O) object / executable files +in GRUB often used in MacOS. + +@node mda_text_module +@section mda_text +This module provides support for the Monochrome Display Adapter (MDA) terminal +output device. MDA is a predecessor to VGA. + +@node mdraid09_module +@section mdraid09 +This module provides support for handling Linux compatible "version 0.9" +software-based RAID disks in little-endian format. The "version 0.9" format +was largely replaced around the year 2009 with the "version 1.x" format +(@pxref{mdraid1x_module} for more information). + +@node mdraid09_be_module +@section mdraid09_be +This module provides support for handling Linux compatible "version 0.9" +software-based RAID disks in bid-endian format. The "version 0.9" format +was largely replaced around the year 2009 with the "version 1.x" format +(@pxref{mdraid1x_module} for more information). + +@node mdraid1x_module +@section mdraid1x +This module provides support for handling Linux compatible "version 1.x" +software-based RAID disks. This includes the current version used by Linux +at the time of writing. + +@node memdisk_module +@section memdisk +This module provides support for a memdisk device. A memdisk is a memory mapped +emulated disk. + +@node memrw_module +@section memrw +This module provides support for commands @command{read_byte}, +@command{read_word}, @command{read_dword}, @command{write_byte}, +@command{write_word}, and @command{write_dword} to read / +write data to physical memory (addresses). The "read" commands accept one +parameter to specify the source address. The "write" commands require either two +or three parameters, with the order: address, value, . +Note: The commands provided by this module are not allowed when lockdown is +enforced (@pxref{Lockdown}). + +@node memtools_module +@section memtools +This module provides support for GRUB development / debugging commands +@command{lsmem}, @command{lsfreemem}, and @command{stress_big_allocs}. + +@node minicmd_module +@section minicmd +This module provides support for a subset of commands for GRUB rescue mode +including: @command{cat}, @command{help}, @command{dump}, @command{rmmod}, +@command{lsmod}, and @command{exit}. The version of the commands in this module +are similar to their full-fledged counterparts implemented in other GRUB +modules. +Note: The @command{dump} command is not allowed when lockdown is enforced +(@pxref{Lockdown}). + +@node minix_module +@section minix +This module provides support for the Minix filesystem, version 1. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node minix2_module +@section minix2 +This module provides support for the Minix filesystem, version 2. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node minix2_be_module +@section minix2_be +This module provides support for the Minix filesystem, version 2 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node minix3_module +@section minix3 +This module provides support for the Minix filesystem, version 3. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node minix3_be_module +@section minix3_be +This module provides support for the Minix filesystem, version 3 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node minix_be_module +@section minix_be +This module provides support for the Minix filesystem, version 1 big-endian. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node mmap_module +@section mmap +This module provides support for mapping or unmapping devices or files into +memory as well as commands @command{badram} and @command{cutmem}. +@xref{badram} and @ref{cutmem}. + +@node morse_module +@section morse +This module provides support for outputting terminal output via Morse code +to an audio speaker output. + +@node mpi_module +@section mpi +This module provides support for multi-precision-integers (MPIs) in GRUB. MPIs +are used by the crypto functions as many depend on mathematics of large numbers. +This GRUB module is based on libgcrypt. + +@node msdospart_module +@section msdospart +This module provides support for modifying MSDOS formatted disk partitions +through the separate @command{parttool} command. + +@node mul_test_module +@section mul_test +This module is intended for performing a functional test of the multiplication +operations in GRUB. + +@node multiboot_module +@section multiboot +This module provides support for commands @command{multiboot} and +@command{module} to load a multiboot kernel and load a multiboot module, +respectively. @xref{multiboot} and @ref{module} for more information. This +is for loading data formatted per the GNU Multiboot specification. + +@node multiboot2_module +@section multiboot2 +This module provides support for commands @command{multiboot2} and +@command{module2} to load a multiboot kernel and load a multiboot module, +respectively. This is for loading data formatted per the GNU Multiboot +specification. + +@node nand_module +@section nand +This module provides support for accessing an IEEE-1275 compliant NAND disk +from GRUB. + +@node nativedisk_module +@section nativedisk +This module provides support for the @command{nativedisk} command. +@xref{nativedisk} for more information. + +@node net_module +@section net +This module provides support for networking protocols including ARP, BOOTP, +DNS, Ethernet, ICMPv6, ICMP, IP, TCP, and UDP. Support is included for both +IPv4 and IPv6. +This includes the following commands: +@itemize @bullet +@item +@command{net_bootp} - @pxref{net_bootp} + +@item +@command{net_dhcp} - @pxref{net_dhcp} + +@item +@command{net_get_dhcp_option} - @pxref{net_get_dhcp_option} + +@item +@command{net_nslookup} - @pxref{net_nslookup} + +@item +@command{net_add_dns} - @pxref{net_add_dns} + +@item +@command{net_del_dns} - @pxref{net_del_dns} + +@item +@command{net_ls_dns} - @pxref{net_ls_dns} + +@item +@command{net_add_addr} - @pxref{net_add_addr} + +@item +@command{net_ipv6_autoconf} - @pxref{net_ipv6_autoconf} + +@item +@command{net_del_addr} - @pxref{net_del_addr} + +@item +@command{net_add_route} - @pxref{net_add_route} + +@item +@command{net_del_route} - @pxref{net_del_route} + +@item +@command{net_set_vlan} - @pxref{net_set_vlan} + +@item +@command{net_ls_routes} - @pxref{net_ls_routes} + +@item +@command{net_ls_cards} - @pxref{net_ls_cards} + +@item +@command{net_ls_addr} - @pxref{net_ls_addr} + +@end itemize + +@node newc_module +@section newc +This module provides support for accessing a CPIO archive as a file system +from GRUB. This module is for the following newer variants of the CPIO archive +supported by GNU CPIO (but GNU CPIO defaults to the "bin" format which is +handled by the module @ref{cpio_module}). + +These are the variants supported by this module: + +@itemize @bullet +@item +"newc" - SVR4 portable format without CRC. GNU file utility will identify these +as something like "ASCII cpio archive (SVR4 with no CRC)" + +@item +‘crc’ - SVR4 portable format with CRC. GNU file utility will identify these as +something like "ASCII cpio archive (SVR4 with CRC)" + +@end itemize + +@node nilfs2_module +@section nilfs2 +This module provides support for the New Implementation of Log filesystem +(nilfs2). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node normal_module +@section normal +This module provides support for the normal mode in GRUB. @xref{normal} for +more information. + +@node ntfs_module +@section ntfs +This module provides support for the New Technology File System (NTFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node ntfscomp_module +@section ntfscomp +This module provides support for compression with the New Technology File +System (NTFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node ntldr_module +@section ntldr +This module provides support for the @command{ntldr} command. This is may be +used to boot a Windows boot loader such as NTLDR or BootMGR. + +@node odc_module +@section odc +This module provides support for accessing a CPIO archive as a file system +from GRUB. This module is for "odc" variant of the CPIO archive +supported by GNU CPIO (but GNU CPIO defaults to the "bin" format which is +handled by the module @ref{cpio_module}). + +GNU file utility will identify these as something like "ASCII cpio archive +(pre-SVR4 or odc)" + +@node offsetio_module +@section offsetio +This module provides support for reading from a file / archive at specified +offsets in GRUB. + +@node ofnet_module +@section ofnet +This module provides support for the Open Firmware (IEEE-1275) network device +support in GRUB. + +@node ohci_module +@section ohci +This module provides support for the Open Host Controller Interface (OHCI) for +USB 1 / USB 1.1 support in GRUB. + +@node part_acorn_module +@section part_acorn +This module provides support for reading from disks partitioned with the +Acorn Disc Filing System (ADFS) used on RiscOS. + +@node part_amiga_module +@section part_amiga +This module provides support for reading from disks partitioned with the +Amiga partition table. + +@node part_apple_module +@section part_apple +This module provides support for reading from disks partitioned with the +Macintosh partition table. + +@node part_bsd_module +@section part_bsd +This module provides support for reading from disks partitioned with BSD +style partition tables. + +@node part_dfly_module +@section part_dfly +This module provides support for reading from disks partitioned with the +DragonFly BSD partition table. + +@node part_dvh_module +@section part_dvh +This module provides support for reading from disks partitioned with the +SGI Disk Volume Header partition table. + +@node part_gpt_module +@section part_gpt +This module provides support for reading from disks partitioned with the +GUID Partition Tables (GPT) partition table. + +@node part_msdos_module +@section part_msdos +This module provides support for reading from disks partitioned with the +MSDOS (Master Boot Record / MBR) style partition tables. + +@node part_plan_module +@section part_plan +This module provides support for reading from disk partitioned with the +Plan9 style partition table. + +@node part_sun_module +@section part_sun +This module provides support for reading from disk partitioned with the +Sun style partition table. + +@node part_sunpc_module +@section part_sunpc +This module provides support for reading from disk partitioned with the +Sun PC style partition table. + +@node parttool_module +@section parttool +This module provides support for the @command{parttool} command. @xref{parttool} +for more information. + +@node password_module +@section password +This module provides support for the @command{password} command. Please note +that this uses the password in plain text, if security is a concern consider +using @ref{password_pbkdf2_module} instead. @xref{password} for more +information. + +@node password_pbkdf2_module +@section password_pbkdf2 +This module provides support for the @command{password_pbkdf2} command. +@xref{password_pbkdf2} for more information. + +@node pata_module +@section pata +This module provides support for Parallel ATA (PATA) disk device interfaces. + +@node pbkdf2_module +@section pbkdf2 +This module provides support for the Password-Based Key Derivation Function 2 +(PBKDF2) / PKCS#5 PBKDF2 as per RFC 2898. + +@node pbkdf2_test_module +@section pbkdf2_test +This module is intended for performing a functional test of the PBKDF2 +operation in GRUB. + +@node pci_module +@section pci +This module provides support for generic Peripheral Component Interconnect (PCI) +bus in GRUB. + +@node pcidump_module +@section pcidump +This module provides support for the @command{pcidump} command in GRUB to dump +the PCI configuration registers in hexadecimal of a specified PCI device +(vendor / device ID) or by position on the bus. + +@node pgp_module +@section pgp +This module provides support for the commands: @command{verify_detached}, +@command{trust}, @command{list_trusted}, @command{distrust} associated with +digital signature checking via the "Open Pretty Good Privacy" (PGP) protocol / +RFC 4880 using a provided public key. This module also uses / sets +environment variable @code{check_signatures}. @xref{verify_detached}, +@ref{trust}, @ref{list_trusted}, @ref{distrust}, and @ref{check_signatures}. + +@node plainmount_module +@section plainmount +This module provides support for accessing / mounting partitions encrypted +by "cryptsetup" operating in "plain mode". @xref{plainmount} for more +information. + +@node plan9_module +@section plan9 +This module provides support for the @command{plan9} command to load a Plan9 +kernel. + +@node play_module +@section play +This module provides support for the @command{play} command to play a tune +through the PC speaker. @xref{play} for more information. + +@node png_module +@section png +This module provides support for reading Portable Network Graphics (PNG) image +files in GRUB. + +@node priority_queue_module +@section priority_queue +This module provides support for a priority queue function within GRUB such as +to support networking functions. + +@node probe_module +@section probe +This module provides support for the @command{probe} command to retrieve device +information. @xref{probe} for more information. + +@node procfs_module +@section procfs +This module provides support for a Proc File System to provide a file system +like interface to some GRUB internal data. + +@node progress_module +@section progress +This module provides support for showing file loading progress to the terminal. + +@node pxe_module +@section pxe +This module provides support for Preboot Execution Environment (PXE) network +boot services as a file system driver for other GRUB modules. + +@node pxechain_module +@section pxechain +This module provides support for the @command{pxechainloader} command to load +another bootloader by PXE. + +@node raid5rec_module +@section raid5rec +This module provides support for recovering from faulty RAID4/5 disk arrays + +@node raid6rec_module +@section raid6rec +This module provides support for recovering from faulty RAID6 disk arrays. + +@node random_module +@section random +This module provides support for library functions to get random data via +the hardware ACPI Power Management Timer and the TSC time source (Timestamp +Counter). + +@node rdmsr_module +@section rdmsr +This module provides support for the @command{rdmsr} command to read CPU +Model Specific Registers. @xref{rdmsr} for more information. + +@node read_module +@section read +This module provides support for the @command{read} command for getting user +input. @xref{read} for more information. + +@node reboot_module +@section reboot +This module provides support for the @command{reboot} command to reboot the +computer. @xref{reboot} for more information. + +@node regexp_module +@section regexp +This module provides support for the @command{regexp} command to check if a +regular expression matches a string. This module also provides support for the +GRUB script wildcard translator. @xref{regexp} for more information. + +@node reiserfs_module +@section reiserfs +This module provides support for the ReiserFS File System in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node relocator_module +@section relocator +This module provides support for relocating the image / executable being loaded +to the expected memory location(s) and jumping to (invoking) the executable. + +@node romfs_module +@section romfs +This module provides support for the Read-Only Memory File System (ROMFS). +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node scsi_module +@section scsi +This module provides support for the Small Computer System Interface (SCSI) +protocol used for some types of disk communication include some modern ones +such as USB Mass Storage Devices supporting "USB Attached SCSI" (UAS). + +@node sdl_module +@section sdl +This module provides support for Simple DirectMedia Layer (SDL) video / image +output from the grub-emu tool used to preview the GRUB menu from a running +Operating System such as Linux (useful to test GRUB menu configuration changes +without rebooting). When available in the compilation target environment, SDL2 +will be used instead of SDL1. + +@node search_module +@section search +This module provides support for the @command{search} command to search devices +by file, filesystem label, or filesystem UUID. @xref{search} for more +information. + +@node search_fs_file_module +@section search_fs_file +This module provides support for the @command{search.file} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node search_fs_uuid_module +@section search_fs_uuid +This module provides support for the @command{search.fs_uuid} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node search_label_module +@section search_label +This module provides support for the @command{search.fs_label} command which +is an alias for the corresponding @command{search} command. @xref{search} for +more information. + +@node sendkey_module +@section sendkey +This module provides support for the @command{sendkey} command to send +emulated keystrokes. @xref{sendkey} for more information. + +@node serial_module +@section serial +This module provides support for the @command{serial} command and associated +driver support for communication over a serial interface from GRUB. +@xref{serial} for more information. + +@node setjmp_module +@section setjmp +This module provides support for the @code{setjmp} and @code{longjmp} functions +used within GRUB. + +@node setjmp_test_module +@section setjmp_test +This module is intended for performing a functional test of the @code{setjmp} +and @code{longjmp} functions in GRUB. + +@node setpci_module +@section setpci +This module provides support for the @command{setpci} command to get / set +values from / to specified PCI / PCIe devices. + +@node sfs_module +@section sfs +This module provides support for the Amiga Smart File System (SFS) in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node shift_test_module +@section shift_test +This module is intended for performing a functional test of the bit-wise shift +operations in GRUB. + +@node signature_test_module +@section signature_test +This module is intended for performing a functional test of the digital +signature verification functions in GRUB. + +@node sleep_module +@section sleep +This module provides support for the @command{sleep} command to wait a specified +number of seconds in GRUB. @xref{sleep} for more information. + +@node sleep_test_module +@section sleep_test +This module is intended for performing a functional test of the sleep function +in GRUB. + +@node smbios_module +@section smbios +This module provides support for the @command{smbios} command to retrieve SMBIOS +information in GRUB. @xref{smbios} for more information. + +@node spkmodem_module +@section spkmodem +This module provides support for outputting GRUB console information over an +audio output. This output can be fed into another computer's sound input +and decoded using the @code{spkmodem_recv} utility. Note that this will slow +down GRUB's performance. + +@node squash4_module +@section squash4 +This module provides support for the SquashFS compressed read-only file system +in GRUB. + +@node strtoull_test_module +@section strtoull_test +This module is intended for performing a functional test of the strtoull +function in GRUB. + +@node suspend_module +@section suspend +This module provides support for the @command{suspend} command in GRUB to +return to IEEE1275 prompt on "Open Firmware" systems. + +@node syslinuxcfg_module +@section syslinuxcfg +This module provides support for commands @command{syslinux_source}, +@command{syslinux_configfile}, @command{extract_syslinux_entries_source}, +and @command{extract_syslinux_entries_configfile} in GRUB. These commands +can be used to parse and display GRUB menu entries based on a Syslinux based +configuration (used for SYSLINUX, ISOLINUX, and PXELINUX). It can also +be used to execute the Syslinux loader from GRUB. + +@node tar_module +@section tar +This module provides support for the GNU Tar and POSIX Tar file archives as a +file system in GRUB. + +@node terminal_module +@section terminal +This module provides support for the commands @command{terminal_input} and +@command{terminal_output} in GRUB. @xref{terminal_input} and +@ref{terminal_output} for more information. + +@node terminfo_module +@section terminfo +This module provides support for the @command{terminfo} command in GRUB to +set various terminal modes / options. @xref{terminfo} for more information. + +@node test_module +@section test +This module provides support for the commands @command{test} and @command{[}. +These commands can be used to evaluate (test) an expression. @xref{test} for +more information. + +@node test_blockarg_module +@section test_blockarg +This module is intended for performing a functional test of the "block" command +argument function in GRUB internal functions via a test command +@command{test_blockarg}. + +@node testload_module +@section testload +This module is intended for performing a functional test of some file reading / +seeking functions in GRUB internals via a test command @command{testload}. + +@node testspeed_module +@section testspeed +This module provides support for the @command{testspeed} command to test and +print file read speed of a specified file. + +@node tftp_module +@section tftp +This module provides support for the Trivial File Transfer Protocol (TFTP) for +receiving files via the network to GRUB. TFTP may be used along with PXE for +network booting for example. + +@node tga_module +@section tga +This module provides support for reading Truevision Graphics Adapter (TGA) +image files in GRUB. + +@node time_module +@section time +This module provides support for the @command{time} command to measure the +time taken by a given command and output it to the terminal. + +@node tpm_module +@section tpm +This module provides support for interacting with a Trusted Platform Module +(TPM) with GRUB to perform Measured Boot. @xref{Measured Boot} for more +information. + +@node tr_module +@section tr +This module provides support for the @command{tr} command in GRUB. This can be +used to translate characters in a string according to the provided arguments. +For example this can be used to convert upper-case to lower-case and visa-versa. + +@node trig_module +@section trig +This module provides support for internal trig functions @code{grub_cos} and +@code{grub_sin} using lookup based computation. Currently these trig functions +are used by the gfxmenu circular progress bar. + +@node true_module +@section true +This module provides support for the commands @command{true} and +@command{false}. @xref{true} and @ref{false} for more information. + +@node truecrypt_module +@section truecrypt +This module provides support for the @command{truecrypt} command. This can be +used to load a Truecrypt ISO image. + +@node ubootnet_module +@section ubootnet +This module provides support for configuring network interfaces in GRUB using +information provided by a U-Boot bootloader. + +@node udf_module +@section udf +This module provides support for the Universal Disk Format (UDF) used on some +newer optical disks. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node ufs1_module +@section ufs1 +This module provides support for the Unix File System version 1 in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node ufs1_be_module +@section ufs1_be +This module provides support for the Unix File System version 1 (big-endian) in +GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node ufs2_module +@section ufs2 +This module provides support for the Unix File System version 2 in GRUB. +Note: This module is not allowed in lockdown mode, @pxref{Lockdown} for more +information. + +@node uhci_module +@section uhci +This module provides support for the Universal Host Controller Interface (UHCI) +for USB 1.x. + +@node usb_module +@section usb +This module provides support for USB interfaces, USB hubs, and USB transfers +in GRUB. + +@node usb_keyboard_module +@section usb_keyboard +This module provides support for a USB keyboard in GRUB. + +@node usbms_module +@section usbms +This module provides support for USB Mass Storage devices in GRUB. + +@node usbserial_common_module +@section usbserial_common +This module provides support for common operations needed to support USB Serial +port adapters in GRUB (to support a model / type specific USB to serial +adapter defined in another module). + +@node usbserial_ftdi_module +@section usbserial_ftdi +This module provides support for USB to serial adapters with vendor ID 0x0403 +and product ID 0x6001 (often associated with FTDI devices). + +@node usbserial_pl2303_module +@section usbserial_pl2303 +This module provides support for USB to serial adapters with vendor ID 0x067b +and product ID 0x2303 (PL2303 USB to Serial adapter). + +@node usbserial_usbdebug_module +@section usbserial_usbdebug +This module provides support for debugging GRUB via a "USB 2.0 Debug Cable". +The USB 2.0 specification includes a "USB2 Debug Device Functional +Specification" that this driver is intended to support for GRUB. This may +integrate with GDB server function in GRUB (@pxref{gdb_module}). + +@node usbtest_module +@section usbtest +This module provides support for the @command{usb} command in GRUB to test USB +functionality by iterating through all connected USB devices and printing +information for each to the terminal. + +@node vbe_module +@section vbe +This module provides support for the VESA BIOS Extension (VBE) Video Driver in +GRUB. + +@node verifiers_module +@section verifiers +This module is a built-in kernel module to provide a framework for GRUB file +verifiers and string verifiers. + +@node vga_module +@section vga +This module provides support for the Video Graphics Array (VGA) Video Driver in +GRUB. + +@node vga_text_module +@section vga_text +This module provides support for the Video Graphics Array (VGA) terminal +output device. + +@node video_module +@section video +This module provides support for video output support functions within GRUB. + +@node video_bochs_module +@section video_bochs +This module provides support for the Bochs PCI Video Driver (also known as +Bochs Graphics Adapter / BGA) in GRUB. + +@node video_cirrus_module +@section video_cirrus +This module provides support for the Cirrus CLGD 5446 PCI Video Driver (Cirrus +Video) in GRUB. + +@node video_colors_module +@section video_colors +This module provides support for interpreting named colors and parsing RBG +hexadecimal values. + +@node video_fb_module +@section video_fb +This module provides support for video frame buffer (FB) support in GRUB. + +@node videoinfo_module +@section videoinfo +This module provides support for the @command{videoinfo} command and (depending +on architecture) the @command{vbeinfo} command. @xref{videoinfo} for more +information. + +@node videotest_module +@section videotest +This module provides support for the @command{videotest} command and (depending +on architecture) the @command{vbetest} to test the video subsystem in the +specified width and height. + +@node videotest_checksum_module +@section videotest_checksum +This module is intended for performing a functional test of the video +functions in GRUB by displaying a test image and capturing a checksum. + +@node wrmsr_module +@section wrmsr +This module provides support for the @command{wrmsr} command to write to CPU +model-specific registers. @xref{wrmsr} for more information. + +@node xen_boot_module +@section xen_boot +This module provides support for the commands @command{xen_hypervisor} and +@command{xen_module} to load a XEN hypervisor and module respectively. + +@node xfs_module +@section xfs +This module provides support for the XFS file system in GRUB. + +@node xnu_module +@section xnu +This module provides support for the commands: @command{xnu_devprop_load}, +@command{xnu_kernel}, @command{xnu_kernel64}, @command{xnu_mkext}, +@command{xnu_kext}, @command{xnu_kextdir}, @command{xnu_ramdisk}, +@command{xnu_splash}, and @command{xnu_resume} (only for emulated machine). +These commands support loading and interacting with a XNU (MacOS / Apple) based +system / kernel. + +@node xnu_uuid_module +@section xnu_uuid +This module provides support for the @command{xnu_uuid} command to transform +a 64-bit UUID to a format suitable for XNU. + +@node xnu_uuid_test_module +@section xnu_uuid_test +This module is intended for performing a functional test of the XNU UUID +conversion function. + +@node xzio_module +@section xzio +This module provides support for decompression of XZ compressed data. + +@node zfs_module +@section zfs +This module provides support for the ZFS file system in GRUB. + +@node zfscrypt_module +@section zfscrypt +This module provides support for the @command{zfskey} to import a decryption +key as well as decryption support for encrypted ZFS file systems. + +@node zfsinfo_module +@section zfsinfo +This module provides support for the commands @command{zfsinfo} to output ZFS +info about a device and @command{zfs-bootfs} to output ZFS-BOOTFSOBJ or store +it into a variable. + +@node zstd_module +@section zstd +This module provides support for the Zstandard (zstd) decompression algorithm +in GRUB. @node Commands -@chapter The list of available commands +@chapter Available commands In this chapter, we list all commands that are available in GRUB. @@ -996,22 +5984,28 @@ the global section of the configuration file (or ``menu''); most of them can be entered on the command-line and can be used either anywhere in the menu or specifically in the menu entries. +In rescue mode, only the @command{insmod} (@pxref{insmod}), @command{ls} +(@pxref{ls}), @command{set} (@pxref{set}), and @command{unset} +(@pxref{unset}) commands are normally available. If you end up in rescue +mode and do not know what to do, then @pxref{GRUB only offers a rescue +shell}. + @menu * Menu-specific commands:: +* Loader commands:: * General commands:: -* Command-line and menu entry commands:: +* Command-line commands:: +* Networking commands:: +* Undocumented commands:: @end menu @node Menu-specific commands -@section The list of commands for the menu only +@section Commands for the menu only The semantics used in parsing the configuration file are the following: @itemize @bullet -@item -The menu-specific commands have to be used before any others. - @item The files @emph{must} be in plain-text format. @@ -1025,114 +6019,496 @@ Options are separated by spaces. @item All numbers can be either decimal or hexadecimal. A hexadecimal number must be preceded by @samp{0x}, and is case-insensitive. - -@item -Extra options or text at the end of the line are ignored unless otherwise -specified. - -@item -Unrecognized commands are added to the current entry, except before entries -start, where they are ignored. @end itemize These commands can only be used in the menu: @menu * menuentry:: Start a menu entry +* submenu:: Group menu entries @end menu @node menuentry @subsection menuentry -@deffn Command title name @dots{} -Start a new boot entry, and set its name to the contents of the rest of -the line, starting with the first non-space character. +@deffn Command menuentry @var{title} @ + [@option{--class=class} @dots{}] [@option{--users=users}] @ + [@option{--unrestricted}] [@option{--hotkey=key}] [@option{--id=id}] @ + [@var{arg} @dots{}] @{ @var{command}; @dots{} @} +This defines a GRUB menu entry named @var{title}. When this entry is +selected from the menu, GRUB will set the @var{chosen} environment variable +to value of @option{--id} if @option{--id} is given, execute the list of +commands given within braces, and if the last command in the list returned +successfully and a kernel was loaded it will execute the @command{boot} command. + +The @option{--class} option may be used any number of times to group menu +entries into classes. Menu themes may display different classes using +different styles. + +The @option{--users} option grants specific users access to specific menu +entries. @xref{Security}. + +The @option{--unrestricted} option grants all users access to specific menu +entries. @xref{Security}. + +The @option{--hotkey} option associates a hotkey with a menu entry. +@var{key} may be a single letter, or one of the aliases @samp{backspace}, +@samp{tab}, or @samp{delete}. + +The @option{--id} may be used to associate unique identifier with a menu entry. +@var{id} is string of ASCII aphanumeric characters, underscore and hyphen +and should not start with a digit. + +All other arguments including @var{title} are passed as positional parameters +when list of commands is executed with @var{title} always assigned to @code{$1}. +@end deffn + + +@node submenu +@subsection submenu + +@deffn Command submenu @var{title} @ + [@option{--class=class} @dots{}] [@option{--users=users}] @ + [@option{--unrestricted}] [@option{--hotkey=key}] [@option{--id=id}] @ + @{ @var{menu entries} @dots{} @} +This defines a submenu. An entry called @var{title} will be added to the +menu; when that entry is selected, a new menu will be displayed showing all +the entries within this submenu. + +All options are the same as in the @command{menuentry} command +(@pxref{menuentry}). +@end deffn + + +@node Loader commands +@section Various loader commands + +These commands are used to load necessary components to boot desired OS. +Many of the loader commands are not sufficiently documented. The following is +a list of commands that could use more documentation: + +@itemize @bullet +@item @command{appleloader} - Boot BIOS-based system. +@item @command{freedos} - Load FreeDOS kernel.sys. +@item @command{kfreebsd_loadenv} - Load FreeBSD env. +@item @command{kfreebsd_module_elf} - Load FreeBSD kernel module (ELF). +@item @command{kfreebsd_module} - Load FreeBSD kernel module. +@item @command{kfreebsd} - Load kernel of FreeBSD. +@item @command{knetbsd_module_elf} - Load NetBSD kernel module (ELF). +@item @command{knetbsd_module} - Load NetBSD kernel module. +@item @command{knetbsd} - Load kernel of NetBSD. +@item @command{kopenbsd} - Load kernel of OpenBSD. +@item @command{kopenbsd_ramdisk} - Load kOpenBSD ramdisk. +@item @command{legacy_initrd_nounzip} - Simulate grub-legacy `modulenounzip' command +@item @command{legacy_initrd} - Simulate grub-legacy `initrd' command +@item @command{legacy_kernel} - Simulate grub-legacy `kernel' command +@item @command{module2} - Load a multiboot 2 module. +@item @command{module} - Load a multiboot module. +@item @command{multiboot2} - Load a multiboot 2 kernel. +@item @command{multiboot} - Load a multiboot kernel. +@item @command{ntldr} - Load NTLDR or BootMGR. +@item @command{plan9} - Load Plan9 kernel. +@item @command{pxechainloader} - Load a PXE image. +@item @command{truecrypt} - Load Truecrypt ISO. +@item @command{xnu_kernel64} - Load 64-bit XNU image. +@item @command{xnu_kernel} - Load XNU image. +@item @command{xnu_kextdir} - Load XNU extension directory. +@item @command{xnu_kext} - Load XNU extension. +@item @command{xnu_mkext} - Load XNU extension package. +@item @command{xnu_ramdisk} - Load XNU ramdisk. It will be available in OS as md0. +@item @command{xnu_resume} - Load an image of hibernated XNU. +@item @command{xnu_splash} - Load a splash image for XNU. +@end itemize + + +@menu +* chainloader:: Chain-load another boot loader +* initrd:: Load a Linux initrd +* initrd16:: Load a Linux initrd (16-bit mode) +* linux:: Load a Linux kernel +* linux16:: Load a Linux kernel (16-bit mode) +@comment * xen_*:: Xen boot commands for AArch64 +* xen_hypervisor:: Load xen hypervisor binary (only on AArch64) +* xen_module:: Load xen modules for xen hypervisor (only on AArch64) +@end menu + + +@node chainloader +@subsection chainloader + +@deffn Command chainloader [@option{--force}] file [args...] +Load @var{file} as a chain-loader. Like any other file loaded by the +filesystem code, it can use the blocklist notation (@pxref{Block list +syntax}) to grab the first sector of the current partition with @samp{+1}. +On EFI platforms, any arguments after @var{file} will be sent to the loaded +image. + +If you specify the option @option{--force}, then load @var{file} forcibly, +whether it has a correct signature or not. This is required when you want to +load a defective boot loader, such as SCO UnixWare 7.1. +@end deffn + + +@node initrd +@subsection initrd + +@deffn Command initrd file [file @dots{}] +Load, in order, all initrds for a Linux kernel image, and set the +appropriate parameters in the Linux setup area in memory. This may only +be used after the @command{linux} command (@pxref{linux}) has been run. +See @ref{GNU/Linux} for more info on booting GNU/Linux. For more +information on initrds see the GNU/Linux kernel +@uref{https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html, +documentation}. + +A new-style initrd (for kernels newer than 2.6) containing one file +with leading path components can also be generated at run time. This +can be done by prefixing an argument with @code{newc:} followed by the +path of the file in the new initrd, a @code{:}, and then the GRUB file +path to the file data to be be included. + +For example: +@example +initrd newc:/etc/ssh/config:(hd0,2)/home/user/.ssh/config \ + newc:/etc/ssh/ssh_host_rsa_key:/etc/ssh/ssh_host_rsa_key \ + /boot/initrd.gz \ + newc:/init:/home/user/init.fixed +@end example + +This command will generate two new-style initrds on the fly. The first +contains the path @samp{/etc/ssh/config} with the contents of +@samp{(hd0,2)/home/user/.ssh/config} and the path +@samp{/etc/ssh/ssh_host_rsa_key} with the contents of +@samp{/etc/ssh/ssh_host_rsa_key} on the @var{root} device. Parent directory +paths will automatically be generated as needed. This first generated initrd +will then have @samp{/boot/initrd.gz} concatenated after it. Next, another +new-style archive will be generated with the contents of @samp{/home/user/init.fixed} +in the path @samp{/init} and appended to the previous concatenation. Finally, +the result will be sent to the kernel when booted. + +Keep in mind that paths that come later will take precedence. So in the +example above, the generated path @samp{/init} will overwrite any @samp{/init} +in @samp{/boot/initrd.gz}. This can be useful when changing the main initrd +is undesirable or difficult. +@end deffn + + +@node initrd16 +@subsection initrd16 + +@deffn Command initrd16 file [file @dots{}] +Load, in order, all initrds for a Linux kernel image to be booted in +16-bit mode, and set the appropriate parameters in the Linux setup area in +memory. This may only be used after the @command{linux16} command +(@pxref{linux16}) has been run. See also @ref{GNU/Linux} and the @command{initrd} +command (@pxref{initrd}) for more details on arguments. + +This command is only available on the pc platform for x86 systems. +@end deffn + + +@node linux +@subsection linux + +@deffn Command linux file @dots{} +Load a Linux kernel image from @var{file}. The rest of the line is passed +verbatim as the @dfn{kernel command-line}. Any initrd must be reloaded +after using this command (@pxref{initrd}). + +On x86 systems, the kernel will be booted using the 32-bit boot protocol. +Note that this means that the @samp{vga=} boot option will not work; if you +want to set a special video mode, you will need to use GRUB commands such as +@samp{set gfxpayload=1024x768} or @samp{set gfxpayload=keep} (to keep the +same mode as used in GRUB) instead. GRUB can automatically detect some uses +of @samp{vga=} and translate them to appropriate settings of +@samp{gfxpayload}. The @command{linux16} command (@pxref{linux16}) avoids +this restriction. +@end deffn + + +@node linux16 +@subsection linux16 + +@deffn Command linux16 file @dots{} +Load a Linux kernel image from @var{file} in 16-bit mode. The rest of the +line is passed verbatim as the @dfn{kernel command-line}. Any initrd must +be reloaded after using this command (@pxref{initrd16}). + +The kernel will be booted using the traditional 16-bit boot protocol. As +well as bypassing problems with @samp{vga=} described in @ref{linux}, this +permits booting some other programs that implement the Linux boot protocol +for the sake of convenience. + +This command is only available on x86 systems. +@end deffn + + +@node xen_hypervisor +@subsection xen_hypervisor + +@deffn Command xen_hypervisor file [arguments] @dots{} +Load a Xen hypervisor binary from @var{file}. The rest of the line is passed +verbatim as the @dfn{kernel command-line}. Any other binaries must be +reloaded after using this command. +This command is only available on AArch64 systems. +@end deffn + + +@node xen_module +@subsection xen_module + +@deffn Command xen_module [--nounzip] file [arguments] +Load a module for xen hypervisor at the booting process of xen. +The rest of the line is passed verbatim as the module command line. +Modules should be loaded in the following order: + - dom0 kernel image + - dom0 ramdisk if present + - XSM policy if present +This command is only available on AArch64 systems. @end deffn @node General commands -@section The list of general commands +@section General commands Commands usable anywhere in the menu and in the command-line. @menu * serial:: Set up a serial device -* terminfo:: Define escape sequences for a terminal +* terminal_input:: Manage input terminals +* terminal_output:: Manage output terminals +* terminfo:: Define terminal type @end menu @node serial @subsection serial -@deffn Command serial [@option{--unit=unit}] [@option{--port=port}] [@option{--speed=speed}] [@option{--word=word}] [@option{--parity=parity}] [@option{--stop=stop}] [@option{--device=dev}] +@deffn Command serial [@option{--unit=unit}] [@option{--port=port}] [@option{--speed=speed}] [@option{--word=word}] [@option{--parity=parity}] [@option{--stop=stop}] Initialize a serial device. @var{unit} is a number in the range 0-3 specifying which serial port to use; default is 0, which corresponds to -the port often called COM1. @var{port} is the I/O port where the UART -is to be found; if specified it takes precedence over @var{unit}. +the port often called COM1. + +@var{port} is the I/O port where the UART is to be found or, if prefixed +with @samp{mmio,}, the MMIO address of the UART. If specified it takes +precedence over @var{unit}. + +Additionally, an MMIO address can be suffixed with: +@itemize @bullet +@item +@samp{.b} for bytes access (default) +@item +@samp{.w} for 16-bit word access +@item +@samp{.l} for 32-bit long word access or +@item +@samp{.q} for 64-bit long long word access +@end itemize + +Also, @var{port} can be of the form @samp{pci,XX:XX.X} to indicate a serial +device exposed on the PCI bus. + @var{speed} is the transmission speed; default is 9600. @var{word} and @var{stop} are the number of data bits and stop bits. Data bits must be in the range 5-8 and stop bits must be 1 or 2. Default is 8 data bits and one stop bit. @var{parity} is one of @samp{no}, @samp{odd}, -@samp{even} and defaults to @samp{no}. The option @option{--device} -can only be used in the grub shell and is used to specify the -tty device to be used in the host operating system (@pxref{Invoking the -grub shell}). +@samp{even} and defaults to @samp{no}. + +If passed no @var{unit} nor @var{port}, or if @var{port} is set to +@samp{auto} then GRUB will attempt to use ACPI to automatically detect +the system default serial port and its configuration. If this information +is not available, it will default to @var{unit} 0. The serial port is not used as a communication channel unless the -@command{terminal} command is used (@pxref{terminal}). +@command{terminal_input} or @command{terminal_output} command is used +(@pxref{terminal_input}, @pxref{terminal_output}). -This command is only available if GRUB is compiled with serial -support. See also @ref{Serial terminal}. +Note, valid @var{port} values, excluding IO port addresses, can be found +by listing terminals with @command{terminal_output}, selecting all names +prefixed by @samp{serial_} and removing that prefix. + +Examples: +@example +serial --port=0x3f8 --speed=9600 +serial --port=mmio,fefb0000.l --speed=115200 +serial --port=pci,00:16.3 --speed=115200 +@end example + +See also @ref{Serial terminal}. +@end deffn + + +@node terminal_input +@subsection terminal_input + +@deffn Command terminal_input [@option{--append}|@option{--remove}] @ + [terminal1] [terminal2] @dots{} +List or select an input terminal. + +With no arguments, list the active and available input terminals. + +With @option{--append}, add the named terminals to the list of active input +terminals; any of these may be used to provide input to GRUB. + +With @option{--remove}, remove the named terminals from the active list. + +With no options but a list of terminal names, make only the listed terminal +names active. +@end deffn + + +@node terminal_output +@subsection terminal_output + +@deffn Command terminal_output [@option{--append}|@option{--remove}] @ + [terminal1] [terminal2] @dots{} +List or select an output terminal. + +With no arguments, list the active and available output terminals. + +With @option{--append}, add the named terminals to the list of active output +terminals; all of these will receive output from GRUB. + +With @option{--remove}, remove the named terminals from the active list. + +With no options but a list of terminal names, make only the listed terminal +names active. @end deffn @node terminfo @subsection terminfo -@deffn Command terminfo @option{--name=name} @option{--cursor-address=seq} [@option{--clear-screen=seq}] [@option{--enter-standout-mode=seq}] [@option{--exit-standout-mode=seq}] -Define the capabilities of your terminal. Use this command to define -escape sequences, if it is not vt100-compatible. You may use @samp{\e} -for @key{ESC} and @samp{^X} for a control character. +@deffn Command terminfo [@option{-a}|@option{-u}|@option{-v}] [@option{-g WxH}] [term] [type] +Define the capabilities of your terminal by giving the name of an entry in +the terminfo database, which should correspond roughly to a @samp{TERM} +environment variable in Unix. -You can use the utility @command{grub-terminfo} to generate -appropriate arguments to this command. @xref{Invoking grub-terminfo}. +The currently available terminal types are @samp{vt100}, @samp{vt100-color}, +@samp{ieee1275}, and @samp{dumb}. If you need other terminal types, please +contact us to discuss the best way to include support for these in GRUB. -If no option is specified, the current settings are printed. +The @option{-a} (@option{--ascii}), @option{-u} (@option{--utf8}), and +@option{-v} (@option{--visual-utf8}) options control how non-ASCII text is +displayed. @option{-a} specifies an ASCII-only terminal; @option{-u} +specifies logically-ordered UTF-8; and @option{-v} specifies +"visually-ordered UTF-8" (in other words, arranged such that a terminal +emulator without bidirectional text support will display right-to-left text +in the proper order; this is not really proper UTF-8, but a workaround). + +The @option{-g} (@option{--geometry}) can be used to specify terminal geometry. + +If no option or terminal type is specified, the current terminal type is +printed. @end deffn -@node Command-line and menu entry commands -@section The list of command-line and menu entry commands +@node Command-line commands +@section Command-line commands These commands are usable in the command-line and in menu entries. If you forget a command, you can run the command @command{help} (@pxref{help}). @menu +* [:: Check file types and compare values * acpi:: Load ACPI tables +* authenticate:: Check whether user is in user list +* background_color:: Set background color for active terminal +* background_image:: Load background image for active terminal +* badram:: Filter out bad regions of RAM * blocklist:: Print a block list * boot:: Start up your operating system * cat:: Show the contents of a file -* chainloader:: Chain-load another boot loader +* clear:: Clear the screen +* cmosclean:: Clear bit in CMOS +* cmosdump:: Dump CMOS contents +* cmostest:: Test bit in CMOS * cmp:: Compare two files * configfile:: Load a configuration file -* crc:: Calculate CRC32 checksums +* cpuid:: Check for CPU features +* crc:: Compute or check CRC32 checksums +* cryptocheck:: Check if a device is encrypted +* cryptomount:: Mount a crypto device +* cutmem:: Remove memory regions * date:: Display or set current date and time +* devicetree:: Load a device tree blob +* distrust:: Remove a pubkey from trusted keys +* drivemap:: Map a drive to another * echo:: Display a line of text +* efitextmode:: Set/Get text output mode resolution +* eval:: Evaluate agruments as GRUB commands * export:: Export an environment variable +* false:: Do nothing, unsuccessfully +* fdtdump:: Retrieve device tree information +* file:: Test the provided file against a type +* fwsetup:: Reboot into the firmware setup menu +* gdbinfo:: Provide info for debugging with GDB +* gettext:: Translate a string +* gptsync:: Fill an MBR based on GPT entries * halt:: Shut down your computer +* hashsum:: Compute or check hash checksum * help:: Show help messages +* hexdump:: Show raw contents of a file or memory * insmod:: Insert a module * keystatus:: Check key modifier status +* list_env:: List variables in environment block +* list_trusted:: List trusted public keys +* load_env:: Load variables from environment block +* loadfont:: Load font files +* loopback:: Make a device from a filesystem image * ls:: List devices or files +* lsfonts:: List loaded fonts +* lsmod:: Show loaded modules +* md5sum:: Compute or check MD5 hash +* module:: Load module for multiboot kernel +* multiboot:: Load multiboot compliant kernel +* nativedisk:: Switch to native disk drivers +* normal:: Enter normal mode +* normal_exit:: Exit from normal mode +* parttool:: Modify partition table entries +* password:: Set a clear-text password +* password_pbkdf2:: Set a hashed password +* plainmount:: Open device encrypted in plain mode * play:: Play a tune +* probe:: Retrieve device info +* rdmsr:: Read values from model-specific registers +* read:: Read user input * reboot:: Reboot your computer +* regexp:: Test if regular expression matches string +* rmmod:: Remove a module +* save_env:: Save variables to environment block +* search:: Search devices by file, label, or UUID +* sendkey:: Emulate keystrokes * set:: Set an environment variable +* sha1sum:: Compute or check SHA1 hash +* sha256sum:: Compute or check SHA256 hash +* sha512sum:: Compute or check SHA512 hash +* sleep:: Wait for a specified number of seconds +* smbios:: Retrieve SMBIOS information +* source:: Read a configuration file in same context +* test:: Check file types and compare values +* tpm2_key_protector_init:: Initialize the TPM2 key protector +* tpm2_key_protector_clear:: Clear the TPM2 key protector +* tpm2_dump_pcr:: Dump TPM2 PCRs +* true:: Do nothing, successfully +* trust:: Add public key to list of trusted keys * unset:: Unset an environment variable +@comment * vbeinfo:: List available video modes +* verify_detached:: Verify detached digital signature +* videoinfo:: List available video modes +* wrmsr:: Write values to model-specific registers @end menu +@node [ +@subsection [ +@deffn Command @code{[} expression @code{]} +Alias for @code{test @var{expression}} (@pxref{test}). +@end deffn + + @node acpi @subsection acpi @@ -1152,9 +6528,77 @@ Normally, this command will replace the Root System Description Pointer (RSDP) in the Extended BIOS Data Area to point to the new tables. If the @option{--no-ebda} option is used, the new tables will be known only to GRUB, but may be used by GRUB's EFI emulation. + +Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). + Otherwise an attacker can instruct the GRUB to load an SSDT table to + overwrite the kernel lockdown configuration and later load and execute + unsigned code. @end deffn +@node authenticate +@subsection authenticate +@deffn Command authenticate [userlist] +Check whether user is in @var{userlist} or listed in the value of variable +@samp{superusers}. See @pxref{superusers} for valid user list format. +If @samp{superusers} is empty, this command returns true. @xref{Security}. +@end deffn + + +@node background_color +@subsection background_color + +@deffn Command background_color color +Set background color for active terminal. For valid color specifications see +@pxref{Theme file format, ,Colors}. Background color can be changed only when +using @samp{gfxterm} for terminal output. + +This command sets color of empty areas without text. Text background color +is controlled by environment variables @var{color_normal}, @var{color_highlight}, +@var{menu_color_normal}, @var{menu_color_highlight}. @xref{Special environment variables}. +@end deffn + + +@node background_image +@subsection background_image + +@deffn Command background_image [[@option{--mode} @samp{stretch}|@samp{normal}] file] +Load background image for active terminal from @var{file}. Image is stretched +to fill up entire screen unless option @option{--mode} @samp{normal} is given. +Without arguments remove currently loaded background image. Background image +can be changed only when using @samp{gfxterm} for terminal output. + +@end deffn + + +@node badram +@subsection badram + +@deffn Command badram addr,mask[,addr,mask...] +Filter out bad RAM. + +This command notifies the memory manager that specified regions of +RAM ought to be filtered out (usually, because they're damaged). This +remains in effect after a payload kernel has been loaded by GRUB, as +long as the loaded kernel obtains its memory map from GRUB. Kernels that +support this include Linux, GNU Mach, the kernel of FreeBSD and Multiboot +kernels in general. + +Syntax is the same as provided by the @uref{http://www.memtest.org/, +Memtest86+ utility}: a list of address/mask pairs. Given a page-aligned +address and a base address / mask pair, if all the bits of the page-aligned +address that are enabled by the mask match with the base address, it means +this page is to be filtered. This syntax makes it easy to represent patterns +that are often result of memory damage, due to physical distribution of memory +cells. + +The command is similar to @command{cutmem} command. + +Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). + This prevents removing EFI memory regions to potentially subvert the + security mechanisms provided by the UEFI secure boot. +@end deffn + @node blocklist @subsection blocklist @@ -1176,35 +6620,75 @@ a menu entry). @node cat @subsection cat -@deffn Command cat file +@deffn Command cat [@option{--dos}] file Display the contents of the file @var{file}. This command may be useful to remind you of your OS's root partition: @example grub> @kbd{cat /etc/fstab} @end example + +If the @option{--dos} option is used, then carriage return / new line pairs +will be displayed as a simple new line. Otherwise, the carriage return will +be displayed as a control character (@samp{}) to make it easier to see +when boot problems are caused by a file formatted using DOS-style line +endings. + +Note: @command{cat} can be used to view the contents of devices using the +block list syntax (@pxref{Block list syntax}). However, it is not advised +to view binary data because it will try to decode UTF-8 strings, which can +lead to some bytes missing or added in the output. Instead, use the +@command{hexdump} command (@pxref{hexdump}). @end deffn -@node chainloader -@subsection chainloader +@node clear +@subsection clear -@deffn Command chainloader [@option{--force}] file -Load @var{file} as a chain-loader. Like any other file loaded by the -filesystem code, it can use the blocklist notation to grab the first -sector of the current partition with @samp{+1}. If you specify the -option @option{--force}, then load @var{file} forcibly, whether it has a -correct signature or not. This is required when you want to load a -defective boot loader, such as SCO UnixWare 7.1 (@pxref{SCO UnixWare}). +@deffn Command clear +Clear the screen. +@end deffn + + +@node cmosclean +@subsection cmosclean + +@deffn Command cmosclean byte:bit +Clear value of bit in CMOS at location @var{byte}:@var{bit}. This command +is available only on platforms that support CMOS. +@end deffn + + +@node cmosdump +@subsection cmosdump + +@deffn Dump CMOS contents +Dump full CMOS contents as hexadecimal values. This command is available only +on platforms that support CMOS. +@end deffn + + +@node cmostest +@subsection cmostest + +@deffn Command cmostest byte:bit +Test value of bit in CMOS at location @var{byte}:@var{bit}. Exit status +is zero if bit is set, non zero otherwise. This command is available only +on platforms that support CMOS. @end deffn @node cmp @subsection cmp -@deffn Command cmp file1 file2 -Compare the file @var{file1} with the file @var{file2}. If they differ -in size, print the sizes like this: +@deffn Command cmp [@option{-v}] file1 file2 +Compare the file @var{file1} with the file @var{file2}. If they are completely +identical, @code{$?} will be set to 0. Otherwise, if the files are not identical, +@code{$?} will be set to a nonzero value. + +By default nothing will be output. If the @option{-v} is used, verbose mode is +enabled. In this mode when when the files differ in size, print the sizes like +this: @example Differ in size: 0x1234 [foo], 0x4321 [bar] @@ -1217,7 +6701,6 @@ bytes like this: Differ at the offset 777: 0xbe [foo], 0xef [bar] @end example -If they are completely identical, nothing will be printed. @end deffn @@ -1225,17 +6708,113 @@ If they are completely identical, nothing will be printed. @subsection configfile @deffn Command configfile file -Load @var{file} as a configuration file. +Load @var{file} as a configuration file. If @var{file} defines any menu +entries, then show a menu containing them immediately. Any environment +variable changes made by the commands in @var{file} will not be preserved +after @command{configfile} returns. +@end deffn + + +@node cpuid +@subsection cpuid + +@deffn Command cpuid [-l] [-p] +Check for CPU features. This command is only available on x86 systems. + +With the @option{-l} option, return true if the CPU supports long mode +(64-bit). + +With the @option{-p} option, return true if the CPU supports Physical +Address Extension (PAE). + +If invoked without options, this command currently behaves as if it had been +invoked with @option{-l}. This may change in the future. @end deffn @node crc @subsection crc -@deffn Command crc file -Display the CRC32 checksum of @var{file}. +@deffn Command crc arg @dots{} +Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum} +(@pxref{hashsum}) for full description. @end deffn +@node cryptocheck +@subsection cryptocheck + +@deffn Command cryptocheck [ @option{--quiet} ] device +Check if a given diskfilter device is backed by encrypted devices +(@pxref{cryptomount} for additional information). + +The command examines all backing devices, physical volumes, of a specified +logical volume, like LVM2, and fails when at least one of them is unencrypted. + +The option @option{--quiet} can be given to suppress the output. +@end deffn + +@node cryptomount +@subsection cryptomount + +@deffn Command cryptomount [ [@option{-p} password] | [@option{-k} keyfile [@option{-O} keyoffset] [@option{-S} keysize] ] | [@option{-P} protector] ] [@option{-H} file] device|@option{-u} uuid|@option{-a}|@option{-b} +Setup access to encrypted device. A passphrase will be requested interactively, +if neither the @option{-p} nor @option{-k} options are given. The option +@option{-p} can be used to supply a passphrase (useful for scripts). +Alternatively the @option{-k} option can be used to supply a keyfile with +options @option{-O} and @option{-S} optionally supplying the offset and size, +respectively, of the key data in the given key file. Besides the keyfile, +the key can be stored in a key protector, and option @option{-P} configures +specific key protector, e.g. tpm2, to retrieve the key from. +The @option{-H} options can be used to supply cryptomount backends with an +alternative header file (aka detached header). Not all backends have headers +nor support alternative header files (currently only LUKS1 and LUKS2 support them). +Argument @var{device} configures specific grub device +(@pxref{Naming convention}); option @option{-u} @var{uuid} configures device +with specified @var{uuid}; option @option{-a} configures all detected encrypted +devices; option @option{-b} configures all geli containers that have boot flag set. + +Devices are not allowed to be given as key files nor as detached header files. +However, this limitation can be worked around by using blocklist syntax. So +for instance, @code{(hd1,gpt2)} can not be used, but @code{(hd1,gpt2)0+} will +achieve the desired result. + +GRUB supports devices encrypted using LUKS, LUKS2 and geli. Note that necessary +modules (@var{luks}, @var{luks2} and @var{geli}) have to be loaded manually +before this command can be used. For LUKS2 only the PBKDF2 key derivation +function is supported, as Argon2 is not yet supported. + +Successfully decrypted disks are named as (cryptoX) and have increasing numeration +suffix for each new decrypted disk. If the encrypted disk hosts some higher level +of abstraction (like LVM2 or MDRAID) it will be created under a separate device +namespace in addition to the cryptodisk namespace. + +Support for plain encryption mode (plain dm-crypt) is provided via separate +@command{@pxref{plainmount}} command. + +On the EFI platform, GRUB tries to erase master keys from memory when the cryptodisk +module is unloaded or the command @command{exit} is executed. All secrets remain in +memory when the command @command{chainloader} is issued, because execution can +return to GRUB on the EFI platform. +@end deffn + +@node cutmem +@subsection cutmem + +@deffn Command cutmem from[K|M|G] to[K|M|G] +Remove any memory regions in specified range. + +This command notifies the memory manager that specified regions of RAM ought to +be filtered out. This remains in effect after a payload kernel has been loaded +by GRUB, as long as the loaded kernel obtains its memory map from GRUB. Kernels +that support this include Linux, GNU Mach, the kernel of FreeBSD and Multiboot +kernels in general. + +The command is similar to @command{badram} command. + +Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). + This prevents removing EFI memory regions to potentially subvert the + security mechanisms provided by the UEFI secure boot. +@end deffn @node date @subsection date @@ -1250,6 +6829,62 @@ hour, minute, and second unchanged. @end deffn +@node devicetree +@subsection devicetree + +@deffn Command devicetree file +Load a device tree blob (.dtb) from a filesystem, for later use by a Linux +kernel. Does not perform merging with any device tree supplied by firmware, +but rather replaces it completely. + +Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). + This is done to prevent subverting various security mechanisms. +@end deffn + +@node distrust +@subsection distrust + +@deffn Command distrust pubkey_id +Remove public key @var{pubkey_id} from GRUB's keyring of trusted keys. +@var{pubkey_id} is the last four bytes (eight hexadecimal digits) of +the GPG v4 key id, which is also the output of @command{list_trusted} +(@pxref{list_trusted}). Outside of GRUB, the key id can be obtained +using @code{gpg --fingerprint}). +These keys are used to validate signatures when environment variable +@code{check_signatures} is set to @code{enforce} +(@pxref{check_signatures}), and by some invocations of +@command{verify_detached} (@pxref{verify_detached}). @xref{Using +digital signatures}, for more information. +@end deffn + +@node drivemap +@subsection drivemap + +@deffn Command drivemap @option{-l}|@option{-r}|[@option{-s}] @ + from_drive to_drive +Without options, map the drive @var{from_drive} to the drive @var{to_drive}. +This is necessary when you chain-load some operating systems, such as DOS, +if such an OS resides at a non-first drive. For convenience, any partition +suffix on the drive is ignored, so you can safely use @verb{'${root}'} as a +drive specification. + +With the @option{-s} option, perform the reverse mapping as well, swapping +the two drives. + +With the @option{-l} option, list the current mappings. + +With the @option{-r} option, reset all mappings to the default values. + +For example: + +@example +drivemap -s (hd0) (hd1) +@end example + +NOTE: Only available on i386-pc. +@end deffn + + @node echo @subsection echo @@ -1293,6 +6928,56 @@ character will print that character. @end deffn +@node efitextmode +@subsection efitextmode + +@deffn Command efitextmode [min | max | | ] +When used with no arguments displays all available text output modes. The +set mode determines the columns and rows of the text display when in +text mode. An asterisk, @samp{*}, will be at the end of the line of the +currently set mode. + +If given a single parameter, it must be @samp{min}, @samp{max}, or a mode +number given by the listing when run with no arguments. These arguments set +the mode to the minimum, maximum, and particular mode respectively. + +Otherwise, the command must be given two numerical arguments specifying the +columns and rows of the desired mode. Specifying a columns and rows +combination that corresponds to no supported mode, will return error, but +otherwise have no effect. + +By default GRUB will start in whatever mode the EFI firmware defaults to. +There are firmwares known to set up the default mode such that output +behaves strangely, for example the cursor in the GRUB shell never reaches +the bottom of the screen or, when typing characters at the prompt, +characters from previous command output are overwritten. Setting the mode +may fix this. + +The EFI specification says that mode 0 must be available and have +columns and rows of 80 and 25 respectively. Mode 1 may be defined and if +so must have columns and rows of 80 and 50 respectively. Any other modes +may have columns and rows arbitrarily defined by the firmware. This means +that a mode with columns and rows of 100 and 31 on one firmware may be +a different mode number on a different firmware or not exist at all. +Likewise, mode number 2 on one firmware may have a different number of +columns and rows than mode 2 on a different firmware. So one should not +rely on a particular mode number or a mode of a certain number of columns +and rows existing on all firmwares, except for mode 0. + +Note: This command is only available on EFI platforms and is similar to +EFI shell "mode" command. +@end deffn + + +@node eval +@subsection eval + +@deffn Command eval string ... +Concatenate arguments together using single space as separator and evaluate +result as sequence of GRUB commands. +@end deffn + + @node export @subsection export @@ -1302,28 +6987,245 @@ to subsidiary configuration files loaded using @command{configfile}. @end deffn +@node false +@subsection false + +@deffn Command false +Do nothing, unsuccessfully. This is mainly useful in control constructs +such as @code{if} and @code{while} (@pxref{Shell-like scripting}). +@end deffn + + +@node fdtdump +@subsection fdtdump + +@deffn Command fdtdump @ + [@option{--prop} @var{prop}] @ + [@option{--set} @var{variable}] +Retrieve device tree information. + +The @command{fdtdump} command returns the value of a property in the device +tree provided by the firmware. The @option{--prop} option determines which +property to select. + +The default action is to print the value of the requested field to the console, +but a variable name can be specified with @option{--set} to store the value +instead of printing it. + +For example, this will store and then display the model string. + +@example +fdtdump --prop model --set machine_model +echo $machine_model +@end example +@end deffn + + +@node file +@subsection file + +@deffn Command file is_file_type filename + +The @command{file} command tests whether the provided @var{filename} is the +type provided by @var{is_file_type}. When the @command{file} is of type +@var{is_file_type} this command will return 0, otherwise it will return +non-zero (no output is provided to the terminal). + +@var{is_file_type} may be one of the following options: +@itemize @bullet +@item +@option{--is-i386-xen-pae-domu} Check if @var{filename} can be booted as i386 +PAE Xen unprivileged guest kernel +@item +@option{--is-x86_64-xen-domu} Check if @var{filename} can be booted as x86_64 +Xen unprivileged guest kernel +@item +@option{--is-x86-xen-dom0} Check if @var{filename} can be used as Xen x86 +privileged guest kernel +@item +@option{--is-x86-multiboot} Check if @var{filename} can be used as x86 +multiboot kernel +@item +@option{--is-x86-multiboot2} Check if @var{filename} can be used as x86 +multiboot2 kernel +@item +@option{--is-arm-linux} Check if @var{filename} is ARM Linux +@item +@option{--is-arm64-linux} Check if @var{filename} is ARM64 Linux +@item +@option{--is-ia64-linux} Check if @var{filename} is IA64 Linux +@item +@option{--is-mips-linux} Check if @var{filename} is MIPS Linux +@item +@option{--is-mipsel-linux} Check if @var{filename} is MIPSEL Linux +@item +@option{--is-sparc64-linux} Check if @var{filename} is SPARC64 Linux +@item +@option{--is-powerpc-linux} Check if @var{filename} is POWERPC Linux +@item +@option{--is-x86-linux} Check if @var{filename} is x86 Linux +@item +@option{--is-x86-linux32} Check if @var{filename} is x86 Linux supporting +32-bit protocol +@item +@option{--is-x86-kfreebsd} Check if @var{filename} is x86 kFreeBSD +@item +@option{--is-i386-kfreebsd} Check if @var{filename} is i386 kFreeBSD +@item +@option{--is-x86_64-kfreebsd} Check if @var{filename} is x86_64 kFreeBSD +@item +@option{--is-x86-knetbsd} Check if @var{filename} is x86 kNetBSD +@item +@option{--is-i386-knetbsd} Check if @var{filename} is i386 kNetBSD +@item +@option{--is-x86_64-knetbsd} Check if @var{filename} is x86_64 kNetBSD +@item +@option{--is-i386-efi} Check if @var{filename} is i386 EFI file +@item +@option{--is-x86_64-efi} Check if @var{filename} is x86_64 EFI file +@item +@option{--is-ia64-efi} Check if @var{filename} is IA64 EFI file +@item +@option{--is-arm64-efi} Check if @var{filename} is ARM64 EFI file +@item +@option{--is-arm-efi} Check if @var{filename} is ARM EFI file +@item +@option{--is-riscv32-efi} Check if @var{filename} is RISC-V 32bit EFI file +@item +@option{--is-riscv64-efi} Check if @var{filename} is RISC-V 64bit EFI file +@item +@option{--is-hibernated-hiberfil} Check if @var{filename} is hiberfil.sys in +hibernated state +@item +@option{--is-x86_64-xnu} Check if @var{filename} is x86_64 XNU (Mac OS X kernel) +@item +@option{--is-i386-xnu} Check if @var{filename} is i386 XNU (Mac OS X kernel) +@item +@option{--is-xnu-hibr} Check if @var{filename} is XNU (Mac OS X kernel) +hibernated image +@item +@option{--is-x86-bios-bootsector} Check if @var{filename} is BIOS bootsector +@end itemize +@end deffn + + +@node fwsetup +@subsection fwsetup + +@deffn Command fwsetup [@option{--is-supported}] +Reboot into the firmware setup menu. If @option{--is-supported} option is +specified, instead check whether the firmware supports a setup menu and +exit successfully if so. +@end deffn + + +@node gdbinfo +@subsection gdbinfo + +@deffn Command gdbinfo +Output text to be used as a GDB command for a GDB session using the gdb_grub +script and attached to a running GRUB instance. The GDB command that is +output will tell GDB how to load debugging symbols to their proper runtime +address. Currently this is only available for EFI platforms. See the Debugging +in the developer documentation for more information. +@end deffn + + +@node gettext +@subsection gettext + +@deffn Command gettext string +Translate @var{string} into the current language. + +The current language code is stored in the @samp{lang} variable in GRUB's +environment (@pxref{lang}). Translation files in MO format are read from +@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub/locale}. +@end deffn + + +@node gptsync +@subsection gptsync + +@deffn Command gptsync device [partition[+/-[type]]] @dots{} +Disks using the GUID Partition Table (GPT) also have a legacy Master Boot +Record (MBR) partition table for compatibility with the BIOS and with older +operating systems. The legacy MBR can only represent a limited subset of +GPT partition entries. + +This command populates the legacy MBR with the specified @var{partition} +entries on @var{device}. Up to three partitions may be used. + +@var{type} is an MBR partition type code; prefix with @samp{0x} if you want +to enter this in hexadecimal. The separator between @var{partition} and +@var{type} may be @samp{+} to make the partition active, or @samp{-} to make +it inactive; only one partition may be active. If both the separator and +type are omitted, then the partition will be inactive. +@end deffn + + @node halt @subsection halt -@deffn Command halt @option{--no-apm} -The command halts the computer. If the @option{--no-apm} option -is specified, no APM BIOS call is performed. Otherwise, the computer -is shut down using APM. +@deffn Command halt [@option{--no-apm}] +The command halts the computer. On the i386-pc target, the @option{--no-apm} +option, or short @option{-n}, is specified, no APM BIOS call is performed. +Otherwise, the computer is shut down using APM on that target. +@end deffn + + +@node hashsum +@subsection hashsum + +@deffn Command hashsum @option{--hash} hash @option{--keep-going} @option{--uncompress} @option{--check} file [@option{--prefix} dir]|file @dots{} +Compute or verify file hashes. Hash type is selected with option @option{--hash}. +Supported hashes are: @samp{adler32}, @samp{crc64}, @samp{crc32}, +@samp{crc32rfc1510}, @samp{crc24rfc2440}, @samp{md4}, @samp{md5}, +@samp{ripemd160}, @samp{sha1}, @samp{sha224}, @samp{sha256}, @samp{sha512}, +@samp{sha384}, @samp{tiger192}, @samp{tiger}, @samp{tiger2}, @samp{whirlpool}. +Option @option{--uncompress} uncompresses files before computing hash. + +When list of files is given, hash of each file is computed and printed, +followed by file name, each file on a new line. + +When option @option{--check} is given, it points to a file that contains +list of @var{hash name} pairs in the same format as used by UNIX +@command{md5sum} command. Option @option{--prefix} +may be used to give directory where files are located. Hash verification +stops after the first mismatch was found unless option @option{--keep-going} +was given. The exit code @code{$?} is set to 0 if hash verification +is successful. If it fails, @code{$?} is set to a nonzero value. @end deffn @node help @subsection help -@deffn Command help @option{--all} [pattern @dots{}] +@deffn Command help [pattern @dots{}] Display helpful information about builtin commands. If you do not -specify @var{pattern}, this command shows short descriptions of most of -available commands. If you specify the option @option{--all} to this -command, short descriptions of rarely used commands (such as -@ref{testload}) are displayed as well. +specify @var{pattern}, this command shows short descriptions of all +available commands. If you specify any @var{patterns}, it displays longer information -about each of the commands which match those @var{patterns}. +about each of the commands whose names begin with those @var{patterns}. +@end deffn + + +@node hexdump +@subsection hexdump + +@deffn Command hexdump [--skip offset] [--length len] FILE_OR_DEVICE +Show raw contents of a file or memory. When option @option{--skip} is given, +@samp{offset} number of bytes are skipped from the start of the device or +file given. And @option{--length} allows specifying a maximum number of bytes +to be shown. + +If given the special device named @samp{(mem)}, then the @samp{offset} given to +@option{--skip} is treated as the address of a memory location to dump from. + +Note: The dumping of RAM memory (by the (mem) argument) is not allowed when +when lockdown is enforced (@pxref{Lockdown}). The dumping of disk or file +data is allowed when lockdown is enforced. + @end deffn @@ -1349,26 +7251,331 @@ only if checking key modifier status is supported. @end deffn +@node list_env +@subsection list_env + +@deffn Command list_env [@option{--file} file] +List all variables in the environment block file. @xref{Environment block}. + +The @option{--file} option overrides the default location of the +environment block. +@end deffn + +@node list_trusted +@subsection list_trusted + +@deffn Command list_trusted +List all public keys trusted by GRUB for validating signatures. +The output is in GPG's v4 key fingerprint format (i.e., the output of +@code{gpg --fingerprint}). The least significant four bytes (last +eight hexadecimal digits) can be used as an argument to +@command{distrust} (@pxref{distrust}). +@xref{Using digital signatures}, for more information about uses for +these keys. +@end deffn + +@node load_env +@subsection load_env + +@deffn Command load_env [@option{--file} file] [@option{--skip-sig}] [whitelisted_variable_name] @dots{} +Load all variables from the environment block file into the environment. +@xref{Environment block}. + +The @option{--file} option overrides the default location of the environment +block. + +The @option{--skip-sig} option skips signature checking even when the +value of environment variable @code{check_signatures} is set to +@code{enforce} (@pxref{check_signatures}). + +If one or more variable names are provided as arguments, they are +interpreted as a whitelist of variables to load from the environment +block file. Variables set in the file but not present in the +whitelist are ignored. + +The @option{--skip-sig} option should be used with care, and should +always be used in concert with a whitelist of acceptable variables +whose values should be set. Failure to employ a carefully constructed +whitelist could result in reading a malicious value into critical +environment variables from the file, such as setting +@code{check_signatures=no}, modifying @code{prefix} to boot from an +unexpected location or not at all, etc. + +When used with care, @option{--skip-sig} and the whitelist enable an +administrator to configure a system to boot only signed +configurations, but to allow the user to select from among multiple +configurations, and to enable ``one-shot'' boot attempts and +``savedefault'' behavior. @xref{Using digital signatures}, for more +information. +@end deffn + + +@node loadfont +@subsection loadfont + +@deffn Command loadfont file @dots{} +Load specified font files. Unless absolute pathname is given, @var{file} +is assumed to be in directory @samp{$prefix/fonts} with +suffix @samp{.pf2} appended. @xref{Theme file format,,Fonts}. +@end deffn + + +@node loopback +@subsection loopback + +@deffn Command loopback [@option{-d}] [@option{-D}] device file +Make the device named @var{device} correspond to the contents of the +filesystem image in @var{file}. For example: + +@example +loopback loop0 /path/to/image +ls (loop0)/ +@end example + +Specifying the @option{-D} option allows the loopback file to be tranparently +decompressed if there is an appropriate decompressor loaded. + +With the @option{-d} option, delete a device previously created using this +command. +@end deffn + + @node ls @subsection ls -@deffn Command ls [arg] +@deffn Command ls [arg @dots{}] List devices or files. With no arguments, print all devices known to GRUB. If the argument is a device name enclosed in parentheses (@pxref{Device -syntax}), then list all files at the root directory of that device. +syntax}), then print the name of the filesystem of that device. If the argument is a directory given as an absolute file name (@pxref{File name syntax}), then list the contents of that directory. @end deffn +@node lsfonts +@subsection lsfonts + +@deffn Command lsfonts +List loaded fonts. +@end deffn + + +@node lsmod +@subsection lsmod + +@deffn Command lsmod +Show list of loaded modules. +@end deffn + +@node md5sum +@subsection md5sum + +@deffn Command md5sum arg @dots{} +Alias for @code{hashsum --hash md5 arg @dots{}}. See command @command{hashsum} +(@pxref{hashsum}) for full description. +@end deffn + +@node module +@subsection module + +@deffn Command module [--nounzip] file [arguments] +Load a module for multiboot kernel image. The rest of the +line is passed verbatim as the module command line. +@end deffn + +@node multiboot +@subsection multiboot + +@deffn Command multiboot [--quirk-bad-kludge] [--quirk-modules-after-kernel] file @dots{} +Load a multiboot kernel image from @var{file}. The rest of the +line is passed verbatim as the @dfn{kernel command-line}. Any module must +be reloaded after using this command (@pxref{module}). + +Some kernels have known problems. You need to specify --quirk-* for those. +--quirk-bad-kludge is a problem seen in several products that they include +loading kludge information with invalid data in ELF file. GRUB prior to 0.97 +and some custom builds preferred ELF information while 0.97 and GRUB 2 +use kludge. Use this option to ignore kludge. +Known affected systems: old Solaris, SkyOS. + +--quirk-modules-after-kernel is needed for kernels which load at relatively +high address e.g. 16MiB mark and can't cope with modules stuffed between +1MiB mark and beginning of the kernel. +Known afftected systems: VMWare. +@end deffn + +@node nativedisk +@subsection nativedisk + +@deffn Command nativedisk +Switch from firmware disk drivers to native ones. +Really useful only on platforms where both +firmware and native disk drives are available. +Currently i386-pc, i386-efi, i386-ieee1275 and +x86_64-efi. +@end deffn + +@node normal +@subsection normal + +@deffn Command normal [file] +Enter normal mode and display the GRUB menu. + +In normal mode, commands, filesystem modules, and cryptography modules are +automatically loaded, and the full GRUB script parser is available. Other +modules may be explicitly loaded using @command{insmod} (@pxref{insmod}). + +If a @var{file} is given, then commands will be read from that file. +Otherwise, they will be read from @file{$prefix/grub.cfg} if it exists. + +@command{normal} may be called from within normal mode, creating a nested +environment. It is more usual to use @command{configfile} +(@pxref{configfile}) for this. +@end deffn + + +@node normal_exit +@subsection normal_exit + +@deffn Command normal_exit +Exit normal mode (@pxref{normal}). If this instance of normal mode was not +nested within another one, then return to rescue mode. +@end deffn + + +@node parttool +@subsection parttool + +@deffn Command parttool partition commands +Make various modifications to partition table entries. + +Each @var{command} is either a boolean option, in which case it must be +followed with @samp{+} or @samp{-} (with no intervening space) to enable or +disable that option, or else it takes a value in the form +@samp{@var{command}=@var{value}}. + +Currently, @command{parttool} is only useful on DOS partition tables (also +known as Master Boot Record, or MBR). On these partition tables, the +following commands are available: + +@table @asis +@item @samp{boot} (boolean) +When enabled, this makes the selected partition be the active (bootable) +partition on its disk, clearing the active flag on all other partitions. +This command is limited to @emph{primary} partitions. + +@item @samp{type} (value) +Change the type of an existing partition. The value must be a number in the +range 0-0xFF (prefix with @samp{0x} to enter it in hexadecimal). + +@item @samp{hidden} (boolean) +When enabled, this hides the selected partition by setting the @dfn{hidden} +bit in its partition type code; when disabled, unhides the selected +partition by clearing this bit. This is useful only when booting DOS or +Windows and multiple primary FAT partitions exist in one disk. See also +@ref{DOS/Windows}. +@end table +@end deffn + + +@node password +@subsection password + +@deffn Command password user clear-password +Define a user named @var{user} with password @var{clear-password}. +@xref{Security}. +@end deffn + + +@node password_pbkdf2 +@subsection password_pbkdf2 + +@deffn Command password_pbkdf2 user hashed-password +Define a user named @var{user} with password hash @var{hashed-password}. +Use @command{grub-mkpasswd-pbkdf2} (@pxref{Invoking grub-mkpasswd-pbkdf2}) +to generate password hashes. @xref{Security}. +@end deffn + + +@node plainmount +@subsection plainmount + +@deffn Command plainmount device @option{-c} cipher @option{-s} key size [@option{-h} hash] +[@option{-S} sector size] [@option{-p} password] [@option{-u} uuid] +[[@option{-d} keyfile] [@option{-O} keyfile offset]] + + +Setup access to the encrypted device in plain mode. Offset of the encrypted +data at the device is specified in terms of 512 byte sectors using the blocklist +syntax and loopback device. The following example shows how to specify 1MiB +offset: + +@example +loopback node (hd0,gpt1)2048+ +plainmount node @var{...} +@end example + +The @command{plainmount} command can be used to open LUKS encrypted volume +if its master key and parameters (key size, cipher, offset, etc) are known. + +There are two ways to specify a password: a keyfile and a secret passphrase. +The keyfile path parameter has higher priority than the secret passphrase +parameter and is specified with the option @option{-d}. Password data obtained +from keyfiles is not hashed and is used directly as a cipher key. An optional +offset of password data in the keyfile can be specified with the option +@option{-O} or directly with the option @option{-d} and GRUB blocklist syntax, +if the keyfile data can be accessed from a device and is 512 byte aligned. +The following example shows both methods to specify password data in the +keyfile at offset 1MiB: + +@example +plainmount -d (hd0,gpt1)2048+ @var{...} +plainmount -d (hd0,gpt1)+ -O 1048576 @var{...} +@end example + +If no keyfile is specified then the password is set to the string specified +by option @option{-p} or is requested interactively from the console. In both +cases the provided password is hashed with the algorithm specified by the +option @option{-h}. This option is mandatory if no keyfile is specified, but +it can be set to @samp{plain} which means that no hashing is done and such +password is used directly as a key. + +Cipher @option{-c} and keysize @option{-s} options specify the cipher algorithm +and the key size respectively and are mandatory options. Cipher must be specified +with the mode separated by a dash (for example, @samp{aes-xts-plain64}). Key size +option @option{-s} is the key size of the cipher in bits, not to be confused with +the offset of the key data in a keyfile specified with the @option{-O} option. It +must not exceed 1024 bits, so a 32 byte key would be specified as 256 bits + +The optional parameter @option{-S} specifies encrypted device sector size. It +must be at least 512 bytes long (default value) and a power of 2. @footnote{Current +implementation of cryptsetup supports only 512/1024/2048/4096 byte sectors}. +Disk sector size is configured when creating the encrypted volume. Attempting +to decrypt volumes with a different sector size than it was created with will +not result in an error, but will decrypt to random bytes and thus prevent +accessing the volume (in some cases the filesystem driver can detect the presence +of a filesystem, but nevertheless will refuse to mount it). + +By default new plainmount devices will be given a UUID starting with +'109fea84-a6b7-34a8-4bd1-1c506305a401' where the last digits are incremented +by one for each plainmounted device beyond the first up to 2^10 devices. + +All encryption arguments (cipher, hash, key size, disk offset and disk sector +size) must match the parameters used to create the volume. If any of them does +not match the actual arguments used during the initial encryption, plainmount +will create virtual device with the garbage data and GRUB will report unknown +filesystem for such device. +@end deffn + + @node play @subsection play -@deffn Command play file | tempo [pitch1 duration1] [pitch2 duration2] ... +@deffn Command play file | tempo [pitch1 duration1] [pitch2 duration2] @dots{} Plays a tune If the argument is a file name (@pxref{File name syntax}), play the tune @@ -1379,7 +7586,48 @@ pitch and duration pairs. If the arguments are a series of numbers, play the inline tune. The tempo is the base for all note durations. 60 gives a 1-second base, 120 -gives a half-second base, etc. Pitches are Hz. +gives a half-second base, etc. Pitches are Hz. Set pitch to 0 to produce +a rest. +@end deffn + + +@node probe +@subsection probe + +@deffn Command probe [@option{--set} var] @option{--driver}|@option{--partmap}|@option{--fs}|@option{--fs-uuid}|@option{--label}|@option{--part-uuid} device +Retrieve device information. If option @option{--set} is given, assign result +to variable @var{var}, otherwise print information on the screen. + +The option @option{--part-uuid} is currently only implemented for MSDOS and GPT formatted disks. +@end deffn + + +@node rdmsr +@subsection rdmsr + +@deffn Command: rdmsr 0xADDR [-v VARNAME] +Read a model-specific register at address 0xADDR. If the parameter +@option{-v} is used and an environment variable @var{VARNAME} is +given, set that environment variable to the value that was read. + +Please note that on SMP systems, reading from a MSR that has a +scope per hardware thread, implies that the value that is returned +only applies to the particular cpu/core/thread that runs the command. + +Also, if you specify a reserved or unimplemented MSR address, it will +cause a general protection exception (which is not currently being handled) +and the system will reboot. +@end deffn + + +@node read +@subsection read + +@deffn Command read [-s] [var] +Read a line of input from the user. If an environment variable @var{var} is +given, set that environment variable to the line of input that was read, +with no terminating newline. If the parameter @option{-s} is used, enable +silent mode where input is not printed to the terminal. @end deffn @@ -1391,12 +7639,536 @@ Reboot the computer. @end deffn +@node regexp +@subsection regexp + +@deffn Command regexp [@option{--set} [number:]var] regexp string +Test if regular expression @var{regexp} matches @var{string}. Supported +regular expressions are POSIX.2 Extended Regular Expressions. If option +@option{--set} is given, store @var{number}th matched subexpression in +variable @var{var}. Subexpressions are numbered in order of their opening +parentheses starting from @samp{1}. @var{number} defaults to @samp{1}. +@end deffn + + +@node rmmod +@subsection rmmod + +@deffn Command rmmod module +Remove a loaded @var{module}. +@end deffn + + +@node save_env +@subsection save_env + +@deffn Command save_env [@option{--file} file] var @dots{} +Save the named variables from the environment to the environment block file. +@xref{Environment block}. + +The @option{--file} option overrides the default location of the environment +block. + +This command will operate successfully even when environment variable +@code{check_signatures} is set to @code{enforce} +(@pxref{check_signatures}), since it writes to disk and does not alter +the behavior of GRUB based on any contents of disk that have been +read. It is possible to modify a digitally signed environment block +file from within GRUB using this command, such that its signature will +no longer be valid on subsequent boots. Care should be taken in such +advanced configurations to avoid rendering the system +unbootable. @xref{Using digital signatures}, for more information. +@end deffn + + +@node search +@subsection search + +@deffn Command search @ + [@option{--file}|@option{--label}|@option{--fs-uuid}] @ + [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}|@option{--cryptodisk-only}] @ + name +Search devices by file (@option{-f}, @option{--file}), filesystem label +(@option{-l}, @option{--label}), or filesystem UUID (@option{-u}, +@option{--fs-uuid}). + +If the (@option{-s}, @option{--set}) option is used, the first device found is +set as the value of environment variable @var{var}. The default variable is +@samp{root}. + +The (@option{-n}, @option{--no-floppy}) option prevents searching floppy +devices, which can be slow. + +The (@option{--efidisk-only}) option prevents searching any other devices then +EFI disks. This is typically used when chainloading to local EFI partition. + +The (@option{--cryptodisk-only}) option prevents searching any devices other +than encrypted disks. This is typically used when booting from an encrypted +file system to ensure that no code gets executed from an unencrypted device +having the same filesystem UUID or label. + +This option implicitly invokes the command @command{cryptocheck}, if it is +available (@pxref{cryptocheck} for additional information). + +The @samp{search.file}, @samp{search.fs_label}, and @samp{search.fs_uuid} +commands are aliases for @samp{search --file}, @samp{search --label}, and +@samp{search --fs-uuid} respectively. + +Also hints as to which device may be the most likely to contain the item +searched for may be given via the (@option{-h}, @option{--hint}) option with +a device name as an argument. If the argument ends with a comma, then partitions +on the device are also searched. Furthermore, platform specific hints may be +given via the options @option{--hint-ieee1275}, @option{--hint-bios}, +@option{--hint-baremetal}, @option{--hint-efi}, and @option{--hint-arc}. When +specified, these options take an argument and operate like @option{--hint}, but +only on the specified platform. +@end deffn + + +@node sendkey +@subsection sendkey + +@deffn Command sendkey @ + [@option{--num}|@option{--caps}|@option{--scroll}|@option{--insert}|@ +@option{--pause}|@option{--left-shift}|@option{--right-shift}|@ +@option{--sysrq}|@option{--numkey}|@option{--capskey}|@option{--scrollkey}|@ +@option{--insertkey}|@option{--left-alt}|@option{--right-alt}|@ +@option{--left-ctrl}|@option{--right-ctrl} @ + @samp{on}|@samp{off}]@dots{} @ + [@option{no-led}] @ + keystroke +Insert keystrokes into the keyboard buffer when booting. Sometimes an +operating system or chainloaded boot loader requires particular keys to be +pressed: for example, one might need to press a particular key to enter +"safe mode", or when chainloading another boot loader one might send +keystrokes to it to navigate its menu. + +Note: This command is currently only available on the i386-pc target. + +You may provide up to 16 keystrokes (the length of the BIOS keyboard +buffer). Keystroke names may be upper-case or lower-case letters, digits, +or taken from the following table: + +@c Please keep this table in the same order as in +@c commands/i386/pc/sendkey.c, for ease of maintenance. +@c Exception: The function and numeric keys are sorted, for aesthetics. + +@multitable @columnfractions .4 .5 +@headitem Name @tab Key +@item escape @tab Escape +@item exclam @tab ! +@item at @tab @@ +@item numbersign @tab # +@item dollar @tab $ +@item percent @tab % +@item caret @tab ^ +@item ampersand @tab & +@item asterisk @tab * +@item parenleft @tab ( +@item parenright @tab ) +@item minus @tab - +@item underscore @tab _ +@item equal @tab = +@item plus @tab + +@item backspace @tab Backspace +@item tab @tab Tab +@item bracketleft @tab [ +@item braceleft @tab @{ +@item bracketright @tab ] +@item braceright @tab @} +@item enter @tab Enter +@item control @tab press and release Control +@item semicolon @tab ; +@item colon @tab : +@item quote @tab ' +@item doublequote @tab " +@item backquote @tab ` +@item tilde @tab ~ +@item shift @tab press and release left Shift +@item backslash @tab \ +@item bar @tab | +@item comma @tab , +@item less @tab < +@item period @tab . +@item greater @tab > +@item slash @tab / +@item question @tab ? +@item rshift @tab press and release right Shift +@item alt @tab press and release Alt +@item space @tab space bar +@item capslock @tab Caps Lock +@item F1 @tab F1 +@item F2 @tab F2 +@item F3 @tab F3 +@item F4 @tab F4 +@item F5 @tab F5 +@item F6 @tab F6 +@item F7 @tab F7 +@item F8 @tab F8 +@item F9 @tab F9 +@item F10 @tab F10 +@item F11 @tab F11 +@item F12 @tab F12 +@item num1 @tab 1 (numeric keypad) +@item num2 @tab 2 (numeric keypad) +@item num3 @tab 3 (numeric keypad) +@item num4 @tab 4 (numeric keypad) +@item num5 @tab 5 (numeric keypad) +@item num6 @tab 6 (numeric keypad) +@item num7 @tab 7 (numeric keypad) +@item num8 @tab 8 (numeric keypad) +@item num9 @tab 9 (numeric keypad) +@item num0 @tab 0 (numeric keypad) +@item numperiod @tab . (numeric keypad) +@item numend @tab End (numeric keypad) +@item numdown @tab Down (numeric keypad) +@item numpgdown @tab Page Down (numeric keypad) +@item numleft @tab Left (numeric keypad) +@item numcenter @tab 5 with Num Lock inactive (numeric keypad) +@item numright @tab Right (numeric keypad) +@item numhome @tab Home (numeric keypad) +@item numup @tab Up (numeric keypad) +@item numpgup @tab Page Up (numeric keypad) +@item numinsert @tab Insert (numeric keypad) +@item numdelete @tab Delete (numeric keypad) +@item numasterisk @tab * (numeric keypad) +@item numminus @tab - (numeric keypad) +@item numplus @tab + (numeric keypad) +@item numslash @tab / (numeric keypad) +@item numenter @tab Enter (numeric keypad) +@item delete @tab Delete +@item insert @tab Insert +@item home @tab Home +@item end @tab End +@item pgdown @tab Page Down +@item pgup @tab Page Up +@item down @tab Down +@item up @tab Up +@item left @tab Left +@item right @tab Right +@end multitable + +As well as keystrokes, the @command{sendkey} command takes various options +that affect the BIOS keyboard status flags. These options take an @samp{on} +or @samp{off} parameter, specifying that the corresponding status flag be +set or unset; omitting the option for a given status flag will leave that +flag at its initial state at boot. The @option{--num}, @option{--caps}, +@option{--scroll}, and @option{--insert} options emulate setting the +corresponding mode, while the @option{--numkey}, @option{--capskey}, +@option{--scrollkey}, and @option{--insertkey} options emulate pressing and +holding the corresponding key. The other status flag options are +self-explanatory. + +If the @option{--no-led} option is given, the status flag options will have +no effect on keyboard LEDs. + +If the @command{sendkey} command is given multiple times, then only the last +invocation has any effect. + +Since @command{sendkey} manipulates the BIOS keyboard buffer, it may cause +hangs, reboots, or other misbehaviour on some systems. If the operating +system or boot loader that runs after GRUB uses its own keyboard driver +rather than the BIOS keyboard functions, then @command{sendkey} will have no +effect. + +This command is only available on PC BIOS systems. +@end deffn + + @node set @subsection set @deffn Command set [envvar=value] Set the environment variable @var{envvar} to @var{value}. If invoked with no -arguments, print all environment variables with their values. +arguments, print all environment variables with their values. For the list of +environment variables currently used by GRUB itself see the relevant section +@pxref{Environment}. +@end deffn + + +@node sha1sum +@subsection sha1sum + +@deffn Command sha1sum arg @dots{} +Alias for @code{hashsum --hash sha1 arg @dots{}}. See command @command{hashsum} +(@pxref{hashsum}) for full description. +@end deffn + + +@node sha256sum +@subsection sha256sum + +@deffn Command sha256sum arg @dots{} +Alias for @code{hashsum --hash sha256 arg @dots{}}. See command @command{hashsum} +(@pxref{hashsum}) for full description. +@end deffn + + +@node sha512sum +@subsection sha512sum + +@deffn Command sha512sum arg @dots{} +Alias for @code{hashsum --hash sha512 arg @dots{}}. See command @command{hashsum} +(@pxref{hashsum}) for full description. +@end deffn + + +@node sleep +@subsection sleep + +@deffn Command sleep [@option{--verbose}] [@option{--interruptible}] count +Sleep for @var{count} seconds. If option @option{--interruptible} is given, +allow pressing @key{ESC}, @key{F4} or holding down @key{SHIFT} to interrupt +sleep. With @option{--verbose} show countdown of remaining seconds. Exit code +is set to 0 if timeout expired and to 1 if timeout was interrupted using any +of the mentioned keys. +@end deffn + + +@node smbios +@subsection smbios + +@deffn Command smbios @ + [@option{--type} @var{type}] @ + [@option{--handle} @var{handle}] @ + [@option{--match} @var{match}] @ + (@option{--get-byte} | @option{--get-word} | @option{--get-dword} | @ + @option{--get-qword} | @option{--get-string} | @option{--get-uuid}) @ + @var{offset} @ + [@option{--set} @var{variable}] +Retrieve SMBIOS information. + +The @command{smbios} command returns the value of a field in an SMBIOS +structure. The following options determine which structure to select. + +@itemize @bullet +@item +Specifying @option{--type} will select structures with a matching +@var{type}. The type can be any integer from 0 to 255. +@item +Specifying @option{--handle} will select structures with a matching +@var{handle}. The handle can be any integer from 0 to 65535. +@item +Specifying @option{--match} will select structure number @var{match} in the +filtered list of structures; e.g. @code{smbios --type 4 --match 2} will select +the second Process Information (Type 4) structure. The list is always ordered +the same as the hardware's SMBIOS table. The match number must be a positive +integer. If unspecified, the first matching structure will be selected. +@end itemize + +The remaining options determine which field in the selected SMBIOS structure to +return. Only one of these options may be specified at a time. + +@itemize @bullet +@item +When given @option{--get-byte}, return the value of the byte +at @var{offset} bytes into the selected SMBIOS structure. +It will be formatted as an unsigned decimal integer. +@item +When given @option{--get-word}, return the value of the word (two bytes) +at @var{offset} bytes into the selected SMBIOS structure. +It will be formatted as an unsigned decimal integer. +@item +When given @option{--get-dword}, return the value of the dword (four bytes) +at @var{offset} bytes into the selected SMBIOS structure. +It will be formatted as an unsigned decimal integer. +@item +When given @option{--get-qword}, return the value of the qword (eight bytes) +at @var{offset} bytes into the selected SMBIOS structure. +It will be formatted as an unsigned decimal integer. +@item +When given @option{--get-string}, return the string with its index found +at @var{offset} bytes into the selected SMBIOS structure. +@item +When given @option{--get-uuid}, return the value of the UUID (sixteen bytes) +at @var{offset} bytes into the selected SMBIOS structure. +It will be formatted as lower-case hyphenated hexadecimal digits, with the +first three fields as little-endian, and the rest printed byte-by-byte. +@end itemize + +The default action is to print the value of the requested field to the console, +but a variable name can be specified with @option{--set} to store the value +instead of printing it. + +For example, this will store and then display the system manufacturer's name. + +@example +smbios --type 1 --get-string 4 --set system_manufacturer +echo $system_manufacturer +@end example +@end deffn + + +@node source +@subsection source + +@deffn Command source file +Read @var{file} as a configuration file, as if its contents had been +incorporated directly into the sourcing file. Unlike @command{configfile} +(@pxref{configfile}), this executes the contents of @var{file} without +changing context: any environment variable changes made by the commands in +@var{file} will be preserved after @command{source} returns, and the menu +will not be shown immediately. +@end deffn + + +@node test +@subsection test + +@deffn Command test expression +Evaluate @var{expression} and return zero exit status if result is true, +non zero status otherwise. + +@var{expression} is one of: + +@table @asis +@item @var{string1} @code{==} @var{string2} +the strings are equal +@item @var{string1} @code{!=} @var{string2} +the strings are not equal +@item @var{string1} @code{<} @var{string2} +@var{string1} is lexicographically less than @var{string2} +@item @var{string1} @code{<=} @var{string2} +@var{string1} is lexicographically less or equal than @var{string2} +@item @var{string1} @code{>} @var{string2} +@var{string1} is lexicographically greater than @var{string2} +@item @var{string1} @code{>=} @var{string2} +@var{string1} is lexicographically greater or equal than @var{string2} +@item @var{integer1} @code{-eq} @var{integer2} +@var{integer1} is equal to @var{integer2} +@item @var{integer1} @code{-ge} @var{integer2} +@var{integer1} is greater than or equal to @var{integer2} +@item @var{integer1} @code{-gt} @var{integer2} +@var{integer1} is greater than @var{integer2} +@item @var{integer1} @code{-le} @var{integer2} +@var{integer1} is less than or equal to @var{integer2} +@item @var{integer1} @code{-lt} @var{integer2} +@var{integer1} is less than @var{integer2} +@item @var{integer1} @code{-ne} @var{integer2} +@var{integer1} is not equal to @var{integer2} +@item @var{prefix}@var{integer1} @code{-pgt} @var{prefix}@var{integer2} +@var{integer1} is greater than @var{integer2} after stripping off common non-numeric @var{prefix}. +@item @var{prefix}@var{integer1} @code{-plt} @var{prefix}@var{integer2} +@var{integer1} is less than @var{integer2} after stripping off common non-numeric @var{prefix}. +@item @var{file1} @code{-nt} @var{file2} +@var{file1} is newer than @var{file2} (modification time). Optionally numeric @var{bias} may be directly appended to @code{-nt} in which case it is added to the first file modification time. +@item @var{file1} @code{-ot} @var{file2} +@var{file1} is older than @var{file2} (modification time). Optionally numeric @var{bias} may be directly appended to @code{-ot} in which case it is added to the first file modification time. +@item @code{-d} @var{file} +@var{file} exists and is a directory +@item @code{-e} @var{file} +@var{file} exists +@item @code{-f} @var{file} +@var{file} exists and is not a directory +@item @code{-s} @var{file} +@var{file} exists and has a size greater than zero +@item @code{-n} @var{string} +the length of @var{string} is nonzero +@item @var{string} +@var{string} is equivalent to @code{-n @var{string}} +@item @code{-z} @var{string} +the length of @var{string} is zero +@item @code{(} @var{expression} @code{)} +@var{expression} is true +@item @code{!} @var{expression} +@var{expression} is false +@item @var{expression1} @code{-a} @var{expression2} +both @var{expression1} and @var{expression2} are true +@item @var{expression1} @var{expression2} +both @var{expression1} and @var{expression2} are true. This syntax is not POSIX-compliant and is not recommended. +@item @var{expression1} @code{-o} @var{expression2} +either @var{expression1} or @var{expression2} is true +@end table +@end deffn + +@node tpm2_key_protector_init +@subsection tpm2_key_protector_init + +@deffn Command tpm2_key_protector_init [@option{--mode} | @option{-m} mode] | [@option{--pcrs} | @option{-p} pcrlist] | [@option{--bank} | @option{-b} pcrbank] | [ [@option{--tpm2key} | @option{-T} tpm2key_file] | [@option{--keyfile} | @option{-k} keyfile] ] | [@option{--srk} | @option{-s} handle] | [@option{--asymmetric} | @option{-a} srk_type] | [@option{--nvindex} | @option{-n} nv_index] +Initialize the TPM2 key protector to unseal the key for the @command{cryptomount} +(@pxref{cryptomount}) command. There are two supported modes, +SRK(@kbd{srk}) and NV index(@kbd{nv}), to be specified by the option +@option{-m}. The default mode is SRK. The main difference between SRK mode +and NV index mode is the storage of the sealed key. For SRK mode, the sealed +key is stored in a file while NV index mode stores the sealed key in the +non-volatile memory inside TPM with a given NV index. + +The @option{-p} and @option{-b} options are used to supply the PCR list and +bank that the key is sealed with. The PCR list is a comma-separated list, e.g., +'0,2,4,7,9', to represent the involved PCRs, and the default is '7'. The PCR +bank is chosen by selecting a hash algorithm. The current supported PCR banks +are SHA1, SHA256, SHA384, and SHA512, and the default is SHA256. + +Some options are only available for the specific mode. The SRK-specific +options are @option{-T}, @option{-k}, @option{-a}, and @option{-s}. On the +other hand, the NV index-specific option is @option{-n}. + +The key file for SRK mode can be supplied with either @option{-T} or +@option{-k}. Those two options were used to distinguish the file formats but +are same now. There are two supported file formats: raw format and TPM 2.0 +Key File format. When using the key file in the raw format, the @option{-p} +and @option{-b} options are necessary for the non-default PCR list or bank. +On the other hand, when using the key file in TPM 2.0 Key File format, the +the parameters for the TPM commands are written in the file, and there is no +need to set the PCR list(@option{-p}) and bank(@option{-b}). In general, +TPM 2.0 Key File format is preferred due to the simplified GRUB command +options and the authorized policy support + +Besides the key file, there are two options, @option{-a} and @option{-s}, to +tweak the TPM Storage Root Key (SRK). The SRK can be either created at +runtime or stored in the non-volatile memory. When creating SRK at runtime, +GRUB provides the SRK template to the TPM to create the key. There are two SRK +templates for the @option{-a} option, ECC and RSA, and the default is ECC. +If the SRK is stored in a specific handle, e.g. @code{0x81000001}, the +@option{-s} option can be used to set the handle to notify GRUB to load +the SRK from the given handle. + +The only NV index-specific option is the @option{-n} option which is used to +set the NV index containing the sealed key. Then GRUB can load the sealed +key and unseal it with the given PCR list and bank. +@end deffn + +@node tpm2_key_protector_clear +@subsection tpm2_key_protector_clear + +@deffn Command tpm2_key_protector_clear +Clear the TPM2 key protector if previously initialized. +@end deffn + +@node tpm2_dump_pcr +@subsection tpm2_dump_pcr + +@deffn Command tpm2_dump_pcr [@var{bank}] +Print all PCRs of the specified TPM 2.0 @var{bank}. The supported banks are +@samp{sha1}, @samp{sha256}, @samp{sha384}, and @samp{sha512}. If @var{bank} +is not specified, @samp{sha256} is chosen by default. + +Since GRUB measures every command into PCR 8, invoking @command{tpm2_dump_pcr} +also extends PCR 8, so PCR 8 will not be a stable value in GRUB shell. +@end deffn + +@node true +@subsection true + +@deffn Command true +Do nothing, successfully. This is mainly useful in control constructs such +as @code{if} and @code{while} (@pxref{Shell-like scripting}). +@end deffn + +@node trust +@subsection trust + +@deffn Command trust [@option{--skip-sig}] pubkey_file +Read public key from @var{pubkey_file} and add it to GRUB's internal +list of trusted public keys. These keys are used to validate digital +signatures when environment variable @code{check_signatures} is set to +@code{enforce}. Note that if @code{check_signatures} is set to +@code{enforce} when @command{trust} executes, then @var{pubkey_file} +must itself be properly signed. The @option{--skip-sig} option can be +used to disable signature-checking when reading @var{pubkey_file} +itself. It is expected that @option{--skip-sig} is useful for testing +and manual booting. @xref{Using digital signatures}, for more +information. @end deffn @@ -1408,11 +8180,1640 @@ Unset the environment variable @var{envvar}. @end deffn -@node Invoking grub-install -@chapter Invoking grub-install +@ignore +@node vbeinfo +@subsection vbeinfo -The program @command{grub-install} installs GRUB on your drive using the -grub shell (@pxref{Invoking the grub shell}). You must specify the +@deffn Command vbeinfo [[WxH]xD] +Alias for command @command{videoinfo} (@pxref{videoinfo}). It is available +only on PC BIOS platforms. +@end deffn +@end ignore + + +@node verify_detached +@subsection verify_detached + +@deffn Command verify_detached [@option{--skip-sig}] file signature_file [pubkey_file] +Verifies a GPG-style detached signature, where the signed file is +@var{file}, and the signature itself is in file @var{signature_file}. +Optionally, a specific public key to use can be specified using +@var{pubkey_file}. When environment variable @code{check_signatures} +is set to @code{enforce}, then @var{pubkey_file} must itself be +properly signed by an already-trusted key. An unsigned +@var{pubkey_file} can be loaded by specifying @option{--skip-sig}. +If @var{pubkey_file} is omitted, then public keys from GRUB's trusted keys +(@pxref{list_trusted}, @pxref{trust}, and @pxref{distrust}) are +tried. + +Exit code @code{$?} is set to 0 if the signature validates +successfully. If validation fails, it is set to a non-zero value. +@xref{Using digital signatures}, for more information. +@end deffn + +@node videoinfo +@subsection videoinfo + +@deffn Command videoinfo [[WxH]xD] +List available video modes. If resolution is given, show only matching modes. +@end deffn + +@node wrmsr +@subsection wrmsr + +@deffn Command: wrmsr 0xADDR 0xVALUE +Write a 0xVALUE to a model-specific register at address 0xADDR. + +Please note that on SMP systems, writing to a MSR that has a scope +per hardware thread, implies that the value that is written +only applies to the particular cpu/core/thread that runs the command. + +Also, if you specify a reserved or unimplemented MSR address, it will +cause a general protection exception (which is not currently being handled) +and the system will reboot. + +Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). + This is done to prevent subverting various security mechanisms. +@end deffn + +@node Networking commands +@section Networking commands + +@menu +* net_add_addr:: Add a network address +* net_add_dns:: Add a DNS server +* net_add_route:: Add routing entry +* net_bootp:: Perform a bootp/DHCP autoconfiguration +* net_del_addr:: Remove IP address from interface +* net_del_dns:: Remove a DNS server +* net_del_route:: Remove a route entry +* net_dhcp:: Perform a DHCP autoconfiguration +* net_get_dhcp_option:: Retrieve DHCP options +* net_ipv6_autoconf:: Perform IPv6 autoconfiguration +* net_ls_addr:: List interfaces +* net_ls_cards:: List network cards +* net_ls_dns:: List DNS servers +* net_ls_routes:: List routing entries +* net_nslookup:: Perform a DNS lookup +* net_set_vlan:: Set vlan id on an interface +@end menu + + +@node net_add_addr +@subsection net_add_addr + +@deffn Command net_add_addr @var{interface} @var{card} @var{address} +Configure additional network @var{interface} with @var{address} on a +network @var{card}. @var{address} can be either IP in dotted decimal notation, +or symbolic name which is resolved using DNS lookup. If successful, this command +also adds local link routing entry to the default subnet of @var{address} +with name @var{interface}@samp{:local} via @var{interface}. +@end deffn + + +@node net_add_dns +@subsection net_add_dns + +@deffn Command net_add_dns @var{server} +Resolve @var{server} IP address and add to the list of DNS servers used during +name lookup. +@end deffn + + +@node net_add_route +@subsection net_add_route + +@deffn Command net_add_route @var{shortname} @var{ip}[/@var{prefix}] [@var{interface} | @samp{gw} @var{gateway}] +Add route to network with address @var{ip} as modified by @var{prefix} via +either local @var{interface} or @var{gateway}. @var{prefix} is optional and +defaults to 32 for IPv4 address and 128 for IPv6 address. Route is identified +by @var{shortname} which can be used to remove it (@pxref{net_del_route}). +@end deffn + + +@node net_bootp +@subsection net_bootp + +@deffn Command net_bootp [@var{card}] +Alias for net_dhcp, for compatibility with older Grub versions. Will perform +the same DHCP handshake with potential fallback to BOOTP as the net_dhcp +command (@pxref{net_dhcp}). + +@end deffn + + +@node net_del_addr +@subsection net_del_addr + +@deffn Command net_del_addr @var{interface} +Remove configured @var{interface} with associated address. +@end deffn + + +@node net_del_dns +@subsection net_del_dns + +@deffn Command net_del_dns @var{address} +Remove @var{address} from list of servers used during name lookup. +@end deffn + + +@node net_del_route +@subsection net_del_route + +@deffn Command net_del_route @var{shortname} +Remove route entry identified by @var{shortname}. +@end deffn + + +@node net_dhcp +@subsection net_dhcp + +@deffn Command net_dhcp [@var{card}] +Perform configuration of @var{card} using DHCP protocol. If no card name +is specified, try to configure all existing cards. +Falls back to the BOOTP protocol, if needed. If configuration was +successful, interface with name @var{card}@samp{:dhcp} and configured +address is added to @var{card}. +@comment If server provided gateway information in +@comment DHCP ACK packet, it is added as route entry with the name @var{card}@samp{:dhcp:gw}. +Additionally the following DHCP options are recognized and processed: + +@table @samp +@item 1 (Subnet Mask) +Used to calculate network local routing entry for interface @var{card}@samp{:dhcp}. +@item 3 (Router) +Adds default route entry with the name @var{card}@samp{:dhcp:default} via gateway +from DHCP option. Note that only option with single route is accepted. +@item 6 (Domain Name Server) +Adds all servers from option value to the list of servers used during name resolution. +@item 12 (Host Name) +Sets environment variable @samp{net_}@var{}@samp{_dhcp_hostname} +(@pxref{net_@var{}_hostname}) to the value of option. +@item 15 (Domain Name) +Sets environment variable @samp{net_}@var{}@samp{_dhcp_domain} +(@pxref{net_@var{}_domain}) to the value of option. +@item 17 (Root Path) +Sets environment variable @samp{net_}@var{}@samp{_dhcp_rootpath} +(@pxref{net_@var{}_rootpath}) to the value of option. +@item 18 (Extensions Path) +Sets environment variable @samp{net_}@var{}@samp{_dhcp_extensionspath} +(@pxref{net_@var{}_extensionspath}) to the value of option. +@item 66 (TFTP Server Name) +Sets environment variable @samp{net_}@var{}@samp{_dhcp_server_name} +(@pxref{net_@var{}_dhcp_server_name}) to the value of option. +@item 67 (Filename) +Sets environment variable @samp{net_}@var{}@samp{_boot_file} +(@pxref{net_@var{}_boot_file}) to the value of option. +@end table + +@end deffn + + +@node net_get_dhcp_option +@subsection net_get_dhcp_option + +@deffn Command net_get_dhcp_option @var{var} @var{interface} @var{number} @var{type} +Request DHCP option @var{number} of @var{type} via @var{interface}. @var{type} +can be one of @samp{string}, @samp{number} or @samp{hex}. If option is found, +assign its value to variable @var{var}. Values of types @samp{number} and @samp{hex} +are converted to string representation. +@end deffn + + +@node net_ipv6_autoconf +@subsection net_ipv6_autoconf + +@deffn Command net_ipv6_autoconf [@var{card}] +Perform IPv6 autoconfiguration by adding to the @var{card} interface with name +@var{card}@samp{:link} and link local MAC-based address. If no card is specified, +perform autoconfiguration for all existing cards. +@end deffn + + +@node net_ls_addr +@subsection net_ls_addr + +@deffn Command net_ls_addr +List all configured interfaces with their MAC and IP addresses. +@end deffn + + +@node net_ls_cards +@subsection net_ls_cards + +@deffn Command net_ls_cards +List all detected network cards with their MAC address. +@end deffn + + +@node net_ls_dns +@subsection net_ls_dns + +@deffn Command net_ls_dns +List addresses of DNS servers used during name lookup. +@end deffn + + +@node net_ls_routes +@subsection net_ls_routes + +@deffn Command net_ls_routes +List routing entries. +@end deffn + + +@node net_nslookup +@subsection net_nslookup + +@deffn Command net_nslookup @var{name} [@var{server}] +Resolve address of @var{name} using DNS server @var{server}. If no server +is given, use default list of servers. +@end deffn + + +@node net_set_vlan +@subsection net_set_vlan + +@deffn Command net_set_vlan @var{interface} @var{vlanid} +Set the 802.1Q VLAN identifier on @var{interface} to @var{vlanid}. For example, +to set the VLAN identifier on interface @samp{efinet1} to @samp{100}: + +@example +net_set_vlan efinet1 100 +@end example + +The VLAN identifier can be removed by setting it to @samp{0}: + +@example +net_set_vlan efinet1 0 +@end example +@end deffn + + +@node Undocumented commands +@section Commands currently undocumented +Unfortunately, not all GRUB commands are documented at this time due to +developer resource constraints. One way to contribute back to the GRUB +project would be to help document these commands, and submit patches or +ideas to the mailing list. The following is a (most likely incomplete) +list of undocumented or poorly documented commands and not all of them +are allowed for all platforms. Running the command help from within the +GRUB shell may provide more information on parameters and usage. + +@itemize @bullet +@item @command{all_functional_test} - Run all functional tests. +@item @command{backtrace} - Print backtrace. +@item @command{boottime} - Show boot time statistics. +@item @command{cacheinfo} - Get disk cache info. +@item @command{cbmemc} - Show CBMEM console content. +@item @command{cmosset} - Set bit at BYTE:BIT in CMOS. +@item @command{coreboot_boottime} - Show coreboot boot time statistics. +@item @command{dump} - Show memory contents. +@item @command{efiemu_loadcore} - Load and initialize EFI emulator. +@item @command{efiemu_prepare} - Finalize loading of EFI emulator. +@item @command{efiemu_unload} - Unload EFI emulator. +@item @command{exit} - Exit from GRUB. +@item @command{extract_entries_configfile} - Load another config file but take only menu entries. +@item @command{extract_entries_source} - Load another config file without changing context but take only menu entries. +@item @command{extract_legacy_entries_configfile} - Parse legacy config in new context taking only menu entries +@item @command{extract_legacy_entries_source} - Parse legacy config in same context taking only menu entries +@item @command{extract_syslinux_entries_configfile} - Execute syslinux config in new context taking only menu entries +@item @command{extract_syslinux_entries_source} - Execute syslinux config in same context taking only menu entries +@item @command{fakebios} - Create BIOS-like structures for backward compatibility with existing OS. +@item @command{fix_video} - Fix video problem. +@item @command{fpswa} - Display FPSWA version. +@item @command{functional_test} - Run all loaded functional tests. +@item @command{gdbstub_break} - Break into GDB +@item @command{gdbstub} - Start GDB stub on given port +@item @command{gdbstub_stop} - Stop GDB stub +@item @command{hdparm} - Get/set ATA disk parameters. +@item @command{hexdump_random} - Hexdump random data. +@item @command{inb} - Read 8-bit value from PORT. +@item @command{inl} - Read 32-bit value from PORT. +@item @command{inw} - Read 16-bit value from PORT. +@item @command{jpegtest} - Tests loading of JPEG bitmap. +@item @command{keymap} - Load a keyboard layout. +@item @command{legacy_check_password} - Simulate grub-legacy `password' command in menu entry mode +@item @command{legacy_configfile} - Parse legacy config in new context +@item @command{legacy_password} - Simulate grub-legacy `password' command +@item @command{legacy_source} - Parse legacy config in same context +@item @command{loadbios} - Load BIOS dump. +@item @command{lsacpi} - Show ACPI information. +@item @command{lsapm} - Show APM information. +@item @command{lscoreboot} - List coreboot tables. +@item @command{lsdev} - List devices. +@item @command{lsefi} - Display EFI handles. +@item @command{lsefimmap} - Display EFI memory map. +@item @command{lsefisystab} - Display EFI system tables. +@item @command{lsmmap} - List memory map provided by firmware. +@item @command{lspci} - List PCI devices. +@item @command{lssal} - Display SAL system table. +@item @command{lsspd} - Print Memory information. +@item @command{macppcbless} - Bless DIR of HFS or HFS+ partition for PPC macs. +@item @command{mactelbless} - Bless FILE of HFS or HFS+ partition for intel macs. +@item @command{net_set_vlan} - Set an interface's vlan id. +@item @command{outb} - Write 8-bit VALUE to PORT. +@item @command{outl} - Write 32-bit VALUE to PORT. +@item @command{outw} - Write 16-bit VALUE to PORT. +@item @command{pcidump} - Show raw dump of the PCI configuration space. +@item @command{pngtest} - Tests loading of PNG bitmap. +@item @command{read_byte} - Read 8-bit value from ADDR. +@item @command{read_dword} - Read 32-bit value from ADDR. +@item @command{read_word} - Read 16-bit value from ADDR. +@item @command{setpci} - Manipulate PCI devices. +@item @command{suspend} - Return to IEEE1275 prompt. +@item @command{syslinux_configfile} - Execute syslinux config in new context +@item @command{syslinux_source} - Execute syslinux config in same context +@item @command{test_blockarg} - Print and execute block argument., 0 +@item @command{testload} - Load the same file in multiple ways. +@item @command{testspeed} - Test file read speed. +@item @command{tgatest} - Tests loading of TGA bitmap. +@item @command{time} - Measure time used by COMMAND +@item @command{tr} - Translate SET1 characters to SET2 in STRING. +@item @command{usb} - Test USB support. +@item @command{vbeinfo} - List available video modes. If resolution is given show only modes matching it. +@item @command{vbetest} - Test video subsystem. +@item @command{videotest} - Test video subsystem in mode WxH. +@item @command{write_byte} - Write 8-bit VALUE to ADDR. +@item @command{write_dword} - Write 32-bit VALUE to ADDR. +@item @command{write_word} - Write 16-bit VALUE to ADDR. +@item @command{xen_cat} - List Xen storage. +@item @command{xen_ls} - List Xen storage. +@item @command{xnu_devprop_load} - Load `device-properties' dump. +@item @command{xnu_uuid} - Transform 64-bit UUID to format suitable for XNU. If -l is given keep it lowercase as done by blkid. +@item @command{zfs-bootfs} - Print ZFS-BOOTFSOBJ or store it into VARIABLE +@item @command{zfsinfo} - Print ZFS info about DEVICE. +@item @command{zfskey} - Import ZFS wrapping key stored in FILE. +@end itemize + + +@node Internationalisation +@chapter Internationalisation + +@section Charset +GRUB uses UTF-8 internally other than in rendering where some GRUB-specific +appropriate representation is used. All text files (including config) are +assumed to be encoded in UTF-8. + +@section Filesystems +NTFS, JFS, UDF, HFS+, exFAT, long filenames in FAT, Joliet part of +ISO9660 are treated as UTF-16 as per specification. AFS and BFS are read +as UTF-8, again according to specification. BtrFS, cpio, tar, squash4, minix, +minix2, minix3, ROMFS, ReiserFS, XFS, EROFS, ext2, ext3, ext4, FAT (short names), +F2FS, RockRidge part of ISO9660, nilfs2, UFS1, UFS2 and ZFS are assumed +to be UTF-8. This might be false on systems configured with legacy charset +but as long as the charset used is superset of ASCII you should be able to +access ASCII-named files. And it's recommended to configure your system to use +UTF-8 to access the filesystem, convmv may help with migration. ISO9660 (plain) +filenames are specified as being ASCII or being described with unspecified +escape sequences. GRUB assumes that the ISO9660 names are UTF-8 (since +any ASCII is valid UTF-8). There are some old CD-ROMs which use CP437 +in non-compliant way. You're still able to access files with names containing +only ASCII characters on such filesystems though. You're also able to access +any file if the filesystem contains valid Joliet (UTF-16) or RockRidge (UTF-8). +AFFS, SFS and HFS never use unicode and GRUB assumes them to be in Latin1, +Latin1 and MacRoman respectively. GRUB handles filesystem case-insensitivity +however no attempt is performed at case conversion of international characters +so e.g. a file named lowercase greek alpha is treated as different from +the one named as uppercase alpha. The filesystems in questions are +NTFS (except POSIX namespace), HFS+ (configurable at mkfs time, default +insensitive), SFS (configurable at mkfs time, default insensitive), +JFS (configurable at mkfs time, default sensitive), HFS, AFFS, FAT, exFAT +and ZFS (configurable on per-subvolume basis by property ``casesensitivity'', +default sensitive). On ZFS subvolumes marked as case insensitive files +containing lowercase international characters are inaccessible. +Also like all supported filesystems except HFS+ and ZFS (configurable on +per-subvolume basis by property ``normalization'', default none) GRUB makes +no attempt at check of canonical equivalence so a file name u-diaresis is +treated as distinct from u+combining diaresis. This however means that in +order to access file on HFS+ its name must be specified in normalisation form D. +On normalized ZFS subvolumes filenames out of normalisation are inaccessible. + +@section Output terminal +Firmware output console ``console'' on ARC and IEEE1275 are limited to ASCII. + +BIOS firmware console and VGA text are limited to ASCII and some pseudographics. + +None of above mentioned is appropriate for displaying international and any +unsupported character is replaced with question mark except pseudographics +which we attempt to approximate with ASCII. + +EFI console on the other hand nominally supports UTF-16 but actual language +coverage depends on firmware and may be very limited. + +The encoding used on serial can be chosen with @command{terminfo} as +either ASCII, UTF-8 or ``visual UTF-8''. Last one is against the specification +but results in correct rendering of right-to-left on some readers which don't +have own bidi implementation. + +On emu GRUB checks if charset is UTF-8 and uses it if so and uses ASCII +otherwise. + +When using gfxterm or gfxmenu GRUB itself is responsible for rendering the +text. In this case GRUB is limited by loaded fonts. If fonts contain all +required characters then bidirectional text, cursive variants and combining +marks other than enclosing, half (e.g. left half tilde or combining overline) +and double ones. Ligatures aren't supported though. This should cover European, +Middle Eastern (if you don't mind lack of lam-alif ligature in Arabic) and +East Asian scripts. Notable unsupported scripts are Brahmic family and +derived as well as Mongolian, Tifinagh, Korean Jamo (precomposed characters +have no problem) and tonal writing (2e5-2e9). GRUB also ignores deprecated +(as specified in Unicode) characters (e.g. tags). GRUB also doesn't handle so +called ``annotation characters'' If you can complete either of +two lists or, better, propose a patch to improve rendering, please contact +developer team. + +@section Input terminal +Firmware console on BIOS, IEEE1275 and ARC doesn't allow you to enter non-ASCII +characters. EFI specification allows for such but author is unaware of any +actual implementations. Serial input is currently limited for latin1 (unlikely +to change). Own keyboard implementations (at_keyboard and usb_keyboard) +supports any key but work on one-char-per-keystroke. +So no dead keys or advanced input method. Also there is no keymap change hotkey. +In practice it makes difficult to enter any text using non-Latin alphabet. +Moreover all current input consumers are limited to ASCII. + +@section Gettext +GRUB supports being translated. For this you need to have language *.mo files in $prefix/locale, load gettext module and set ``lang'' variable. + +@section Regexp +Regexps work on unicode characters, however no attempt at checking canonical +equivalence has been made. Moreover the classes like [:alpha:] match only +ASCII subset. + +@section Other +Currently GRUB always uses YEAR-MONTH-DAY HOUR:MINUTE:SECOND [WEEKDAY] 24-hour +datetime format but weekdays are translated. +GRUB always uses the decimal number format with [0-9] as digits and . as +descimal separator and no group separator. +IEEE1275 aliases are matched case-insensitively except non-ASCII which is +matched as binary. Similar behaviour is for matching OSBundleRequired. +Since IEEE1275 aliases and OSBundleRequired don't contain any non-ASCII it +should never be a problem in practice. +Case-sensitive identifiers are matched as raw strings, no canonical +equivalence check is performed. Case-insensitive identifiers are matched +as RAW but additionally [a-z] is equivalent to [A-Z]. GRUB-defined +identifiers use only ASCII and so should user-defined ones. +Identifiers containing non-ASCII may work but aren't supported. +Only the ASCII space characters (space U+0020, tab U+000b, CR U+000d and +LF U+000a) are recognised. Other unicode space characters aren't a valid +field separator. +@command{test} (@pxref{test}) tests <, >, <=, >=, -pgt and -plt compare the strings in the +lexicographical order of unicode codepoints, replicating the behaviour of +test from coreutils. +environment variables and commands are listed in the same order. + +@node Security +@chapter Security + +@menu +* Authentication and authorisation:: Users and access control +* Using digital signatures:: Booting digitally signed code +* UEFI secure boot and shim:: Booting digitally signed PE files +* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation +* Measured Boot:: Measuring boot components +* Lockdown:: Lockdown when booting on a secure setup +* TPM2 key protector:: Managing disk key with TPM2 key protector +@end menu + +@node Authentication and authorisation +@section Authentication and authorisation in GRUB + +By default, the boot loader interface is accessible to anyone with physical +access to the console: anyone can select and edit any menu entry, and anyone +can get direct access to a GRUB shell prompt. For most systems, this is +reasonable since anyone with direct physical access has a variety of other +ways to gain full access, and requiring authentication at the boot loader +level would only serve to make it difficult to recover broken systems. + +However, in some environments, such as kiosks, it may be appropriate to lock +down the boot loader to require authentication before performing certain +operations. + +The @samp{password} (@pxref{password}) and @samp{password_pbkdf2} +(@pxref{password_pbkdf2}) commands can be used to define users, each of +which has an associated password. @samp{password} sets the password in +plain text, requiring @file{grub.cfg} to be secure; @samp{password_pbkdf2} +sets the password hashed using the Password-Based Key Derivation Function +(RFC 2898), requiring the use of @command{grub-mkpasswd-pbkdf2} +(@pxref{Invoking grub-mkpasswd-pbkdf2}) to generate password hashes. + +In order to enable authentication support, the @samp{superusers} environment +variable must be set to a list of usernames, separated by any of spaces, +commas, semicolons, pipes, or ampersands. Superusers are permitted to use +the GRUB command line, edit menu entries, and execute any menu entry. If +@samp{superusers} is set, then use of the command line and editing of menu +entries are automatically restricted to superusers. Setting @samp{superusers} +to empty string effectively disables both access to CLI and editing of menu +entries. Building a grub image with @samp{--disable-cli} option will also +disable access to CLI and editing of menu entries, as well as disabling rescue +mode. Note: The environment variable needs to be exported to also affect the +section defined by the @samp{submenu} command (@pxref{submenu}). + +Other users may be allowed to execute specific menu entries by giving a list of +usernames (as above) using the @option{--users} option to the +@samp{menuentry} command (@pxref{menuentry}). If the @option{--unrestricted} +option is used for a menu entry, then that entry is unrestricted. +If the @option{--users} option is not used for a menu entry, then that +only superusers are able to use it. + +Putting this together, a typical @file{grub.cfg} fragment might look like +this: + +@example +@group +set superusers="root" +password_pbkdf2 root grub.pbkdf2.sha512.10000.biglongstring +password user1 insecure + +menuentry "May be run by any user" --unrestricted @{ + set root=(hd0,1) + linux /vmlinuz +@} + +menuentry "Superusers only" --users "" @{ + set root=(hd0,1) + linux /vmlinuz single +@} + +menuentry "May be run by user1 or a superuser" --users user1 @{ + set root=(hd0,2) + chainloader +1 +@} +@end group +@end example + +The @command{grub-mkconfig} program does not yet have built-in support for +generating configuration files with authentication. You can use +@file{/etc/grub.d/40_custom} to add simple superuser authentication, by +adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} +commands. + +@node Using digital signatures +@section Using digital signatures in GRUB + +GRUB's @file{core.img} can optionally provide enforcement that all files +subsequently read from disk are covered by a valid digital signature. +This document does @strong{not} cover how to ensure that your +platform's firmware (e.g., Coreboot) validates @file{core.img}. + +If environment variable @code{check_signatures} +(@pxref{check_signatures}) is set to @code{enforce}, then every +attempt by the GRUB @file{core.img} to load another file @file{foo} +implicitly invokes @code{verify_detached foo foo.sig} +(@pxref{verify_detached}). @code{foo.sig} must contain a valid +digital signature over the contents of @code{foo}, which can be +verified with a public key currently trusted by GRUB +(@pxref{list_trusted}, @pxref{trust}, and @pxref{distrust}). If +validation fails, then file @file{foo} cannot be opened. This failure +may halt or otherwise impact the boot process. + +An initial trusted public key can be embedded within the GRUB @file{core.img} +using the @code{--pubkey} option to @command{grub-install} +(@pxref{Invoking grub-install}). + +GRUB uses GPG-style detached signatures (meaning that a file +@file{foo.sig} will be produced when file @file{foo} is signed), and +currently supports the DSA and RSA signing algorithms. A signing key +can be generated as follows: + +@example +gpg --gen-key +@end example + +An individual file can be signed as follows: + +@example +gpg --detach-sign /path/to/file +@end example + +For successful validation of all of GRUB's subcomponents and the +loaded OS kernel, they must all be signed. One way to accomplish this +is the following (after having already produced the desired +@file{grub.cfg} file, e.g., by running @command{grub-mkconfig} +(@pxref{Invoking grub-mkconfig}): + +@example +@group +# Edit /dev/shm/passphrase.txt to contain your signing key's passphrase +for i in `find /boot -name "*.cfg" -or -name "*.lst" -or \ + -name "*.mod" -or -name "vmlinuz*" -or -name "initrd*" -or \ + -name "grubenv"`; +do + gpg --batch --detach-sign --passphrase-fd 0 $i < \ + /dev/shm/passphrase.txt +done +shred /dev/shm/passphrase.txt +@end group +@end example + +See also: @ref{check_signatures}, @ref{verify_detached}, @ref{trust}, +@ref{list_trusted}, @ref{distrust}, @ref{load_env}, @ref{save_env}. + +Note that internally signature enforcement is controlled by setting +the environment variable @code{check_signatures} equal to +@code{enforce}. Passing one or more @code{--pubkey} options to +@command{grub-mkimage} implicitly defines @code{check_signatures} +equal to @code{enforce} in @file{core.img} prior to processing any +configuration files. + +Note that signature checking does @strong{not} prevent an attacker +with (serial, physical, ...) console access from dropping manually to +the GRUB console and executing: + +@example +set check_signatures=no +@end example + +To prevent this, password-protection (@pxref{Authentication and +authorisation}) is essential. Note that even with GRUB password +protection, GRUB itself cannot prevent someone with physical access to +the machine from altering that machine's firmware (e.g., Coreboot +or BIOS) configuration to cause the machine to boot from a different +(attacker-controlled) device. GRUB is at best only one link in a +secure boot chain. + +@node UEFI secure boot and shim +@section UEFI secure boot and shim support + +The GRUB works with UEFI secure boot and the shim. This functionality is +provided by the shim_lock verifier. It is built into the @file{core.img} and is +registered if the UEFI secure boot is enabled. The @samp{shim_lock} variable is +set to @samp{y} when shim_lock verifier is registered. If it is desired to use +UEFI secure boot without shim, one can disable shim_lock by disabling shim +verification with MokSbState UEFI variable or by building grub image with +@samp{--disable-shim-lock} option. + +All GRUB modules not stored in the @file{core.img}, OS kernels, ACPI tables, +Device Trees, etc. have to be signed, e.g, using PGP. Additionally, the commands +that can be used to subvert the UEFI secure boot mechanism, such as @command{iorw} +and @command{memrw} will not be available when the UEFI secure boot is enabled. +This is done for security reasons and are enforced by the GRUB Lockdown mechanism +(@pxref{Lockdown}). + +@node Secure Boot Advanced Targeting +@section Embedded information for generation number based revocation + +The Secure Boot Advanced Targeting (SBAT) is a mechanism to allow the revocation +of components in the boot path by using generation numbers embedded into the EFI +binaries. The SBAT metadata is located in an .sbat data section that has set of +UTF-8 strings as comma-separated values (CSV). See +@uref{https://github.com/rhboot/shim/blob/main/SBAT.md} for more details. + +To add a data section containing the SBAT information into the binary, the +@option{--sbat} option of @command{grub-mkimage} command should be used. The content +of a CSV file, encoded with UTF-8, is copied as is to the .sbat data section into +the generated EFI binary. The CSV file can be stored anywhere on the file system. + +@example +grub-mkimage -O x86_64-efi -o grubx64.efi -p '(tftp)/grub' --sbat sbat.csv efinet tftp +@end example + +@node Measured Boot +@section Measuring boot components + +If the tpm module is loaded and the platform has a Trusted Platform Module +installed, GRUB will log each command executed and each file loaded into the +TPM event log and extend the PCR values in the TPM correspondingly. All events +will be logged into the PCR described below with a type of EV_IPL and an +event description as described below. + +@multitable @columnfractions 0.3 0.1 0.6 +@headitem Event type @tab PCR @tab Description +@item Command +@tab 8 +@tab All executed commands (including those from configuration files) will be +logged and measured as entered with a prefix of ``grub_cmd: `` +@item Kernel command line +@tab 8 +@tab Any command line passed to a kernel will be logged and measured as entered +with a prefix of ``kernel_cmdline: '' +@item Module command line +@tab 8 +@tab Any command line passed to a kernel module will be logged and measured as +entered with a prefix of ``module_cmdline: `` +@item Files +@tab 9 +@tab Any file read by GRUB will be logged and measured with a descriptive text +corresponding to the filename. +@end multitable + +GRUB will not measure its own @file{core.img} - it is expected that firmware +will carry this out. GRUB will also not perform any measurements until the +tpm module is loaded. As such it is recommended that the tpm module be built +into @file{core.img} in order to avoid a potential gap in measurement between +@file{core.img} being loaded and the tpm module being loaded. + +Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC +platforms. + +@node Lockdown +@section Lockdown when booting on a secure setup + +The GRUB can be locked down when booted on a secure boot environment, for example +if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will +be restricted and some operations/commands cannot be executed. This also includes +limiting which filesystems are supported to those thought to be more robust and +widely used within GRUB. + +The filesystems currently allowed in lockdown mode include: +@itemize @bullet +@item BtrFS +@item cpio +@item exFAT +@item Enhanced Read-Only File System (EROFS) +@item Linux ext2/ext3/ext4 +@item F2FS +@item DOS FAT12/FAT16/FAT32 +@item HFS+ +@item ISO9660 +@item Squash4 +@item tar +@item XFS +@item ZFS +@end itemize + +The filesystems currently not allowed in lockdown mode include: +@itemize @bullet +@item Amiga Fast FileSystem (AFFS) +@item AtheOS File System (AFS) +@item Bee File System (BFS) +@item Coreboot File System (CBFS) +@item Hierarchical File System (HFS) +@item Journaled File System (JFS) +@item Minix filesystem +@item New Implementation of Log filesystem (nilfs2) +@item Windows New Technology File System (NTFS) +@item ReiserFS +@item Read-Only Memory File System (ROMFS) +@item Amiga Smart File System (SFS) +@item Universal Disk Format (UDF) +@item Unix File System (UFS) +@end itemize + +The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. +Otherwise it does not exist. + +@node TPM2 key protector +@section TPM2 key protector in GRUB + +TPM2 key protector extends measured boot to unlock the encrypted partition +without user intervention. It uses the TPM Storage Root Key (SRK) to seal +the disk key with a given set of PCR values. If the system state matches, +i.e. PCR values match the sealed PCR set, TPM2 key protector unseals the +disk key for @command{cryptomount} (@pxref{cryptomount}) to unlock the +encrypted partition. In case the unsealed key fails to unlock the +partition, @command{cryptomount} falls back to the passphrase prompt. + +Please note that TPM2 key protector uses the SRK in the owner hierarchy +@emph{without} authorization. If the owner hierarchy is password-protected, +TPM2 key protector may fail to unseal the key due to the absence of the +password. For the systems that already enable the password protection for the +owner hierarchy, the following command removes the password protection with +the existing password. + +@example +# @kbd{tpm2_changeauth -c owner -p password} +@end example + +There are two supported modes to store the sealed key, SRK and NV index. +The details will be addressed in later sections. + +TPM2 key protector is currently only supported on EFI and EMU platforms. + +@subsection TPM PCR usage + +Since TPM2 key protector relies on PCRs to check the system state, it is +important to decide which PCRs to seal the key with. The following table +lists uses of PCRs and the measured objects on EFI platforms. + +@multitable @columnfractions 0.1 0.2 0.7 +@headitem PCR @tab Used by @tab Measured Objects +@item 0 +@tab Firmware +@tab Core system firmware executable code +@item 1 +@tab Firmware +@tab Core system firmware data/host platform configuration; typically +contains serial and model numbers +@item 2 +@tab Firmware +@tab Extended or pluggable executable code; includes option ROMs on +pluggable hardware +@item 3 +@tab Firmware +@tab Extended or pluggable firmware data; includes information about +pluggable hardware +@item 4 +@tab Firmware +@tab Boot loader and additional drivers; binaries and extensions loaded +by the boot loader +@item 5 +@tab Firmware +@tab GPT/Partition table +@item 7 +@tab Firmware +@tab SecureBoot state +@item 8 +@tab GRUB +@tab Commands and kernel command line +@item 9 +@tab GRUB +@tab All files read (including kernel image) +@item 9 +@tab Linux Kernel +@tab All passed initrds (when the new LOAD_FILE2 initrd protocol is used) +@item 10 +@tab Linux Kernel +@tab Protection of the IMA measurement log +@item 14 +@tab shim +@tab “MOK†certificates and hashes +@end multitable + +PCR 0, 2, 4, and 7 can be used to check the integrity of the firmware code +and bootloaders. PCR 8 and 9 are useful to check the file and data processed +by GRUB. PCRs 10, 11, 12, 13, and 15 are controlled by the operating system, +so those PCRs are usually still in the initial state when GRUB is running. + +In general, it is nice to include PCR 0, 2, 4, and 7 to ensure the integrity +of the firmware and bootloaders. For PCR 8 and 9, a sophisticated tool is +required to examine the GRUB configuration files and the files to be loaded +to calculate the correct PCR values. + +Please note that PCRs are sensitive to any change, so an update of a component +could invalidate the sealed key, due to the so-called PCR brittleness. For the +bootloader update, PCR 4 may be affected. This can be mitigated by extracting +the events from the TPM event log and predict the value with the updated +bootloader binary. On the other hand, it is difficult to predict PCR 0~7 after +a firmware update since the content of the code and the order of drivers may +not follow the TPM event log from the previous firmware version, so it is +necessary to reboot the system to update the measurement results of PCR 0~7 +and seal or sign the sealed key again. + +Reference: @url{https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/, Linux TPM PCR Registry} + +@subsection Setting up the extra disk key + +Instead of using the existing password, it is recommended to seal a new +random disk key and use the existing password for recovery. + +Here are the sample commands to create a 128 random bytes key file and +enroll the key into the target partition (sda2). + +@example +# @kbd{dd if=/dev/urandom of=luks.key bs=1 count=128} +# @kbd{cryptsetup luksAddKey /dev/sda2 luks.key --pbkdf=pbkdf2 --hash=sha512} +@end example + +@subsection SRK mode + +To unlock the partition with SRK mode, assume that the sealed key is in +@file{(hd0,gpt1)/efi/grub/sealed.tpm}, the following GRUB commands +unseal the disk key with SRK mode and supply it to @command{cryptomount}. + +@example +grub> @kbd{tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +There are two programs to create the sealed key for SRK mode: @command{grub-protect} +and @command{pcr-oracle} (@url{https://github.com/okirch/pcr-oracle}). + +The following sample command uses @command{grub-protect} to seal the random +key, @file{luks.key}, with PCR 0, 2, 4 and 7 in TPM 2.0 Key File format. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +@command{grub-protect} only seals the key with the current PCR values. +Therefore, when a boot component, such as shim or GRUB, is updated, it is +necessary to reboot the system to update the measurement results and seal +the key again. That means the random disk key has to be stored in cleartext +for the next key sealing. Besides this, the measurement result of some PCRs +may differ between boot time and OS runtime. For example, PCR 9 measures the +files loaded by GRUB including the Linux kernel and initrd. To unlock the disk +containing the kernel and initrd, the key has to be sealed with PCR 9 value +before loading the kernel and initrd. However, PCR 9 changes after GRUB +loading the kernel and initrd, so PCR 9 at OS runtime cannot be used directly +for key sealing. + +To solve these problems, @command{pcr-oracle} takes a different approach. It +reads the TPM eventlog and predicts the PCR values. Besides, +@command{pcr-oracle} also supports ``authorized policy'' which allows the +PCR policy to be updated with a valid signature, so that the user only seals +the random disk key once. If at some later time the PCR values change due to +an update of the system firmware, bootloader, or config file, the user just +needs to update the signature of the PCR policy. + +To seal the key with the authorized policy, the first thing is to generate +the RSA policy key, @file{policy-key.pem}, and the authorized policy file, +@file{authorized.policy}. In this example, PCR 0, 2, 4, 7 and 9 are chosen +for key sealing. + +@example +@group +# @kbd{pcr-oracle --rsa-generate-key \ + --private-key policy-key.pem \ + --auth authorized.policy \ + create-authorized-policy 0,2,4,7,9} +@end group +@end example + +Then, we seal the random disk key, @file{luks.key}, with the authorized +policy file and save the sealed key in @file{sealed.key}. + +@example +@group +# @kbd{pcr-oracle --key-format tpm2.0 \ + --auth authorized.policy \ + --input luks.key \ + --output sealed.key \ + seal-secret} +@end group +@end example + +Since we now have the sealed key, we can remove the random disk key file +@file{luks.key}. + +The last step is to sign the predicted PCR policy and save the final key +file, @file{sealed.tpm}. + +@example +@group +# @kbd{pcr-oracle --key-format tpm2.0 \ + --private-key policy-key.pem \ + --from eventlog \ + --stop-event "grub-file=grub.cfg" \ + --after \ + --input sealed.key \ + --output /boot/efi/efi/grub/sealed.tpm \ + sign 0,2,4,7,9} +@end group +@end example + +Here we also set a stop event for the prediction. With +@kbd{--stop-event grub-file=grub.cfg --after}, @command{pcr-oracle} stops +the calculation of PCR values right after GRUB loads @file{grub.cfg}. + +When/After the shim or GRUB are updated, it only requires to run the last +@command{pcr-oracle} command to update the predicted PCR policy. + +@subsection NV index mode + +Instead of storing the sealed key in a file, NV index mode uses the TPM +non-volatile memory to store the sealed key and could be useful when accessing +the file is not possible. + +However, the Linux root user must be careful who she/he gives access to the +TPM (tss group) since those users will also be able to modify the NV index +that's holding the key. + +There are two types of TPM handles supported by NV index mode: persistent +handle and NV index handle. + +@subsubsection Persistent handle + +The range of persistent handles is from @kbd{0x81000000} to @kbd{0x81FFFFFF}. +The persistent handle is designed to make TPM objects persistent through +power cycles, and only TPM objects, such as RSA or EC keys, are accepted. +Thus, only the raw format is supported by persistent handles. The following +shows the @command{grub-protect} command to seal the disk key @file{luks.key} +into the persistent handle @kbd{0x81000000} with the PCRs @kbd{0,2,4,7}. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=add \ + --tpm2-bank=sha256 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x81000000} +@end group +@end example + +To unseal the key, we have to specify the mode @kbd{nv}, the persistent handle +@kbd{0x81000000}, and the PCRs @kbd{0,2,4,7} for the @command{tpm2_key_protector_init} +command. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=0,2,4,7} +grub> @kbd{cryptomount -u --protector tpm2} +@end example + +If the key in the persistent handle becomes unwanted, the following +@command{grub-protect} command removes the specified persistent handle +@kbd{0x81000000}. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=remove \ + --tpm2-evict \ + --tpm2-nvindex=0x81000000} +@end group +@end example + +@subsubsection NV index handle + +The range of NV index handles is from @kbd{0x1000000} to @kbd{0x1FFFFFF}. +Unlike the persistent handle, the NV index handle allows user-defined data, +so it can easily support both the TPM 2.0 Key File format as well as the raw +format. + +The following @kbd{grub-protect} command seals the disk key @file{luks.key} +into the NV index handle @kbd{0x1000000} with the PCRs @kbd{0,2,4,7} while +using the TPM 2.0 Key File format. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=add \ + --tpm2key \ + --tpm2-bank=sha256 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x1000000} +@end group +@end example + +Furthermore, it is also possible to insert an existing key file, +@file{sealed.tpm}, into a specific NV index handle using the following +tpm2-tools (@url{https://github.com/tpm2-software/tpm2-tools}) commands. + +@example +@group +# @kbd{tpm2_nvdefine -C o \ + -a "ownerread|ownerwrite" \ + -s $(stat -c %s sealed.tpm) \ + 0x1000000} +@end group +# @kbd{tpm2_nvwrite -C o -i sealed.tpm 0x1000000} +@end example + +When unsealing the key in TPM 2.0 Key File format, only the mode @kbd{nv} +and the NV index handle @kbd{0x1000000} have to be specified for the +@command{tpm2_key_protector_init} command. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x1000000} +grub> @kbd{cryptomount -u --protector tpm2} +@end example + +The following @command{grub-protect} command allows to remove the specified +NV index handle @kbd{0x1000000}. + +@example +@group +# @kbd{grub-protect \ + --protector=tpm2 \ + --action=remove \ + --tpm2-evict \ + --tpm2-nvindex=0x1000000} +@end group +@end example + +@subsection Setting up software TPM for EMU platform + +In order to test TPM2 key protector and TPM2 Software Stack (TSS2), it is +useful to set up a software TPM (swtpm) instance and run the commands on the +EMU platform. + +Here are the commands to start a swtpm instance which provides a character +device interface. To store the TPM states, the directory, @file{swtpm-state}, +is created before the @command{swtpm} command. All the messages are stored +in @file{swtpm.log} including the name of the character device. + +@example +# @kbd{mkdir swtpm-state} +@group +# @kbd{swtpm chardev --vtpm-proxy --tpmstate dir=swtpm-state \ + --tpm2 --ctrl type=unixio,path="swtpm-state/ctrl" \ + --flags startup-clear --daemon > swtpm.log} +@end group +@end example + +Then, we extract the name of the character device from @file{swtpm.log} and +save it to the variable, @samp{tpm2dev}. + +@example +# @kbd{tpm2dev=$(grep "New TPM device" swtpm.log | cut -d' ' -f 4)} +@end example + +Now we can start @kbd{grub-emu} with @kbd{--tpm-device $tpm2dev} to interact +with the swtpm instance. + +@example +# @kbd{grub-emu --tpm-device $tpm2dev} +@end example + +On the host, the tpm2-tools commands can interact with the swtpm instance by +setting @samp{TPM2TOOLS_TCTI}. + +@example +# @kbd{export TPM2TOOLS_TCTI="device:$tpm2dev"} +@end example + +When the test is done, use @kbd{swtpm_ioctl} to send the shutdown +command through the swtpm control channel. + +@example +# @kbd{swtpm_ioctl -s --unix swtpm-state/ctrl} +@end example + +@subsection Command line and menuentry editor protection + +The TPM key protector provides full disk encryption support on servers or +virtual machine images, meanwhile keeping the boot process unattended. This +prevents service disruptions by eliminating the need for manual password input +during startup, improving system uptime and continuity. It is achieved by TPM, +which verifies the integrity of boot components by checking cryptographic +hashes against securely stored values, to confirm the disks are unlocked in a +trusted state. + +However, for users to access the system interactively, some form of +authentication is still required, as the disks are not unlocked by an +authorized user. This raised concerns about using an unprotected +@samp{command-line interface} (@pxref{Command-line interface}), as anyone could +execute commands to access decrypted data. To address this issue, the LUKS +password is used to ensure that only authorized users are granted access to the +interface. Additionally, the @samp{menu entry editor} (@pxref{Menu entry +editor}) is also safeguarded by the LUKS password, as modifying a boot entry is +effectively the same as altering the @file{grub.cfg} file read from encrypted +files. + +It is worth mentioning that the built-in password support, as described in +@samp{Authentication and Authorization in GRUB} (@pxref{Authentication and +authorisation}), can also be used to protect the command-line interface from +unauthorized access. However, it is not recommended to rely on this approach as +it is an optional step. Setting it up requires additional manual intervention, +which increases the risk of password leakage during the process. Moreover, the +superuser list must be well maintained, and the password used cannot be +synchronized with LUKS key rotation. + +@node Platform limitations +@chapter Platform limitations + +GRUB2 is designed to be portable and is actually ported across platforms. We +try to keep all platforms at the level. Unfortunately some platforms are better +supported than others. This is detailed in current and 2 following sections. + +All platforms have an artificially GRUB imposed disk size restriction of 1 EiB. +In some cases, larger disk sizes can be used, but access will not be allowed +beyond 1 EiB. + +LUKS2 devices with size larger than 16 EiB are currently not supported. They +can not be created as crypto devices by cryptomount, so can not even be +partially read from. LUKS have no limitations other than those imposed by the +format. + +ARC platform is unable to change datetime (firmware doesn't seem to provide a +function for it). +EMU has similar limitation. + +On EMU platform no serial port is available. + +Console charset refers only to firmware-assisted console. gfxterm is always +Unicode (see Internationalisation section for its limitations). Serial is +configurable to UTF-8 or ASCII (see Internationalisation). In case of qemu +and coreboot ports the referred console is vga_text. Loongson always uses +gfxterm. + +Most limited one is ASCII. CP437 provides additionally pseudographics. +GRUB2 doesn't use any language characters from CP437 as often CP437 is replaced +by national encoding compatible only in pseudographics. +Unicode is the most versatile charset which supports many languages. However +the actual console may be much more limited depending on firmware + +On BIOS, network is supported only if the image is loaded through network. +On sparc64, GRUB is unable to determine which server it was booted from. + +Direct ATA/AHCI support allows to circumvent various firmware limitations but +isn't needed for normal operation except on baremetal ports. + +AT keyboard support allows keyboard layout remapping and support for keys not +available through firmware. It isn't needed for normal operation except +baremetal ports. + +Speaker allows morse and spkmodem communication. + +USB support provides benefits similar to ATA (for USB disks) or AT (for USB +keyboards). In addition it allows USBserial. + +Chainloading refers to the ability to load another bootloader through the same protocol +and on some platforms, like EFI, allow that bootloader to return to the GRUB. + +Hints allow faster disk discovery by already knowing in advance which is the disk in +question. On some platforms hints are correct unless you move the disk between boots. +On other platforms it's just an educated guess. +Note that hint failure results in just reduced performance, not a failure + +BadRAM is the ability to mark some of the RAM as ``bad''. Note: due to protocol +limitations mips-loongson (with Linux protocol) +and mips-qemu_mips can use only memory up to first hole. + +Bootlocation is ability of GRUB to automatically detect where it boots from. +``disk'' means the detection is limited to detecting the disk with partition +being discovered on install time. ``partition'' means that disk and partiton +can be automatically discovered. ``file'' means that boot image file name as +well as disk and partition can be discovered. For consistency, default install ignores +partition and relies solely on disk detection. If no bootlocation discovery is available +or boot and grub-root disks are different, UUID is used instead. On ARC if no device +to install to is specified, UUID is used instead as well. + + +@multitable @columnfractions .20 .20 .20 .20 .20 +@item @tab BIOS @tab Coreboot @tab Multiboot @tab Qemu +@item video @tab yes @tab yes @tab yes @tab yes +@item console charset @tab CP437 @tab CP437 @tab CP437 @tab CP437 +@item network @tab yes (*) @tab no @tab no @tab no +@item ATA/AHCI @tab yes @tab yes @tab yes @tab yes +@item AT keyboard @tab yes @tab yes @tab yes @tab yes +@item Speaker @tab yes @tab yes @tab yes @tab yes +@item USB @tab yes @tab yes @tab yes @tab yes +@item chainloader @tab local @tab yes @tab yes @tab no +@item cpuid @tab partial @tab partial @tab partial @tab partial +@item rdmsr @tab partial @tab partial @tab partial @tab partial +@item wrmsr @tab partial @tab partial @tab partial @tab partial +@item hints @tab guess @tab guess @tab guess @tab guess +@item PCI @tab yes @tab yes @tab yes @tab yes +@item badram @tab yes @tab yes @tab yes @tab yes +@item compression @tab always @tab pointless @tab no @tab no +@item exit @tab yes @tab no @tab no @tab no +@item bootlocation @tab disk @tab no @tab no @tab no +@end multitable + +@multitable @columnfractions .20 .20 .20 .20 .20 +@item @tab ia32 EFI @tab amd64 EFI @tab ia32 IEEE1275 @tab Itanium +@item video @tab yes @tab yes @tab no @tab no +@item console charset @tab Unicode @tab Unicode @tab ASCII @tab Unicode +@item network @tab yes @tab yes @tab yes @tab yes +@item ATA/AHCI @tab yes @tab yes @tab yes @tab no +@item AT keyboard @tab yes @tab yes @tab yes @tab no +@item Speaker @tab yes @tab yes @tab yes @tab no +@item USB @tab yes @tab yes @tab yes @tab no +@item chainloader @tab local @tab local @tab no @tab local +@item cpuid @tab partial @tab partial @tab partial @tab no +@item rdmsr @tab partial @tab partial @tab partial @tab no +@item wrmsr @tab partial @tab partial @tab partial @tab no +@item hints @tab guess @tab guess @tab good @tab guess +@item PCI @tab yes @tab yes @tab yes @tab no +@item badram @tab yes @tab yes @tab no @tab yes +@item compression @tab no @tab no @tab no @tab no +@item exit @tab yes @tab yes @tab yes @tab yes +@item bootlocation @tab file @tab file @tab file, ignored @tab file +@end multitable + +@multitable @columnfractions .20 .20 .20 .20 .20 +@item @tab Loongson @tab sparc64 @tab Powerpc @tab ARC +@item video @tab yes @tab no @tab yes @tab no +@item console charset @tab N/A @tab ASCII @tab ASCII @tab ASCII +@item network @tab no @tab yes (*) @tab yes @tab no +@item ATA/AHCI @tab yes @tab no @tab no @tab no +@item AT keyboard @tab yes @tab no @tab no @tab no +@item Speaker @tab no @tab no @tab no @tab no +@item USB @tab yes @tab no @tab no @tab no +@item chainloader @tab yes @tab no @tab no @tab no +@item cpuid @tab no @tab no @tab no @tab no +@item rdmsr @tab no @tab no @tab no @tab no +@item wrmsr @tab no @tab no @tab no @tab no +@item hints @tab good @tab good @tab good @tab no +@item PCI @tab yes @tab no @tab no @tab no +@item badram @tab yes (*) @tab no @tab no @tab no +@item compression @tab configurable @tab no @tab no @tab configurable +@item exit @tab no @tab yes @tab yes @tab yes +@item bootlocation @tab no @tab partition @tab file @tab file (*) +@end multitable + +@multitable @columnfractions .20 .20 .20 .20 .20 +@item @tab MIPS qemu @tab emu @tab xen +@item video @tab no @tab yes @tab no +@item console charset @tab CP437 @tab Unicode (*) @tab ASCII +@item network @tab no @tab yes @tab no +@item ATA/AHCI @tab yes @tab no @tab no +@item AT keyboard @tab yes @tab no @tab no +@item Speaker @tab no @tab no @tab no +@item USB @tab N/A @tab yes @tab no +@item chainloader @tab yes @tab no @tab yes +@item cpuid @tab no @tab no @tab yes +@item rdmsr @tab no @tab no @tab yes +@item wrmsr @tab no @tab no @tab yes +@item hints @tab guess @tab no @tab no +@item PCI @tab no @tab no @tab no +@item badram @tab yes (*) @tab no @tab no +@item compression @tab configurable @tab no @tab no +@item exit @tab no @tab yes @tab no +@item bootlocation @tab no @tab file @tab no +@end multitable + +@node Platform-specific operations +@chapter Outline + +Some platforms have features which allows to implement +some commands useless or not implementable on others. + +Quick summary: + +Information retrieval: + +@itemize +@item mipsel-loongson: lsspd +@item mips-arc: lsdev +@item efi: lsefisystab, lssal, lsefimmap, lsefi +@item i386-pc: lsapm +@item i386-coreboot: lscoreboot, coreboot_boottime, cbmemc +@item acpi-enabled (i386-pc, i386-coreboot, i386-multiboot, *-efi): lsacpi +@end itemize + +Workarounds for platform-specific issues: +@itemize +@item i386-efi/x86_64-efi: loadbios, fakebios, fix_video +@item acpi-enabled (i386-pc, i386-coreboot, i386-multiboot, *-efi): + acpi (override ACPI tables) +@item i386-pc: drivemap +@item i386-pc: sendkey +@end itemize + +Advanced operations for power users: +@itemize +@item x86: iorw (direct access to I/O ports) +@end itemize + +Miscellaneous: +@itemize +@item cmos (x86-*, ieee1275, mips-qemu_mips, mips-loongson): cmostest + (used on some laptops to check for special power-on key), cmosclean +@item i386-pc: play +@end itemize + +@node Supported kernels +@chapter Supported boot targets + +X86 support is summarised in the following table. ``Yes'' means that the kernel works on the given platform, ``crashes'' means an early kernel crash which we hope will be fixed by concerned kernel developers. ``no'' means GRUB doesn't load the given kernel on a given platform. ``headless'' means that the kernel works but lacks console drivers (you can still use serial or network console). In case of ``no'' and ``crashes'' the reason is given in footnote. +@multitable @columnfractions .50 .22 .22 +@item @tab BIOS @tab Coreboot +@item BIOS chainloading @tab yes @tab no (1) +@item NTLDR @tab yes @tab no (1) +@item Plan9 @tab yes @tab no (1) +@item Freedos @tab yes @tab no (1) +@item FreeBSD bootloader @tab yes @tab crashes (1) +@item 32-bit kFreeBSD @tab yes @tab crashes (5) +@item 64-bit kFreeBSD @tab yes @tab crashes (5) +@item 32-bit kNetBSD @tab yes @tab crashes (1) +@item 64-bit kNetBSD @tab yes @tab crashes +@item 32-bit kOpenBSD @tab yes @tab yes +@item 64-bit kOpenBSD @tab yes @tab yes +@item Multiboot @tab yes @tab yes +@item Multiboot2 @tab yes @tab yes +@item 32-bit Linux (legacy protocol) @tab yes @tab no (1) +@item 64-bit Linux (legacy protocol) @tab yes @tab no (1) +@item 32-bit Linux (modern protocol) @tab yes @tab yes +@item 64-bit Linux (modern protocol) @tab yes @tab yes +@item 32-bit XNU @tab yes @tab ? +@item 64-bit XNU @tab yes @tab ? +@item 32-bit EFI chainloader @tab no (2) @tab no (2) +@item 64-bit EFI chainloader @tab no (2) @tab no (2) +@item Appleloader @tab no (2) @tab no (2) +@end multitable + +@multitable @columnfractions .50 .22 .22 +@item @tab Multiboot @tab Qemu +@item BIOS chainloading @tab no (1) @tab no (1) +@item NTLDR @tab no (1) @tab no (1) +@item Plan9 @tab no (1) @tab no (1) +@item FreeDOS @tab no (1) @tab no (1) +@item FreeBSD bootloader @tab crashes (1) @tab crashes (1) +@item 32-bit kFreeBSD @tab crashes (5) @tab crashes (5) +@item 64-bit kFreeBSD @tab crashes (5) @tab crashes (5) +@item 32-bit kNetBSD @tab crashes (1) @tab crashes (1) +@item 64-bit kNetBSD @tab yes @tab yes +@item 32-bit kOpenBSD @tab yes @tab yes +@item 64-bit kOpenBSD @tab yes @tab yes +@item Multiboot @tab yes @tab yes +@item Multiboot2 @tab yes @tab yes +@item 32-bit Linux (legacy protocol) @tab no (1) @tab no (1) +@item 64-bit Linux (legacy protocol) @tab no (1) @tab no (1) +@item 32-bit Linux (modern protocol) @tab yes @tab yes +@item 64-bit Linux (modern protocol) @tab yes @tab yes +@item 32-bit XNU @tab ? @tab ? +@item 64-bit XNU @tab ? @tab ? +@item 32-bit EFI chainloader @tab no (2) @tab no (2) +@item 64-bit EFI chainloader @tab no (2) @tab no (2) +@item Appleloader @tab no (2) @tab no (2) +@end multitable + +@multitable @columnfractions .50 .22 .22 +@item @tab ia32 EFI @tab amd64 EFI +@item BIOS chainloading @tab no (1) @tab no (1) +@item NTLDR @tab no (1) @tab no (1) +@item Plan9 @tab no (1) @tab no (1) +@item FreeDOS @tab no (1) @tab no (1) +@item FreeBSD bootloader @tab crashes (1) @tab crashes (1) +@item 32-bit kFreeBSD @tab headless @tab headless +@item 64-bit kFreeBSD @tab headless @tab headless +@item 32-bit kNetBSD @tab crashes (1) @tab crashes (1) +@item 64-bit kNetBSD @tab yes @tab yes +@item 32-bit kOpenBSD @tab headless @tab headless +@item 64-bit kOpenBSD @tab headless @tab headless +@item Multiboot @tab yes @tab yes +@item Multiboot2 @tab yes @tab yes +@item 32-bit Linux (legacy protocol) @tab no (1) @tab no (1) +@item 64-bit Linux (legacy protocol) @tab no (1) @tab no (1) +@item 32-bit Linux (modern protocol) @tab yes @tab yes +@item 64-bit Linux (modern protocol) @tab yes @tab yes +@item 32-bit XNU @tab yes @tab yes +@item 64-bit XNU @tab yes (4) @tab yes +@item 32-bit EFI chainloader @tab yes @tab no (3) +@item 64-bit EFI chainloader @tab no (3) @tab yes +@item Appleloader @tab yes @tab yes +@end multitable + +@multitable @columnfractions .50 .22 .22 +@item @tab ia32 IEEE1275 +@item BIOS chainloading @tab no (1) +@item NTLDR @tab no (1) +@item Plan9 @tab no (1) +@item FreeDOS @tab no (1) +@item FreeBSD bootloader @tab crashes (1) +@item 32-bit kFreeBSD @tab crashes (5) +@item 64-bit kFreeBSD @tab crashes (5) +@item 32-bit kNetBSD @tab crashes (1) +@item 64-bit kNetBSD @tab ? +@item 32-bit kOpenBSD @tab ? +@item 64-bit kOpenBSD @tab ? +@item Multiboot @tab ? +@item Multiboot2 @tab ? +@item 32-bit Linux (legacy protocol) @tab no (1) +@item 64-bit Linux (legacy protocol) @tab no (1) +@item 32-bit Linux (modern protocol) @tab ? +@item 64-bit Linux (modern protocol) @tab ? +@item 32-bit XNU @tab ? +@item 64-bit XNU @tab ? +@item 32-bit EFI chainloader @tab no (2) +@item 64-bit EFI chainloader @tab no (2) +@item Appleloader @tab no (2) +@end multitable + +@enumerate +@item Requires BIOS +@item EFI only +@item 32-bit and 64-bit EFI have different structures and work in different CPU modes so it's not possible to chainload 32-bit bootloader on 64-bit platform and vice-versa +@item Some modules may need to be disabled +@item Requires ACPI +@end enumerate + +PowerPC, IA64 and Sparc64 ports support only Linux. MIPS port supports Linux +and multiboot2. + +@section Boot tests + +As you have seen in previous chapter the support matrix is pretty big and some of the configurations are only rarely used. To ensure the quality bootchecks are available for all x86 targets except EFI chainloader, Appleloader and XNU. All x86 platforms have bootcheck facility except ieee1275. Multiboot, multiboot2, BIOS chainloader, ntldr and freebsd-bootloader boot targets are tested only with a fake kernel images. Only Linux is tested among the payloads using Linux protocols. + +Following variables must be defined: + +@multitable @columnfractions .30 .65 +@item GRUB_PAYLOADS_DIR @tab directory containing the required kernels +@item GRUB_CBFSTOOL @tab cbfstool from Coreboot package (for coreboot platform only) +@item GRUB_COREBOOT_ROM @tab empty Coreboot ROM +@item GRUB_QEMU_OPTS @tab additional options to be supplied to QEMU +@end multitable + +Required files are: + +@multitable @columnfractions .40 .55 +@item kfreebsd_env.i386 @tab 32-bit kFreeBSD device hints +@item kfreebsd.i386 @tab 32-bit FreeBSD kernel image +@item kfreebsd.x86_64, kfreebsd_env.x86_64 @tab same from 64-bit kFreeBSD +@item knetbsd.i386 @tab 32-bit NetBSD kernel image +@item knetbsd.miniroot.i386 @tab 32-bit kNetBSD miniroot.kmod. +@item knetbsd.x86_64, knetbsd.miniroot.x86_64 @tab same from 64-bit kNetBSD +@item kopenbsd.i386 @tab 32-bit OpenBSD kernel bsd.rd image +@item kopenbsd.x86_64 @tab same from 64-bit kOpenBSD +@item linux.i386 @tab 32-bit Linux +@item linux.x86_64 @tab 64-bit Linux +@end multitable + +@node Troubleshooting +@chapter Error messages produced by GRUB + +@menu +* GRUB only offers a rescue shell:: +* Firmware stalls instead of booting GRUB:: +@end menu + + +@node GRUB only offers a rescue shell +@section GRUB only offers a rescue shell + +GRUB's normal start-up procedure involves setting the @samp{prefix} +environment variable to a value set in the core image by +@command{grub-install}, setting the @samp{root} variable to match, loading +the @samp{normal} module from the prefix, and running the @samp{normal} +command (@pxref{normal}). This command is responsible for reading +@file{/boot/grub/grub.cfg}, running the menu, and doing all the useful +things GRUB is supposed to do. + +If, instead, you only get a rescue shell, this usually means that GRUB +failed to load the @samp{normal} module for some reason. It may be possible +to work around this temporarily: for instance, if the reason for the failure +is that @samp{prefix} is wrong (perhaps it refers to the wrong device, or +perhaps the path to @file{/boot/grub} was not correctly made relative to the +device), then you can correct this and enter normal mode manually: + +@example +@group +# Inspect the current prefix (and other preset variables): +set +# Find out which devices are available: +ls +# Set to the correct value, which might be something like this: +set prefix=(hd0,1)/grub +set root=(hd0,1) +insmod normal +normal +@end group +@end example + +However, any problem that leaves you in the rescue shell probably means that +GRUB was not correctly installed. It may be more useful to try to reinstall +it properly using @kbd{grub-install @var{device}} (@pxref{Invoking +grub-install}). When doing this, there are a few things to remember: + +@itemize @bullet{} +@item +Drive ordering in your operating system may not be the same as the boot +drive ordering used by your firmware. Do not assume that your first hard +drive (e.g. @samp{/dev/sda}) is the one that your firmware will boot from. +@file{device.map} (@pxref{Device map}) can be used to override this, but it +is usually better to use UUIDs or file system labels and avoid depending on +drive ordering entirely. + +@item +At least on BIOS systems, if you tell @command{grub-install} to install GRUB +to a partition but GRUB has already been installed in the master boot +record, then the GRUB installation in the partition will be ignored. + +@item +If possible, it is generally best to avoid installing GRUB to a partition +(unless it is a special partition for the use of GRUB alone, such as the +BIOS Boot Partition used on GPT). Doing this means that GRUB may stop being +able to read its core image due to a file system moving blocks around, such +as while defragmenting, running checks, or even during normal operation. +Installing to the whole disk device is normally more robust. + +@item +Check that GRUB actually knows how to read from the device and file system +containing @file{/boot/grub}. It will not be able to read from encrypted +devices with unsupported encryption scheme, nor from file systems for which +support has not yet been added to GRUB. +@end itemize + + +@node Firmware stalls instead of booting GRUB +@section Firmware stalls instead of booting GRUB + +The EFI implementation of some older MacBook laptops stalls when it gets +presented a grub-mkrescue ISO image for x86_64-efi target on an USB stick. +Affected are models of year 2010 or earlier. Workaround is to zeroize the +bytes 446 to 461 of the EFI partition, where mformat has put a partition table +entry which claims partition start at block 0. This change will not hamper +bootability on other machines. + + +@node User-space utilities +@chapter User-space utilities + +@menu +* Invoking grub-install:: How to use the GRUB installer +* Invoking grub-mkconfig:: Generate a GRUB configuration file +* Invoking grub-mkpasswd-pbkdf2:: + Generate GRUB password hashes +* Invoking grub-mkrelpath:: Make system path relative to its root +* Invoking grub-mkrescue:: Make a GRUB rescue image +* Invoking grub-mount:: Mount a file system using GRUB +* Invoking grub-probe:: Probe device information for GRUB +* Invoking grub-protect:: Protect a disk key with a key protector +* Invoking grub-script-check:: Check GRUB script file for syntax errors +@end menu + + +@node Invoking grub-install +@section Invoking grub-install + +The program @command{grub-install} generates a GRUB core image using +@command{grub-mkimage} and installs it on your system. You must specify the device name on which you want to install GRUB, like this: @example @@ -1431,21 +9832,620 @@ Print a summary of the command-line options and exit. @item --version Print the version number of GRUB and exit. -@item --root-directory=@var{dir} -Install GRUB images under the directory @var{dir} instead of the root -directory. This option is useful when you want to install GRUB into a -separate partition or a removable disk. Here is an example in which -you have a separate @dfn{boot} partition which is mounted on -@file{/boot}: +@item --boot-directory=@var{dir} +Install GRUB images under the directory @file{@var{dir}/grub/} +This option is useful when you want to install GRUB into a +separate partition or a removable disk. +If this option is not specified then it defaults to @file{/boot}, so @example -@kbd{grub-install --root-directory=/boot hd0} +@kbd{grub-install /dev/sda} +@end example + +is equivalent to + +@example +@kbd{grub-install --boot-directory=/boot/ /dev/sda} +@end example + +Here is an example in which you have a separate @dfn{boot} partition which is +mounted on +@file{/mnt/boot}: + +@example +@kbd{grub-install --boot-directory=/mnt/boot /dev/sdb} @end example @item --recheck Recheck the device map, even if @file{/boot/grub/device.map} already exists. You should use this option whenever you add/remove a disk into/from your computer. + +@item --no-rs-codes +By default on x86 BIOS systems, @command{grub-install} will use some +extra space in the bootloader embedding area for Reed-Solomon +error-correcting codes. This enables GRUB to still boot successfully +if some blocks are corrupted. The exact amount of protection offered +is dependent on available space in the embedding area. R sectors of +redundancy can tolerate up to R/2 corrupted sectors. This +redundancy may be cumbersome if attempting to cryptographically +validate the contents of the bootloader embedding area, or in more +modern systems with GPT-style partition tables (@pxref{BIOS +installation}) where GRUB does not reside in any unpartitioned space +outside of the MBR. Disable the Reed-Solomon codes with this option. +@end table + +@node Invoking grub-mkconfig +@section Invoking grub-mkconfig + +The program @command{grub-mkconfig} generates a configuration file for GRUB +(@pxref{Simple configuration}). + +@example +grub-mkconfig -o /boot/grub/grub.cfg +@end example + +@command{grub-mkconfig} accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -o @var{file} +@itemx --output=@var{file} +Send the generated configuration file to @var{file}. The default is to send +it to standard output. +@end table + + +@node Invoking grub-mkpasswd-pbkdf2 +@section Invoking grub-mkpasswd-pbkdf2 + +The program @command{grub-mkpasswd-pbkdf2} generates password hashes for +GRUB (@pxref{Security}). + +@example +grub-mkpasswd-pbkdf2 +@end example + +@command{grub-mkpasswd-pbkdf2} accepts the following options: + +@table @option +@item -c @var{number} +@itemx --iteration-count=@var{number} +Number of iterations of the underlying pseudo-random function. Defaults to +10000. + +@item -l @var{number} +@itemx --buflen=@var{number} +Length of the generated hash. Defaults to 64. + +@item -s @var{number} +@itemx --salt=@var{number} +Length of the salt. Defaults to 64. +@end table + + +@node Invoking grub-mkrelpath +@section Invoking grub-mkrelpath + +The program @command{grub-mkrelpath} makes a file system path relative to +the root of its containing file system. For instance, if @file{/usr} is a +mount point, then: + +@example +$ @kbd{grub-mkrelpath /usr/share/grub/unicode.pf2} +@samp{/share/grub/unicode.pf2} +@end example + +This is mainly used internally by other GRUB utilities such as +@command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}), but may +occasionally also be useful for debugging. + +@command{grub-mkrelpath} accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. +@end table + + +@node Invoking grub-mkrescue +@section Invoking grub-mkrescue + +The program @command{grub-mkrescue} generates a bootable GRUB rescue image +(@pxref{Making a GRUB bootable CD-ROM}). + +@example +grub-mkrescue -o grub.iso +@end example + +All arguments not explicitly listed as @command{grub-mkrescue} options are +passed on directly to @command{xorriso} in @command{mkisofs} emulation mode. +Options passed to @command{xorriso} will normally be interpreted as +@command{mkisofs} options; if the option @samp{--} is used, then anything +after that will be interpreted as native @command{xorriso} options. + +Non-option arguments specify additional source directories. This is +commonly used to add extra files to the image: + +@example +mkdir -p disk/boot/grub +@r{(add extra files to @file{disk/boot/grub})} +grub-mkrescue -o grub.iso disk +@end example + +@command{grub-mkrescue} accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -o @var{file} +@itemx --output=@var{file} +Save output in @var{file}. This "option" is required. + +@item --modules=@var{modules} +Pre-load the named GRUB modules in the image. Multiple entries in +@var{modules} should be separated by whitespace (so you will probably need +to quote this for your shell). + +@item --rom-directory=@var{dir} +If generating images for the QEMU or Coreboot platforms, copy the resulting +@file{qemu.img} or @file{coreboot.elf} files respectively to the @var{dir} +directory as well as including them in the image. + +@item --xorriso=@var{file} +Use @var{file} as the @command{xorriso} program, rather than the built-in +default. + +@item --grub-mkimage=@var{file} +Use @var{file} as the @command{grub-mkimage} program, rather than the +built-in default. +@end table + + +@node Invoking grub-mount +@section Invoking grub-mount + +The program @command{grub-mount} performs a read-only mount of any file +system or file system image that GRUB understands, using GRUB's file system +drivers via FUSE. (It is only available if FUSE development files were +present when GRUB was built.) This has a number of uses: + +@itemize @bullet +@item +It provides a convenient way to check how GRUB will view a file system at +boot time. You can use normal command-line tools to compare that view with +that of your operating system, making it easy to find bugs. + +@item +It offers true read-only mounts. Linux does not have these for journalling +file systems, because it will always attempt to replay the journal at mount +time; while you can temporarily mark the block device read-only to avoid +this, that causes the mount to fail. Since GRUB intentionally contains no +code for writing to file systems, it can easily provide a guaranteed +read-only mount mechanism. + +@item +It allows you to examine any file system that GRUB understands without +needing to load additional modules into your running kernel, which may be +useful in constrained environments such as installers. + +@item +Since it can examine file system images (contained in regular files) just as +easily as file systems on block devices, you can use it to inspect any file +system image that GRUB understands with only enough privileges to use FUSE, +even if nobody has yet written a FUSE module specifically for that file +system type. +@end itemize + +Using @command{grub-mount} is normally as simple as: + +@example +grub-mount /dev/sda1 /mnt +@end example + +@command{grub-mount} must be given one or more images and a mount point as +non-option arguments (if it is given more than one image, it will treat them +as a RAID set), and also accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -C +@itemx --crypto +Mount encrypted devices, prompting for a passphrase if necessary. + +@item -d @var{string} +@itemx --debug=@var{string} +Show debugging output for conditions matching @var{string}. + +@item -K prompt|@var{file} +@itemx --zfs-key=prompt|@var{file} +Load a ZFS encryption key. If you use @samp{prompt} as the argument, +@command{grub-mount} will read a passphrase from the terminal; otherwise, it +will read key material from the specified file. + +@item -r @var{device} +@itemx --root=@var{device} +Set the GRUB root device to @var{device}. You do not normally need to set +this; @command{grub-mount} will automatically set the root device to the +root of the supplied file system. + +If @var{device} is just a number, then it will be treated as a partition +number within the supplied image. This means that, if you have an image of +an entire disk in @file{disk.img}, then you can use this command to mount +its second partition: + +@example +grub-mount -r 2 disk.img mount-point +@end example + +@item -v +@itemx --verbose +Print verbose messages. +@end table + + +@node Invoking grub-probe +@section Invoking grub-probe + +The program @command{grub-probe} probes device information for a given path +or device. + +@example +grub-probe --target=fs /boot/grub +grub-probe --target=drive --device /dev/sda1 +@end example + +@command{grub-probe} must be given a path or device as a non-option +argument, and also accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -d +@itemx --device +If this option is given, then the non-option argument is a system device +name (such as @samp{/dev/sda1}), and @command{grub-probe} will print +information about that device. If it is not given, then the non-option +argument is a filesystem path (such as @samp{/boot/grub}), and +@command{grub-probe} will print information about the device containing that +part of the filesystem. + +@item -m @var{file} +@itemx --device-map=@var{file} +Use @var{file} as the device map (@pxref{Device map}) rather than the +default, usually @samp{/boot/grub/device.map}. + +@item -t @var{target} +@itemx --target=@var{target} +Print information about the given path or device as defined by @var{target}. +The available targets and their meanings are: + +@table @samp +@item fs +GRUB filesystem module. +@item fs_uuid +Filesystem Universally Unique Identifier (UUID). +@item fs_label +Filesystem label. +@item drive +GRUB device name. +@item device +System device name. +@item partmap +GRUB partition map module. +@item abstraction +GRUB abstraction module (e.g. @samp{lvm}). +@item cryptodisk_uuid +Crypto device UUID. +@item msdos_parttype +MBR partition type code (two hexadecimal digits). +@item hints_string +A string of platform search hints suitable for passing to the +@command{search} command (@pxref{search}). +@item bios_hints +Search hints for the PC BIOS platform. +@item ieee1275_hints +Search hints for the IEEE1275 platform. +@item baremetal_hints +Search hints for platforms where disks are addressed directly rather than +via firmware. +@item efi_hints +Search hints for the EFI platform. +@item arc_hints +Search hints for the ARC platform. +@item compatibility_hint +A guess at a reasonable GRUB drive name for this device, which may be +used as a fallback if the @command{search} command fails. +@item disk +System device name for the whole disk. +@end table + +@item -v +@itemx --verbose +Print verbose messages. +@end table + + +@node Invoking grub-protect +@section Invoking grub-protect + +The program @command{grub-protect} protects a disk encryption key with +a specified key protector. + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -a add|remove +@itemx --action=add|remove +Add or remove a key protector to or from a key. + +@item -p @var{protector} +@itemx --protector=@var{protector} +Set the key protector. Currently, @samp{tpm2} is the only supported key +protector. + +@item --tpm2-asymmetric=@var{type} +Choose the the type of SRK. The valid options are @samp{RSA} (@samp{RSA2048}) +and @samp{ECC} (@samp{ECC_NIST_P256}).(default: @samp{ECC}) + +@item --tpm2-bank=@var{alg} +Choose bank of PCRs used to authorize key release: @samp{SHA1}, @samp{SHA256}, +@samp{SHA384}, or @samp{SHA512}. (default: @samp{SHA256}) + +@item --tpm2-device=@var{device} +Set the path to the TPM2 device. (default: @samp{/dev/tpm0}) + +@item --tpm2-evict +Evict a previously persisted SRK from the TPM, if any. + +@item --tpm2-keyfile=@var{file} +Set the path to a file that contains the cleartext key to protect. + +@item --tpm2-outfile=@var{file} +Set the path to the file that will contain the key after sealing +(must be accessible to GRUB during boot). + +@item --tpm2-pcrs=@var{pcrs} +Set a comma-separated list of PCRs used to authorize key release e.g., @samp{7,11}. +Please be aware that PCR 0~7 are used by the firmware and the measurement result +may change after a firmware update (for baremetal systems) or a package +(OVMF/SLOF) update in the VM host. This may lead to the failure of key +unsealing. (default: @samp{7}) + +@item --tpm2-srk=@var{handle} +Set the SRK handle, e.g. @samp{0x81000000}, if the SRK is to be made persistent. + +@item --tpm2-nvindex=@var{handle} +Set the handle, e.g. @samp{0x81000000} or @samp{0x1000000}, for NV index mode. + +@item --tpm2key +Use TPM 2.0 Key File format. + +@end table + +@subsection 'Add' action + +Before sealing the key, please check the TPM PCR usage +(@pxref{TPM2 key protector, TPM PCR usage}) to choose a proper set of PCRs. + +Assume that there is a key file, @file{luks.key}, to be sealed with PCR 0, 2, +4, and 7, and here is the @command{grub-protect} command to create the sealed +key file: + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +Then, GRUB can unlock the target partition with the following commands: + +@example +grub> @kbd{tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +Besides writing the PCR-sealed key into a file, @command{grub-protect} can +write the sealed key into TPM non-volatile memory. Here is the +@command{grub-protect} command to write the sealed key into the NV index +handle @samp{0x1000000}. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-nvindex=0x1000000} +@end group +@end example + +Later, GRUB can fetch the key from @samp{0x1000000}. + +@example +grub> @kbd{tpm2_key_protector_init --mode=nv --nvindex=0x1000000} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +In most of cases, the user only needs to create the key with the `add' action. +If auto-unlocking is unwanted, just remove the file and the +@command{tpm2_key_protector_init} command and invoke the @command{cryptomount} +command without @kbd{-P tpm2}. + +@subsection 'Remove' action + +The `remove' action is used to remove the handles for NV index mode and the +persistent SRK. + +@subsubsection Handles for NV index mode + +There are two types of TPM handles supported by NV index mode: persistent +handles and NV index handles, and @command{tpm2_getcap} can be used to +check the existing handles. + +To display the list of existing persistent handles: + +@example +@group +# @kbd{tpm2_getcap handles-persistent} +- 0x81000000 +@end group +@end example + +Similarly, to display the list of existing NV index handles: + +@example +@group +# @kbd{tpm2_getcap handles-nv-index} +- 0x1000000 +@end group +@end example + +If the sealed key at an NV index handle is not needed anymore, the user can +remove the handle with @kbd{--tpm2-nvindex} and @kbd{--tpm2-evict}. For +example, this command removes the data from NV index @samp{0x1000000}: + +@example +@group +# @kbd{grub-protect --action=remove \ + --protector=tpm2 \ + --tpm2-evict \ + --tpm2-nvindex 0x1000000} \ +@end group +@end example + +@subsubsection Persistent SRK + +There are two supported SRKs in @command{grub-protect}: @samp{RSA} and @samp{ECC}. +Due to slower key generation, some users of the @samp{RSA} SRK may prefer +making it persistent so that the TPM can skip the SRK generation when GRUB tries +to unseal the key. + +The available persistent handles can be checked with @command{tpm2_getcap}. + +@example +@group +# @kbd{tpm2_getcap properties-variable} +... +TPM2_PT_HR_PERSISTENT: 0x0 +TPM2_PT_HR_PERSISTENT_AVAIL: 0x41 +... +@end group +@end example + +In this system, there is no persistent handle. A TPM handle is an unsigned +32-bit integer, and the persistent handles starts with @samp{0x81}. Here +we choose the well-known persistent handle: @samp{0x81000000}. + +@example +@group +# @kbd{grub-protect --action=add \ + --protector=tpm2 \ + --tpm2-pcrs=0,2,4,7 \ + --tpm2-asymmetric=RSA \ + --tpm2-srk=0x81000000 \ + --tpm2key \ + --tpm2-keyfile=luks.key \ + --tpm2-outfile=/boot/efi/efi/grub/sealed.tpm} +@end group +@end example + +The additional @kbd{--tpm2-asymmetric=RSA} and @kbd{--tpm2-srk=0x81000000} +options are used to make the key sealed with the RSA SRK and store the SRK +in @samp{0x81000000}. + +For the @command{tpm2_key_protector_init} command, the additional @kbd{-s 0x81000000} +informs the TPM2 key protector to fetch the SRK from @samp{0x81000000}. + +@example +grub> @kbd{tpm2_key_protector_init -s 0x81000000 -T (hd0,gpt1)/efi/grub/sealed.tpm} +grub> @kbd{cryptomount -u -P tpm2} +@end example + +After making the SRK handle persistent, we can check the status of the +persistent handles with @command{tpm2_getcap}. + +@example +@group +# @kbd{tpm2_getcap properties-variable} +... +TPM2_PT_HR_PERSISTENT: 0x1 +TPM2_PT_HR_PERSISTENT_AVAIL: 0x40 +... +# @kbd{tpm2_getcap handles-persistent} +- 0x81000000 +@end group +@end example + +The sealed key can be removed once the user does not want to use the TPM2 key +protector anymore. Here is the command to remove the persistent SRK handle +(@samp{0x81000000}) with @kbd{--tpm2-srk} and @kbd{--tpm2-evict}. + +@example +@group +# @kbd{grub-protect --action=remove \ + --protector=tpm2 \ + --tpm2-srk 0x81000000 \ + --tpm2-evict} +@end group +@end example + + +@node Invoking grub-script-check +@section Invoking grub-script-check + +The program @command{grub-script-check} takes a GRUB script file +(@pxref{Shell-like scripting}) and checks it for syntax errors, similar to +commands such as @command{sh -n}. It may take a @var{path} as a non-option +argument; if none is supplied, it will read from standard input. + +@example +grub-script-check /boot/grub/grub.cfg +@end example + +@command{grub-script-check} accepts the following options: + +@table @option +@item --help +Print a summary of the command-line options and exit. + +@item --version +Print the version number of GRUB and exit. + +@item -v +@itemx --verbose +Print each line of input after reading it. @end table @@ -1461,11 +10461,11 @@ how to get the latest version. @end quotation GRUB is available from the GNU alpha archive site -@uref{ftp://alpha.gnu.org/gnu/grub} or any of its mirrors. The file +@uref{ftp://ftp.gnu.org/gnu/grub} or any of its mirrors. The file will be named grub-version.tar.gz. The current version is @value{VERSION}, so the file you should grab is: -@uref{ftp://alpha.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} +@uref{ftp://ftp.gnu.org/gnu/grub/grub-@value{VERSION}.tar.gz} To unbundle GRUB use the instruction: @@ -1486,8 +10486,9 @@ just do: @end group @end example -Also, the latest version is available from the SVN. See -@uref{http://savannah.gnu.org/svn/?group=grub} for more information. +Also, the latest version is available using Git. See +@uref{http://www.gnu.org/software/grub/grub-download.html} for more +information. @node Reporting bugs @appendix Reporting bugs @@ -1539,7 +10540,7 @@ for. @item Write down anything that you think might be related. Please understand -that we often need to reproduce the same problem you encounterred in our +that we often need to reproduce the same problem you encountered in our environment. So your information should be sufficient for us to do the same thing---Don't forget that we cannot see your computer directly. If you are not sure whether to state a fact or leave it out, state it! @@ -1559,12 +10560,13 @@ Once we get your report, we will try to fix the bugs. @node Future @appendix Where GRUB will go -We started the next generation of GRUB, GRUB 2. GRUB 2 includes -internationalization, dynamic module loading, real memory management, -multiple architecture support, a scripting language, and many other -nice feature. If you are interested in the development of GRUB 2, take -a look at @uref{http://www.gnu.org/software/grub/grub.html, the -homepage}. +GRUB 2 is now quite stable and used in many production systems. We are +currently working towards a 2.0 release. + +If you are interested in the development of GRUB 2, take a look at +@uref{http://www.gnu.org/software/grub/grub.html, the homepage}. + + diff --git a/docs/man/grub-bios-setup.h2m b/docs/man/grub-bios-setup.h2m new file mode 100644 index 000000000..ac6ede362 --- /dev/null +++ b/docs/man/grub-bios-setup.h2m @@ -0,0 +1,6 @@ +[NAME] +grub-bios-setup \- set up a device to boot using GRUB +[SEE ALSO] +.BR grub-install (8), +.BR grub-mkimage (1), +.BR grub-mkrescue (1) diff --git a/docs/man/grub-editenv.h2m b/docs/man/grub-editenv.h2m new file mode 100644 index 000000000..3859d3d4c --- /dev/null +++ b/docs/man/grub-editenv.h2m @@ -0,0 +1,5 @@ +[NAME] +grub-editenv \- edit GRUB environment block +[SEE ALSO] +.BR grub-reboot (8), +.BR grub-set-default (8) diff --git a/docs/man/grub-emu.h2m b/docs/man/grub-emu.h2m new file mode 100644 index 000000000..ef1c00065 --- /dev/null +++ b/docs/man/grub-emu.h2m @@ -0,0 +1,6 @@ +[NAME] +grub-emu \- GRUB emulator +[SEE ALSO] +If you are trying to install GRUB, then you should use +.BR grub-install (8) +rather than this program. diff --git a/docs/man/grub-file.h2m b/docs/man/grub-file.h2m new file mode 100644 index 000000000..e09bb4d31 --- /dev/null +++ b/docs/man/grub-file.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-file \- check file type diff --git a/docs/man/grub-fstest.h2m b/docs/man/grub-fstest.h2m new file mode 100644 index 000000000..9676b159a --- /dev/null +++ b/docs/man/grub-fstest.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-fstest \- debug tool for GRUB filesystem drivers +[SEE ALSO] +.BR grub-probe (8) diff --git a/docs/man/grub-glue-efi.h2m b/docs/man/grub-glue-efi.h2m new file mode 100644 index 000000000..c1c6ded49 --- /dev/null +++ b/docs/man/grub-glue-efi.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-glue-efi \- generate a fat binary for EFI +[DESCRIPTION] +grub-glue-efi processes ia32 and amd64 EFI images and glues them according to Apple format. diff --git a/docs/man/grub-install.h2m b/docs/man/grub-install.h2m new file mode 100644 index 000000000..8cbbc87a0 --- /dev/null +++ b/docs/man/grub-install.h2m @@ -0,0 +1,6 @@ +[NAME] +grub-install \- install GRUB to a device +[SEE ALSO] +.BR grub-mkconfig (8), +.BR grub-mkimage (1), +.BR grub-mkrescue (1) diff --git a/docs/man/grub-kbdcomp.h2m b/docs/man/grub-kbdcomp.h2m new file mode 100644 index 000000000..d81f9157e --- /dev/null +++ b/docs/man/grub-kbdcomp.h2m @@ -0,0 +1,10 @@ +[NAME] +grub-kbdcomp \- generate a GRUB keyboard layout file +[DESCRIPTION] +grub-kbdcomp processes a X keyboard layout description in +.BR keymaps (5) +format into a format that can be used by GRUB's +.B keymap +command. +[SEE ALSO] +.BR grub-mklayout (8) diff --git a/docs/man/grub-macbless.h2m b/docs/man/grub-macbless.h2m new file mode 100644 index 000000000..0197c0087 --- /dev/null +++ b/docs/man/grub-macbless.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-macbless \- bless a mac file/directory +[SEE ALSO] +.BR grub-install (1) diff --git a/docs/man/grub-macho2img.h2m b/docs/man/grub-macho2img.h2m new file mode 100644 index 000000000..d79aaeed8 --- /dev/null +++ b/docs/man/grub-macho2img.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-macho2img \- convert Mach-O to raw image +[SEE ALSO] +.BR grub-mkimage (1) diff --git a/docs/man/grub-menulst2cfg.h2m b/docs/man/grub-menulst2cfg.h2m new file mode 100644 index 000000000..c2e0055ed --- /dev/null +++ b/docs/man/grub-menulst2cfg.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-menulst2cfg \- transform legacy menu.lst into grub.cfg +[SEE ALSO] +.BR grub-mkconfig (8) diff --git a/docs/man/grub-mkconfig.h2m b/docs/man/grub-mkconfig.h2m new file mode 100644 index 000000000..9b42f8130 --- /dev/null +++ b/docs/man/grub-mkconfig.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkconfig \- generate a GRUB configuration file +[SEE ALSO] +.BR grub-install (8) diff --git a/docs/man/grub-mkfont.h2m b/docs/man/grub-mkfont.h2m new file mode 100644 index 000000000..d46fe600e --- /dev/null +++ b/docs/man/grub-mkfont.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkfont \- make GRUB font files +[SEE ALSO] +.BR grub-mkconfig (8) diff --git a/docs/man/grub-mkimage.h2m b/docs/man/grub-mkimage.h2m new file mode 100644 index 000000000..f0fbc2bb1 --- /dev/null +++ b/docs/man/grub-mkimage.h2m @@ -0,0 +1,6 @@ +[NAME] +grub-mkimage \- make a bootable image of GRUB +[SEE ALSO] +.BR grub-install (8), +.BR grub-mkrescue (1), +.BR grub-mknetdir (8) diff --git a/docs/man/grub-mklayout.h2m b/docs/man/grub-mklayout.h2m new file mode 100644 index 000000000..1e43409c0 --- /dev/null +++ b/docs/man/grub-mklayout.h2m @@ -0,0 +1,10 @@ +[NAME] +grub-mklayout \- generate a GRUB keyboard layout file +[DESCRIPTION] +grub-mklayout processes a keyboard layout description in +.BR keymaps (5) +format into a format that can be used by GRUB's +.B keymap +command. +[SEE ALSO] +.BR grub-mkconfig (8) diff --git a/docs/man/grub-mknetdir.h2m b/docs/man/grub-mknetdir.h2m new file mode 100644 index 000000000..a2ef13ec1 --- /dev/null +++ b/docs/man/grub-mknetdir.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mknetdir \- prepare a GRUB netboot directory. +[SEE ALSO] +.BR grub-mkimage (1) diff --git a/docs/man/grub-mkpasswd-pbkdf2.h2m b/docs/man/grub-mkpasswd-pbkdf2.h2m new file mode 100644 index 000000000..4d202f3da --- /dev/null +++ b/docs/man/grub-mkpasswd-pbkdf2.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkpasswd-pbkdf2 \- generate hashed password for GRUB +[SEE ALSO] +.BR grub-mkconfig (8) diff --git a/docs/man/grub-mkrelpath.h2m b/docs/man/grub-mkrelpath.h2m new file mode 100644 index 000000000..d01f3961e --- /dev/null +++ b/docs/man/grub-mkrelpath.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkrelpath \- make a system path relative to its root +[SEE ALSO] +.BR grub-probe (8) diff --git a/docs/man/grub-mkrescue.h2m b/docs/man/grub-mkrescue.h2m new file mode 100644 index 000000000..a427f02e3 --- /dev/null +++ b/docs/man/grub-mkrescue.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkrescue \- make a GRUB rescue image +[SEE ALSO] +.BR grub-mkimage (1) diff --git a/docs/man/grub-mkstandalone.h2m b/docs/man/grub-mkstandalone.h2m new file mode 100644 index 000000000..c77313978 --- /dev/null +++ b/docs/man/grub-mkstandalone.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-mkstandalone \- make a memdisk-based GRUB image +[SEE ALSO] +.BR grub-mkimage (1) diff --git a/docs/man/grub-mount.h2m b/docs/man/grub-mount.h2m new file mode 100644 index 000000000..8d168982d --- /dev/null +++ b/docs/man/grub-mount.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-mount \- export GRUB filesystem with FUSE diff --git a/docs/man/grub-ofpathname.h2m b/docs/man/grub-ofpathname.h2m new file mode 100644 index 000000000..74b43eea0 --- /dev/null +++ b/docs/man/grub-ofpathname.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-ofpathname \- find OpenBOOT path for a device +[SEE ALSO] +.BR grub-probe (8) diff --git a/docs/man/grub-pe2elf.h2m b/docs/man/grub-pe2elf.h2m new file mode 100644 index 000000000..7ca29bd70 --- /dev/null +++ b/docs/man/grub-pe2elf.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-pe2elf \- convert PE image to ELF +[SEE ALSO] +.BR grub-mkimage (1) diff --git a/docs/man/grub-probe.h2m b/docs/man/grub-probe.h2m new file mode 100644 index 000000000..6e1ffdcf9 --- /dev/null +++ b/docs/man/grub-probe.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-probe \- probe device information for GRUB +[SEE ALSO] +.BR grub-fstest (1) diff --git a/docs/man/grub-protect.h2m b/docs/man/grub-protect.h2m new file mode 100644 index 000000000..ecf1c9eab --- /dev/null +++ b/docs/man/grub-protect.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-protect \- protect a disk key with a key protector +[DESCRIPTION] +grub-protect helps to protect a disk encryption key with a specified key protector. diff --git a/docs/man/grub-reboot.h2m b/docs/man/grub-reboot.h2m new file mode 100644 index 000000000..e4acace65 --- /dev/null +++ b/docs/man/grub-reboot.h2m @@ -0,0 +1,5 @@ +[NAME] +grub-reboot \- set the default boot entry for GRUB, for the next boot only +[SEE ALSO] +.BR grub-set-default (8), +.BR grub-editenv (1) diff --git a/docs/man/grub-render-label.h2m b/docs/man/grub-render-label.h2m new file mode 100644 index 000000000..50ae5247c --- /dev/null +++ b/docs/man/grub-render-label.h2m @@ -0,0 +1,3 @@ +[NAME] +grub-render-label \- generate a .disk_label for Apple Macs. + diff --git a/docs/man/grub-script-check.h2m b/docs/man/grub-script-check.h2m new file mode 100644 index 000000000..365368267 --- /dev/null +++ b/docs/man/grub-script-check.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-script-check \- check grub.cfg for syntax errors +[SEE ALSO] +.BR grub-mkconfig (8) diff --git a/docs/man/grub-set-default.h2m b/docs/man/grub-set-default.h2m new file mode 100644 index 000000000..7945001c1 --- /dev/null +++ b/docs/man/grub-set-default.h2m @@ -0,0 +1,5 @@ +[NAME] +grub-set-default \- set the saved default boot entry for GRUB +[SEE ALSO] +.BR grub-reboot (8), +.BR grub-editenv (1) diff --git a/docs/man/grub-sparc64-setup.h2m b/docs/man/grub-sparc64-setup.h2m new file mode 100644 index 000000000..18f803a50 --- /dev/null +++ b/docs/man/grub-sparc64-setup.h2m @@ -0,0 +1,6 @@ +[NAME] +grub-sparc64-setup \- set up a device to boot using GRUB +[SEE ALSO] +.BR grub-install (8), +.BR grub-mkimage (1), +.BR grub-mkrescue (1) diff --git a/docs/man/grub-syslinux2cfg.h2m b/docs/man/grub-syslinux2cfg.h2m new file mode 100644 index 000000000..ad25c8ab7 --- /dev/null +++ b/docs/man/grub-syslinux2cfg.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-syslinux2cfg \- transform syslinux config into grub.cfg +[SEE ALSO] +.BR grub-menulst2cfg (8) diff --git a/docs/osdetect.cfg b/docs/osdetect.cfg new file mode 100644 index 000000000..47455601d --- /dev/null +++ b/docs/osdetect.cfg @@ -0,0 +1,331 @@ +# Sample GRUB script to autodetect operating systems +# +# Copyright (C) 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 . + +set saved_root=$root + +function freebsd_ufs_variants { + set device=$1 + set fstype=$2 + set uuid=$3 + + menuentry "FreeBSD (on $fstype $device)" $device $uuid { + set root=$2 + set uuid=$3 + + freebsd /boot/kernel/kernel + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + set FreeBSD.vfs.root.mountfrom=ufs:ufsid/$uuid + frebsd_loadenv /boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (single)" $device $uuid { + set root=$2 + set uuid=$3 + + freebsd /boot/kernel/kernel --single + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + set FreeBSD.vfs.root.mountfrom=ufs:ufsid/$uuid + frebsd_loadenv /boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (verbose)" $device $uuid { + set root=$2 + set uuid=$3 + + freebsd /boot/kernel/kernel --verbose + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + set FreeBSD.vfs.root.mountfrom=ufs:ufsid/$uuid + frebsd_loadenv /boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (without ACPI)" $device $uuid { + set root=$2 + set uuid=$3 + + freebsd /boot/kernel/kernel --verbose + unset FreeBSD.acpi_load + set FreeBSD.hint.acpi.0.disabled=1 + set FreeBSD.loader.acpi_disabled_by_user=1 + set FreeBSD.vfs.root.mountfrom=ufs:ufsid/$uuid + frebsd_loadenv /boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (safe mode)" $device $uuid { + set root=$2 + set uuid=$3 + + freebsd /boot/kernel/kernel --verbose + unset FreeBSD.acpi_load + set FreeBSD.hint.acpi.0.disabled=1 + set FreeBSD.loader.acpi_disabled_by_user=1 + set FreeBSD.hint.apic.0.disabled=1 + set FreeBSD.hw.ata.ata_dma=0 + set FreeBSD.hw.ata.atapi_dma=0 + set FreeBSD.hw.ata.wc=0 + set FreeBSD.hw.eisa_slots=0 + set FreeBSD.hint.kbdmux.0.disabled=1 + set FreeBSD.vfs.root.mountfrom=ufs:ufsid/$uuid + frebsd_loadenv /boot/device.hints + } +} + +function freebsd_zfs_variants { + set device=$1 + set fstype=zfs + + menuentry "FreeBSD (on $fstype $device)" $device { + set root=$2 + + freebsd /@/boot/kernel/kernel + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + freebsd_module_elf /@/boot/kernel/opensolaris.ko + freebsd_module_elf /@/boot/kernel/zfs.ko + freebsd_module /@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + probe -l -s name $root + set FreeBSD.vfs.root.mountfrom=zfs:$name + freebsd_loadenv /@/boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (single)" $device { + set root=$2 + + freebsd /@/boot/kernel/kernel --single + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + freebsd_module_elf /@/boot/kernel/opensolaris.ko + freebsd_module_elf /@/boot/kernel/zfs.ko + freebsd_module /@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + probe -l -s name $root + set FreeBSD.vfs.root.mountfrom=zfs:$name + freebsd_loadenv /@/boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (verbose)" $device { + set root=$2 + + freebsd /@/boot/kernel/kernel --verbose + set FreeBSD.acpi_load=YES + set FreeBSD.hint.acpi.0.disabled=0 + freebsd_module_elf /@/boot/kernel/opensolaris.ko + freebsd_module_elf /@/boot/kernel/zfs.ko + freebsd_module /@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + probe -l -s name $root + set FreeBSD.vfs.root.mountfrom=zfs:$name + freebsd_loadenv /@/boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (without ACPI)" $device { + set root=$2 + + freebsd /@/boot/kernel/kernel --verbose + unset FreeBSD.acpi_load + set FreeBSD.hint.acpi.0.disabled=1 + set FreeBSD.loader.acpi_disabled_by_user=1 + freebsd_module_elf /@/boot/kernel/opensolaris.ko + freebsd_module_elf /@/boot/kernel/zfs.ko + freebsd_module /@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + probe -l -s name $root + set FreeBSD.vfs.root.mountfrom=zfs:$name + freebsd_loadenv /@/boot/device.hints + } + + menuentry "FreeBSD (on $fstype $device) (safe mode)" $device { + set root=$2 + + freebsd /@/boot/kernel/kernel --verbose + unset FreeBSD.acpi_load + set FreeBSD.hint.acpi.0.disabled=1 + set FreeBSD.loader.acpi_disabled_by_user=1 + set FreeBSD.hint.apic.0.disabled=1 + set FreeBSD.hw.ata.ata_dma=0 + set FreeBSD.hw.ata.atapi_dma=0 + set FreeBSD.hw.ata.wc=0 + set FreeBSD.hw.eisa_slots=0 + set FreeBSD.hint.kbdmux.0.disabled=1 + freebsd_module_elf /@/boot/kernel/opensolaris.ko + freebsd_module_elf /@/boot/kernel/zfs.ko + freebsd_module /@/boot/zfs/zpool.cache type=/boot/zfs/zpool.cache + probe -l -s name $root + set FreeBSD.vfs.root.mountfrom=zfs:$name + freebsd_loadenv /@/boot/device.hints + } +} + +insmod regexp +for dev in (*); do + # $device: parenthesis removed from $dev + regexp -s device '\((.*)\)' $dev + # $fstype: filesystem type identified + probe -s fstype -f $dev + # uuid: filesystem UUID + probe -s uuid -u $dev + + if test -f ($device)/isolinux/isolinux.cfg ; then + menuentry "ISOLINUX config (on $device)" $device { + set root=$2 + syslinux_configfile -i /isolinux/isolinux.cfg + } + fi + if test -f ($device)/bootmgr -a -f ($device)/boot/bcd; then + menuentry "Windows Vista bootmgr (on $device)" $device { + set root=$2 + chainloader +1 + } + elif test -f ($device)/ntldr -a \ + -e ($device)/ntdetect.com -a -f ($device)/boot.ini; then + menuentry "Windows NT/2000/XP loader (on $device)" $device { + set root=$2 + regexp -s devnum 'hd([0-9]+)' $root + if test "$devnum" != "0"; then + drivemap -s hd0 $root + fi + chainloader +1 + } + elif test -f ($device)/windows/win.com; then + menuentry "Windows 98/ME (on $device)" $device { + set root=$2 + regexp -s devnum 'hd([0-9]+)' $root + if test "$devnum" != "0"; then + drivemap -s hd0 $root + fi + chainloader +1 + } + elif test -f ($device)/io.sys -a -f ($device)/command.com; then + menuentry "MS-DOS (on $device)" $device { + set root=$2 + regexp -s devnum 'hd([0-9]+)' $root + if test "$devnum" != "0"; then + drivemap -s hd0 $root + fi + chainloader +1 + } + elif test -f ($device)/kernel.sys; then + menuentry "FreeDOS (on $device)" $device { + set root=$2 + regexp -s type '([fh])d[0-9]+' $root + regexp -s devnum '[fh]d([0-9]+)' $root + if test $type = 'h' -a "$devnum" != "0"; then + drivemap -s hd0 $root + fi + chainloader +1 + } + elif test "$fstype" = ufs1 -o "$fstype" = ufs2 -a \ + -e ($device)/boot/kernel/kernel -a \ + -e ($device)/boot/device.hints; then + + freebsd_ufs_variants $device $fstype $uuid + + elif test "$fstype" = zfs -a \ + -e ($device)/@/boot/kernel/kernel -a \ + -e ($device)/@/boot/device.hints; then + + freebsd_zfs_variants $device + + elif test "$fstype" = hfsplus -a -f ($device)/mach_kernel; then + menuentry "Mac OS X/Darwin" $device $uuid { + set root=$2 + set uuid=$3 + + insmod vbe + do_resume=0 + if [ /var/vm/sleepimage -nt10 / ]; then + if xnu_resume /var/vm/sleepimage; then + do_resume=1 + fi + fi + if [ $do_resume = 1 ]; then + xnu_uuid $uuid uuid + if [ -f /Extra/DSDT.aml ]; then + acpi -e /Extra/DSDT.aml + fi + xnu_kernel /mach_kernel boot-uuid=${uuid} rd=*uuid + if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then + xnu_mkext /System/Library/Extensions.mkext + else + xnu_mkext /System/Library/Extensions + fi + if [ -f /Extra/Extensions.mkext ]; then + xnu_mkext /Extra/Extensions.mkext + fi + if [ -d /Extra/Extensions ]; then + xnu_kextdir /Extra/Extensions + fi + if [ -f /Extra/devtree.txt ]; then + xnu_devtree /Extra/devtree.txt + fi + if [ -f /Extra/splash.jpg ]; then + insmod jpeg + xnu_splash /Extra/splash.jpg + fi + if [ -f /Extra/splash.png ]; then + insmod png + xnu_splash /Extra/splash.png + fi + if [ -f /Extra/splash.tga ]; then + insmod tga + xnu_splash /Extra/splash.tga + fi + fi + } + else + set root=$device + for file in /boot/vmlinuz-* /boot/linux-*; do + if test -f $file; then + regexp -s version '/boot/vmlinuz-(.*)' $file + regexp -s version '/boot/linux-(.*)' $file + + menuentry "Linux $file" $device $uuid $file $version { + set root=$2 + set uuid=$3 + set kernel=$4 + set version=$5 + + linux $kernel root=UUID=$uuid ro + if test -f /boot/initrd-$version.img; then + initrd /boot/initrd-$version.img + elif test -f /boot/initrd.img-$version; then + initrd /boot/initrd.img-$version + elif test -f /boot/initrd-$version; then + initrd /boot/initrd-$version + fi + } + + menuentry "Linux $file (single)" $device $uuid $file $version { + set root=$2 + set uuid=$3 + set kernel=$4 + set version=$5 + + linux $kernel root=UUID=$uuid ro single + if test -f /boot/initrd-$version.img; then + initrd /boot/initrd-$version.img + elif test -f /boot/initrd.img-$version; then + initrd /boot/initrd.img-$version + elif test -f /boot/initrd-$version; then + initrd /boot/initrd-$version + fi + } + fi + done + fi +done + +set root=$saved_root diff --git a/efiemu/i386/coredetect.c b/efiemu/i386/coredetect.c deleted file mode 100644 index 828508dee..000000000 --- a/efiemu/i386/coredetect.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 . - */ - -#include -#include -#include - -#define cpuid(num,a,b,c,d) \ - asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "0" (num)) - -#define bit_LM (1 << 29) - -char * -grub_efiemu_get_default_core_name (void) -{ - - unsigned int eax, ebx, ecx, edx; - unsigned int max_level; - unsigned int ext_level; - - /* See if we can use cpuid. */ - asm volatile ("pushfl; pushfl; popl %0; movl %0,%1; xorl %2,%0;" - "pushl %0; popfl; pushfl; popl %0; popfl" - : "=&r" (eax), "=&r" (ebx) - : "i" (0x00200000)); - if (((eax ^ ebx) & 0x00200000) == 0) - return "efiemu32.o"; - - /* Check the highest input value for eax. */ - cpuid (0, eax, ebx, ecx, edx); - /* We only look at the first four characters. */ - max_level = eax; - if (max_level == 0) - return "efiemu32.o"; - - cpuid (0x80000000, eax, ebx, ecx, edx); - ext_level = eax; - if (ext_level < 0x80000000) - return "efiemu32.o"; - - cpuid (0x80000001, eax, ebx, ecx, edx); - return (edx & bit_LM) ? "efiemu64.o" : "efiemu32.o"; -} diff --git a/efiemu/runtime/efiemu.sh b/efiemu/runtime/efiemu.sh deleted file mode 100644 index 5a492dc2f..000000000 --- a/efiemu/runtime/efiemu.sh +++ /dev/null @@ -1,4 +0,0 @@ -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 diff --git a/font/font.c b/font/font.c deleted file mode 100644 index 1b3dc6387..000000000 --- a/font/font.c +++ /dev/null @@ -1,1128 +0,0 @@ -/* font.c - Font API and font file loader. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_ASCII_FAILBACK -#include "ascii.h" -#endif - -#ifndef FONT_DEBUG -#define FONT_DEBUG 0 -#endif - -struct char_index_entry -{ - grub_uint32_t code; - grub_uint8_t storage_flags; - grub_uint32_t offset; - - /* Glyph if loaded, or NULL otherwise. */ - struct grub_font_glyph *glyph; -}; - -#define FONT_WEIGHT_NORMAL 100 -#define FONT_WEIGHT_BOLD 200 -#define ASCII_BITMAP_SIZE 16 - -struct grub_font -{ - char *name; - grub_file_t file; - char *family; - short point_size; - short weight; - short max_char_width; - short max_char_height; - short ascent; - short descent; - short leading; - grub_uint32_t num_chars; - struct char_index_entry *char_index; - grub_uint16_t *bmp_idx; -}; - -/* Definition of font registry. */ -struct grub_font_node *grub_font_list; - -static int register_font (grub_font_t font); -static void font_init (grub_font_t font); -static void free_font (grub_font_t font); -static void remove_font (grub_font_t font); - -struct font_file_section -{ - /* The file this section is in. */ - grub_file_t file; - - /* FOURCC name of the section. */ - char name[4]; - - /* Length of the section contents. */ - grub_uint32_t length; - - /* Set by open_section() on EOF. */ - int eof; -}; - -/* Replace unknown glyphs with a rounded question mark. */ -static grub_uint8_t unknown_glyph_bitmap[] = -{ - /* 76543210 */ - 0x7C, /* ooooo */ - 0x82, /* o o */ - 0xBA, /* o ooo o */ - 0xAA, /* o o o o */ - 0xAA, /* o o o o */ - 0x8A, /* o o o */ - 0x9A, /* o oo o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x7C, /* ooooo */ - 0x00 /* */ -}; - -/* The "unknown glyph" glyph, used as a last resort. */ -static struct grub_font_glyph *unknown_glyph; - -/* The font structure used when no other font is loaded. This functions - as a "Null Object" pattern, so that code everywhere does not have to - check for a NULL grub_font_t to avoid dereferencing a null pointer. */ -static struct grub_font null_font; - -/* Flag to ensure module is initialized only once. */ -static grub_uint8_t font_loader_initialized; - -#ifdef USE_ASCII_FAILBACK -static struct grub_font_glyph *ascii_font_glyph[0x80]; -#endif - -static struct grub_font_glyph * -ascii_glyph_lookup (grub_uint32_t code) -{ -#ifdef USE_ASCII_FAILBACK - static int ascii_failback_initialized = 0; - - if (code >= 0x80) - return unknown_glyph; - - if (ascii_failback_initialized == 0) - { - int current; - for (current = 0; current < 0x80; current++) - { - ascii_font_glyph[current] = grub_malloc(sizeof(struct grub_font_glyph) - + ASCII_BITMAP_SIZE); - - ascii_font_glyph[current]->width = 8; - ascii_font_glyph[current]->height = 16; - ascii_font_glyph[current]->offset_x = 0; - ascii_font_glyph[current]->offset_y = -2; - ascii_font_glyph[current]->device_width = 8; - - grub_memcpy (ascii_font_glyph[current]->bitmap, - &ascii_bitmaps[(0x7f - current) * ASCII_BITMAP_SIZE], - ASCII_BITMAP_SIZE); - } - - ascii_failback_initialized = 1; - } - - return ascii_font_glyph[code]; -#else - (void) code; - return unknown_glyph; -#endif -} - -void -grub_font_loader_init (void) -{ - /* Only initialize font loader once. */ - if (font_loader_initialized) - return; - - /* Make glyph for unknown glyph. */ - unknown_glyph = grub_malloc(sizeof(struct grub_font_glyph) - + sizeof(unknown_glyph_bitmap)); - if (! unknown_glyph) - return; - - unknown_glyph->width = 8; - unknown_glyph->height = 16; - unknown_glyph->offset_x = 0; - unknown_glyph->offset_y = -3; - unknown_glyph->device_width = 8; - grub_memcpy(unknown_glyph->bitmap, - unknown_glyph_bitmap, sizeof(unknown_glyph_bitmap)); - - /* Initialize the null font. */ - font_init (&null_font); - null_font.name = ""; - null_font.ascent = unknown_glyph->height-3; - null_font.descent = 3; - null_font.max_char_width = unknown_glyph->width; - null_font.max_char_height = unknown_glyph->height; - - font_loader_initialized = 1; -} - -/* Initialize the font object with initial default values. */ -static void -font_init (grub_font_t font) -{ - font->name = 0; - font->file = 0; - font->family = 0; - font->point_size = 0; - font->weight = 0; - - /* Default leading value, not in font file yet. */ - font->leading = 1; - - font->max_char_width = 0; - font->max_char_height = 0; - font->ascent = 0; - font->descent = 0; - font->num_chars = 0; - font->char_index = 0; - font->bmp_idx = 0; -} - -/* Open the next section in the file. - - On success, the section name is stored in section->name and the length in - section->length, and 0 is returned. On failure, 1 is returned and - grub_errno is set appropriately with an error message. - - If 1 is returned due to being at the end of the file, then section->eof is - set to 1; otherwise, section->eof is set to 0. */ -static int -open_section (grub_file_t file, struct font_file_section *section) -{ - grub_ssize_t retval; - grub_uint32_t raw_length; - - section->file = file; - section->eof = 0; - - /* Read the FOURCC section name. */ - retval = grub_file_read (file, section->name, 4); - if (retval >= 0 && retval < 4) - { - /* EOF encountered. */ - section->eof = 1; - return 1; - } - else if (retval < 0) - { - grub_error (GRUB_ERR_BAD_FONT, - "font format error: can't read section name"); - return 1; - } - - /* Read the big-endian 32-bit section length. */ - retval = grub_file_read (file, &raw_length, 4); - if (retval >= 0 && retval < 4) - { - /* EOF encountered. */ - section->eof = 1; - return 1; - } - else if (retval < 0) - { - grub_error (GRUB_ERR_BAD_FONT, - "font format error: can't read section length"); - return 1; - } - - /* Convert byte-order and store in *length. */ - section->length = grub_be_to_cpu32 (raw_length); - - return 0; -} - -/* Size in bytes of each character index (CHIX section) - entry in the font file. */ -#define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4) - -/* Load the character index (CHIX) section contents from the font file. This - presumes that the position of FILE is positioned immediately after the - section length for the CHIX section (i.e., at the start of the section - contents). Returns 0 upon success, nonzero for failure (in which case - grub_errno is set appropriately). */ -static int -load_font_index (grub_file_t file, grub_uint32_t sect_length, struct - grub_font *font) -{ - unsigned i; - grub_uint32_t last_code; - -#if FONT_DEBUG >= 2 - grub_printf("load_font_index(sect_length=%d)\n", sect_length); -#endif - - /* Sanity check: ensure section length is divisible by the entry size. */ - if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0) - { - grub_error (GRUB_ERR_BAD_FONT, - "font file format error: character index length %d " - "is not a multiple of the entry size %d", - sect_length, FONT_CHAR_INDEX_ENTRY_SIZE); - return 1; - } - - /* Calculate the number of characters. */ - font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE; - - /* Allocate the character index array. */ - font->char_index = grub_malloc (font->num_chars - * sizeof (struct char_index_entry)); - if (! font->char_index) - return 1; - font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t)); - if (! font->bmp_idx) - { - grub_free (font->char_index); - return 1; - } - grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t)); - - -#if FONT_DEBUG >= 2 - grub_printf("num_chars=%d)\n", font->num_chars); -#endif - - last_code = 0; - - /* Load the character index data from the file. */ - for (i = 0; i < font->num_chars; i++) - { - struct char_index_entry *entry = &font->char_index[i]; - - /* Read code point value; convert to native byte order. */ - if (grub_file_read (file, &entry->code, 4) != 4) - return 1; - entry->code = grub_be_to_cpu32 (entry->code); - - /* Verify that characters are in ascending order. */ - if (i != 0 && entry->code <= last_code) - { - grub_error (GRUB_ERR_BAD_FONT, - "font characters not in ascending order: %u <= %u", - entry->code, last_code); - return 1; - } - - if (entry->code < 0x10000) - font->bmp_idx[entry->code] = i; - - last_code = entry->code; - - /* Read storage flags byte. */ - if (grub_file_read (file, &entry->storage_flags, 1) != 1) - return 1; - - /* Read glyph data offset; convert to native byte order. */ - if (grub_file_read (file, &entry->offset, 4) != 4) - return 1; - entry->offset = grub_be_to_cpu32 (entry->offset); - - /* No glyph loaded. Will be loaded on demand and cached thereafter. */ - entry->glyph = 0; - -#if FONT_DEBUG >= 5 - /* Print the 1st 10 characters. */ - if (i < 10) - grub_printf("c=%d o=%d\n", entry->code, entry->offset); -#endif - } - - return 0; -} - -/* Read the contents of the specified section as a string, which is - allocated on the heap. Returns 0 if there is an error. */ -static char * -read_section_as_string (struct font_file_section *section) -{ - char *str; - grub_ssize_t ret; - - str = grub_malloc (section->length + 1); - if (! str) - return 0; - - ret = grub_file_read (section->file, str, section->length); - if (ret < 0 || ret != (grub_ssize_t) section->length) - { - grub_free (str); - return 0; - } - - str[section->length] = '\0'; - return str; -} - -/* Read the contents of the current section as a 16-bit integer value, - which is stored into *VALUE. - Returns 0 upon success, nonzero upon failure. */ -static int -read_section_as_short (struct font_file_section *section, grub_int16_t *value) -{ - grub_uint16_t raw_value; - - if (section->length != 2) - { - grub_error (GRUB_ERR_BAD_FONT, - "font file format error: section %c%c%c%c length " - "is %d but should be 2", - section->name[0], section->name[1], - section->name[2], section->name[3], - section->length); - return 1; - } - if (grub_file_read (section->file, &raw_value, 2) != 2) - return 1; - - *value = grub_be_to_cpu16 (raw_value); - return 0; -} - -/* Load a font and add it to the beginning of the global font list. - Returns 0 upon success, nonzero upon failure. */ -int -grub_font_load (const char *filename) -{ - grub_file_t file = 0; - struct font_file_section section; - char magic[4]; - grub_font_t font = 0; - -#if FONT_DEBUG >= 1 - grub_printf("add_font(%s)\n", filename); -#endif - - file = grub_buffile_open (filename, 1024); - if (!file) - goto fail; - -#if FONT_DEBUG >= 3 - grub_printf("file opened\n"); -#endif - - /* Read the FILE section. It indicates the file format. */ - if (open_section (file, §ion) != 0) - goto fail; - -#if FONT_DEBUG >= 3 - grub_printf("opened FILE section\n"); -#endif - if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE, - sizeof(FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0) - { - grub_error (GRUB_ERR_BAD_FONT, - "font file format error: 1st section must be FILE"); - goto fail; - } - -#if FONT_DEBUG >= 3 - grub_printf("section name ok\n"); -#endif - if (section.length != 4) - { - grub_error (GRUB_ERR_BAD_FONT, - "font file format error (file type ID length is %d " - "but should be 4)", section.length); - goto fail; - } - -#if FONT_DEBUG >= 3 - grub_printf("section length ok\n"); -#endif - /* Check the file format type code. */ - if (grub_file_read (file, magic, 4) != 4) - goto fail; - -#if FONT_DEBUG >= 3 - grub_printf("read magic ok\n"); -#endif - - if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0) - { - grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x", - magic[0], magic[1], magic[2], magic[3]); - goto fail; - } - -#if FONT_DEBUG >= 3 - grub_printf("compare magic ok\n"); -#endif - - /* Allocate the font object. */ - font = (grub_font_t) grub_malloc (sizeof (struct grub_font)); - if (! font) - goto fail; - - font_init (font); - font->file = file; - -#if FONT_DEBUG >= 3 - grub_printf("allocate font ok; loading font info\n"); -#endif - - /* Load the font information. */ - while (1) - { - if (open_section (file, §ion) != 0) - { - if (section.eof) - break; /* Done reading the font file. */ - else - goto fail; - } - -#if FONT_DEBUG >= 2 - grub_printf("opened section %c%c%c%c ok\n", - section.name[0], section.name[1], - section.name[2], section.name[3]); -#endif - - if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME, - sizeof(FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0) - { - font->name = read_section_as_string (§ion); - if (!font->name) - goto fail; - } - else if (grub_memcmp (section.name, - FONT_FORMAT_SECTION_NAMES_POINT_SIZE, - sizeof(FONT_FORMAT_SECTION_NAMES_POINT_SIZE) - 1) == 0) - { - if (read_section_as_short (§ion, &font->point_size) != 0) - goto fail; - } - else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT, - sizeof(FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1) == 0) - { - char *wt; - wt = read_section_as_string (§ion); - if (!wt) - continue; - /* Convert the weight string 'normal' or 'bold' into a number. */ - if (grub_strcmp (wt, "normal") == 0) - font->weight = FONT_WEIGHT_NORMAL; - else if (grub_strcmp (wt, "bold") == 0) - font->weight = FONT_WEIGHT_BOLD; - grub_free (wt); - } - else if (grub_memcmp (section.name, - FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH, - sizeof(FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH) - 1) == 0) - { - if (read_section_as_short (§ion, &font->max_char_width) != 0) - goto fail; - } - else if (grub_memcmp (section.name, - FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT, - sizeof(FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT) - 1) == 0) - { - if (read_section_as_short (§ion, &font->max_char_height) != 0) - goto fail; - } - else if (grub_memcmp (section.name, - FONT_FORMAT_SECTION_NAMES_ASCENT, - sizeof(FONT_FORMAT_SECTION_NAMES_ASCENT) - 1) == 0) - { - if (read_section_as_short (§ion, &font->ascent) != 0) - goto fail; - } - else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT, - sizeof(FONT_FORMAT_SECTION_NAMES_DESCENT) - 1) == 0) - { - if (read_section_as_short (§ion, &font->descent) != 0) - goto fail; - } - else if (grub_memcmp (section.name, - FONT_FORMAT_SECTION_NAMES_CHAR_INDEX, - sizeof(FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) - 1) == 0) - { - if (load_font_index (file, section.length, font) != 0) - goto fail; - } - else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA, - sizeof(FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0) - { - /* When the DATA section marker is reached, we stop reading. */ - break; - } - else - { - /* Unhandled section type, simply skip past it. */ -#if FONT_DEBUG >= 3 - grub_printf("Unhandled section type, skipping.\n"); -#endif - grub_off_t section_end = grub_file_tell (file) + section.length; - if ((int) grub_file_seek (file, section_end) == -1) - goto fail; - } - } - - if (! font->name) - { - grub_printf ("Note: Font has no name.\n"); - font->name = grub_strdup ("Unknown"); - } - -#if FONT_DEBUG >= 1 - grub_printf ("Loaded font `%s'.\n" - "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n", - font->name, - font->ascent, font->descent, - font->max_char_width, font->max_char_height, - font->num_chars); -#endif - - if (font->max_char_width == 0 - || font->max_char_height == 0 - || font->num_chars == 0 - || font->char_index == 0 - || font->ascent == 0 - || font->descent == 0) - { - grub_error (GRUB_ERR_BAD_FONT, - "invalid font file: missing some required data"); - goto fail; - } - - /* Add the font to the global font registry. */ - if (register_font (font) != 0) - goto fail; - - return 0; - -fail: - free_font (font); - return 1; -} - -/* Read a 16-bit big-endian integer from FILE, convert it to native byte - order, and store it in *VALUE. - Returns 0 on success, 1 on failure. */ -static int -read_be_uint16 (grub_file_t file, grub_uint16_t * value) -{ - if (grub_file_read (file, value, 2) != 2) - return 1; - *value = grub_be_to_cpu16 (*value); - return 0; -} - -static int -read_be_int16 (grub_file_t file, grub_int16_t * value) -{ - /* For the signed integer version, use the same code as for unsigned. */ - return read_be_uint16 (file, (grub_uint16_t *) value); -} - -/* Return a pointer to the character index entry for the glyph corresponding to - the codepoint CODE in the font FONT. If not found, return zero. */ -static inline struct char_index_entry * -find_glyph (const grub_font_t font, grub_uint32_t code) -{ - struct char_index_entry *table; - grub_size_t lo; - grub_size_t hi; - grub_size_t mid; - - table = font->char_index; - - /* Use BMP index if possible. */ - if (code < 0x10000 && font->bmp_idx) - { - if (font->bmp_idx[code] == 0xffff) - return 0; - return &table[font->bmp_idx[code]]; - } - - /* Do a binary search in `char_index', which is ordered by code point. */ - lo = 0; - hi = font->num_chars - 1; - - if (! table) - return 0; - - while (lo <= hi) - { - mid = lo + (hi - lo) / 2; - if (code < table[mid].code) - hi = mid - 1; - else if (code > table[mid].code) - lo = mid + 1; - else - return &table[mid]; - } - - return 0; -} - -/* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded - from the font file if has not been loaded yet. - Returns a pointer to the glyph if found, or 0 if it is not found. */ -static struct grub_font_glyph * -grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) -{ - struct char_index_entry *index_entry; - - index_entry = find_glyph (font, code); - if (index_entry) - { - struct grub_font_glyph *glyph = 0; - grub_uint16_t width; - grub_uint16_t height; - grub_int16_t xoff; - grub_int16_t yoff; - grub_int16_t dwidth; - int len; - - if (index_entry->glyph) - /* Return cached glyph. */ - return index_entry->glyph; - - if (! font->file) - /* No open file, can't load any glyphs. */ - return 0; - - /* Make sure we can find glyphs for error messages. Push active - error message to error stack and reset error message. */ - grub_error_push (); - - grub_file_seek (font->file, index_entry->offset); - - /* Read the glyph width, height, and baseline. */ - if (read_be_uint16(font->file, &width) != 0 - || read_be_uint16(font->file, &height) != 0 - || read_be_int16(font->file, &xoff) != 0 - || read_be_int16(font->file, &yoff) != 0 - || read_be_int16(font->file, &dwidth) != 0) - { - remove_font (font); - return 0; - } - - len = (width * height + 7) / 8; - glyph = grub_malloc (sizeof (struct grub_font_glyph) + len); - if (! glyph) - { - remove_font (font); - return 0; - } - - glyph->font = font; - glyph->width = width; - glyph->height = height; - glyph->offset_x = xoff; - glyph->offset_y = yoff; - glyph->device_width = dwidth; - - /* Don't try to read empty bitmaps (e.g., space characters). */ - if (len != 0) - { - if (grub_file_read (font->file, glyph->bitmap, len) != len) - { - remove_font (font); - return 0; - } - } - - /* Restore old error message. */ - grub_error_pop (); - - /* Cache the glyph. */ - index_entry->glyph = glyph; - - return glyph; - } - - return 0; -} - -/* Free the memory used by FONT. - This should not be called if the font has been made available to - users (once it is added to the global font list), since there would - be the possibility of a dangling pointer. */ -static void -free_font (grub_font_t font) -{ - if (font) - { - if (font->file) - grub_file_close (font->file); - grub_free (font->name); - grub_free (font->family); - grub_free (font->char_index); - grub_free (font); - } -} - -/* Add FONT to the global font registry. - Returns 0 upon success, nonzero on failure - (the font was not registered). */ -static int -register_font (grub_font_t font) -{ - struct grub_font_node *node = 0; - - node = grub_malloc (sizeof (struct grub_font_node)); - if (! node) - return 1; - - node->value = font; - node->next = grub_font_list; - grub_font_list = node; - - return 0; -} - -/* Remove the font from the global font list. We don't actually free the - font's memory since users could be holding references to the font. */ -static void -remove_font (grub_font_t font) -{ - struct grub_font_node **nextp, *cur; - - for (nextp = &grub_font_list, cur = *nextp; - cur; - nextp = &cur->next, cur = cur->next) - { - if (cur->value == font) - { - *nextp = cur->next; - - /* Free the node, but not the font itself. */ - grub_free (cur); - - return; - } - } -} - -/* Get a font from the list of loaded fonts. This function will return - another font if the requested font is not available. If no fonts are - loaded, then a special 'null font' is returned, which contains no glyphs, - but is not a null pointer so the caller may omit checks for NULL. */ -grub_font_t -grub_font_get (const char *font_name) -{ - struct grub_font_node *node; - - for (node = grub_font_list; node; node = node->next) - { - grub_font_t font = node->value; - if (grub_strcmp (font->name, font_name) == 0) - return font; - } - - /* If no font by that name is found, return the first font in the list - as a fallback. */ - if (grub_font_list && grub_font_list->value) - return grub_font_list->value; - else - /* The null_font is a last resort. */ - return &null_font; -} - -/* Get the full name of the font. */ -const char * -grub_font_get_name (grub_font_t font) -{ - return font->name; -} - -/* Get the maximum width of any character in the font in pixels. */ -int -grub_font_get_max_char_width (grub_font_t font) -{ - return font->max_char_width; -} - -/* Get the maximum height of any character in the font in pixels. */ -int -grub_font_get_max_char_height (grub_font_t font) -{ - return font->max_char_height; -} - -/* Get the distance in pixels from the top of characters to the baseline. */ -int -grub_font_get_ascent (grub_font_t font) -{ - return font->ascent; -} - -/* Get the distance in pixels from the baseline to the lowest descenders - (for instance, in a lowercase 'y', 'g', etc.). */ -int -grub_font_get_descent (grub_font_t font) -{ - return font->descent; -} - -/* Get the *standard leading* of the font in pixel, which is the spacing - between two lines of text. Specifically, it is the space between the - descent of one line and the ascent of the next line. This is included - in the *height* metric. */ -int -grub_font_get_leading (grub_font_t font) -{ - return font->leading; -} - -/* Get the distance in pixels between baselines of adjacent lines of text. */ -int -grub_font_get_height (grub_font_t font) -{ - return font->ascent + font->descent + font->leading; -} - -/* Get the width in pixels of the specified UTF-8 string, when rendered in - in the specified font (but falling back on other fonts for glyphs that - are missing). */ -int -grub_font_get_string_width (grub_font_t font, const char *str) -{ - int width; - struct grub_font_glyph *glyph; - grub_uint32_t code; - const grub_uint8_t *ptr; - - for (ptr = (const grub_uint8_t *) str, width = 0; - grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; ) - { - glyph = grub_font_get_glyph_with_fallback (font, code); - width += glyph->device_width; - } - - return width; -} - -/* Get the glyph for FONT corresponding to the Unicode code point CODE. - Returns the ASCII glyph for the code if no other fonts are available. - The glyphs are cached once loaded. */ -struct grub_font_glyph * -grub_font_get_glyph (grub_font_t font, grub_uint32_t code) -{ - struct grub_font_glyph *glyph = 0; - if (font) - glyph = grub_font_get_glyph_internal (font, code); - if (glyph == 0) - { - glyph = ascii_glyph_lookup (code); - } - return glyph; -} - - -/* Calculate a subject value representing "how similar" two fonts are. - This is used to prioritize the order that fonts are scanned for missing - glyphs. The object is to select glyphs from the most similar font - possible, for the best appearance. - The heuristic is crude, but it helps greatly when fonts of similar - sizes are used so that tiny 8 point glyphs are not mixed into a string - of 24 point text unless there is no other choice. */ -static int -get_font_diversity(grub_font_t a, grub_font_t b) -{ - int d; - - d = 0; - - if (a->ascent && b->ascent) - d += grub_abs (a->ascent - b->ascent) * 8; - else - /* Penalty for missing attributes. */ - d += 50; - - if (a->max_char_height && b->max_char_height) - d += grub_abs (a->max_char_height - b->max_char_height) * 8; - else - /* Penalty for missing attributes. */ - d += 50; - - /* Weight is a minor factor. */ - d += (a->weight != b->weight) ? 5 : 0; - - return d; -} - -/* Get a glyph corresponding to the codepoint CODE. If FONT contains the - specified glyph, then it is returned. Otherwise, all other loaded fonts - are searched until one is found that contains a glyph for CODE. - If no glyph is available for CODE in the loaded fonts, then a glyph - representing an unknown character is returned. - This function never returns NULL. - The returned glyph is owned by the font manager and should not be freed - by the caller. The glyphs are cached. */ -struct grub_font_glyph * -grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) -{ - struct grub_font_glyph *glyph; - struct grub_font_node *node; - /* Keep track of next node, in case there's an I/O error in - grub_font_get_glyph_internal() and the font is removed from the list. */ - struct grub_font_node *next; - /* Information on the best glyph found so far, to help find the glyph in - the best matching to the requested one. */ - int best_diversity; - struct grub_font_glyph *best_glyph; - - if (font) - { - /* First try to get the glyph from the specified font. */ - glyph = grub_font_get_glyph_internal (font, code); - if (glyph) - return glyph; - } - - /* Otherwise, search all loaded fonts for the glyph and use the one from - the font that best matches the requested font. */ - best_diversity = 10000; - best_glyph = 0; - - for (node = grub_font_list; node; node = next) - { - grub_font_t curfont; - - curfont = node->value; - next = node->next; - - glyph = grub_font_get_glyph_internal (curfont, code); - if (glyph) - { - int d; - - d = get_font_diversity (curfont, font); - if (d < best_diversity) - { - best_diversity = d; - best_glyph = glyph; - } - } - } - - if (best_glyph) - return best_glyph; - else - /* Glyph not available in any font. Return ASCII failback. */ - return ascii_glyph_lookup (code); -} - - -/* Draw the specified glyph at (x, y). The y coordinate designates the - baseline of the character, while the x coordinate designates the left - side location of the character. */ -grub_err_t -grub_font_draw_glyph (struct grub_font_glyph *glyph, - grub_video_color_t color, - int left_x, int baseline_y) -{ - struct grub_video_bitmap glyph_bitmap; - - /* Don't try to draw empty glyphs (U+0020, etc.). */ - if (glyph->width == 0 || glyph->height == 0) - return GRUB_ERR_NONE; - - glyph_bitmap.mode_info.width = glyph->width; - glyph_bitmap.mode_info.height = glyph->height; - glyph_bitmap.mode_info.mode_type = - (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) - | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; - glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; - glyph_bitmap.mode_info.bpp = 1; - - /* Really 1 bit per pixel. */ - glyph_bitmap.mode_info.bytes_per_pixel = 0; - - /* Packed densely as bits. */ - glyph_bitmap.mode_info.pitch = glyph->width; - - glyph_bitmap.mode_info.number_of_colors = 2; - glyph_bitmap.mode_info.bg_red = 0; - glyph_bitmap.mode_info.bg_green = 0; - glyph_bitmap.mode_info.bg_blue = 0; - glyph_bitmap.mode_info.bg_alpha = 0; - grub_video_unmap_color(color, - &glyph_bitmap.mode_info.fg_red, - &glyph_bitmap.mode_info.fg_green, - &glyph_bitmap.mode_info.fg_blue, - &glyph_bitmap.mode_info.fg_alpha); - glyph_bitmap.data = glyph->bitmap; - - int bitmap_left = left_x + glyph->offset_x; - int bitmap_bottom = baseline_y - glyph->offset_y; - int bitmap_top = bitmap_bottom - glyph->height; - - return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND, - bitmap_left, bitmap_top, - 0, 0, - glyph->width, glyph->height); -} - -/* Draw a UTF-8 string of text on the current video render target. - The x coordinate specifies the starting x position for the first character, - while the y coordinate specifies the baseline position. - If the string contains a character that FONT does not contain, then - a glyph from another loaded font may be used instead. */ -grub_err_t -grub_font_draw_string (const char *str, grub_font_t font, - grub_video_color_t color, - int left_x, int baseline_y) -{ - int x; - struct grub_font_glyph *glyph; - grub_uint32_t code; - const grub_uint8_t *ptr; - - for (ptr = (const grub_uint8_t *) str, x = left_x; - grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; ) - { - glyph = grub_font_get_glyph_with_fallback (font, code); - if (grub_font_draw_glyph (glyph, color, x, baseline_y) - != GRUB_ERR_NONE) - return grub_errno; - x += glyph->device_width; - } - - return GRUB_ERR_NONE; -} - diff --git a/fs/affs.c b/fs/affs.c deleted file mode 100644 index 3dc80752d..000000000 --- a/fs/affs.c +++ /dev/null @@ -1,550 +0,0 @@ -/* affs.c - Amiga Fast FileSystem. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* The affs bootblock. */ -struct grub_affs_bblock -{ - grub_uint8_t type[3]; - grub_uint8_t flags; - grub_uint32_t checksum; - grub_uint32_t rootblock; -} __attribute__ ((packed)); - -/* Set if the filesystem is a AFFS filesystem. Otherwise this is an - OFS filesystem. */ -#define GRUB_AFFS_FLAG_FFS 1 - -/* The affs rootblock. */ -struct grub_affs_rblock -{ - grub_uint8_t type[4]; - grub_uint8_t unused1[8]; - grub_uint32_t htsize; - grub_uint32_t unused2; - grub_uint32_t checksum; - grub_uint32_t hashtable[1]; -} __attribute__ ((packed)); - -/* The second part of a file header block. */ -struct grub_affs_file -{ - grub_uint8_t unused1[12]; - grub_uint32_t size; - grub_uint8_t unused2[104]; - grub_uint8_t namelen; - grub_uint8_t name[30]; - grub_uint8_t unused3[33]; - grub_uint32_t next; - grub_uint32_t parent; - grub_uint32_t extension; - grub_int32_t type; -} __attribute__ ((packed)); - -/* The location of `struct grub_affs_file' relative to the end of a - file header block. */ -#define GRUB_AFFS_FILE_LOCATION 200 - -/* The offset in both the rootblock and the file header block for the - hashtable, symlink and block pointers (all synonyms). */ -#define GRUB_AFFS_HASHTABLE_OFFSET 24 -#define GRUB_AFFS_BLOCKPTR_OFFSET 24 -#define GRUB_AFFS_SYMLINK_OFFSET 24 - -#define GRUB_AFFS_SYMLINK_SIZE(blocksize) ((blocksize) - 225) - -#define GRUB_AFFS_FILETYPE_DIR -3 -#define GRUB_AFFS_FILETYPE_REG 2 -#define GRUB_AFFS_FILETYPE_SYMLINK 3 - - -struct grub_fshelp_node -{ - struct grub_affs_data *data; - int block; - int size; - int parent; -}; - -/* Information about a "mounted" affs filesystem. */ -struct grub_affs_data -{ - struct grub_affs_bblock bblock; - struct grub_fshelp_node diropen; - grub_disk_t disk; - - /* Blocksize in sectors. */ - int blocksize; - - /* The number of entries in the hashtable. */ - int htsize; -}; - -static grub_dl_t my_mod; - - -static grub_disk_addr_t -grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) -{ - int links; - grub_uint32_t pos; - int block = node->block; - struct grub_affs_file file; - struct grub_affs_data *data = node->data; - grub_uint32_t mod; - - /* Find the block that points to the fileblock we are looking up by - following the chain until the right table is reached. */ - for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--) - { - grub_disk_read (data->disk, block + data->blocksize - 1, - data->blocksize * (GRUB_DISK_SECTOR_SIZE - - GRUB_AFFS_FILE_LOCATION), - sizeof (file), &file); - if (grub_errno) - return 0; - - block = grub_be_to_cpu32 (file.extension); - } - - /* Translate the fileblock to the block within the right table. */ - fileblock = mod; - grub_disk_read (data->disk, block, - GRUB_AFFS_BLOCKPTR_OFFSET - + (data->htsize - fileblock - 1) * sizeof (pos), - sizeof (pos), &pos); - if (grub_errno) - return 0; - - return grub_be_to_cpu32 (pos); -} - - -/* Read LEN bytes from the file described by DATA starting with byte - POS. Return the amount of read bytes in READ. */ -static grub_ssize_t -grub_affs_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) -{ - return grub_fshelp_read_file (node->data->disk, node, read_hook, - pos, len, buf, grub_affs_read_block, - node->size, 0); -} - - -static struct grub_affs_data * -grub_affs_mount (grub_disk_t disk) -{ - struct grub_affs_data *data; - grub_uint32_t *rootblock = 0; - struct grub_affs_rblock *rblock; - - int checksum = 0; - int checksumr = 0; - int blocksize = 0; - - data = grub_malloc (sizeof (struct grub_affs_data)); - if (!data) - return 0; - - /* Read the bootblock. */ - grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock), - &data->bblock); - if (grub_errno) - goto fail; - - /* Make sure this is an affs filesystem. */ - if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3)) - { - grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem"); - goto fail; - } - - /* Test if the filesystem is a OFS filesystem. */ - if (! (data->bblock.flags & GRUB_AFFS_FLAG_FFS)) - { - grub_error (GRUB_ERR_BAD_FS, "OFS not yet supported"); - goto fail; - } - - /* Read the bootblock. */ - grub_disk_read (disk, 0, 0, sizeof (struct grub_affs_bblock), - &data->bblock); - if (grub_errno) - goto fail; - - /* No sane person uses more than 8KB for a block. At least I hope - for that person because in that case this won't work. */ - rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE * 16); - if (!rootblock) - goto fail; - - rblock = (struct grub_affs_rblock *) rootblock; - - /* Read the rootblock. */ - grub_disk_read (disk, (disk->total_sectors >> 1) + blocksize, 0, - GRUB_DISK_SECTOR_SIZE * 16, rootblock); - if (grub_errno) - goto fail; - - /* The filesystem blocksize is not stored anywhere in the filesystem - itself. One way to determine it is reading blocks for the - rootblock until the checksum is correct. */ - checksumr = grub_be_to_cpu32 (rblock->checksum); - rblock->checksum = 0; - for (blocksize = 0; blocksize < 8; blocksize++) - { - grub_uint32_t *currblock = rootblock + GRUB_DISK_SECTOR_SIZE * blocksize; - unsigned int i; - - for (i = 0; i < GRUB_DISK_SECTOR_SIZE / sizeof (*currblock); i++) - checksum += grub_be_to_cpu32 (currblock[i]); - - if (checksumr == -checksum) - break; - } - if (-checksum != checksumr) - { - grub_error (GRUB_ERR_BAD_FS, "AFFS blocksize couldn't be determined"); - goto fail; - } - blocksize++; - - data->blocksize = blocksize; - data->disk = disk; - data->htsize = grub_be_to_cpu32 (rblock->htsize); - data->diropen.data = data; - data->diropen.block = (disk->total_sectors >> 1); - - grub_free (rootblock); - - return data; - - fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) - grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem"); - - grub_free (data); - grub_free (rootblock); - return 0; -} - - -static char * -grub_affs_read_symlink (grub_fshelp_node_t node) -{ - struct grub_affs_data *data = node->data; - char *symlink; - - symlink = grub_malloc (GRUB_AFFS_SYMLINK_SIZE (data->blocksize)); - if (!symlink) - return 0; - - grub_disk_read (data->disk, node->block, GRUB_AFFS_SYMLINK_OFFSET, - GRUB_AFFS_SYMLINK_SIZE (data->blocksize), symlink); - if (grub_errno) - { - grub_free (symlink); - return 0; - } - grub_dprintf ("affs", "Symlink: `%s'\n", symlink); - return symlink; -} - - -static int -grub_affs_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - int i; - struct grub_affs_file file; - struct grub_fshelp_node *node = 0; - struct grub_affs_data *data = dir->data; - grub_uint32_t *hashtable; - - auto int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block, - int size, int type); - - int NESTED_FUNC_ATTR grub_affs_create_node (const char *name, int block, - int size, int type) - { - node = grub_malloc (sizeof (*node)); - if (!node) - { - grub_free (hashtable); - return 1; - } - - node->data = data; - node->size = size; - node->block = block; - node->parent = grub_be_to_cpu32 (file.parent); - - if (hook (name, type, node)) - { - grub_free (hashtable); - return 1; - } - return 0; - } - - hashtable = grub_malloc (data->htsize * sizeof (*hashtable)); - if (!hashtable) - return 1; - - grub_disk_read (data->disk, dir->block, GRUB_AFFS_HASHTABLE_OFFSET, - data->htsize * sizeof (*hashtable), (char *) hashtable); - if (grub_errno) - goto fail; - - /* Create the directory entries for `.' and `..'. */ - if (grub_affs_create_node (".", dir->block, dir->size, GRUB_FSHELP_DIR)) - return 1; - if (grub_affs_create_node ("..", dir->parent ? dir->parent : dir->block, - dir->size, GRUB_FSHELP_DIR)) - return 1; - - for (i = 0; i < data->htsize; i++) - { - enum grub_fshelp_filetype type; - grub_uint64_t next; - - if (!hashtable[i]) - continue; - - /* Every entry in the hashtable can be chained. Read the entire - chain. */ - next = grub_be_to_cpu32 (hashtable[i]); - - while (next) - { - grub_disk_read (data->disk, next + data->blocksize - 1, - data->blocksize * GRUB_DISK_SECTOR_SIZE - - GRUB_AFFS_FILE_LOCATION, - sizeof (file), (char *) &file); - if (grub_errno) - goto fail; - - file.name[file.namelen] = '\0'; - - if ((int) grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_DIR) - type = GRUB_FSHELP_REG; - else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_REG) - type = GRUB_FSHELP_DIR; - else if (grub_be_to_cpu32 (file.type) == GRUB_AFFS_FILETYPE_SYMLINK) - type = GRUB_FSHELP_SYMLINK; - else - type = GRUB_FSHELP_UNKNOWN; - - if (grub_affs_create_node ((char *) (file.name), next, - grub_be_to_cpu32 (file.size), type)) - return 1; - - next = grub_be_to_cpu32 (file.next); - } - } - - grub_free (hashtable); - return 0; - - fail: - grub_free (node); - grub_free (hashtable); - return 0; -} - - -/* Open a file named NAME and initialize FILE. */ -static grub_err_t -grub_affs_open (struct grub_file *file, const char *name) -{ - struct grub_affs_data *data; - struct grub_fshelp_node *fdiro = 0; - - grub_dl_ref (my_mod); - - data = grub_affs_mount (file->device->disk); - if (!data) - goto fail; - - grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir, - grub_affs_read_symlink, GRUB_FSHELP_REG); - if (grub_errno) - goto fail; - - file->size = fdiro->size; - data->diropen = *fdiro; - grub_free (fdiro); - - file->data = data; - file->offset = 0; - - return 0; - - fail: - if (data && fdiro != &data->diropen) - grub_free (fdiro); - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - - -static grub_err_t -grub_affs_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - - -/* Read LEN bytes data from FILE into BUF. */ -static grub_ssize_t -grub_affs_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_affs_data *data = - (struct grub_affs_data *) file->data; - - int size = grub_affs_read_file (&data->diropen, file->read_hook, - file->offset, len, buf); - - return size; -} - - -static grub_err_t -grub_affs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_affs_data *data = 0; - struct grub_fshelp_node *fdiro = 0; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_affs_mount (device->disk); - if (!data) - goto fail; - - grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir, - grub_affs_read_symlink, GRUB_FSHELP_DIR); - if (grub_errno) - goto fail; - - grub_affs_iterate_dir (fdiro, iterate); - - fail: - if (data && fdiro != &data->diropen) - grub_free (fdiro); - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - - -static grub_err_t -grub_affs_label (grub_device_t device, char **label) -{ - struct grub_affs_data *data; - struct grub_affs_file file; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_affs_mount (disk); - if (data) - { - /* The rootblock maps quite well on a file header block, it's - something we can use here. */ - grub_disk_read (data->disk, disk->total_sectors >> 1, - data->blocksize * (GRUB_DISK_SECTOR_SIZE - - GRUB_AFFS_FILE_LOCATION), - sizeof (file), &file); - if (grub_errno) - return 0; - - *label = grub_strndup ((char *) (file.name), file.namelen); - } - else - *label = 0; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - - -static struct grub_fs grub_affs_fs = - { - .name = "affs", - .dir = grub_affs_dir, - .open = grub_affs_open, - .read = grub_affs_read, - .close = grub_affs_close, - .label = grub_affs_label, - .next = 0 - }; - -GRUB_MOD_INIT(affs) -{ - grub_fs_register (&grub_affs_fs); - my_mod = mod; -} - -GRUB_MOD_FINI(affs) -{ - grub_fs_unregister (&grub_affs_fs); -} diff --git a/fs/afs.c b/fs/afs.c deleted file mode 100644 index cd61f4db9..000000000 --- a/fs/afs.c +++ /dev/null @@ -1,718 +0,0 @@ -/* afs.c - The native AtheOS file-system. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef MODE_BIGENDIAN -#define GRUB_AFS_FSNAME_SUFFIX "_be" -#else -#define GRUB_AFS_FSNAME_SUFFIX "" -#endif - -#ifdef MODE_BFS -#define GRUB_AFS_FSNAME "befs" GRUB_AFS_FSNAME_SUFFIX -#else -#define GRUB_AFS_FSNAME "afs" GRUB_AFS_FSNAME_SUFFIX -#endif - -#define GRUB_AFS_DIRECT_BLOCK_COUNT 12 -#define GRUB_AFS_BLOCKS_PER_DI_RUN 4 - -#ifdef MODE_BFS -#define GRUB_AFS_SBLOCK_SECTOR 1 -#define GRUB_AFS_SBLOCK_MAGIC1 0x42465331 /* BFS1. */ -#else -#define GRUB_AFS_SBLOCK_SECTOR 2 -#define GRUB_AFS_SBLOCK_MAGIC1 0x41465331 /* AFS1. */ -#endif - -#define GRUB_AFS_SBLOCK_MAGIC2 0xdd121031 -#define GRUB_AFS_SBLOCK_MAGIC3 0x15b6830e - -#define GRUB_AFS_INODE_MAGIC 0x64358428 - -#ifdef MODE_BFS -#define GRUB_AFS_BTREE_MAGIC 0x69f6c2e8 -#else -#define GRUB_AFS_BTREE_MAGIC 0x65768995 -#endif - -#define GRUB_AFS_BNODE_SIZE 1024 - -#define GRUB_AFS_S_IFMT 00170000 -#define GRUB_AFS_S_IFLNK 0120000 - -#define GRUB_AFS_S_IFREG 0100000 -#define GRUB_AFS_S_IFDIR 0040000 -#define GRUB_AFS_S_IFIFO 0010000 - -#define GRUB_AFS_NULL_VAL ((grub_afs_bvalue_t)-1) - -#ifdef MODE_BIGENDIAN -#define grub_afs_to_cpu16(x) grub_be_to_cpu16 (x) -#define grub_afs_to_cpu32(x) grub_be_to_cpu32 (x) -#define grub_afs_to_cpu64(x) grub_be_to_cpu64 (x) -#else -#define grub_afs_to_cpu16(x) grub_le_to_cpu16 (x) -#define grub_afs_to_cpu32(x) grub_le_to_cpu32 (x) -#define grub_afs_to_cpu64(x) grub_le_to_cpu64 (x) -#endif - -#ifdef MODE_BFS -#define B_KEY_INDEX_ALIGN 8 -#else -#define B_KEY_INDEX_ALIGN 4 -#endif - -#define B_KEY_INDEX_OFFSET(node) ((grub_uint16_t *) \ - ((char *) (node) \ - + ALIGN_UP (sizeof (struct grub_afs_bnode) \ - + node->key_size, \ - B_KEY_INDEX_ALIGN))) - -#define B_KEY_VALUE_OFFSET(node) ((grub_afs_bvalue_t *) \ - ((char *) B_KEY_INDEX_OFFSET (node) + \ - node->key_count * 2)) - -typedef grub_uint64_t grub_afs_off_t; -typedef grub_uint64_t grub_afs_bigtime; -typedef grub_uint64_t grub_afs_bvalue_t; - -struct grub_afs_blockrun -{ - grub_uint32_t group; - grub_uint16_t start; - grub_uint16_t len; -} __attribute__ ((packed)); - -struct grub_afs_datastream -{ - struct grub_afs_blockrun direct[GRUB_AFS_DIRECT_BLOCK_COUNT]; - grub_afs_off_t max_direct_range; - struct grub_afs_blockrun indirect; - grub_afs_off_t max_indirect_range; - struct grub_afs_blockrun double_indirect; - grub_afs_off_t max_double_indirect_range; - grub_afs_off_t size; -} __attribute__ ((packed)); - -struct grub_afs_bnode -{ - grub_afs_bvalue_t left; - grub_afs_bvalue_t right; - grub_afs_bvalue_t overflow; -#ifdef MODE_BFS - grub_uint16_t key_count; - grub_uint16_t key_size; -#else - grub_uint32_t key_count; - grub_uint32_t key_size; -#endif - char key_data[0]; -} __attribute__ ((packed)); - -#ifdef MODE_BFS -struct grub_afs_btree -{ - grub_uint32_t magic; - grub_uint32_t unused1; - grub_uint32_t tree_depth; - grub_uint32_t unused2; - grub_afs_bvalue_t root; - grub_uint32_t unused3[4]; -} __attribute__ ((packed)); -#else -struct grub_afs_btree -{ - grub_uint32_t magic; - grub_afs_bvalue_t root; - grub_uint32_t tree_depth; - grub_afs_bvalue_t last_node; - grub_afs_bvalue_t first_free; -} __attribute__ ((packed)); -#endif - -/* Beware that following structure describes AtheFS and if you write code - which uses currently unused fields check it with both AtheFS and BeFS. - */ -struct grub_afs_sblock -{ - char name[32]; - grub_uint32_t magic1; - grub_uint32_t byte_order; - grub_uint32_t block_size; - grub_uint32_t block_shift; - grub_afs_off_t num_blocks; - grub_afs_off_t used_blocks; - grub_uint32_t inode_size; - grub_uint32_t magic2; - grub_uint32_t block_per_group; /* Number of blocks per allocation - group. (Max 65536) */ - grub_uint32_t alloc_group_shift; /* Number of bits to shift a group - number to get a byte address. */ - grub_uint32_t alloc_group_count; - grub_uint32_t flags; - struct grub_afs_blockrun log_block; - grub_afs_off_t log_start; - grub_uint32_t valid_log_blocks; - grub_uint32_t log_size; - grub_uint32_t magic3; - struct grub_afs_blockrun root_dir; /* Root dir inode. */ - struct grub_afs_blockrun deleted_files; /* Directory containing files - scheduled for deletion. */ - struct grub_afs_blockrun index_dir; /* Directory of index files. */ - grub_uint32_t boot_loader_size; - grub_uint32_t pad[7]; -} __attribute__ ((packed)); - -struct grub_afs_inode -{ - grub_uint32_t magic1; - struct grub_afs_blockrun inode_num; - grub_uint32_t uid; - grub_uint32_t gid; - grub_uint32_t mode; - grub_uint32_t flags; -#ifndef MODE_BFS - grub_uint32_t link_count; -#endif - grub_afs_bigtime create_time; - grub_afs_bigtime modified_time; - struct grub_afs_blockrun parent; - struct grub_afs_blockrun attrib_dir; - grub_uint32_t index_type; /* Key data-key only used for index files. */ - grub_uint32_t inode_size; - grub_uint32_t unused; - struct grub_afs_datastream stream; - grub_uint32_t pad[4]; - grub_uint32_t small_data[1]; -} __attribute__ ((packed)); - -struct grub_fshelp_node -{ - struct grub_afs_data *data; - struct grub_afs_inode inode; -}; - -struct grub_afs_data -{ - grub_disk_t disk; - struct grub_afs_sblock sblock; - struct grub_afs_inode *inode; - struct grub_fshelp_node diropen; -}; - -static grub_dl_t my_mod; - -static grub_afs_off_t -grub_afs_run_to_num (struct grub_afs_sblock *sb, - struct grub_afs_blockrun *run) -{ - return ((grub_afs_off_t) grub_afs_to_cpu32 (run->group) - * sb->block_per_group + grub_afs_to_cpu16 (run->start)); -} - -static grub_err_t -grub_afs_read_inode (struct grub_afs_data *data, - grub_uint32_t ino, struct grub_afs_inode *inode) -{ - return grub_disk_read (data->disk, - ino * - (data->sblock.block_size >> GRUB_DISK_SECTOR_BITS), - 0, sizeof (struct grub_afs_inode), - inode); -} - -static grub_disk_addr_t -grub_afs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) -{ - struct grub_afs_sblock *sb = &node->data->sblock; - struct grub_afs_datastream *ds = &node->inode.stream; - - if (fileblock < grub_afs_to_cpu64 (ds->max_direct_range)) - { - int i; - - for (i = 0; i < GRUB_AFS_DIRECT_BLOCK_COUNT; i++) - { - if (fileblock < grub_afs_to_cpu16 (ds->direct[i].len)) - return grub_afs_run_to_num (sb, &ds->direct[i]) + fileblock; - fileblock -= grub_afs_to_cpu16 (ds->direct[i].len); - } - } - else if (fileblock < grub_afs_to_cpu64 (ds->max_indirect_range)) - { - int ptrs_per_blk = sb->block_size / sizeof (struct grub_afs_blockrun); - struct grub_afs_blockrun indir[ptrs_per_blk]; - grub_afs_off_t blk = grub_afs_run_to_num (sb, &ds->indirect); - int i; - - fileblock -= grub_afs_to_cpu64 (ds->max_direct_range); - for (i = 0; i < ds->indirect.len; i++, blk++) - { - int j; - - if (grub_disk_read (node->data->disk, - blk * (sb->block_size >> GRUB_DISK_SECTOR_BITS), - 0, sizeof (indir), - indir)) - return 0; - - for (j = 0; j < ptrs_per_blk; j++) - { - if (fileblock < grub_afs_to_cpu16 (indir[j].len)) - return grub_afs_run_to_num (sb, &indir[j]) + fileblock; - - fileblock -= grub_afs_to_cpu16 (indir[j].len); - } - } - } - else - { - int ptrs_per_blk = sb->block_size / sizeof (struct grub_afs_blockrun); - struct grub_afs_blockrun indir[ptrs_per_blk]; - - /* ([idblk][idptr]) ([dblk][dptr]) [blk] */ - int cur_pos = fileblock - grub_afs_to_cpu64 (ds->max_indirect_range); - - int dptr_size = GRUB_AFS_BLOCKS_PER_DI_RUN; - int dblk_size = dptr_size * ptrs_per_blk; - int idptr_size = dblk_size * GRUB_AFS_BLOCKS_PER_DI_RUN; - int idblk_size = idptr_size * ptrs_per_blk; - - int off = cur_pos % GRUB_AFS_BLOCKS_PER_DI_RUN; - int dptr = (cur_pos / dptr_size) % ptrs_per_blk; - int dblk = (cur_pos / dblk_size) % GRUB_AFS_BLOCKS_PER_DI_RUN; - int idptr = (cur_pos / idptr_size) % ptrs_per_blk; - int idblk = (cur_pos / idblk_size); - - if (grub_disk_read (node->data->disk, - (grub_afs_run_to_num (sb, &ds->double_indirect) - + idblk) * - (sb->block_size >> GRUB_DISK_SECTOR_BITS), - 0, sizeof (indir), - indir)) - return 0; - - if (grub_disk_read (node->data->disk, - (grub_afs_run_to_num (sb, &indir[idptr]) + dblk) * - (sb->block_size >> GRUB_DISK_SECTOR_BITS), - 0, sizeof (indir), - indir)) - return 0; - - return grub_afs_run_to_num (sb, &indir[dptr]) + off; - } - - return 0; -} - -static grub_ssize_t -grub_afs_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) -{ - return grub_fshelp_read_file (node->data->disk, node, read_hook, - pos, len, buf, grub_afs_read_block, - grub_afs_to_cpu64 (node->inode.stream.size), - node->data->sblock.block_shift - - GRUB_DISK_SECTOR_BITS); -} - -static char * -grub_afs_read_symlink (grub_fshelp_node_t node) -{ - char *ret; - grub_afs_off_t size = grub_afs_to_cpu64 (node->inode.stream.size); - - if (size == 0) - { - size = sizeof (node->inode.stream); - ret = grub_zalloc (size + 1); - if (! ret) - return 0; - grub_memcpy (ret, (char *) &(node->inode.stream), - sizeof (node->inode.stream)); - return ret; - } - ret = grub_zalloc (size + 1); - if (! ret) - return 0; - grub_afs_read_file (node, 0, 0, size, ret); - return ret; -} - -static int -grub_afs_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - struct grub_afs_btree head; - char node_data [GRUB_AFS_BNODE_SIZE]; - struct grub_afs_bnode *node = (struct grub_afs_bnode *) node_data; - int i; - - if ((dir->inode.stream.size == 0) - || ((grub_afs_to_cpu32 (dir->inode.mode) & GRUB_AFS_S_IFMT) - != GRUB_AFS_S_IFDIR)) - return 0; - - grub_afs_read_file (dir, 0, 0, sizeof (head), (char *) &head); - if (grub_errno) - return 0; - - grub_afs_read_file (dir, 0, grub_afs_to_cpu64 (head.root), - GRUB_AFS_BNODE_SIZE, (char *) node); - if (grub_errno) - return 0; - - for (i = 0; i < (int) grub_afs_to_cpu32 (head.tree_depth) - 1; i++) - { - grub_afs_bvalue_t blk; - - blk = grub_afs_to_cpu64(B_KEY_VALUE_OFFSET (node) [0]); - grub_afs_read_file (dir, 0, blk, GRUB_AFS_BNODE_SIZE, (char *) node); - if (grub_errno) - return 0; - } - - if (node->key_count) - { - grub_uint32_t cur_key = 0; - - while (1) - { - int key_start, key_size; - grub_uint16_t *index; - - index = B_KEY_INDEX_OFFSET (node); - - key_start = (cur_key > 0) - ? grub_afs_to_cpu16 (index[cur_key - 1]) : 0; - key_size = grub_afs_to_cpu16 (index[cur_key]) - key_start; - if (key_size > 0) - { - char filename [key_size + 1]; - struct grub_fshelp_node *fdiro; - int mode, type; - - fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); - if (! fdiro) - return 0; - - fdiro->data = dir->data; - if (grub_afs_read_inode (dir->data, - grub_afs_to_cpu64 - (B_KEY_VALUE_OFFSET (node) [cur_key]), - &fdiro->inode)) - return 0; - - grub_memcpy (filename, &node->key_data[key_start], key_size); - filename [key_size] = 0; - - mode = (grub_afs_to_cpu32 (fdiro->inode.mode) & GRUB_AFS_S_IFMT); - if (mode == GRUB_AFS_S_IFDIR) - type = GRUB_FSHELP_DIR; - else if (mode == GRUB_AFS_S_IFREG) - type = GRUB_FSHELP_REG; - else if (mode == GRUB_AFS_S_IFLNK) - type = GRUB_FSHELP_SYMLINK; - else - type = GRUB_FSHELP_UNKNOWN; - - if (hook (filename, type, fdiro)) - return 1; - } - - cur_key++; - if (cur_key >= grub_afs_to_cpu32 (node->key_count)) - { - if (node->right == GRUB_AFS_NULL_VAL) - break; - - grub_afs_read_file (dir, 0, grub_afs_to_cpu64 (node->right), - GRUB_AFS_BNODE_SIZE, (char *) node); - if (grub_errno) - return 0; - - cur_key = 0; - } - } - } - - return 0; -} - -static int -grub_afs_validate_sblock (struct grub_afs_sblock *sb) -{ - if (grub_afs_to_cpu32 (sb->magic1) == GRUB_AFS_SBLOCK_MAGIC1) - { - sb->magic2 = grub_afs_to_cpu32 (sb->magic2); - sb->magic3 = grub_afs_to_cpu32 (sb->magic3); - sb->block_shift = grub_afs_to_cpu32 (sb->block_shift); - sb->block_size = grub_afs_to_cpu32 (sb->block_size); - sb->used_blocks = grub_afs_to_cpu64 (sb->used_blocks); - sb->num_blocks = grub_afs_to_cpu64 (sb->num_blocks); - sb->inode_size = grub_afs_to_cpu32 (sb->inode_size); - sb->alloc_group_count = grub_afs_to_cpu32 (sb->alloc_group_count); - sb->alloc_group_shift = grub_afs_to_cpu32 (sb->alloc_group_shift); - sb->block_per_group = grub_afs_to_cpu32 (sb->block_per_group); - sb->alloc_group_count = grub_afs_to_cpu32 (sb->alloc_group_count); - sb->log_size = grub_afs_to_cpu32 (sb->log_size); - } - else - return 0; - - if ((sb->magic2 != GRUB_AFS_SBLOCK_MAGIC2) || - (sb->magic3 != GRUB_AFS_SBLOCK_MAGIC3)) - return 0; - -#ifdef MODE_BFS - sb->block_per_group = 1 << (sb->alloc_group_shift); -#endif - - if (((grub_uint32_t) (1 << sb->block_shift) != sb->block_size) - || (sb->used_blocks > sb->num_blocks ) - || (sb->inode_size != sb->block_size) - || (0 == sb->block_size) -#ifndef MODE_BFS - || ((grub_uint32_t) (1 << sb->alloc_group_shift) != - sb->block_per_group * sb->block_size) - || (sb->alloc_group_count * sb->block_per_group < sb->num_blocks) - || (grub_afs_to_cpu16 (sb->log_block.len) != sb->log_size) - || (grub_afs_to_cpu32 (sb->valid_log_blocks) > sb->log_size) -#endif - ) - return 0; - - return 1; -} - -static struct grub_afs_data * -grub_afs_mount (grub_disk_t disk) -{ - struct grub_afs_data *data = 0; - - data = grub_malloc (sizeof (struct grub_afs_data)); - if (!data) - return 0; - - /* Read the superblock. */ - if (grub_disk_read (disk, GRUB_AFS_SBLOCK_SECTOR, 0, - sizeof (struct grub_afs_sblock), &data->sblock)) - goto fail; - - if (! grub_afs_validate_sblock (&data->sblock)) - goto fail; - - data->diropen.data = data; - data->inode = &data->diropen.inode; - data->disk = disk; - - if (grub_afs_read_inode (data, - grub_afs_run_to_num (&data->sblock, - &data->sblock.root_dir), - data->inode)) - goto fail; - - return data; - -fail: - grub_error (GRUB_ERR_BAD_FS, "not an " GRUB_AFS_FSNAME " filesystem"); - - grub_free (data); - return 0; -} - -static grub_err_t -grub_afs_open (struct grub_file *file, const char *name) -{ - struct grub_afs_data *data; - struct grub_fshelp_node *fdiro = 0; - - grub_dl_ref (my_mod); - - data = grub_afs_mount (file->device->disk); - if (! data) - goto fail; - - grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_afs_iterate_dir, - grub_afs_read_symlink, GRUB_FSHELP_REG); - if (grub_errno) - goto fail; - - grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_afs_inode)); - grub_free (fdiro); - - file->size = grub_afs_to_cpu64 (data->inode->stream.size); - file->data = data; - file->offset = 0; - - return 0; - -fail: - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_ssize_t -grub_afs_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_afs_data *data = (struct grub_afs_data *) file->data; - - return grub_afs_read_file (&data->diropen, file->read_hook, - file->offset, len, buf); -} - -static grub_err_t -grub_afs_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_afs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_afs_data *data = 0; - struct grub_fshelp_node *fdiro = 0; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - info.mtimeset = 1; -#ifdef MODE_BFS - info.mtime = grub_afs_to_cpu64 (node->inode.modified_time) >> 16; -#else - info.mtime = grub_divmod64 (grub_afs_to_cpu64 (node->inode.modified_time), - 1000000, 0); -#endif - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_afs_mount (device->disk); - if (! data) - goto fail; - - grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_afs_iterate_dir, - grub_afs_read_symlink, GRUB_FSHELP_DIR); - if (grub_errno) - goto fail; - - grub_afs_iterate_dir (fdiro, iterate); - - if (fdiro != &data->diropen) - grub_free (fdiro); - - fail: - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_afs_label (grub_device_t device, char **label) -{ - struct grub_afs_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_afs_mount (disk); - if (data) - *label = grub_strndup (data->sblock.name, sizeof (data->sblock.name)); - else - *label = NULL; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - - -static struct grub_fs grub_afs_fs = { - .name = GRUB_AFS_FSNAME, - .dir = grub_afs_dir, - .open = grub_afs_open, - .read = grub_afs_read, - .close = grub_afs_close, - .label = grub_afs_label, - .next = 0 -}; - -#if defined (MODE_BIGENDIAN) && defined (MODE_BFS) -GRUB_MOD_INIT (befs_be) -#elif defined (MODE_BFS) -GRUB_MOD_INIT (befs) -#elif defined (MODE_BIGENDIAN) -GRUB_MOD_INIT (afs_be) -#else -GRUB_MOD_INIT (afs) -#endif -{ - grub_fs_register (&grub_afs_fs); - my_mod = mod; -} - -#if defined (MODE_BIGENDIAN) && defined (MODE_BFS) -GRUB_MOD_FINI (befs_be) -#elif defined (MODE_BFS) -GRUB_MOD_FINI (befs) -#elif defined (MODE_BIGENDIAN) -GRUB_MOD_FINI (afs_be) -#else -GRUB_MOD_FINI (afs) -#endif -{ - grub_fs_unregister (&grub_afs_fs); -} diff --git a/fs/befs.c b/fs/befs.c deleted file mode 100644 index c54d8e1cc..000000000 --- a/fs/befs.c +++ /dev/null @@ -1,3 +0,0 @@ -/* befs.c - The native BeOS/Haiku file-system. */ -#define MODE_BFS 1 -#include "afs.c" diff --git a/fs/befs_be.c b/fs/befs_be.c deleted file mode 100644 index f6e8179f4..000000000 --- a/fs/befs_be.c +++ /dev/null @@ -1,4 +0,0 @@ -/* befs.c - The native BeOS/Haiku file-system. */ -#define MODE_BFS 1 -#define MODE_BIGENDIAN 1 -#include "afs.c" diff --git a/fs/cpio.c b/fs/cpio.c deleted file mode 100644 index c087b4f90..000000000 --- a/fs/cpio.c +++ /dev/null @@ -1,376 +0,0 @@ -/* cpio.c - cpio and tar filesystem. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#ifndef MODE_USTAR -/* cpio support */ -#define MAGIC_BCPIO 070707 -struct head -{ - grub_uint16_t magic; - grub_uint16_t dev; - grub_uint16_t ino; - grub_uint16_t mode; - grub_uint16_t uid; - grub_uint16_t gid; - grub_uint16_t nlink; - grub_uint16_t rdev; - grub_uint16_t mtime_1; - grub_uint16_t mtime_2; - grub_uint16_t namesize; - grub_uint16_t filesize_1; - grub_uint16_t filesize_2; -} __attribute__ ((packed)); -#else -/* tar support */ -#define MAGIC_USTAR "ustar" -struct head -{ - char name[100]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char chksum[8]; - char typeflag; - char linkname[100]; - char magic[6]; - char version[2]; - char uname[32]; - char gname[32]; - char devmajor[8]; - char devminor[8]; - char prefix[155]; -} __attribute__ ((packed)); -#endif - -struct grub_cpio_data -{ - grub_disk_t disk; - grub_uint32_t hofs; - grub_uint32_t dofs; - grub_uint32_t size; -}; - -static grub_dl_t my_mod; - -static grub_err_t -grub_cpio_find_file (struct grub_cpio_data *data, char **name, - grub_uint32_t * ofs) -{ -#ifndef MODE_USTAR - struct head hd; - - if (grub_disk_read - (data->disk, 0, data->hofs, sizeof (hd), &hd)) - return grub_errno; - - if (hd.magic != MAGIC_BCPIO) - return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive"); - - data->size = (((grub_uint32_t) hd.filesize_1) << 16) + hd.filesize_2; - - if (hd.namesize & 1) - hd.namesize++; - - if ((*name = grub_malloc (hd.namesize)) == NULL) - return grub_errno; - - if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd), - hd.namesize, *name)) - { - grub_free (*name); - return grub_errno; - } - - if (data->size == 0 && hd.mode == 0 && hd.namesize == 11 + 1 - && ! grub_memcmp(*name, "TRAILER!!!", 11)) - { - *ofs = 0; - return GRUB_ERR_NONE; - } - - data->dofs = data->hofs + sizeof (hd) + hd.namesize; - *ofs = data->dofs + data->size; - if (data->size & 1) - (*ofs)++; -#else - struct head hd; - - if (grub_disk_read - (data->disk, 0, data->hofs, sizeof (hd), &hd)) - return grub_errno; - - if (!hd.name[0]) - { - *ofs = 0; - return GRUB_ERR_NONE; - } - - if (grub_memcmp (hd.magic, MAGIC_USTAR, sizeof (MAGIC_USTAR) - 1)) - return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); - - if ((*name = grub_strdup (hd.name)) == NULL) - return grub_errno; - - data->size = grub_strtoul (hd.size, NULL, 8); - data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; - *ofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & - ~(GRUB_DISK_SECTOR_SIZE - 1)); -#endif - return GRUB_ERR_NONE; -} - -static struct grub_cpio_data * -grub_cpio_mount (grub_disk_t disk) -{ - struct head hd; - struct grub_cpio_data *data; - - if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd)) - goto fail; - -#ifndef MODE_USTAR - if (hd.magic != MAGIC_BCPIO) -#else - if (grub_memcmp (hd.magic, MAGIC_USTAR, - sizeof (MAGIC_USTAR) - 1)) -#endif - goto fail; - - data = (struct grub_cpio_data *) grub_malloc (sizeof (*data)); - if (!data) - goto fail; - - data->disk = disk; - - return data; - -fail: - grub_error (GRUB_ERR_BAD_FS, "not a " -#ifdef MODE_USTAR - "tar" -#else - "cpio" -#endif - " filesystem"); - return 0; -} - -static grub_err_t -grub_cpio_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_cpio_data *data; - grub_uint32_t ofs; - char *prev, *name; - const char *np; - int len; - - grub_dl_ref (my_mod); - - prev = 0; - - data = grub_cpio_mount (device->disk); - if (!data) - goto fail; - - np = path + 1; - len = grub_strlen (path) - 1; - - data->hofs = 0; - while (1) - { - if (grub_cpio_find_file (data, &name, &ofs)) - goto fail; - - if (!ofs) - break; - - if (grub_memcmp (np, name, len) == 0) - { - char *p, *n; - - n = name + len; - if (*n == '/') - n++; - - p = grub_strchr (name + len, '/'); - if (p) - *p = 0; - - if ((!prev) || (grub_strcmp (prev, name) != 0)) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = (p != NULL); - - hook (name + len, &info); - if (prev) - grub_free (prev); - prev = name; - } - else - grub_free (name); - } - data->hofs = ofs; - } - -fail: - - if (prev) - grub_free (prev); - - if (data) - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_cpio_open (grub_file_t file, const char *name) -{ - struct grub_cpio_data *data; - grub_uint32_t ofs; - char *fn; - int i, j; - - grub_dl_ref (my_mod); - - data = grub_cpio_mount (file->device->disk); - if (!data) - goto fail; - - data->hofs = 0; - while (1) - { - if (grub_cpio_find_file (data, &fn, &ofs)) - goto fail; - - if (!ofs) - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - break; - } - - /* Compare NAME and FN by hand in order to cope with duplicate - slashes. */ - i = 0; - j = 0; - while (name[i] == '/') - i++; - while (1) - { - if (name[i] != fn[j]) - goto no_match; - - if (name[i] == '\0') - break; - - while (name[i] == '/' && name[i+1] == '/') - i++; - - i++; - j++; - } - - if (name[i] != fn[j]) - goto no_match; - - file->data = data; - file->size = data->size; - grub_free (fn); - - return GRUB_ERR_NONE; - - no_match: - - grub_free (fn); - data->hofs = ofs; - } - -fail: - - if (data) - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_ssize_t -grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_cpio_data *data; - - data = file->data; - return (grub_disk_read (data->disk, 0, data->dofs + file->offset, - len, buf)) ? -1 : (grub_ssize_t) len; -} - -static grub_err_t -grub_cpio_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static struct grub_fs grub_cpio_fs = { -#ifdef MODE_USTAR - .name = "tarfs", -#else - .name = "cpiofs", -#endif - .dir = grub_cpio_dir, - .open = grub_cpio_open, - .read = grub_cpio_read, - .close = grub_cpio_close, -}; - -#ifdef MODE_USTAR -GRUB_MOD_INIT (tar) -#else -GRUB_MOD_INIT (cpio) -#endif -{ - grub_fs_register (&grub_cpio_fs); - my_mod = mod; -} - -#ifdef MODE_USTAR -GRUB_MOD_FINI (tar) -#else -GRUB_MOD_FINI (cpio) -#endif -{ - grub_fs_unregister (&grub_cpio_fs); -} diff --git a/fs/fat.c b/fs/fat.c deleted file mode 100644 index 89050943c..000000000 --- a/fs/fat.c +++ /dev/null @@ -1,876 +0,0 @@ -/* fat.c - FAT filesystem */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2000,2001,2002,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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GRUB_FAT_DIR_ENTRY_SIZE 32 - -#define GRUB_FAT_ATTR_READ_ONLY 0x01 -#define GRUB_FAT_ATTR_HIDDEN 0x02 -#define GRUB_FAT_ATTR_SYSTEM 0x04 -#define GRUB_FAT_ATTR_VOLUME_ID 0x08 -#define GRUB_FAT_ATTR_DIRECTORY 0x10 -#define GRUB_FAT_ATTR_ARCHIVE 0x20 - -#define GRUB_FAT_MAXFILE 256 - -#define GRUB_FAT_ATTR_LONG_NAME (GRUB_FAT_ATTR_READ_ONLY \ - | GRUB_FAT_ATTR_HIDDEN \ - | GRUB_FAT_ATTR_SYSTEM \ - | GRUB_FAT_ATTR_VOLUME_ID) -#define GRUB_FAT_ATTR_VALID (GRUB_FAT_ATTR_READ_ONLY \ - | GRUB_FAT_ATTR_HIDDEN \ - | GRUB_FAT_ATTR_SYSTEM \ - | GRUB_FAT_ATTR_DIRECTORY \ - | GRUB_FAT_ATTR_ARCHIVE \ - | GRUB_FAT_ATTR_VOLUME_ID) - -struct grub_fat_bpb -{ - grub_uint8_t jmp_boot[3]; - grub_uint8_t oem_name[8]; - grub_uint16_t bytes_per_sector; - grub_uint8_t sectors_per_cluster; - grub_uint16_t num_reserved_sectors; - grub_uint8_t num_fats; - grub_uint16_t num_root_entries; - grub_uint16_t num_total_sectors_16; - grub_uint8_t media; - grub_uint16_t sectors_per_fat_16; - grub_uint16_t sectors_per_track; - grub_uint16_t num_heads; - grub_uint32_t num_hidden_sectors; - grub_uint32_t num_total_sectors_32; - union - { - struct - { - grub_uint8_t num_ph_drive; - grub_uint8_t reserved; - grub_uint8_t boot_sig; - grub_uint32_t num_serial; - grub_uint8_t label[11]; - grub_uint8_t fstype[8]; - } __attribute__ ((packed)) fat12_or_fat16; - struct - { - grub_uint32_t sectors_per_fat_32; - grub_uint16_t extended_flags; - grub_uint16_t fs_version; - grub_uint32_t root_cluster; - grub_uint16_t fs_info; - grub_uint16_t backup_boot_sector; - grub_uint8_t reserved[12]; - grub_uint8_t num_ph_drive; - grub_uint8_t reserved1; - grub_uint8_t boot_sig; - grub_uint32_t num_serial; - grub_uint8_t label[11]; - grub_uint8_t fstype[8]; - } __attribute__ ((packed)) fat32; - } __attribute__ ((packed)) version_specific; -} __attribute__ ((packed)); - -struct grub_fat_dir_entry -{ - grub_uint8_t name[11]; - grub_uint8_t attr; - grub_uint8_t nt_reserved; - grub_uint8_t c_time_tenth; - grub_uint16_t c_time; - grub_uint16_t c_date; - grub_uint16_t a_date; - grub_uint16_t first_cluster_high; - grub_uint16_t w_time; - grub_uint16_t w_date; - grub_uint16_t first_cluster_low; - grub_uint32_t file_size; -} __attribute__ ((packed)); - -struct grub_fat_long_name_entry -{ - grub_uint8_t id; - grub_uint16_t name1[5]; - grub_uint8_t attr; - grub_uint8_t reserved; - grub_uint8_t checksum; - grub_uint16_t name2[6]; - grub_uint16_t first_cluster; - grub_uint16_t name3[2]; -} __attribute__ ((packed)); - -struct grub_fat_data -{ - int logical_sector_bits; - grub_uint32_t num_sectors; - - grub_uint16_t fat_sector; - grub_uint32_t sectors_per_fat; - int fat_size; - - grub_uint32_t root_cluster; - grub_uint32_t root_sector; - grub_uint32_t num_root_sectors; - - int cluster_bits; - grub_uint32_t cluster_eof_mark; - grub_uint32_t cluster_sector; - grub_uint32_t num_clusters; - - grub_uint8_t attr; - grub_ssize_t file_size; - grub_uint32_t file_cluster; - grub_uint32_t cur_cluster_num; - grub_uint32_t cur_cluster; - - grub_uint32_t uuid; -}; - -static grub_dl_t my_mod; - -static int -fat_log2 (unsigned x) -{ - int i; - - if (x == 0) - return -1; - - for (i = 0; (x & 1) == 0; i++) - x >>= 1; - - if (x != 1) - return -1; - - return i; -} - -static struct grub_fat_data * -grub_fat_mount (grub_disk_t disk) -{ - struct grub_fat_bpb bpb; - struct grub_fat_data *data = 0; - grub_uint32_t first_fat, magic; - - if (! disk) - goto fail; - - data = (struct grub_fat_data *) grub_malloc (sizeof (*data)); - if (! data) - goto fail; - - /* Read the BPB. */ - if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb)) - goto fail; - - if (grub_strncmp((const char *) bpb.version_specific.fat12_or_fat16.fstype, "FAT12", 5) - && grub_strncmp((const char *) bpb.version_specific.fat12_or_fat16.fstype, "FAT16", 5) - && grub_strncmp((const char *) bpb.version_specific.fat32.fstype, "FAT32", 5)) - goto fail; - - /* Get the sizes of logical sectors and clusters. */ - data->logical_sector_bits = - fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector)); - if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS) - goto fail; - data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS; - - data->cluster_bits = fat_log2 (bpb.sectors_per_cluster); - if (data->cluster_bits < 0) - goto fail; - data->cluster_bits += data->logical_sector_bits; - - /* Get information about FATs. */ - data->fat_sector = (grub_le_to_cpu16 (bpb.num_reserved_sectors) - << data->logical_sector_bits); - if (data->fat_sector == 0) - goto fail; - - data->sectors_per_fat = ((bpb.sectors_per_fat_16 - ? grub_le_to_cpu16 (bpb.sectors_per_fat_16) - : grub_le_to_cpu32 (bpb.version_specific.fat32.sectors_per_fat_32)) - << data->logical_sector_bits); - if (data->sectors_per_fat == 0) - goto fail; - - /* Get the number of sectors in this volume. */ - data->num_sectors = ((bpb.num_total_sectors_16 - ? grub_le_to_cpu16 (bpb.num_total_sectors_16) - : grub_le_to_cpu32 (bpb.num_total_sectors_32)) - << data->logical_sector_bits); - if (data->num_sectors == 0) - goto fail; - - /* Get information about the root directory. */ - if (bpb.num_fats == 0) - goto fail; - - data->root_sector = data->fat_sector + bpb.num_fats * data->sectors_per_fat; - data->num_root_sectors - = ((((grub_uint32_t) grub_le_to_cpu16 (bpb.num_root_entries) - * GRUB_FAT_DIR_ENTRY_SIZE - + grub_le_to_cpu16 (bpb.bytes_per_sector) - 1) - >> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS)) - << (data->logical_sector_bits)); - - data->cluster_sector = data->root_sector + data->num_root_sectors; - data->num_clusters = (((data->num_sectors - data->cluster_sector) - >> (data->cluster_bits + data->logical_sector_bits)) - + 2); - - if (data->num_clusters <= 2) - goto fail; - - if (! bpb.sectors_per_fat_16) - { - /* FAT32. */ - grub_uint16_t flags = grub_le_to_cpu16 (bpb.version_specific.fat32.extended_flags); - - data->root_cluster = grub_le_to_cpu32 (bpb.version_specific.fat32.root_cluster); - data->fat_size = 32; - data->cluster_eof_mark = 0x0ffffff8; - - if (flags & 0x80) - { - /* Get an active FAT. */ - unsigned active_fat = flags & 0xf; - - if (active_fat > bpb.num_fats) - goto fail; - - data->fat_sector += active_fat * data->sectors_per_fat; - } - - if (bpb.num_root_entries != 0 || bpb.version_specific.fat32.fs_version != 0) - goto fail; - } - else - { - /* FAT12 or FAT16. */ - data->root_cluster = ~0U; - - if (data->num_clusters <= 4085 + 2) - { - /* FAT12. */ - data->fat_size = 12; - data->cluster_eof_mark = 0x0ff8; - } - else - { - /* FAT16. */ - data->fat_size = 16; - data->cluster_eof_mark = 0xfff8; - } - } - - /* More sanity checks. */ - if (data->num_sectors <= data->fat_sector) - goto fail; - - if (grub_disk_read (disk, - data->fat_sector, - 0, - sizeof (first_fat), - &first_fat)) - goto fail; - - first_fat = grub_le_to_cpu32 (first_fat); - - if (data->fat_size == 32) - { - first_fat &= 0x0fffffff; - magic = 0x0fffff00; - } - else if (data->fat_size == 16) - { - first_fat &= 0x0000ffff; - magic = 0xff00; - } - else - { - first_fat &= 0x00000fff; - magic = 0x0f00; - } - - /* Serial number. */ - if (bpb.sectors_per_fat_16) - data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat12_or_fat16.num_serial); - else - data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat32.num_serial); - - /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media - descriptor, even if it is a so-called superfloppy (e.g. an USB key). - The check may be too strict for this kind of stupid BIOSes, as - they overwrite the media descriptor. */ - if ((first_fat | 0x8) != (magic | bpb.media | 0x8)) - goto fail; - - /* Start from the root directory. */ - data->file_cluster = data->root_cluster; - data->cur_cluster_num = ~0U; - data->attr = GRUB_FAT_ATTR_DIRECTORY; - return data; - - fail: - - grub_free (data); - grub_error (GRUB_ERR_BAD_FS, "not a FAT filesystem"); - return 0; -} - -static grub_ssize_t -grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - grub_off_t offset, grub_size_t len, char *buf) -{ - grub_size_t size; - grub_uint32_t logical_cluster; - unsigned logical_cluster_bits; - grub_ssize_t ret = 0; - unsigned long sector; - - /* This is a special case. FAT12 and FAT16 doesn't have the root directory - in clusters. */ - if (data->file_cluster == ~0U) - { - size = (data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset; - if (size > len) - size = len; - - if (grub_disk_read (disk, data->root_sector, offset, size, buf)) - return -1; - - return size; - } - - /* Calculate the logical cluster number and offset. */ - logical_cluster_bits = (data->cluster_bits - + data->logical_sector_bits - + GRUB_DISK_SECTOR_BITS); - logical_cluster = offset >> logical_cluster_bits; - offset &= (1 << logical_cluster_bits) - 1; - - if (logical_cluster < data->cur_cluster_num) - { - data->cur_cluster_num = 0; - data->cur_cluster = data->file_cluster; - } - - while (len) - { - while (logical_cluster > data->cur_cluster_num) - { - /* Find next cluster. */ - grub_uint32_t next_cluster; - unsigned long fat_offset; - - switch (data->fat_size) - { - case 32: - fat_offset = data->cur_cluster << 2; - break; - case 16: - fat_offset = data->cur_cluster << 1; - break; - default: - /* case 12: */ - fat_offset = data->cur_cluster + (data->cur_cluster >> 1); - break; - } - - /* Read the FAT. */ - if (grub_disk_read (disk, data->fat_sector, fat_offset, - (data->fat_size + 7) >> 3, - (char *) &next_cluster)) - return -1; - - next_cluster = grub_le_to_cpu32 (next_cluster); - switch (data->fat_size) - { - case 16: - next_cluster &= 0xFFFF; - break; - case 12: - if (data->cur_cluster & 1) - next_cluster >>= 4; - - next_cluster &= 0x0FFF; - break; - } - - grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n", - data->fat_size, next_cluster); - - /* Check the end. */ - if (next_cluster >= data->cluster_eof_mark) - return ret; - - if (next_cluster < 2 || next_cluster >= data->num_clusters) - { - grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u", - next_cluster); - return -1; - } - - data->cur_cluster = next_cluster; - data->cur_cluster_num++; - } - - /* Read the data here. */ - sector = (data->cluster_sector - + ((data->cur_cluster - 2) - << (data->cluster_bits + data->logical_sector_bits))); - size = (1 << logical_cluster_bits) - offset; - if (size > len) - size = len; - - disk->read_hook = read_hook; - grub_disk_read (disk, sector, offset, size, buf); - disk->read_hook = 0; - if (grub_errno) - return -1; - - len -= size; - buf += size; - ret += size; - logical_cluster++; - offset = 0; - } - - return ret; -} - -static grub_err_t -grub_fat_iterate_dir (grub_disk_t disk, struct grub_fat_data *data, - int (*hook) (const char *filename, - struct grub_fat_dir_entry *dir)) -{ - struct grub_fat_dir_entry dir; - char *filename, *filep = 0; - grub_uint16_t *unibuf; - int slot = -1, slots = -1; - int checksum = -1; - grub_ssize_t offset = -sizeof(dir); - - if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY)) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - - /* Allocate space enough to hold a long name. */ - filename = grub_malloc (0x40 * 13 * 4 + 1); - unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2); - if (! filename || ! unibuf) - { - grub_free (filename); - grub_free (unibuf); - return 0; - } - - while (1) - { - unsigned i; - - /* Adjust the offset. */ - offset += sizeof (dir); - - /* Read a directory entry. */ - if ((grub_fat_read_data (disk, data, 0, - offset, sizeof (dir), (char *) &dir) - != sizeof (dir) || dir.name[0] == 0)) - break; - /* Handle long name entries. */ - if (dir.attr == GRUB_FAT_ATTR_LONG_NAME) - { - struct grub_fat_long_name_entry *long_name - = (struct grub_fat_long_name_entry *) &dir; - grub_uint8_t id = long_name->id; - - if (id & 0x40) - { - id &= 0x3f; - slots = slot = id; - checksum = long_name->checksum; - } - - if (id != slot || slot == 0 || checksum != long_name->checksum) - { - checksum = -1; - continue; - } - - slot--; - grub_memcpy (unibuf + slot * 13, long_name->name1, 5 * 2); - grub_memcpy (unibuf + slot * 13 + 5, long_name->name2, 6 * 2); - grub_memcpy (unibuf + slot * 13 + 11, long_name->name3, 2 * 2); - continue; - } - - /* Check if this entry is valid. */ - if (dir.name[0] == 0xe5 || (dir.attr & ~GRUB_FAT_ATTR_VALID)) - continue; - - /* This is a workaround for Japanese. */ - if (dir.name[0] == 0x05) - dir.name[0] = 0xe5; - - if (checksum != -1 && slot == 0) - { - grub_uint8_t sum; - - for (sum = 0, i = 0; i < sizeof (dir.name); i++) - sum = ((sum >> 1) | (sum << 7)) + dir.name[i]; - - if (sum == checksum) - { - int u; - - for (u = 0; u < slots * 13; u++) - unibuf[u] = grub_le_to_cpu16 (unibuf[u]); - - *grub_utf16_to_utf8 ((grub_uint8_t *) filename, unibuf, - slots * 13) = '\0'; - - if (hook (filename, &dir)) - break; - - checksum = -1; - continue; - } - - checksum = -1; - } - - /* Convert the 8.3 file name. */ - filep = filename; - if (dir.attr & GRUB_FAT_ATTR_VOLUME_ID) - { - for (i = 0; i < sizeof (dir.name) && dir.name[i] - && ! grub_isspace (dir.name[i]); i++) - *filep++ = dir.name[i]; - } - else - { - for (i = 0; i < 8 && dir.name[i] && ! grub_isspace (dir.name[i]); i++) - *filep++ = grub_tolower (dir.name[i]); - - *filep = '.'; - - for (i = 8; i < 11 && dir.name[i] && ! grub_isspace (dir.name[i]); i++) - *++filep = grub_tolower (dir.name[i]); - - if (*filep != '.') - filep++; - } - *filep = '\0'; - - if (hook (filename, &dir)) - break; - } - - grub_free (filename); - grub_free (unibuf); - - return grub_errno; -} - - -/* Find the underlying directory or file in PATH and return the - next path. If there is no next path or an error occurs, return NULL. - If HOOK is specified, call it with each file name. */ -static char * -grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data, - const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - char *dirname, *dirp; - int call_hook; - int found = 0; - - auto int iter_hook (const char *filename, struct grub_fat_dir_entry *dir); - int iter_hook (const char *filename, struct grub_fat_dir_entry *dir) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - - info.dir = !! (dir->attr & GRUB_FAT_ATTR_DIRECTORY); - info.case_insensitive = 1; - - if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID) - return 0; - if (*dirname == '\0' && call_hook) - return hook (filename, &info); - - if (grub_strcasecmp (dirname, filename) == 0) - { - found = 1; - data->attr = dir->attr; - data->file_size = grub_le_to_cpu32 (dir->file_size); - data->file_cluster = ((grub_le_to_cpu16 (dir->first_cluster_high) << 16) - | grub_le_to_cpu16 (dir->first_cluster_low)); - data->cur_cluster_num = ~0U; - - if (call_hook) - hook (filename, &info); - - return 1; - } - return 0; - } - - if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY)) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - return 0; - } - - /* Extract a directory name. */ - while (*path == '/') - path++; - - dirp = grub_strchr (path, '/'); - if (dirp) - { - unsigned len = dirp - path; - - dirname = grub_malloc (len + 1); - if (! dirname) - return 0; - - grub_memcpy (dirname, path, len); - dirname[len] = '\0'; - } - else - /* This is actually a file. */ - dirname = grub_strdup (path); - - call_hook = (! dirp && hook); - - grub_fat_iterate_dir (disk, data, iter_hook); - if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook) - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - - grub_free (dirname); - - return found ? dirp : 0; -} - -static grub_err_t -grub_fat_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_fat_data *data = 0; - grub_disk_t disk = device->disk; - grub_size_t len; - char *dirname = 0; - char *p; - - grub_dl_ref (my_mod); - - data = grub_fat_mount (disk); - if (! data) - goto fail; - - /* Make sure that DIRNAME terminates with '/'. */ - len = grub_strlen (path); - dirname = grub_malloc (len + 1 + 1); - if (! dirname) - goto fail; - grub_memcpy (dirname, path, len); - p = dirname + len; - if (path[len - 1] != '/') - *p++ = '/'; - *p = '\0'; - p = dirname; - - do - { - p = grub_fat_find_dir (disk, data, p, hook); - } - while (p && grub_errno == GRUB_ERR_NONE); - - fail: - - grub_free (dirname); - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_fat_open (grub_file_t file, const char *name) -{ - struct grub_fat_data *data = 0; - char *p = (char *) name; - - grub_dl_ref (my_mod); - - data = grub_fat_mount (file->device->disk); - if (! data) - goto fail; - - do - { - p = grub_fat_find_dir (file->device->disk, data, p, 0); - if (grub_errno != GRUB_ERR_NONE) - goto fail; - } - while (p); - - if (data->attr & GRUB_FAT_ATTR_DIRECTORY) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file"); - goto fail; - } - - file->data = data; - file->size = data->file_size; - - return GRUB_ERR_NONE; - - fail: - - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_ssize_t -grub_fat_read (grub_file_t file, char *buf, grub_size_t len) -{ - return grub_fat_read_data (file->device->disk, file->data, file->read_hook, - file->offset, len, buf); -} - -static grub_err_t -grub_fat_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_fat_label (grub_device_t device, char **label) -{ - struct grub_fat_data *data; - grub_disk_t disk = device->disk; - - auto int iter_hook (const char *filename, struct grub_fat_dir_entry *dir); - int iter_hook (const char *filename, struct grub_fat_dir_entry *dir) - { - if (dir->attr == GRUB_FAT_ATTR_VOLUME_ID) - { - *label = grub_strdup (filename); - return 1; - } - return 0; - } - - grub_dl_ref (my_mod); - - data = grub_fat_mount (disk); - if (! data) - goto fail; - - if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY)) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - return 0; - } - - *label = 0; - - grub_fat_iterate_dir (disk, data, iter_hook); - - fail: - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - -static grub_err_t -grub_fat_uuid (grub_device_t device, char **uuid) -{ - struct grub_fat_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_fat_mount (disk); - if (data) - { - *uuid = grub_xasprintf ("%04x-%04x", - (grub_uint16_t) (data->uuid >> 16), - (grub_uint16_t) data->uuid); - } - else - *uuid = NULL; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - -static struct grub_fs grub_fat_fs = - { - .name = "fat", - .dir = grub_fat_dir, - .open = grub_fat_open, - .read = grub_fat_read, - .close = grub_fat_close, - .label = grub_fat_label, - .uuid = grub_fat_uuid, -#ifdef GRUB_UTIL - .reserved_first_sector = 1, -#endif - .next = 0 - }; - -GRUB_MOD_INIT(fat) -{ - grub_fs_register (&grub_fat_fs); - my_mod = mod; -} - -GRUB_MOD_FINI(fat) -{ - grub_fs_unregister (&grub_fat_fs); -} - diff --git a/fs/fshelp.c b/fs/fshelp.c deleted file mode 100644 index d0b1e493e..000000000 --- a/fs/fshelp.c +++ /dev/null @@ -1,315 +0,0 @@ -/* fshelp.c -- Filesystem helper functions */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,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 . - */ - -#include -#include -#include -#include -#include - - -/* Lookup the node PATH. The node ROOTNODE describes the root of the - directory tree. The node found is returned in FOUNDNODE, which is - either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to - iterate over all directory entries in the current node. - READ_SYMLINK is used to read the symlink if a node is a symlink. - EXPECTTYPE is the type node that is expected by the called, an - error is generated if the node is not of the expected type. Make - sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required - because GCC has a nasty bug when using regparm=3. */ -grub_err_t -grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode, - grub_fshelp_node_t *foundnode, - int (*iterate_dir) (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR (*hook) - (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)), - char *(*read_symlink) (grub_fshelp_node_t node), - enum grub_fshelp_filetype expecttype) -{ - grub_err_t err; - enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR; - int symlinknest = 0; - - auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath, - grub_fshelp_node_t currroot, - grub_fshelp_node_t *currfound); - - grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath, - grub_fshelp_node_t currroot, - grub_fshelp_node_t *currfound) - { - char fpath[grub_strlen (currpath) + 1]; - char *name = fpath; - char *next; - // unsigned int pos = 0; - enum grub_fshelp_filetype type = GRUB_FSHELP_DIR; - grub_fshelp_node_t currnode = currroot; - grub_fshelp_node_t oldnode = currroot; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - auto void free_node (grub_fshelp_node_t node); - - void free_node (grub_fshelp_node_t node) - { - if (node != rootnode && node != currroot) - grub_free (node); - } - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - if (filetype == GRUB_FSHELP_UNKNOWN || - (grub_strcmp (name, filename) && - (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) || - grub_strncasecmp (name, filename, GRUB_LONG_MAX)))) - { - grub_free (node); - return 0; - } - - /* The node is found, stop iterating over the nodes. */ - type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE; - oldnode = currnode; - currnode = node; - - return 1; - } - - grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1); - - /* Remove all leading slashes. */ - while (*name == '/') - name++; - - if (! *name) - { - *currfound = currnode; - return 0; - } - - for (;;) - { - int found; - - /* Extract the actual part from the pathname. */ - next = grub_strchr (name, '/'); - if (next) - { - /* Remove all leading slashes. */ - while (*next == '/') - *(next++) = '\0'; - } - - /* At this point it is expected that the current node is a - directory, check if this is true. */ - if (type != GRUB_FSHELP_DIR) - { - free_node (currnode); - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - } - - /* Iterate over the directory. */ - found = iterate_dir (currnode, iterate); - if (! found) - { - if (grub_errno) - return grub_errno; - - break; - } - - /* Read in the symlink and follow it. */ - if (type == GRUB_FSHELP_SYMLINK) - { - char *symlink; - - /* Test if the symlink does not loop. */ - if (++symlinknest == 8) - { - free_node (currnode); - free_node (oldnode); - return grub_error (GRUB_ERR_SYMLINK_LOOP, - "too deep nesting of symlinks"); - } - - symlink = read_symlink (currnode); - free_node (currnode); - - if (!symlink) - { - free_node (oldnode); - return grub_errno; - } - - /* The symlink is an absolute path, go back to the root inode. */ - if (symlink[0] == '/') - { - free_node (oldnode); - oldnode = rootnode; - } - - /* Lookup the node the symlink points to. */ - find_file (symlink, oldnode, &currnode); - type = foundtype; - grub_free (symlink); - - if (grub_errno) - { - free_node (oldnode); - return grub_errno; - } - } - - free_node (oldnode); - - /* Found the node! */ - if (! next || *next == '\0') - { - *currfound = currnode; - foundtype = type; - return 0; - } - - name = next; - } - - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - } - - if (!path || path[0] != '/') - { - grub_error (GRUB_ERR_BAD_FILENAME, "bad filename"); - return grub_errno; - } - - err = find_file (path, rootnode, foundnode); - if (err) - return err; - - /* Check if the node that was found was of the expected type. */ - if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); - else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - - return 0; -} - -/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF, - beginning with the block POS. READ_HOOK should be set before - reading a block from the file. GET_BLOCK is used to translate file - blocks to disk blocks. The file is FILESIZE bytes big and the - blocks have a size of LOG2BLOCKSIZE (in log2). */ -grub_ssize_t -grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, - unsigned length), - grub_off_t pos, grub_size_t len, char *buf, - grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, - grub_disk_addr_t block), - grub_off_t filesize, int log2blocksize) -{ - grub_disk_addr_t i, blockcnt; - int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS); - - /* Adjust LEN so it we can't read past the end of the file. */ - if (pos + len > filesize) - len = filesize - pos; - - blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS); - - for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++) - { - grub_disk_addr_t blknr; - int blockoff = pos & (blocksize - 1); - int blockend = blocksize; - - int skipfirst = 0; - - blknr = get_block (node, i); - if (grub_errno) - return -1; - - blknr = blknr << log2blocksize; - - /* Last block. */ - if (i == blockcnt - 1) - { - blockend = (len + pos) & (blocksize - 1); - - /* The last portion is exactly blocksize. */ - if (! blockend) - blockend = blocksize; - } - - /* First block. */ - if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS))) - { - skipfirst = blockoff; - blockend -= skipfirst; - } - - /* If the block number is 0 this block is not stored on disk but - is zero filled instead. */ - if (blknr) - { - disk->read_hook = read_hook; - - grub_disk_read (disk, blknr, skipfirst, - blockend, buf); - disk->read_hook = 0; - if (grub_errno) - return -1; - } - else - grub_memset (buf, 0, blockend); - - buf += blocksize - skipfirst; - } - - return len; -} - -unsigned int -grub_fshelp_log2blksize (unsigned int blksize, unsigned int *pow) -{ - int mod; - - *pow = 0; - while (blksize > 1) - { - mod = blksize - ((blksize >> 1) << 1); - blksize >>= 1; - - /* Check if it really is a power of two. */ - if (mod) - return grub_error (GRUB_ERR_BAD_NUMBER, - "the blocksize is not a power of two"); - (*pow)++; - } - - return GRUB_ERR_NONE; -} diff --git a/fs/i386/pc/pxe.c b/fs/i386/pc/pxe.c deleted file mode 100644 index 82d8ee583..000000000 --- a/fs/i386/pc/pxe.c +++ /dev/null @@ -1,595 +0,0 @@ -/* pxe.c - Driver to provide access to the pxe filesystem */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define SEGMENT(x) ((x) >> 4) -#define OFFSET(x) ((x) & 0xF) -#define SEGOFS(x) ((SEGMENT(x) << 16) + OFFSET(x)) -#define LINEAR(x) (void *) (((x >> 16) <<4) + (x & 0xFFFF)) - -struct grub_pxe_disk_data -{ - grub_uint32_t server_ip; - grub_uint32_t gateway_ip; -}; - -struct grub_pxenv *grub_pxe_pxenv; -static grub_uint32_t grub_pxe_your_ip; -static grub_uint32_t grub_pxe_default_server_ip; -static grub_uint32_t grub_pxe_default_gateway_ip; -static unsigned grub_pxe_blksize = GRUB_PXE_MIN_BLKSIZE; - -static grub_file_t curr_file = 0; - -struct grub_pxe_data -{ - grub_uint32_t packet_number; - grub_uint32_t block_size; - char filename[0]; -}; - -static int -grub_pxe_iterate (int (*hook) (const char *name)) -{ - if (hook ("pxe")) - return 1; - return 0; -} - -static grub_err_t -parse_ip (const char *val, grub_uint32_t *ip, const char **rest) -{ - grub_uint32_t newip = 0; - unsigned long t; - int i; - const char *ptr = val; - - for (i = 0; i < 4; i++) - { - t = grub_strtoul (ptr, (char **) &ptr, 0); - if (grub_errno) - return grub_errno; - if (t & ~0xff) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP."); - newip >>= 8; - newip |= (t << 24); - if (i != 3 && *ptr != '.') - return grub_error (GRUB_ERR_OUT_OF_RANGE, "Invalid IP."); - ptr++; - } - *ip = newip; - if (rest) - *rest = ptr - 1; - return 0; -} - -static grub_err_t -grub_pxe_open (const char *name, grub_disk_t disk) -{ - struct grub_pxe_disk_data *data; - - if (grub_strcmp (name, "pxe") != 0 - && grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) != 0) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a pxe disk"); - - data = grub_malloc (sizeof (*data)); - if (!data) - return grub_errno; - - if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) - { - const char *ptr; - grub_err_t err; - - ptr = name + sizeof ("pxe:") - 1; - err = parse_ip (ptr, &(data->server_ip), &ptr); - if (err) - return err; - if (*ptr == ':') - { - err = parse_ip (ptr + 1, &(data->server_ip), 0); - if (err) - return err; - } - else - data->gateway_ip = grub_pxe_default_gateway_ip; - } - else - { - data->server_ip = grub_pxe_default_server_ip; - data->gateway_ip = grub_pxe_default_gateway_ip; - } - - disk->total_sectors = 0; - disk->id = (unsigned long) data; - - disk->has_partitions = 0; - disk->data = data; - - return GRUB_ERR_NONE; -} - -static void -grub_pxe_close (grub_disk_t disk) -{ - grub_free (disk->data); -} - -static grub_err_t -grub_pxe_read (grub_disk_t disk __attribute((unused)), - grub_disk_addr_t sector __attribute((unused)), - grub_size_t size __attribute((unused)), - char *buf __attribute((unused))) -{ - return GRUB_ERR_OUT_OF_RANGE; -} - -static grub_err_t -grub_pxe_write (grub_disk_t disk __attribute((unused)), - grub_disk_addr_t sector __attribute((unused)), - grub_size_t size __attribute((unused)), - const char *buf __attribute((unused))) -{ - return GRUB_ERR_OUT_OF_RANGE; -} - -static struct grub_disk_dev grub_pxe_dev = - { - .name = "pxe", - .id = GRUB_DISK_DEVICE_PXE_ID, - .iterate = grub_pxe_iterate, - .open = grub_pxe_open, - .close = grub_pxe_close, - .read = grub_pxe_read, - .write = grub_pxe_write, - .next = 0 - }; - -static grub_err_t -grub_pxefs_dir (grub_device_t device, - const char *path __attribute__ ((unused)), - int (*hook) (const char *filename, - const struct grub_dirhook_info *info) - __attribute__ ((unused))) -{ - if (device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) - return grub_error (GRUB_ERR_IO, "not a pxe disk"); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_pxefs_open (struct grub_file *file, const char *name) -{ - union - { - struct grub_pxenv_tftp_get_fsize c1; - struct grub_pxenv_tftp_open c2; - } c; - struct grub_pxe_data *data; - struct grub_pxe_disk_data *disk_data = file->device->disk->data; - grub_file_t file_int, bufio; - - if (file->device->disk->dev->id != GRUB_DISK_DEVICE_PXE_ID) - return grub_error (GRUB_ERR_IO, "not a pxe disk"); - - if (curr_file != 0) - { - grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c.c2); - curr_file = 0; - } - - c.c1.server_ip = disk_data->server_ip; - c.c1.gateway_ip = disk_data->gateway_ip; - grub_strcpy ((char *)&c.c1.filename[0], name); - grub_pxe_call (GRUB_PXENV_TFTP_GET_FSIZE, &c.c1); - if (c.c1.status) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - - file->size = c.c1.file_size; - - c.c2.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT); - c.c2.packet_size = grub_pxe_blksize; - grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &c.c2); - if (c.c2.status) - return grub_error (GRUB_ERR_BAD_FS, "open fails"); - - data = grub_zalloc (sizeof (struct grub_pxe_data) + grub_strlen (name) + 1); - if (! data) - return grub_errno; - - data->block_size = c.c2.packet_size; - grub_strcpy (data->filename, name); - - file_int = grub_malloc (sizeof (*file_int)); - if (! file_int) - { - grub_free (data); - return grub_errno; - } - - file->data = data; - grub_memcpy (file_int, file, sizeof (struct grub_file)); - curr_file = file_int; - - bufio = grub_bufio_open (file_int, data->block_size); - if (! bufio) - { - grub_free (file_int); - grub_free (data); - return grub_errno; - } - - grub_memcpy (file, bufio, sizeof (struct grub_file)); - - return GRUB_ERR_NONE; -} - -static grub_ssize_t -grub_pxefs_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_pxenv_tftp_read c; - struct grub_pxe_data *data; - struct grub_pxe_disk_data *disk_data = file->device->disk->data; - grub_uint32_t pn, r; - - data = file->data; - - pn = grub_divmod64 (file->offset, data->block_size, &r); - if (r) - { - grub_error (GRUB_ERR_BAD_FS, - "read access must be aligned to packet size"); - return -1; - } - - if ((curr_file != file) || (data->packet_number > pn)) - { - struct grub_pxenv_tftp_open o; - - if (curr_file != 0) - grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &o); - - o.server_ip = disk_data->server_ip; - o.gateway_ip = disk_data->gateway_ip; - grub_strcpy ((char *)&o.filename[0], data->filename); - o.tftp_port = grub_cpu_to_be16 (GRUB_PXE_TFTP_PORT); - o.packet_size = grub_pxe_blksize; - grub_pxe_call (GRUB_PXENV_TFTP_OPEN, &o); - if (o.status) - { - grub_error (GRUB_ERR_BAD_FS, "open fails"); - return -1; - } - data->block_size = o.packet_size; - data->packet_number = 0; - curr_file = file; - } - - c.buffer = SEGOFS (GRUB_MEMORY_MACHINE_SCRATCH_ADDR); - while (pn >= data->packet_number) - { - c.buffer_size = data->block_size; - grub_pxe_call (GRUB_PXENV_TFTP_READ, &c); - if (c.status) - { - grub_error (GRUB_ERR_BAD_FS, "read fails"); - return -1; - } - data->packet_number++; - } - - grub_memcpy (buf, (char *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, len); - - return len; -} - -static grub_err_t -grub_pxefs_close (grub_file_t file) -{ - struct grub_pxenv_tftp_close c; - - if (curr_file == file) - { - grub_pxe_call (GRUB_PXENV_TFTP_CLOSE, &c); - curr_file = 0; - } - - grub_free (file->data); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_pxefs_label (grub_device_t device __attribute ((unused)), - char **label __attribute ((unused))) -{ - *label = 0; - return GRUB_ERR_NONE; -} - -static struct grub_fs grub_pxefs_fs = - { - .name = "pxefs", - .dir = grub_pxefs_dir, - .open = grub_pxefs_open, - .read = grub_pxefs_read, - .close = grub_pxefs_close, - .label = grub_pxefs_label, - .next = 0 - }; - -static char * -grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), - const char *val __attribute__ ((unused))) -{ - return NULL; -} - -static void -set_mac_env (grub_uint8_t *mac_addr, grub_size_t mac_len) -{ - char buf[(sizeof ("XX:") - 1) * mac_len + 1]; - char *ptr = buf; - unsigned i; - - for (i = 0; i < mac_len; i++) - { - grub_snprintf (ptr, sizeof (buf) - (ptr - buf), - "%02x:", mac_addr[i] & 0xff); - ptr += (sizeof ("XX:") - 1); - } - if (mac_len) - *(ptr - 1) = 0; - else - buf[0] = 0; - - grub_env_set ("net_pxe_mac", buf); - /* XXX: Is it possible to change MAC in PXE? */ - grub_register_variable_hook ("net_pxe_mac", 0, grub_env_write_readonly); -} - -static void -set_env_limn_ro (const char *varname, char *value, grub_size_t len) -{ - char c; - c = value[len]; - value[len] = 0; - grub_env_set (varname, value); - value[len] = c; - grub_register_variable_hook (varname, 0, grub_env_write_readonly); -} - -static void -parse_dhcp_vendor (void *vend, int limit) -{ - grub_uint8_t *ptr, *ptr0; - - ptr = ptr0 = vend; - - if (grub_be_to_cpu32 (*(grub_uint32_t *) ptr) != 0x63825363) - return; - ptr = ptr + sizeof (grub_uint32_t); - while (ptr - ptr0 < limit) - { - grub_uint8_t tagtype; - grub_uint8_t taglength; - - tagtype = *ptr++; - - /* Pad tag. */ - if (tagtype == 0) - continue; - - /* End tag. */ - if (tagtype == 0xff) - return; - - taglength = *ptr++; - - switch (tagtype) - { - case 12: - set_env_limn_ro ("net_pxe_hostname", (char *) ptr, taglength); - break; - - case 15: - set_env_limn_ro ("net_pxe_domain", (char *) ptr, taglength); - break; - - case 17: - set_env_limn_ro ("net_pxe_rootpath", (char *) ptr, taglength); - break; - - case 18: - set_env_limn_ro ("net_pxe_extensionspath", (char *) ptr, taglength); - break; - - /* If you need any other options please contact GRUB - developpement team. */ - } - - ptr += taglength; - } -} - -static void -grub_pxe_detect (void) -{ - struct grub_pxenv *pxenv; - struct grub_pxenv_get_cached_info ci; - struct grub_pxenv_boot_player *bp; - - pxenv = grub_pxe_scan (); - if (! pxenv) - return; - - ci.packet_type = GRUB_PXENV_PACKET_TYPE_DHCP_ACK; - ci.buffer = 0; - ci.buffer_size = 0; - grub_pxe_call (GRUB_PXENV_GET_CACHED_INFO, &ci); - if (ci.status) - return; - - bp = LINEAR (ci.buffer); - - grub_pxe_your_ip = bp->your_ip; - grub_pxe_default_server_ip = bp->server_ip; - grub_pxe_default_gateway_ip = bp->gateway_ip; - set_mac_env (bp->mac_addr, bp->hw_len < sizeof (bp->mac_addr) ? bp->hw_len - : sizeof (bp->mac_addr)); - set_env_limn_ro ("net_pxe_boot_file", (char *) bp->boot_file, - sizeof (bp->boot_file)); - set_env_limn_ro ("net_pxe_dhcp_server_name", (char *) bp->server_name, - sizeof (bp->server_name)); - parse_dhcp_vendor (&bp->vendor, sizeof (bp->vendor)); - grub_pxe_pxenv = pxenv; -} - -void -grub_pxe_unload (void) -{ - if (grub_pxe_pxenv) - { - grub_fs_unregister (&grub_pxefs_fs); - grub_disk_dev_unregister (&grub_pxe_dev); - - grub_pxe_pxenv = 0; - } -} - -static void -set_ip_env (char *varname, grub_uint32_t ip) -{ - char buf[sizeof ("XXX.XXX.XXX.XXX")]; - - grub_snprintf (buf, sizeof (buf), "%d.%d.%d.%d", (ip & 0xff), - (ip >> 8) & 0xff, (ip >> 16) & 0xff, (ip >> 24) & 0xff); - grub_env_set (varname, buf); -} - -static char * -write_ip_env (grub_uint32_t *ip, const char *val) -{ - char *buf; - grub_err_t err; - grub_uint32_t newip; - - err = parse_ip (val, &newip, 0); - if (err) - return 0; - - /* Normalize the IP. */ - buf = grub_xasprintf ("%d.%d.%d.%d", (newip & 0xff), (newip >> 8) & 0xff, - (newip >> 16) & 0xff, (newip >> 24) & 0xff); - if (!buf) - return 0; - - *ip = newip; - - return buf; -} - -static char * -grub_env_write_pxe_default_server (struct grub_env_var *var - __attribute__ ((unused)), - const char *val) -{ - return write_ip_env (&grub_pxe_default_server_ip, val); -} - -static char * -grub_env_write_pxe_default_gateway (struct grub_env_var *var - __attribute__ ((unused)), - const char *val) -{ - return write_ip_env (&grub_pxe_default_gateway_ip, val); -} - -static char * -grub_env_write_pxe_blocksize (struct grub_env_var *var __attribute__ ((unused)), - const char *val) -{ - unsigned size; - char *buf; - - size = grub_strtoul (val, 0, 0); - if (grub_errno) - return 0; - - if (size < GRUB_PXE_MIN_BLKSIZE) - size = GRUB_PXE_MIN_BLKSIZE; - else if (size > GRUB_PXE_MAX_BLKSIZE) - size = GRUB_PXE_MAX_BLKSIZE; - - buf = grub_xasprintf ("%d", size); - if (!buf) - return 0; - - grub_pxe_blksize = size; - - return buf; -} - - -GRUB_MOD_INIT(pxe) -{ - grub_pxe_detect (); - if (grub_pxe_pxenv) - { - char *buf; - - buf = grub_xasprintf ("%d", grub_pxe_blksize); - if (buf) - grub_env_set ("pxe_blksize", buf); - grub_free (buf); - - set_ip_env ("pxe_default_server", grub_pxe_default_server_ip); - set_ip_env ("pxe_default_gateway", grub_pxe_default_gateway_ip); - set_ip_env ("net_pxe_ip", grub_pxe_your_ip); - grub_register_variable_hook ("pxe_default_server", 0, - grub_env_write_pxe_default_server); - grub_register_variable_hook ("pxe_default_gateway", 0, - grub_env_write_pxe_default_gateway); - - /* XXX: Is it possible to change IP in PXE? */ - grub_register_variable_hook ("net_pxe_ip", 0, - grub_env_write_readonly); - grub_register_variable_hook ("pxe_blksize", 0, - grub_env_write_pxe_blocksize); - grub_disk_dev_register (&grub_pxe_dev); - grub_fs_register (&grub_pxefs_fs); - } -} - -GRUB_MOD_FINI(pxe) -{ - grub_pxe_unload (); -} diff --git a/fs/iso9660.c b/fs/iso9660.c deleted file mode 100644 index 6dc465f25..000000000 --- a/fs/iso9660.c +++ /dev/null @@ -1,897 +0,0 @@ -/* iso9660.c - iso9660 implementation with extensions: - SUSP, Rock Ridge. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GRUB_ISO9660_FSTYPE_DIR 0040000 -#define GRUB_ISO9660_FSTYPE_REG 0100000 -#define GRUB_ISO9660_FSTYPE_SYMLINK 0120000 -#define GRUB_ISO9660_FSTYPE_MASK 0170000 - -#define GRUB_ISO9660_LOG2_BLKSZ 2 -#define GRUB_ISO9660_BLKSZ 2048 - -#define GRUB_ISO9660_RR_DOT 2 -#define GRUB_ISO9660_RR_DOTDOT 4 - -#define GRUB_ISO9660_VOLDESC_BOOT 0 -#define GRUB_ISO9660_VOLDESC_PRIMARY 1 -#define GRUB_ISO9660_VOLDESC_SUPP 2 -#define GRUB_ISO9660_VOLDESC_PART 3 -#define GRUB_ISO9660_VOLDESC_END 255 - -/* The head of a volume descriptor. */ -struct grub_iso9660_voldesc -{ - grub_uint8_t type; - grub_uint8_t magic[5]; - grub_uint8_t version; -} __attribute__ ((packed)); - -/* A directory entry. */ -struct grub_iso9660_dir -{ - grub_uint8_t len; - grub_uint8_t ext_sectors; - grub_uint32_t first_sector; - grub_uint32_t first_sector_be; - grub_uint32_t size; - grub_uint32_t size_be; - grub_uint8_t unused1[7]; - grub_uint8_t flags; - grub_uint8_t unused2[6]; - grub_uint8_t namelen; -} __attribute__ ((packed)); - -struct grub_iso9660_date -{ - grub_uint8_t year[4]; - grub_uint8_t month[2]; - grub_uint8_t day[2]; - grub_uint8_t hour[2]; - grub_uint8_t minute[2]; - grub_uint8_t second[2]; - grub_uint8_t hundredth[2]; - grub_uint8_t offset; -} __attribute__ ((packed)); - -/* The primary volume descriptor. Only little endian is used. */ -struct grub_iso9660_primary_voldesc -{ - struct grub_iso9660_voldesc voldesc; - grub_uint8_t unused1[33]; - grub_uint8_t volname[32]; - grub_uint8_t unused2[16]; - grub_uint8_t escape[32]; - grub_uint8_t unused3[12]; - grub_uint32_t path_table_size; - grub_uint8_t unused4[4]; - grub_uint32_t path_table; - grub_uint8_t unused5[12]; - struct grub_iso9660_dir rootdir; - grub_uint8_t unused6[624]; - struct grub_iso9660_date created; - struct grub_iso9660_date modified; -} __attribute__ ((packed)); - -/* A single entry in the path table. */ -struct grub_iso9660_path -{ - grub_uint8_t len; - grub_uint8_t sectors; - grub_uint32_t first_sector; - grub_uint16_t parentdir; - grub_uint8_t name[0]; -} __attribute__ ((packed)); - -/* An entry in the System Usage area of the directory entry. */ -struct grub_iso9660_susp_entry -{ - grub_uint8_t sig[2]; - grub_uint8_t len; - grub_uint8_t version; - grub_uint8_t data[0]; -} __attribute__ ((packed)); - -/* The CE entry. This is used to describe the next block where data - can be found. */ -struct grub_iso9660_susp_ce -{ - struct grub_iso9660_susp_entry entry; - grub_uint32_t blk; - grub_uint32_t blk_be; - grub_uint32_t off; - grub_uint32_t off_be; - grub_uint32_t len; - grub_uint32_t len_be; -} __attribute__ ((packed)); - -struct grub_iso9660_data -{ - struct grub_iso9660_primary_voldesc voldesc; - grub_disk_t disk; - unsigned int first_sector; - int rockridge; - int susp_skip; - int joliet; -}; - -struct grub_fshelp_node -{ - struct grub_iso9660_data *data; - unsigned int size; - unsigned int blk; - unsigned int dir_blk; - unsigned int dir_off; -}; - -static grub_dl_t my_mod; - - -/* Iterate over the susp entries, starting with block SUA_BLOCK on the - offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for - every entry. */ -static grub_err_t -grub_iso9660_susp_iterate (struct grub_iso9660_data *data, - int sua_block, int sua_pos, int sua_size, - grub_err_t (*hook) - (struct grub_iso9660_susp_entry *entry)) -{ - char *sua; - struct grub_iso9660_susp_entry *entry; - - auto grub_err_t load_sua (void); - - /* Load a part of the System Usage Area. */ - grub_err_t load_sua (void) - { - sua = grub_malloc (sua_size); - if (!sua) - return grub_errno; - - if (grub_disk_read (data->disk, sua_block, sua_pos, - sua_size, sua)) - return grub_errno; - - entry = (struct grub_iso9660_susp_entry *) sua; - return 0; - } - - if (load_sua ()) - return grub_errno; - - for (; (char *) entry < (char *) sua + sua_size - 1; - entry = (struct grub_iso9660_susp_entry *) - ((char *) entry + entry->len)) - { - /* The last entry. */ - if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0) - break; - - /* Additional entries are stored elsewhere. */ - if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0) - { - struct grub_iso9660_susp_ce *ce; - - ce = (struct grub_iso9660_susp_ce *) entry; - sua_size = grub_le_to_cpu32 (ce->len); - sua_pos = grub_le_to_cpu32 (ce->off); - sua_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ; - - grub_free (sua); - if (load_sua ()) - return grub_errno; - } - - if (hook (entry)) - { - grub_free (sua); - return 0; - } - } - - grub_free (sua); - return 0; -} - -static char * -grub_iso9660_convert_string (grub_uint16_t *us, int len) -{ - char *p; - int i; - - p = grub_malloc (len * 4 + 1); - if (! p) - return p; - - for (i=0; isig, "ER", 2) == 0) - { - data->rockridge = 1; - return 1; - } - return 0; - } - - data = grub_zalloc (sizeof (struct grub_iso9660_data)); - if (! data) - return 0; - - data->disk = disk; - - block = 16; - do - { - int copy_voldesc = 0; - - /* Read the superblock. */ - if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0, - sizeof (struct grub_iso9660_primary_voldesc), - (char *) &voldesc)) - { - grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); - goto fail; - } - - if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0) - { - grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); - goto fail; - } - - if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY) - copy_voldesc = 1; - else if ((voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP) && - (voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f) && - ((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1. */ - (voldesc.escape[2] == 0x43) || /* UCS-2 Level 2. */ - (voldesc.escape[2] == 0x45))) /* UCS-2 Level 3. */ - { - copy_voldesc = 1; - data->joliet = 1; - } - - if (copy_voldesc) - grub_memcpy((char *) &data->voldesc, (char *) &voldesc, - sizeof (struct grub_iso9660_primary_voldesc)); - - block++; - } while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END); - - /* Read the system use area and test it to see if SUSP is - supported. */ - if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector) - << GRUB_ISO9660_LOG2_BLKSZ), 0, - sizeof (rootdir), (char *) &rootdir)) - { - grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); - goto fail; - } - - sua_pos = (sizeof (rootdir) + rootdir.namelen - + (rootdir.namelen % 2) - 1); - sua_size = rootdir.len - sua_pos; - - sua = grub_malloc (sua_size); - if (! sua) - goto fail; - - if (grub_disk_read (disk, (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector) - << GRUB_ISO9660_LOG2_BLKSZ), sua_pos, - sua_size, sua)) - { - grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); - goto fail; - } - - entry = (struct grub_iso9660_susp_entry *) sua; - - /* Test if the SUSP protocol is used on this filesystem. */ - if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0) - { - /* The 2nd data byte stored how many bytes are skipped every time - to get to the SUA (System Usage Area). */ - data->susp_skip = entry->data[2]; - entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len); - - /* Iterate over the entries in the SUA area to detect - extensions. */ - if (grub_iso9660_susp_iterate (data, - (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector) - << GRUB_ISO9660_LOG2_BLKSZ), - sua_pos, sua_size, susp_iterate)) - goto fail; - } - - return data; - - fail: - grub_free (data); - return 0; -} - - -static char * -grub_iso9660_read_symlink (grub_fshelp_node_t node) -{ - struct grub_iso9660_dir dirent; - int sua_off; - int sua_size; - char *symlink = 0; - int addslash = 0; - - auto void add_part (const char *part, int len); - auto grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *); - - /* Extend the symlink. */ - void add_part (const char *part, int len) - { - int size = grub_strlen (symlink); - - symlink = grub_realloc (symlink, size + len + 1); - if (! symlink) - return; - - grub_strncat (symlink, part, len); - } - - /* Read in a symlink. */ - grub_err_t susp_iterate_sl (struct grub_iso9660_susp_entry *entry) - { - if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0) - { - unsigned int pos = 1; - - /* The symlink is not stored as a POSIX symlink, translate it. */ - while (pos < grub_le_to_cpu32 (entry->len)) - { - if (addslash) - { - add_part ("/", 1); - addslash = 0; - } - - /* The current position is the `Component Flag'. */ - switch (entry->data[pos] & 30) - { - case 0: - { - /* The data on pos + 2 is the actual data, pos + 1 - is the length. Both are part of the `Component - Record'. */ - add_part ((char *) &entry->data[pos + 2], - entry->data[pos + 1]); - if ((entry->data[pos] & 1)) - addslash = 1; - - break; - } - - case 2: - add_part ("./", 2); - break; - - case 4: - add_part ("../", 3); - break; - - case 8: - add_part ("/", 1); - break; - } - /* In pos + 1 the length of the `Component Record' is - stored. */ - pos += entry->data[pos + 1] + 2; - } - - /* Check if `grub_realloc' failed. */ - if (grub_errno) - return grub_errno; - } - - return 0; - } - - if (grub_disk_read (node->data->disk, node->dir_blk, node->dir_off, - sizeof (dirent), (char *) &dirent)) - return 0; - - sua_off = (sizeof (dirent) + dirent.namelen + 1 - (dirent.namelen % 2) - + node->data->susp_skip); - sua_size = dirent.len - sua_off; - - symlink = grub_malloc (1); - if (!symlink) - return 0; - - *symlink = '\0'; - - if (grub_iso9660_susp_iterate (node->data, node->dir_blk, - node->dir_off + sua_off, - sua_size, susp_iterate_sl)) - { - grub_free (symlink); - return 0; - } - - return symlink; -} - - -static int -grub_iso9660_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - struct grub_iso9660_dir dirent; - unsigned int offset = 0; - char *filename; - int filename_alloc = 0; - enum grub_fshelp_filetype type; - - auto grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *); - - grub_err_t susp_iterate_dir (struct grub_iso9660_susp_entry *entry) - { - /* The filename in the rock ridge entry. */ - if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0) - { - /* The flags are stored at the data position 0, here the - filename type is stored. */ - if (entry->data[0] & GRUB_ISO9660_RR_DOT) - filename = "."; - else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT) - filename = ".."; - else - { - int size = 1; - if (filename) - { - size += grub_strlen (filename); - grub_realloc (filename, - grub_strlen (filename) - + entry->len); - } - else - { - size = entry->len - 5; - filename = grub_zalloc (size + 1); - } - filename_alloc = 1; - grub_strncpy (filename, (char *) &entry->data[1], size); - filename[size] = '\0'; - } - } - /* The mode information (st_mode). */ - else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0) - { - /* At position 0 of the PX record the st_mode information is - stored (little-endian). */ - grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8)) - & GRUB_ISO9660_FSTYPE_MASK); - - switch (mode) - { - case GRUB_ISO9660_FSTYPE_DIR: - type = GRUB_FSHELP_DIR; - break; - case GRUB_ISO9660_FSTYPE_REG: - type = GRUB_FSHELP_REG; - break; - case GRUB_ISO9660_FSTYPE_SYMLINK: - type = GRUB_FSHELP_SYMLINK; - break; - default: - type = GRUB_FSHELP_UNKNOWN; - } - } - - return 0; - } - - while (offset < dir->size) - { - if (grub_disk_read (dir->data->disk, - (dir->blk << GRUB_ISO9660_LOG2_BLKSZ) - + offset / GRUB_DISK_SECTOR_SIZE, - offset % GRUB_DISK_SECTOR_SIZE, - sizeof (dirent), (char *) &dirent)) - return 0; - - /* The end of the block, skip to the next one. */ - if (!dirent.len) - { - offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ; - continue; - } - - { - char name[dirent.namelen + 1]; - int nameoffset = offset + sizeof (dirent); - struct grub_fshelp_node *node; - int sua_off = (sizeof (dirent) + dirent.namelen + 1 - - (dirent.namelen % 2)); - int sua_size = dirent.len - sua_off; - - sua_off += offset + dir->data->susp_skip; - - filename = 0; - filename_alloc = 0; - type = GRUB_FSHELP_UNKNOWN; - - if (dir->data->rockridge - && grub_iso9660_susp_iterate (dir->data, - (dir->blk << GRUB_ISO9660_LOG2_BLKSZ) - + (sua_off - / GRUB_DISK_SECTOR_SIZE), - sua_off % GRUB_DISK_SECTOR_SIZE, - sua_size, susp_iterate_dir)) - return 0; - - /* Read the name. */ - if (grub_disk_read (dir->data->disk, - (dir->blk << GRUB_ISO9660_LOG2_BLKSZ) - + nameoffset / GRUB_DISK_SECTOR_SIZE, - nameoffset % GRUB_DISK_SECTOR_SIZE, - dirent.namelen, (char *) name)) - return 0; - - node = grub_malloc (sizeof (struct grub_fshelp_node)); - if (!node) - return 0; - - /* Setup a new node. */ - node->data = dir->data; - node->size = grub_le_to_cpu32 (dirent.size); - node->blk = grub_le_to_cpu32 (dirent.first_sector); - node->dir_blk = ((dir->blk << GRUB_ISO9660_LOG2_BLKSZ) - + offset / GRUB_DISK_SECTOR_SIZE); - node->dir_off = offset % GRUB_DISK_SECTOR_SIZE; - - /* If the filetype was not stored using rockridge, use - whatever is stored in the iso9660 filesystem. */ - if (type == GRUB_FSHELP_UNKNOWN) - { - if ((dirent.flags & 3) == 2) - type = GRUB_FSHELP_DIR; - else - type = GRUB_FSHELP_REG; - } - - /* The filename was not stored in a rock ridge entry. Read it - from the iso9660 filesystem. */ - if (!filename) - { - name[dirent.namelen] = '\0'; - filename = grub_strrchr (name, ';'); - if (filename) - *filename = '\0'; - - if (dirent.namelen == 1 && name[0] == 0) - filename = "."; - else if (dirent.namelen == 1 && name[0] == 1) - filename = ".."; - else - filename = name; - } - - if (dir->data->joliet) - { - char *oldname, *semicolon; - - oldname = filename; - filename = grub_iso9660_convert_string - ((grub_uint16_t *) oldname, dirent.namelen >> 1); - - semicolon = grub_strrchr (filename, ';'); - if (semicolon) - *semicolon = '\0'; - - if (filename_alloc) - grub_free (oldname); - - filename_alloc = 1; - } - - if (hook (filename, type, node)) - { - if (filename_alloc) - grub_free (filename); - return 1; - } - if (filename_alloc) - grub_free (filename); - } - - offset += dirent.len; - } - - return 0; -} - - - -static grub_err_t -grub_iso9660_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_iso9660_data *data = 0; - struct grub_fshelp_node rootnode; - struct grub_fshelp_node *foundnode; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_iso9660_mount (device->disk); - if (! data) - goto fail; - - rootnode.data = data; - rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector); - rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size); - - /* Use the fshelp function to traverse the path. */ - if (grub_fshelp_find_file (path, &rootnode, - &foundnode, - grub_iso9660_iterate_dir, - grub_iso9660_read_symlink, - GRUB_FSHELP_DIR)) - goto fail; - - /* List the files in the directory. */ - grub_iso9660_iterate_dir (foundnode, iterate); - - if (foundnode != &rootnode) - grub_free (foundnode); - - fail: - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - - -/* Open a file named NAME and initialize FILE. */ -static grub_err_t -grub_iso9660_open (struct grub_file *file, const char *name) -{ - struct grub_iso9660_data *data; - struct grub_fshelp_node rootnode; - struct grub_fshelp_node *foundnode; - - grub_dl_ref (my_mod); - - data = grub_iso9660_mount (file->device->disk); - if (!data) - goto fail; - - rootnode.data = data; - rootnode.blk = grub_le_to_cpu32 (data->voldesc.rootdir.first_sector); - rootnode.size = grub_le_to_cpu32 (data->voldesc.rootdir.size); - - /* Use the fshelp function to traverse the path. */ - if (grub_fshelp_find_file (name, &rootnode, - &foundnode, - grub_iso9660_iterate_dir, - grub_iso9660_read_symlink, - GRUB_FSHELP_REG)) - goto fail; - - data->first_sector = foundnode->blk; - - file->data = data; - file->size = foundnode->size; - file->offset = 0; - - return 0; - - fail: - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - - -static grub_ssize_t -grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_iso9660_data *data = - (struct grub_iso9660_data *) file->data; - - /* XXX: The file is stored in as a single extent. */ - data->disk->read_hook = file->read_hook; - grub_disk_read (data->disk, - data->first_sector << GRUB_ISO9660_LOG2_BLKSZ, - file->offset, - len, buf); - data->disk->read_hook = NULL; - - if (grub_errno) - return -1; - - return len; -} - - -static grub_err_t -grub_iso9660_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - - -static grub_err_t -grub_iso9660_label (grub_device_t device, char **label) -{ - struct grub_iso9660_data *data; - data = grub_iso9660_mount (device->disk); - - if (data) - { - if (data->joliet) - *label = grub_iso9660_convert_string - ((grub_uint16_t *) &data->voldesc.volname, 16); - else - *label = grub_strndup ((char *) data->voldesc.volname, 32); - grub_free (data); - } - else - *label = 0; - - return grub_errno; -} - - -static grub_err_t -grub_iso9660_uuid (grub_device_t device, char **uuid) -{ - struct grub_iso9660_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_iso9660_mount (disk); - if (data) - { - if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1] - && ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3] - && ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1] - && ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1] - && ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1] - && ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1] - && ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1] - && ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1]) - { - grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID"); - *uuid = NULL; - } - else - { - *uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c", - data->voldesc.modified.year[0], - data->voldesc.modified.year[1], - data->voldesc.modified.year[2], - data->voldesc.modified.year[3], - data->voldesc.modified.month[0], - data->voldesc.modified.month[1], - data->voldesc.modified.day[0], - data->voldesc.modified.day[1], - data->voldesc.modified.hour[0], - data->voldesc.modified.hour[1], - data->voldesc.modified.minute[0], - data->voldesc.modified.minute[1], - data->voldesc.modified.second[0], - data->voldesc.modified.second[1], - data->voldesc.modified.hundredth[0], - data->voldesc.modified.hundredth[1]); - } - } - else - *uuid = NULL; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - - - -static struct grub_fs grub_iso9660_fs = - { - .name = "iso9660", - .dir = grub_iso9660_dir, - .open = grub_iso9660_open, - .read = grub_iso9660_read, - .close = grub_iso9660_close, - .label = grub_iso9660_label, - .uuid = grub_iso9660_uuid, - .next = 0 - }; - -GRUB_MOD_INIT(iso9660) -{ - grub_fs_register (&grub_iso9660_fs); - my_mod = mod; -} - -GRUB_MOD_FINI(iso9660) -{ - grub_fs_unregister (&grub_iso9660_fs); -} diff --git a/fs/minix.c b/fs/minix.c deleted file mode 100644 index a856e38c4..000000000 --- a/fs/minix.c +++ /dev/null @@ -1,614 +0,0 @@ -/* minix.c - The minix filesystem, version 1 and 2. */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -#define GRUB_MINIX_MAGIC 0x137F -#define GRUB_MINIX2_MAGIC 0x2468 -#define GRUB_MINIX_MAGIC_30 0x138F -#define GRUB_MINIX2_MAGIC_30 0x2478 -#define GRUB_MINIX_BSIZE 1024U -#define GRUB_MINIX_LOG2_BSIZE 1 -#define GRUB_MINIX_ROOT_INODE 1 -#define GRUB_MINIX_MAX_SYMLNK_CNT 8 -#define GRUB_MINIX_SBLOCK 2 - -#define GRUB_MINIX_IFDIR 0040000U -#define GRUB_MINIX_IFLNK 0120000U - -#define GRUB_MINIX_INODE(data,field) (data->version == 1 ? \ - data->inode. field : data->inode2. field) -#define GRUB_MINIX_INODE_ENDIAN(data,field,bits1,bits2) (data->version == 1 ? \ - grub_le_to_cpu##bits1 (data->inode.field) : \ - grub_le_to_cpu##bits2 (data->inode2.field)) -#define GRUB_MINIX_INODE_SIZE(data) GRUB_MINIX_INODE_ENDIAN (data,size,16,32) -#define GRUB_MINIX_INODE_MODE(data) GRUB_MINIX_INODE_ENDIAN (data,mode,16,16) -#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) GRUB_MINIX_INODE_ENDIAN \ - (data,dir_zones[blk],16,32) -#define GRUB_MINIX_INODE_INDIR_ZONE(data) \ - GRUB_MINIX_INODE_ENDIAN (data,indir_zone,16,32) -#define GRUB_MINIX_INODE_DINDIR_ZONE(data) \ - GRUB_MINIX_INODE_ENDIAN (data,double_indir_zone,16,32) -#define GRUB_MINIX_INODE_BLKSZ(data) (data->version == 1 ? 2 : 4) -#define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \ - + grub_le_to_cpu16 (sblock->log2_zone_size)) -#define GRUB_MINIX_ZONESZ (GRUB_MINIX_BSIZE \ - << grub_le_to_cpu16 (sblock->log2_zone_size)) - -struct grub_minix_sblock -{ - grub_uint16_t inode_cnt; - grub_uint16_t zone_cnt; - grub_uint16_t inode_bmap_size; - grub_uint16_t zone_bmap_size; - grub_uint16_t first_data_zone; - grub_uint16_t log2_zone_size; - grub_uint32_t max_file_size; - grub_uint16_t magic; -}; - -struct grub_minix_inode -{ - grub_uint16_t mode; - grub_uint16_t uid; - grub_uint16_t size; - grub_uint32_t ctime; - grub_uint8_t gid; - grub_uint8_t nlinks; - grub_uint16_t dir_zones[7]; - grub_uint16_t indir_zone; - grub_uint16_t double_indir_zone; -}; - -struct grub_minix2_inode -{ - grub_uint16_t mode; - grub_uint16_t nlinks; - grub_uint16_t uid; - grub_uint16_t gid; - grub_uint32_t size; - grub_uint32_t atime; - grub_uint32_t mtime; - grub_uint32_t ctime; - grub_uint32_t dir_zones[7]; - grub_uint32_t indir_zone; - grub_uint32_t double_indir_zone; - grub_uint32_t unused; - -}; - -/* Information about a "mounted" minix filesystem. */ -struct grub_minix_data -{ - struct grub_minix_sblock sblock; - struct grub_minix_inode inode; - struct grub_minix2_inode inode2; - int ino; - int linknest; - grub_disk_t disk; - int version; - int filename_size; -}; - -static grub_dl_t my_mod; - -static grub_err_t grub_minix_find_file (struct grub_minix_data *data, - const char *path); - -static int -grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk) -{ - struct grub_minix_sblock *sblock = &data->sblock; - int indir; - - auto int grub_get_indir (int, int); - - /* Read the block pointer in ZONE, on the offset NUM. */ - int grub_get_indir (int zone, int num) - { - if (data->version == 1) - { - grub_uint16_t indir16; - grub_disk_read (data->disk, - zone << GRUB_MINIX_LOG2_ZONESZ, - sizeof (grub_uint16_t) * num, - sizeof (grub_uint16_t), (char *) &indir16); - return grub_le_to_cpu16 (indir16); - } - else - { - grub_uint32_t indir32; - grub_disk_read (data->disk, - zone << GRUB_MINIX_LOG2_ZONESZ, - sizeof (grub_uint32_t) * num, - sizeof (grub_uint32_t), (char *) &indir32); - return grub_le_to_cpu32 (indir32); - } - } - - /* Direct block. */ - if (blk < 7) - return GRUB_MINIX_INODE_DIR_ZONES (data, blk); - - /* Indirect block. */ - blk -= 7; - if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)) - { - indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk); - return indir; - } - - /* Double indirect block. */ - blk -= GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data); - if (blk < (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)) - * (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))) - { - indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data), - blk / GRUB_MINIX_ZONESZ); - - indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ); - - return indir; - } - - /* This should never happen. */ - grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size"); - - return 0; -} - - -/* Read LEN bytes from the file described by DATA starting with byte - POS. Return the amount of read bytes in READ. */ -static grub_ssize_t -grub_minix_read_file (struct grub_minix_data *data, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_disk_addr_t len, char *buf) -{ - struct grub_minix_sblock *sblock = &data->sblock; - int i; - int blockcnt; - - /* Adjust len so it we can't read past the end of the file. */ - if (len + pos > GRUB_MINIX_INODE_SIZE (data)) - len = GRUB_MINIX_INODE_SIZE (data) - pos; - - blockcnt = (len + pos + GRUB_MINIX_BSIZE - 1) / GRUB_MINIX_BSIZE; - - for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++) - { - int blknr; - int blockoff = pos % GRUB_MINIX_BSIZE; - int blockend = GRUB_MINIX_BSIZE; - - int skipfirst = 0; - - blknr = grub_minix_get_file_block (data, i); - if (grub_errno) - return -1; - - /* Last block. */ - if (i == blockcnt - 1) - { - blockend = (len + pos) % GRUB_MINIX_BSIZE; - - if (!blockend) - blockend = GRUB_MINIX_BSIZE; - } - - /* First block. */ - if (i == (pos / (int) GRUB_MINIX_BSIZE)) - { - skipfirst = blockoff; - blockend -= skipfirst; - } - - data->disk->read_hook = read_hook; - grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ, - skipfirst, blockend, buf); - - data->disk->read_hook = 0; - if (grub_errno) - return -1; - - buf += GRUB_MINIX_BSIZE - skipfirst; - } - - return len; -} - - -/* Read inode INO from the mounted filesystem described by DATA. This - inode is used by default now. */ -static grub_err_t -grub_minix_read_inode (struct grub_minix_data *data, int ino) -{ - struct grub_minix_sblock *sblock = &data->sblock; - - /* Block in which the inode is stored. */ - int block; - data->ino = ino; - - /* The first inode in minix is inode 1. */ - ino--; - - block = ((2 + grub_le_to_cpu16 (sblock->inode_bmap_size) - + grub_le_to_cpu16 (sblock->zone_bmap_size)) - << GRUB_MINIX_LOG2_BSIZE); - - if (data->version == 1) - { - block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode)); - int offs = (ino % (GRUB_DISK_SECTOR_SIZE - / sizeof (struct grub_minix_inode)) - * sizeof (struct grub_minix_inode)); - - grub_disk_read (data->disk, block, offs, - sizeof (struct grub_minix_inode), &data->inode); - } - else - { - block += ino / (GRUB_DISK_SECTOR_SIZE - / sizeof (struct grub_minix2_inode)); - int offs = (ino - % (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix2_inode)) - * sizeof (struct grub_minix2_inode)); - - grub_disk_read (data->disk, block, offs, - sizeof (struct grub_minix2_inode),&data->inode2); - } - - return GRUB_ERR_NONE; -} - - -/* Lookup the symlink the current inode points to. INO is the inode - number of the directory the symlink is relative to. */ -static grub_err_t -grub_minix_lookup_symlink (struct grub_minix_data *data, int ino) -{ - char symlink[GRUB_MINIX_INODE_SIZE (data) + 1]; - - if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT) - return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks"); - - if (grub_minix_read_file (data, 0, 0, - GRUB_MINIX_INODE_SIZE (data), symlink) < 0) - return grub_errno; - - symlink[GRUB_MINIX_INODE_SIZE (data)] = '\0'; - - /* The symlink is an absolute path, go back to the root inode. */ - if (symlink[0] == '/') - ino = GRUB_MINIX_ROOT_INODE; - - /* Now load in the old inode. */ - if (grub_minix_read_inode (data, ino)) - return grub_errno; - - grub_minix_find_file (data, symlink); - if (grub_errno) - grub_error (grub_errno, "cannot follow symlink `%s'", symlink); - - return grub_errno; -} - - -/* Find the file with the pathname PATH on the filesystem described by - DATA. */ -static grub_err_t -grub_minix_find_file (struct grub_minix_data *data, const char *path) -{ - char fpath[grub_strlen (path) + 1]; - char *name = fpath; - char *next; - unsigned int pos = 0; - int dirino; - - grub_strcpy (fpath, path); - - /* Skip the first slash. */ - if (name[0] == '/') - { - name++; - if (!*name) - return 0; - } - - /* Extract the actual part from the pathname. */ - next = grub_strchr (name, '/'); - if (next) - { - next[0] = '\0'; - next++; - } - - do - { - grub_uint16_t ino; - char filename[data->filename_size + 1]; - - if (grub_strlen (name) == 0) - return GRUB_ERR_NONE; - - if (grub_minix_read_file (data, 0, pos, sizeof (ino), - (char *) &ino) < 0) - return grub_errno; - if (grub_minix_read_file (data, 0, pos + sizeof (ino), - data->filename_size, (char *) filename)< 0) - return grub_errno; - - filename[data->filename_size] = '\0'; - - /* Check if the current direntry matches the current part of the - pathname. */ - if (!grub_strcmp (name, filename)) - { - dirino = data->ino; - grub_minix_read_inode (data, grub_le_to_cpu16 (ino)); - - /* Follow the symlink. */ - if ((GRUB_MINIX_INODE_MODE (data) - & GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK) - { - grub_minix_lookup_symlink (data, dirino); - if (grub_errno) - return grub_errno; - } - - if (!next) - return 0; - - pos = 0; - - name = next; - next = grub_strchr (name, '/'); - if (next) - { - next[0] = '\0'; - next++; - } - - if ((GRUB_MINIX_INODE_MODE (data) - & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - - continue; - } - - pos += sizeof (ino) + data->filename_size; - } while (pos < GRUB_MINIX_INODE_SIZE (data)); - - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - return grub_errno; -} - - -/* Mount the filesystem on the disk DISK. */ -static struct grub_minix_data * -grub_minix_mount (grub_disk_t disk) -{ - struct grub_minix_data *data; - - data = grub_malloc (sizeof (struct grub_minix_data)); - if (!data) - return 0; - - /* Read the superblock. */ - grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0, - sizeof (struct grub_minix_sblock),&data->sblock); - if (grub_errno) - goto fail; - - if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC) - { - data->version = 1; - data->filename_size = 14; - } - else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC) - { - data->version = 2; - data->filename_size = 14; - } - else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30) - { - data->version = 1; - data->filename_size = 30; - } - else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC_30) - { - data->version = 2; - data->filename_size = 30; - } - else - goto fail; - - data->disk = disk; - data->linknest = 0; - - return data; - - fail: - grub_free (data); - grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem"); - return 0; -} - -static grub_err_t -grub_minix_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_minix_data *data = 0; - struct grub_minix_sblock *sblock; - unsigned int pos = 0; - - data = grub_minix_mount (device->disk); - if (!data) - return grub_errno; - - grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE); - if (grub_errno) - goto fail; - - sblock = &data->sblock; - - grub_minix_find_file (data, path); - if (grub_errno) - goto fail; - - if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - goto fail; - } - - while (pos < GRUB_MINIX_INODE_SIZE (data)) - { - grub_uint16_t ino; - char filename[data->filename_size + 1]; - int dirino = data->ino; - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - - - if (grub_minix_read_file (data, 0, pos, sizeof (ino), - (char *) &ino) < 0) - return grub_errno; - - if (grub_minix_read_file (data, 0, pos + sizeof (ino), - data->filename_size, - (char *) filename) < 0) - return grub_errno; - filename[data->filename_size] = '\0'; - - /* The filetype is not stored in the dirent. Read the inode to - find out the filetype. This *REALLY* sucks. */ - grub_minix_read_inode (data, grub_le_to_cpu16 (ino)); - info.dir = ((GRUB_MINIX_INODE_MODE (data) - & GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR); - if (hook (filename, &info) ? 1 : 0) - break; - - /* Load the old inode back in. */ - grub_minix_read_inode (data, dirino); - - pos += sizeof (ino) + data->filename_size; - } - - fail: - grub_free (data); - return grub_errno; -} - - -/* Open a file named NAME and initialize FILE. */ -static grub_err_t -grub_minix_open (struct grub_file *file, const char *name) -{ - struct grub_minix_data *data; - data = grub_minix_mount (file->device->disk); - if (!data) - return grub_errno; - - /* Open the inode op the root directory. */ - grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE); - if (grub_errno) - { - grub_free (data); - return grub_errno; - } - - if (!name || name[0] != '/') - { - grub_error (GRUB_ERR_BAD_FILENAME, "bad filename"); - return grub_errno; - } - - /* Traverse the directory tree to the node that should be - opened. */ - grub_minix_find_file (data, name); - if (grub_errno) - { - grub_free (data); - return grub_errno; - } - - file->data = data; - file->size = GRUB_MINIX_INODE_SIZE (data); - - return GRUB_ERR_NONE; -} - - -static grub_ssize_t -grub_minix_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_minix_data *data = - (struct grub_minix_data *) file->data; - - return grub_minix_read_file (data, file->read_hook, file->offset, len, buf); -} - - -static grub_err_t -grub_minix_close (grub_file_t file) -{ - grub_free (file->data); - - return GRUB_ERR_NONE; -} - - -static grub_err_t -grub_minix_label (grub_device_t device __attribute ((unused)), - char **label __attribute ((unused))) -{ - return GRUB_ERR_NONE; -} - - -static struct grub_fs grub_minix_fs = - { - .name = "minix", - .dir = grub_minix_dir, - .open = grub_minix_open, - .read = grub_minix_read, - .close = grub_minix_close, - .label = grub_minix_label, - .next = 0 - }; - -GRUB_MOD_INIT(minix) -{ - grub_fs_register (&grub_minix_fs); - my_mod = mod; -} - -GRUB_MOD_FINI(minix) -{ - grub_fs_unregister (&grub_minix_fs); -} diff --git a/fs/ntfs.c b/fs/ntfs.c deleted file mode 100644 index dd041e23a..000000000 --- a/fs/ntfs.c +++ /dev/null @@ -1,1111 +0,0 @@ -/* ntfs.c - NTFS filesystem */ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static grub_dl_t my_mod; - -ntfscomp_func_t grub_ntfscomp_func; - -static grub_err_t -fixup (struct grub_ntfs_data *data, char *buf, int len, char *magic) -{ - int ss; - char *pu; - grub_uint16_t us; - - if (grub_memcmp (buf, magic, 4)) - return grub_error (GRUB_ERR_BAD_FS, "%s label not found", magic); - - ss = u16at (buf, 6) - 1; - if (ss * (int) data->blocksize != len * GRUB_DISK_SECTOR_SIZE) - return grub_error (GRUB_ERR_BAD_FS, "size not match", - ss * (int) data->blocksize, - len * GRUB_DISK_SECTOR_SIZE); - pu = buf + u16at (buf, 4); - us = u16at (pu, 0); - buf -= 2; - while (ss > 0) - { - buf += data->blocksize; - pu += 2; - if (u16at (buf, 0) != us) - return grub_error (GRUB_ERR_BAD_FS, "fixup signature not match"); - v16at (buf, 0) = v16at (pu, 0); - ss--; - } - - return 0; -} - -static grub_err_t read_mft (struct grub_ntfs_data *data, char *buf, - grub_uint32_t mftno); -static grub_err_t read_attr (struct grub_ntfs_attr *at, char *dest, - grub_disk_addr_t ofs, grub_size_t len, - int cached, - void - NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t - sector, - unsigned offset, - unsigned length)); - -static grub_err_t read_data (struct grub_ntfs_attr *at, char *pa, char *dest, - grub_disk_addr_t ofs, grub_size_t len, - int cached, - void - NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t - sector, - unsigned offset, - unsigned length)); - -static void -init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) -{ - at->mft = mft; - at->flags = (mft == &mft->data->mmft) ? AF_MMFT : 0; - at->attr_nxt = mft->buf + u16at (mft->buf, 0x14); - at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; -} - -static void -free_attr (struct grub_ntfs_attr *at) -{ - grub_free (at->emft_buf); - grub_free (at->edat_buf); - grub_free (at->sbuf); -} - -static char * -find_attr (struct grub_ntfs_attr *at, unsigned char attr) -{ - if (at->flags & AF_ALST) - { - retry: - while (at->attr_nxt < at->attr_end) - { - at->attr_cur = at->attr_nxt; - at->attr_nxt += u16at (at->attr_cur, 4); - if (((unsigned char) *at->attr_cur == attr) || (attr == 0)) - { - char *new_pos; - - if (at->flags & AF_MMFT) - { - if ((grub_disk_read - (at->mft->data->disk, v32at (at->attr_cur, 0x10), 0, - 512, at->emft_buf)) - || - (grub_disk_read - (at->mft->data->disk, v32at (at->attr_cur, 0x14), 0, - 512, at->emft_buf + 512))) - return NULL; - - if (fixup - (at->mft->data, at->emft_buf, at->mft->data->mft_size, - "FILE")) - return NULL; - } - else - { - if (read_mft (at->mft->data, at->emft_buf, - u32at (at->attr_cur, 0x10))) - return NULL; - } - - new_pos = &at->emft_buf[u16at (at->emft_buf, 0x14)]; - while ((unsigned char) *new_pos != 0xFF) - { - if (((unsigned char) *new_pos == - (unsigned char) *at->attr_cur) - && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18))) - { - return new_pos; - } - new_pos += u16at (new_pos, 4); - } - grub_error (GRUB_ERR_BAD_FS, - "can\'t find 0x%X in attribute list", - (unsigned char) *at->attr_cur); - return NULL; - } - } - return NULL; - } - at->attr_cur = at->attr_nxt; - while ((unsigned char) *at->attr_cur != 0xFF) - { - at->attr_nxt += u16at (at->attr_cur, 4); - if ((unsigned char) *at->attr_cur == AT_ATTRIBUTE_LIST) - at->attr_end = at->attr_cur; - if (((unsigned char) *at->attr_cur == attr) || (attr == 0)) - return at->attr_cur; - at->attr_cur = at->attr_nxt; - } - if (at->attr_end) - { - char *pa; - - at->emft_buf = grub_malloc (at->mft->data->mft_size << BLK_SHR); - if (at->emft_buf == NULL) - return NULL; - - pa = at->attr_end; - if (pa[8]) - { - int n; - - n = ((u32at (pa, 0x30) + GRUB_DISK_SECTOR_SIZE - 1) - & (~(GRUB_DISK_SECTOR_SIZE - 1))); - at->attr_cur = at->attr_end; - at->edat_buf = grub_malloc (n); - if (!at->edat_buf) - return NULL; - if (read_data (at, pa, at->edat_buf, 0, n, 0, 0)) - { - grub_error (GRUB_ERR_BAD_FS, - "fail to read non-resident attribute list"); - return NULL; - } - at->attr_nxt = at->edat_buf; - at->attr_end = at->edat_buf + u32at (pa, 0x30); - } - else - { - at->attr_nxt = at->attr_end + u16at (pa, 0x14); - at->attr_end = at->attr_end + u32at (pa, 4); - } - at->flags |= AF_ALST; - while (at->attr_nxt < at->attr_end) - { - if (((unsigned char) *at->attr_nxt == attr) || (attr == 0)) - break; - at->attr_nxt += u16at (at->attr_nxt, 4); - } - if (at->attr_nxt >= at->attr_end) - return NULL; - - if ((at->flags & AF_MMFT) && (attr == AT_DATA)) - { - at->flags |= AF_GPOS; - at->attr_cur = at->attr_nxt; - pa = at->attr_cur; - v32at (pa, 0x10) = at->mft->data->mft_start; - v32at (pa, 0x14) = at->mft->data->mft_start + 1; - pa = at->attr_nxt + u16at (pa, 4); - while (pa < at->attr_end) - { - if ((unsigned char) *pa != attr) - break; - if (read_attr - (at, pa + 0x10, - u32at (pa, 0x10) * (at->mft->data->mft_size << BLK_SHR), - at->mft->data->mft_size << BLK_SHR, 0, 0)) - return NULL; - pa += u16at (pa, 4); - } - at->attr_nxt = at->attr_cur; - at->flags &= ~AF_GPOS; - } - goto retry; - } - return NULL; -} - -static char * -locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, - unsigned char attr) -{ - char *pa; - - init_attr (at, mft); - if ((pa = find_attr (at, attr)) == NULL) - return NULL; - if ((at->flags & AF_ALST) == 0) - { - while (1) - { - if ((pa = find_attr (at, attr)) == NULL) - break; - if (at->flags & AF_ALST) - return pa; - } - grub_errno = GRUB_ERR_NONE; - free_attr (at); - init_attr (at, mft); - pa = find_attr (at, attr); - } - return pa; -} - -static char * -read_run_data (char *run, int nn, grub_disk_addr_t * val, int sig) -{ - grub_disk_addr_t r, v; - - r = 0; - v = 1; - - while (nn--) - { - r += v * (*(unsigned char *) (run++)); - v <<= 8; - } - - if ((sig) && (r & (v >> 1))) - r -= v; - - *val = r; - return run; -} - -grub_err_t -grub_ntfs_read_run_list (struct grub_ntfs_rlst * ctx) -{ - int c1, c2; - grub_disk_addr_t val; - char *run; - - run = ctx->cur_run; -retry: - c1 = ((unsigned char) (*run) & 0xF); - c2 = ((unsigned char) (*run) >> 4); - if (!c1) - { - if ((ctx->attr) && (ctx->attr->flags & AF_ALST)) - { - void NESTED_FUNC_ATTR (*save_hook) (grub_disk_addr_t sector, - unsigned offset, - unsigned length); - - save_hook = ctx->comp.disk->read_hook; - ctx->comp.disk->read_hook = 0; - run = find_attr (ctx->attr, (unsigned char) *ctx->attr->attr_cur); - ctx->comp.disk->read_hook = save_hook; - if (run) - { - if (run[8] == 0) - return grub_error (GRUB_ERR_BAD_FS, - "$DATA should be non-resident"); - - run += u16at (run, 0x20); - ctx->curr_lcn = 0; - goto retry; - } - } - return grub_error (GRUB_ERR_BAD_FS, "run list overflown"); - } - run = read_run_data (run + 1, c1, &val, 0); /* length of current VCN */ - ctx->curr_vcn = ctx->next_vcn; - ctx->next_vcn += val; - run = read_run_data (run, c2, &val, 1); /* offset to previous LCN */ - ctx->curr_lcn += val; - if (val == 0) - ctx->flags |= RF_BLNK; - else - ctx->flags &= ~RF_BLNK; - ctx->cur_run = run; - return 0; -} - -static grub_disk_addr_t -grub_ntfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t block) -{ - struct grub_ntfs_rlst *ctx; - - ctx = (struct grub_ntfs_rlst *) node; - if (block >= ctx->next_vcn) - { - if (grub_ntfs_read_run_list (ctx)) - return -1; - return ctx->curr_lcn; - } - else - return (ctx->flags & RF_BLNK) ? 0 : (block - - ctx->curr_vcn + ctx->curr_lcn); -} - -static grub_err_t -read_data (struct grub_ntfs_attr *at, char *pa, char *dest, - grub_disk_addr_t ofs, grub_size_t len, int cached, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, - unsigned length)) -{ - grub_disk_addr_t vcn; - struct grub_ntfs_rlst cc, *ctx; - - if (len == 0) - return 0; - - grub_memset (&cc, 0, sizeof (cc)); - ctx = &cc; - ctx->attr = at; - ctx->comp.spc = at->mft->data->spc; - ctx->comp.disk = at->mft->data->disk; - - if (pa[8] == 0) - { - if (ofs + len > u32at (pa, 0x10)) - return grub_error (GRUB_ERR_BAD_FS, "read out of range"); - grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len); - return 0; - } - - if (u16at (pa, 0xC) & FLAG_COMPRESSED) - ctx->flags |= RF_COMP; - else - ctx->flags &= ~RF_COMP; - ctx->cur_run = pa + u16at (pa, 0x20); - - if (ctx->flags & RF_COMP) - { - if (!cached) - return grub_error (GRUB_ERR_BAD_FS, "attribute can\'t be compressed"); - - if (at->sbuf) - { - if ((ofs & (~(COM_LEN - 1))) == at->save_pos) - { - grub_disk_addr_t n; - - n = COM_LEN - (ofs - at->save_pos); - if (n > len) - n = len; - - grub_memcpy (dest, at->sbuf + ofs - at->save_pos, n); - if (n == len) - return 0; - - dest += n; - len -= n; - ofs += n; - } - } - else - { - at->sbuf = grub_malloc (COM_LEN); - if (at->sbuf == NULL) - return grub_errno; - at->save_pos = 1; - } - - vcn = ctx->target_vcn = (ofs >> COM_LOG_LEN) * (COM_SEC / ctx->comp.spc); - ctx->target_vcn &= ~0xF; - } - else - vcn = ctx->target_vcn = grub_divmod64 (ofs >> BLK_SHR, ctx->comp.spc, 0); - - ctx->next_vcn = u32at (pa, 0x10); - ctx->curr_lcn = 0; - while (ctx->next_vcn <= ctx->target_vcn) - { - if (grub_ntfs_read_run_list (ctx)) - return grub_errno; - } - - if (at->flags & AF_GPOS) - { - grub_disk_addr_t st0, st1; - grub_uint32_t m; - - grub_divmod64 (ofs >> BLK_SHR, ctx->comp.spc, &m); - - st0 = - (ctx->target_vcn - ctx->curr_vcn + ctx->curr_lcn) * ctx->comp.spc + m; - st1 = st0 + 1; - if (st1 == - (ctx->next_vcn - ctx->curr_vcn + ctx->curr_lcn) * ctx->comp.spc) - { - if (grub_ntfs_read_run_list (ctx)) - return grub_errno; - st1 = ctx->curr_lcn * ctx->comp.spc; - } - v32at (dest, 0) = st0; - v32at (dest, 4) = st1; - return 0; - } - - if (!(ctx->flags & RF_COMP)) - { - unsigned int pow; - - if (!grub_fshelp_log2blksize (ctx->comp.spc, &pow)) - grub_fshelp_read_file (ctx->comp.disk, (grub_fshelp_node_t) ctx, - read_hook, ofs, len, dest, - grub_ntfs_read_block, ofs + len, pow); - return grub_errno; - } - - return (grub_ntfscomp_func) ? grub_ntfscomp_func (at, dest, ofs, len, ctx, - vcn) : - grub_error (GRUB_ERR_BAD_FS, "ntfscomp module not loaded"); -} - -static grub_err_t -read_attr (struct grub_ntfs_attr *at, char *dest, grub_disk_addr_t ofs, - grub_size_t len, int cached, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, - unsigned length)) -{ - char *save_cur; - unsigned char attr; - char *pp; - grub_err_t ret; - - save_cur = at->attr_cur; - at->attr_nxt = at->attr_cur; - attr = (unsigned char) *at->attr_nxt; - if (at->flags & AF_ALST) - { - char *pa; - grub_disk_addr_t vcn; - - vcn = grub_divmod64 (ofs, at->mft->data->spc << BLK_SHR, 0); - pa = at->attr_nxt + u16at (at->attr_nxt, 4); - while (pa < at->attr_end) - { - if ((unsigned char) *pa != attr) - break; - if (u32at (pa, 8) > vcn) - break; - at->attr_nxt = pa; - pa += u16at (pa, 4); - } - } - pp = find_attr (at, attr); - if (pp) - ret = read_data (at, pp, dest, ofs, len, cached, read_hook); - else - ret = - (grub_errno) ? grub_errno : grub_error (GRUB_ERR_BAD_FS, - "attribute not found"); - at->attr_cur = save_cur; - return ret; -} - -static grub_err_t -read_mft (struct grub_ntfs_data *data, char *buf, grub_uint32_t mftno) -{ - if (read_attr - (&data->mmft.attr, buf, mftno * ((grub_disk_addr_t) data->mft_size << BLK_SHR), - data->mft_size << BLK_SHR, 0, 0)) - return grub_error (GRUB_ERR_BAD_FS, "read MFT 0x%X fails", mftno); - return fixup (data, buf, data->mft_size, "FILE"); -} - -static grub_err_t -init_file (struct grub_ntfs_file *mft, grub_uint32_t mftno) -{ - unsigned short flag; - - mft->inode_read = 1; - - mft->buf = grub_malloc (mft->data->mft_size << BLK_SHR); - if (mft->buf == NULL) - return grub_errno; - - if (read_mft (mft->data, mft->buf, mftno)) - return grub_errno; - - flag = u16at (mft->buf, 0x16); - if ((flag & 1) == 0) - return grub_error (GRUB_ERR_BAD_FS, "MFT 0x%X is not in use", mftno); - - if ((flag & 2) == 0) - { - char *pa; - - pa = locate_attr (&mft->attr, mft, AT_DATA); - if (pa == NULL) - return grub_error (GRUB_ERR_BAD_FS, "no $DATA in MFT 0x%X", mftno); - - if (!pa[8]) - mft->size = u32at (pa, 0x10); - else - mft->size = u64at (pa, 0x30); - - if ((mft->attr.flags & AF_ALST) == 0) - mft->attr.attr_end = 0; /* Don't jump to attribute list */ - } - else - init_attr (&mft->attr, mft); - - return 0; -} - -static void -free_file (struct grub_ntfs_file *mft) -{ - free_attr (&mft->attr); - grub_free (mft->buf); -} - -static int -list_file (struct grub_ntfs_file *diro, char *pos, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - char *np; - int ns; - - while (1) - { - char *ustr, namespace; - - if (pos[0xC] & 2) /* end signature */ - break; - - np = pos + 0x50; - ns = (unsigned char) *(np++); - namespace = *(np++); - - /* - * Ignore files in DOS namespace, as they will reappear as Win32 - * names. - */ - if ((ns) && (namespace != 2)) - { - enum grub_fshelp_filetype type; - struct grub_ntfs_file *fdiro; - - if (u16at (pos, 4)) - { - grub_error (GRUB_ERR_BAD_FS, "64-bit MFT number"); - return 0; - } - - type = - (u32at (pos, 0x48) & ATTR_DIRECTORY) ? GRUB_FSHELP_DIR : - GRUB_FSHELP_REG; - - fdiro = grub_zalloc (sizeof (struct grub_ntfs_file)); - if (!fdiro) - return 0; - - fdiro->data = diro->data; - fdiro->ino = u32at (pos, 0); - - ustr = grub_malloc (ns * 4 + 1); - if (ustr == NULL) - return 0; - *grub_utf16_to_utf8 ((grub_uint8_t *) ustr, (grub_uint16_t *) np, - ns) = '\0'; - - if (namespace) - type |= GRUB_FSHELP_CASE_INSENSITIVE; - - if (hook (ustr, type, fdiro)) - { - grub_free (ustr); - return 1; - } - - grub_free (ustr); - } - pos += u16at (pos, 8); - } - return 0; -} - -static int -grub_ntfs_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - unsigned char *bitmap; - struct grub_ntfs_attr attr, *at; - char *cur_pos, *indx, *bmp; - int ret = 0; - grub_size_t bitmap_len; - struct grub_ntfs_file *mft; - - mft = (struct grub_ntfs_file *) dir; - - if (!mft->inode_read) - { - if (init_file (mft, mft->ino)) - return 0; - } - - indx = NULL; - bmp = NULL; - - at = &attr; - init_attr (at, mft); - while (1) - { - if ((cur_pos = find_attr (at, AT_INDEX_ROOT)) == NULL) - { - grub_error (GRUB_ERR_BAD_FS, "no $INDEX_ROOT"); - goto done; - } - - /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */ - if ((u32at (cur_pos, 8) != 0x180400) || - (u32at (cur_pos, 0x18) != 0x490024) || - (u32at (cur_pos, 0x1C) != 0x300033)) - continue; - cur_pos += u16at (cur_pos, 0x14); - if (*cur_pos != 0x30) /* Not filename index */ - continue; - break; - } - - cur_pos += 0x10; /* Skip index root */ - ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook); - if (ret) - goto done; - - bitmap = NULL; - bitmap_len = 0; - free_attr (at); - init_attr (at, mft); - while ((cur_pos = find_attr (at, AT_BITMAP)) != NULL) - { - int ofs; - - ofs = (unsigned char) cur_pos[0xA]; - /* Namelen=4, Name="$I30" */ - if ((cur_pos[9] == 4) && - (u32at (cur_pos, ofs) == 0x490024) && - (u32at (cur_pos, ofs + 4) == 0x300033)) - { - int is_resident = (cur_pos[8] == 0); - - bitmap_len = ((is_resident) ? u32at (cur_pos, 0x10) : - u32at (cur_pos, 0x28)); - - bmp = grub_malloc (bitmap_len); - if (bmp == NULL) - goto done; - - if (is_resident) - { - grub_memcpy (bmp, (char *) (cur_pos + u16at (cur_pos, 0x14)), - bitmap_len); - } - else - { - if (read_data (at, cur_pos, bmp, 0, bitmap_len, 0, 0)) - { - grub_error (GRUB_ERR_BAD_FS, - "fails to read non-resident $BITMAP"); - goto done; - } - bitmap_len = u32at (cur_pos, 0x30); - } - - bitmap = (unsigned char *) bmp; - break; - } - } - - free_attr (at); - cur_pos = locate_attr (at, mft, AT_INDEX_ALLOCATION); - while (cur_pos != NULL) - { - /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */ - if ((u32at (cur_pos, 8) == 0x400401) && - (u32at (cur_pos, 0x40) == 0x490024) && - (u32at (cur_pos, 0x44) == 0x300033)) - break; - cur_pos = find_attr (at, AT_INDEX_ALLOCATION); - } - - if ((!cur_pos) && (bitmap)) - { - grub_error (GRUB_ERR_BAD_FS, "$BITMAP without $INDEX_ALLOCATION"); - goto done; - } - - if (bitmap) - { - grub_disk_addr_t v, i; - - indx = grub_malloc (mft->data->idx_size << BLK_SHR); - if (indx == NULL) - goto done; - - v = 1; - for (i = 0; i < (grub_disk_addr_t)bitmap_len * 8; i++) - { - if (*bitmap & v) - { - if ((read_attr - (at, indx, i * (mft->data->idx_size << BLK_SHR), - (mft->data->idx_size << BLK_SHR), 0, 0)) - || (fixup (mft->data, indx, mft->data->idx_size, "INDX"))) - goto done; - ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], hook); - if (ret) - goto done; - } - v <<= 1; - if (v >= 0x100) - { - v = 1; - bitmap++; - } - } - } - -done: - free_attr (at); - grub_free (indx); - grub_free (bmp); - - return ret; -} - -static struct grub_ntfs_data * -grub_ntfs_mount (grub_disk_t disk) -{ - struct grub_ntfs_bpb bpb; - struct grub_ntfs_data *data = 0; - - if (!disk) - goto fail; - - data = (struct grub_ntfs_data *) grub_zalloc (sizeof (*data)); - if (!data) - goto fail; - - data->disk = disk; - - /* Read the BPB. */ - if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb)) - goto fail; - - if (grub_memcmp ((char *) &bpb.oem_name, "NTFS", 4)) - goto fail; - - data->blocksize = grub_le_to_cpu16 (bpb.bytes_per_sector); - data->spc = bpb.sectors_per_cluster * (data->blocksize >> BLK_SHR); - - if (bpb.clusters_per_mft > 0) - data->mft_size = data->spc * bpb.clusters_per_mft; - else - data->mft_size = 1 << (-bpb.clusters_per_mft - BLK_SHR); - - if (bpb.clusters_per_index > 0) - data->idx_size = data->spc * bpb.clusters_per_index; - else - data->idx_size = 1 << (-bpb.clusters_per_index - BLK_SHR); - - data->mft_start = grub_le_to_cpu64 (bpb.mft_lcn) * data->spc; - - if ((data->mft_size > MAX_MFT) || (data->idx_size > MAX_IDX)) - goto fail; - - data->mmft.data = data; - data->cmft.data = data; - - data->mmft.buf = grub_malloc (data->mft_size << BLK_SHR); - if (!data->mmft.buf) - goto fail; - - if (grub_disk_read - (disk, data->mft_start, 0, data->mft_size << BLK_SHR, data->mmft.buf)) - goto fail; - - data->uuid = grub_le_to_cpu64 (bpb.num_serial); - - if (fixup (data, data->mmft.buf, data->mft_size, "FILE")) - goto fail; - - if (!locate_attr (&data->mmft.attr, &data->mmft, AT_DATA)) - goto fail; - - if (init_file (&data->cmft, FILE_ROOT)) - goto fail; - - return data; - -fail: - grub_error (GRUB_ERR_BAD_FS, "not an ntfs filesystem"); - - if (data) - { - free_file (&data->mmft); - free_file (&data->cmft); - grub_free (data); - } - return 0; -} - -static grub_err_t -grub_ntfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_ntfs_data *data = 0; - struct grub_fshelp_node *fdiro = 0; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_ntfs_mount (device->disk); - if (!data) - goto fail; - - grub_fshelp_find_file (path, &data->cmft, &fdiro, grub_ntfs_iterate_dir, - 0, GRUB_FSHELP_DIR); - - if (grub_errno) - goto fail; - - grub_ntfs_iterate_dir (fdiro, iterate); - -fail: - if ((fdiro) && (fdiro != &data->cmft)) - { - free_file (fdiro); - grub_free (fdiro); - } - if (data) - { - free_file (&data->mmft); - free_file (&data->cmft); - grub_free (data); - } - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_ntfs_open (grub_file_t file, const char *name) -{ - struct grub_ntfs_data *data = 0; - struct grub_fshelp_node *mft = 0; - - grub_dl_ref (my_mod); - - data = grub_ntfs_mount (file->device->disk); - if (!data) - goto fail; - - grub_fshelp_find_file (name, &data->cmft, &mft, grub_ntfs_iterate_dir, - 0, GRUB_FSHELP_REG); - - if (grub_errno) - goto fail; - - if (mft != &data->cmft) - { - free_file (&data->cmft); - grub_memcpy (&data->cmft, mft, sizeof (*mft)); - grub_free (mft); - if (!data->cmft.inode_read) - { - if (init_file (&data->cmft, data->cmft.ino)) - goto fail; - } - } - - file->size = data->cmft.size; - file->data = data; - file->offset = 0; - - return 0; - -fail: - if (data) - { - free_file (&data->mmft); - free_file (&data->cmft); - grub_free (data); - } - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_ssize_t -grub_ntfs_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_ntfs_file *mft; - - mft = &((struct grub_ntfs_data *) file->data)->cmft; - if (file->read_hook) - mft->attr.save_pos = 1; - - read_attr (&mft->attr, buf, file->offset, len, 1, file->read_hook); - return (grub_errno) ? 0 : len; -} - -static grub_err_t -grub_ntfs_close (grub_file_t file) -{ - struct grub_ntfs_data *data; - - data = file->data; - - if (data) - { - free_file (&data->mmft); - free_file (&data->cmft); - grub_free (data); - } - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_ntfs_label (grub_device_t device, char **label) -{ - struct grub_ntfs_data *data = 0; - struct grub_fshelp_node *mft = 0; - char *pa; - - grub_dl_ref (my_mod); - - *label = 0; - - data = grub_ntfs_mount (device->disk); - if (!data) - goto fail; - - grub_fshelp_find_file ("/$Volume", &data->cmft, &mft, grub_ntfs_iterate_dir, - 0, GRUB_FSHELP_REG); - - if (grub_errno) - goto fail; - - if (!mft->inode_read) - { - mft->buf = grub_malloc (mft->data->mft_size << BLK_SHR); - if (mft->buf == NULL) - goto fail; - - if (read_mft (mft->data, mft->buf, mft->ino)) - goto fail; - } - - init_attr (&mft->attr, mft); - pa = find_attr (&mft->attr, AT_VOLUME_NAME); - if ((pa) && (pa[8] == 0) && (u32at (pa, 0x10))) - { - char *buf; - int len; - - len = u32at (pa, 0x10) / 2; - buf = grub_malloc (len * 4 + 1); - pa += u16at (pa, 0x14); - *grub_utf16_to_utf8 ((grub_uint8_t *) buf, (grub_uint16_t *) pa, len) = - '\0'; - *label = buf; - } - -fail: - if ((mft) && (mft != &data->cmft)) - { - free_file (mft); - grub_free (mft); - } - if (data) - { - free_file (&data->mmft); - free_file (&data->cmft); - grub_free (data); - } - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_ntfs_uuid (grub_device_t device, char **uuid) -{ - struct grub_ntfs_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_ntfs_mount (disk); - if (data) - { - *uuid = grub_xasprintf ("%016llx", (unsigned long long) data->uuid); - } - else - *uuid = NULL; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - -static struct grub_fs grub_ntfs_fs = - { - .name = "ntfs", - .dir = grub_ntfs_dir, - .open = grub_ntfs_open, - .read = grub_ntfs_read, - .close = grub_ntfs_close, - .label = grub_ntfs_label, - .uuid = grub_ntfs_uuid, -#ifdef GRUB_UTIL - .reserved_first_sector = 1, -#endif - .next = 0 -}; - -GRUB_MOD_INIT (ntfs) -{ - grub_fs_register (&grub_ntfs_fs); - my_mod = mod; -} - -GRUB_MOD_FINI (ntfs) -{ - grub_fs_unregister (&grub_ntfs_fs); -} diff --git a/fs/tar.c b/fs/tar.c deleted file mode 100644 index 6ab62bca7..000000000 --- a/fs/tar.c +++ /dev/null @@ -1,2 +0,0 @@ -#define MODE_USTAR 1 -#include "cpio.c" diff --git a/fs/udf.c b/fs/udf.c deleted file mode 100644 index cecb6eb78..000000000 --- a/fs/udf.c +++ /dev/null @@ -1,916 +0,0 @@ -/* udf.c - Universal Disk Format filesystem. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define GRUB_UDF_MAX_PDS 2 -#define GRUB_UDF_MAX_PMS 6 - -#define U16 grub_le_to_cpu16 -#define U32 grub_le_to_cpu32 -#define U64 grub_le_to_cpu64 - -#define GRUB_UDF_LOG2_BLKSZ 2 -#define GRUB_UDF_BLKSZ 2048 - -#define GRUB_UDF_TAG_IDENT_PVD 0x0001 -#define GRUB_UDF_TAG_IDENT_AVDP 0x0002 -#define GRUB_UDF_TAG_IDENT_VDP 0x0003 -#define GRUB_UDF_TAG_IDENT_IUVD 0x0004 -#define GRUB_UDF_TAG_IDENT_PD 0x0005 -#define GRUB_UDF_TAG_IDENT_LVD 0x0006 -#define GRUB_UDF_TAG_IDENT_USD 0x0007 -#define GRUB_UDF_TAG_IDENT_TD 0x0008 -#define GRUB_UDF_TAG_IDENT_LVID 0x0009 - -#define GRUB_UDF_TAG_IDENT_FSD 0x0100 -#define GRUB_UDF_TAG_IDENT_FID 0x0101 -#define GRUB_UDF_TAG_IDENT_AED 0x0102 -#define GRUB_UDF_TAG_IDENT_IE 0x0103 -#define GRUB_UDF_TAG_IDENT_TE 0x0104 -#define GRUB_UDF_TAG_IDENT_FE 0x0105 -#define GRUB_UDF_TAG_IDENT_EAHD 0x0106 -#define GRUB_UDF_TAG_IDENT_USE 0x0107 -#define GRUB_UDF_TAG_IDENT_SBD 0x0108 -#define GRUB_UDF_TAG_IDENT_PIE 0x0109 -#define GRUB_UDF_TAG_IDENT_EFE 0x010A - -#define GRUB_UDF_ICBTAG_TYPE_UNDEF 0x00 -#define GRUB_UDF_ICBTAG_TYPE_USE 0x01 -#define GRUB_UDF_ICBTAG_TYPE_PIE 0x02 -#define GRUB_UDF_ICBTAG_TYPE_IE 0x03 -#define GRUB_UDF_ICBTAG_TYPE_DIRECTORY 0x04 -#define GRUB_UDF_ICBTAG_TYPE_REGULAR 0x05 -#define GRUB_UDF_ICBTAG_TYPE_BLOCK 0x06 -#define GRUB_UDF_ICBTAG_TYPE_CHAR 0x07 -#define GRUB_UDF_ICBTAG_TYPE_EA 0x08 -#define GRUB_UDF_ICBTAG_TYPE_FIFO 0x09 -#define GRUB_UDF_ICBTAG_TYPE_SOCKET 0x0A -#define GRUB_UDF_ICBTAG_TYPE_TE 0x0B -#define GRUB_UDF_ICBTAG_TYPE_SYMLINK 0x0C -#define GRUB_UDF_ICBTAG_TYPE_STREAMDIR 0x0D - -#define GRUB_UDF_ICBTAG_FLAG_AD_MASK 0x0007 -#define GRUB_UDF_ICBTAG_FLAG_AD_SHORT 0x0000 -#define GRUB_UDF_ICBTAG_FLAG_AD_LONG 0x0001 -#define GRUB_UDF_ICBTAG_FLAG_AD_EXT 0x0002 -#define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB 0x0003 - -#define GRUB_UDF_EXT_NORMAL 0x00000000 -#define GRUB_UDF_EXT_NREC_ALLOC 0x40000000 -#define GRUB_UDF_EXT_NREC_NALLOC 0x80000000 -#define GRUB_UDF_EXT_MASK 0xC0000000 - -#define GRUB_UDF_FID_CHAR_HIDDEN 0x01 -#define GRUB_UDF_FID_CHAR_DIRECTORY 0x02 -#define GRUB_UDF_FID_CHAR_DELETED 0x04 -#define GRUB_UDF_FID_CHAR_PARENT 0x08 -#define GRUB_UDF_FID_CHAR_METADATA 0x10 - -#define GRUB_UDF_STD_IDENT_BEA01 "BEA01" -#define GRUB_UDF_STD_IDENT_BOOT2 "BOOT2" -#define GRUB_UDF_STD_IDENT_CD001 "CD001" -#define GRUB_UDF_STD_IDENT_CDW02 "CDW02" -#define GRUB_UDF_STD_IDENT_NSR02 "NSR02" -#define GRUB_UDF_STD_IDENT_NSR03 "NSR03" -#define GRUB_UDF_STD_IDENT_TEA01 "TEA01" - -#define GRUB_UDF_CHARSPEC_TYPE_CS0 0x00 -#define GRUB_UDF_CHARSPEC_TYPE_CS1 0x01 -#define GRUB_UDF_CHARSPEC_TYPE_CS2 0x02 -#define GRUB_UDF_CHARSPEC_TYPE_CS3 0x03 -#define GRUB_UDF_CHARSPEC_TYPE_CS4 0x04 -#define GRUB_UDF_CHARSPEC_TYPE_CS5 0x05 -#define GRUB_UDF_CHARSPEC_TYPE_CS6 0x06 -#define GRUB_UDF_CHARSPEC_TYPE_CS7 0x07 -#define GRUB_UDF_CHARSPEC_TYPE_CS8 0x08 - -#define GRUB_UDF_PARTMAP_TYPE_1 1 -#define GRUB_UDF_PARTMAP_TYPE_2 2 - -struct grub_udf_lb_addr -{ - grub_uint32_t block_num; - grub_uint16_t part_ref; -} __attribute__ ((packed)); - -struct grub_udf_short_ad -{ - grub_uint32_t length; - grub_uint32_t position; -} __attribute__ ((packed)); - -struct grub_udf_long_ad -{ - grub_uint32_t length; - struct grub_udf_lb_addr block; - grub_uint8_t imp_use[6]; -} __attribute__ ((packed)); - -struct grub_udf_extent_ad -{ - grub_uint32_t length; - grub_uint32_t start; -} __attribute__ ((packed)); - -struct grub_udf_charspec -{ - grub_uint8_t charset_type; - grub_uint8_t charset_info[63]; -} __attribute__ ((packed)); - -struct grub_udf_timestamp -{ - grub_uint16_t type_and_timezone; - grub_uint16_t year; - grub_uint8_t month; - grub_uint8_t day; - grub_uint8_t hour; - grub_uint8_t minute; - grub_uint8_t second; - grub_uint8_t centi_seconds; - grub_uint8_t hundreds_of_micro_seconds; - grub_uint8_t micro_seconds; -} __attribute__ ((packed)); - -struct grub_udf_regid -{ - grub_uint8_t flags; - grub_uint8_t ident[23]; - grub_uint8_t ident_suffix[8]; -} __attribute__ ((packed)); - -struct grub_udf_tag -{ - grub_uint16_t tag_ident; - grub_uint16_t desc_version; - grub_uint8_t tag_checksum; - grub_uint8_t reserved; - grub_uint16_t tag_serial_number; - grub_uint16_t desc_crc; - grub_uint16_t desc_crc_length; - grub_uint32_t tag_location; -} __attribute__ ((packed)); - -struct grub_udf_fileset -{ - struct grub_udf_tag tag; - struct grub_udf_timestamp datetime; - grub_uint16_t interchange_level; - grub_uint16_t max_interchange_level; - grub_uint32_t charset_list; - grub_uint32_t max_charset_list; - grub_uint32_t fileset_num; - grub_uint32_t fileset_desc_num; - struct grub_udf_charspec vol_charset; - grub_uint8_t vol_ident[128]; - struct grub_udf_charspec fileset_charset; - grub_uint8_t fileset_ident[32]; - grub_uint8_t copyright_file_ident[32]; - grub_uint8_t abstract_file_ident[32]; - struct grub_udf_long_ad root_icb; - struct grub_udf_regid domain_ident; - struct grub_udf_long_ad next_ext; - struct grub_udf_long_ad streamdir_icb; -} __attribute__ ((packed)); - -struct grub_udf_icbtag -{ - grub_uint32_t prior_recorded_num_direct_entries; - grub_uint16_t strategy_type; - grub_uint16_t strategy_parameter; - grub_uint16_t num_entries; - grub_uint8_t reserved; - grub_uint8_t file_type; - struct grub_udf_lb_addr parent_idb; - grub_uint16_t flags; -} __attribute__ ((packed)); - -struct grub_udf_file_ident -{ - struct grub_udf_tag tag; - grub_uint16_t version_num; - grub_uint8_t characteristics; - grub_uint8_t file_ident_length; - struct grub_udf_long_ad icb; - grub_uint16_t imp_use_length; -} __attribute__ ((packed)); - -struct grub_udf_file_entry -{ - struct grub_udf_tag tag; - struct grub_udf_icbtag icbtag; - grub_uint32_t uid; - grub_uint32_t gid; - grub_uint32_t permissions; - grub_uint16_t link_count; - grub_uint8_t record_format; - grub_uint8_t record_display_attr; - grub_uint32_t record_length; - grub_uint64_t file_size; - grub_uint64_t blocks_recorded; - struct grub_udf_timestamp access_time; - struct grub_udf_timestamp modification_time; - struct grub_udf_timestamp attr_time; - grub_uint32_t checkpoint; - struct grub_udf_long_ad extended_attr_idb; - struct grub_udf_regid imp_ident; - grub_uint64_t unique_id; - grub_uint32_t ext_attr_length; - grub_uint32_t alloc_descs_length; - grub_uint8_t ext_attr[1872]; -} __attribute__ ((packed)); - -struct grub_udf_extended_file_entry -{ - struct grub_udf_tag tag; - struct grub_udf_icbtag icbtag; - grub_uint32_t uid; - grub_uint32_t gid; - grub_uint32_t permissions; - grub_uint16_t link_count; - grub_uint8_t record_format; - grub_uint8_t record_display_attr; - grub_uint32_t record_length; - grub_uint64_t file_size; - grub_uint64_t object_size; - grub_uint64_t blocks_recorded; - struct grub_udf_timestamp access_time; - struct grub_udf_timestamp modification_time; - struct grub_udf_timestamp create_time; - struct grub_udf_timestamp attr_time; - grub_uint32_t checkpoint; - grub_uint32_t reserved; - struct grub_udf_long_ad extended_attr_icb; - struct grub_udf_long_ad streamdir_icb; - struct grub_udf_regid imp_ident; - grub_uint64_t unique_id; - grub_uint32_t ext_attr_length; - grub_uint32_t alloc_descs_length; - grub_uint8_t ext_attr[1832]; -} __attribute__ ((packed)); - -struct grub_udf_vrs -{ - grub_uint8_t type; - grub_uint8_t magic[5]; - grub_uint8_t version; -} __attribute__ ((packed)); - -struct grub_udf_avdp -{ - struct grub_udf_tag tag; - struct grub_udf_extent_ad vds; -} __attribute__ ((packed)); - -struct grub_udf_pd -{ - struct grub_udf_tag tag; - grub_uint32_t seq_num; - grub_uint16_t flags; - grub_uint16_t part_num; - struct grub_udf_regid contents; - grub_uint8_t contents_use[128]; - grub_uint32_t access_type; - grub_uint32_t start; - grub_uint32_t length; -} __attribute__ ((packed)); - -struct grub_udf_partmap -{ - grub_uint8_t type; - grub_uint8_t length; - union - { - struct - { - grub_uint16_t seq_num; - grub_uint16_t part_num; - } type1; - - struct - { - grub_uint8_t ident[62]; - } type2; - }; -}; - -struct grub_udf_lvd -{ - struct grub_udf_tag tag; - grub_uint32_t seq_num; - struct grub_udf_charspec charset; - grub_uint8_t ident[128]; - grub_uint32_t bsize; - struct grub_udf_regid domain_ident; - struct grub_udf_long_ad root_fileset; - grub_uint32_t map_table_length; - grub_uint32_t num_part_maps; - struct grub_udf_regid imp_ident; - grub_uint8_t imp_use[128]; - struct grub_udf_extent_ad integrity_seq_ext; - grub_uint8_t part_maps[1608]; -} __attribute__ ((packed)); - -struct grub_udf_data -{ - grub_disk_t disk; - struct grub_udf_lvd lvd; - struct grub_udf_pd pds[GRUB_UDF_MAX_PDS]; - struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS]; - struct grub_udf_long_ad root_icb; - int npd, npm; -}; - -struct grub_fshelp_node -{ - struct grub_udf_data *data; - union - { - struct grub_udf_file_entry fe; - struct grub_udf_extended_file_entry efe; - }; - int part_ref; -}; - -static grub_dl_t my_mod; - -static grub_uint32_t -grub_udf_get_block (struct grub_udf_data *data, - grub_uint16_t part_ref, grub_uint32_t block) -{ - part_ref = U16 (part_ref); - - if (part_ref >= data->npm) - { - grub_error (GRUB_ERR_BAD_FS, "invalid part ref"); - return 0; - } - - return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start) - + U32 (block)); -} - -static grub_err_t -grub_udf_read_icb (struct grub_udf_data *data, - struct grub_udf_long_ad *icb, - struct grub_fshelp_node *node) -{ - grub_uint32_t block; - - block = grub_udf_get_block (data, - icb->block.part_ref, - icb->block.block_num); - - if (grub_errno) - return grub_errno; - - if (grub_disk_read (data->disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_file_entry), - &node->fe)) - return grub_errno; - - if ((U16 (node->fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) && - (U16 (node->fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE)) - return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor"); - - node->part_ref = icb->block.part_ref; - node->data = data; - return 0; -} - -static grub_disk_addr_t -grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) -{ - char *ptr; - int len; - grub_disk_addr_t filebytes; - - if (U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) - { - ptr = (char *) &node->fe.ext_attr[0] + U32 (node->fe.ext_attr_length); - len = U32 (node->fe.alloc_descs_length); - } - else - { - ptr = (char *) &node->efe.ext_attr[0] + U32 (node->efe.ext_attr_length); - len = U32 (node->efe.alloc_descs_length); - } - - if ((U16 (node->fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK) - == GRUB_UDF_ICBTAG_FLAG_AD_SHORT) - { - struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr; - - len /= sizeof (struct grub_udf_short_ad); - filebytes = fileblock * GRUB_UDF_BLKSZ; - while (len > 0) - { - if (filebytes < U32 (ad->length)) - return ((U32 (ad->position) & GRUB_UDF_EXT_MASK) ? 0 : - (grub_udf_get_block (node->data, - node->part_ref, - ad->position) - + (filebytes / GRUB_UDF_BLKSZ))); - - filebytes -= U32 (ad->length); - ad++; - len--; - } - } - else - { - struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr; - - len /= sizeof (struct grub_udf_long_ad); - filebytes = fileblock * GRUB_UDF_BLKSZ; - while (len > 0) - { - if (filebytes < U32 (ad->length)) - return ((U32 (ad->block.block_num) & GRUB_UDF_EXT_MASK) ? 0 : - (grub_udf_get_block (node->data, - ad->block.part_ref, - ad->block.block_num) - + (filebytes / GRUB_UDF_BLKSZ))); - - filebytes -= U32 (ad->length); - ad++; - len--; - } - } - - return 0; -} - -static grub_ssize_t -grub_udf_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR - (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) -{ - switch (U16 (node->fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK) - { - case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB: - { - char *ptr; - - ptr = ((U16 (node->fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ? - ((char *) &node->fe.ext_attr[0] - + U32 (node->fe.ext_attr_length)) : - ((char *) &node->efe.ext_attr[0] - + U32 (node->efe.ext_attr_length))); - - grub_memcpy (buf, ptr + pos, len); - - return len; - } - - case GRUB_UDF_ICBTAG_FLAG_AD_EXT: - grub_error (GRUB_ERR_BAD_FS, "invalid extent type"); - return 0; - } - - return grub_fshelp_read_file (node->data->disk, node, read_hook, - pos, len, buf, grub_udf_read_block, - U64 (node->fe.file_size), - GRUB_UDF_LOG2_BLKSZ); -} - -static int sblocklist[] = { 256, 512, 0 }; - -static struct grub_udf_data * -grub_udf_mount (grub_disk_t disk) -{ - struct grub_udf_data *data = 0; - struct grub_udf_fileset root_fs; - int *sblklist = sblocklist; - grub_uint32_t block; - int i; - - data = grub_malloc (sizeof (struct grub_udf_data)); - if (!data) - return 0; - - data->disk = disk; - - /* Search for Volume Recognition Sequence (VRS). */ - for (block = 16;; block++) - { - struct grub_udf_vrs vrs; - - if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_vrs), &vrs)) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) || - (!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5))) - break; - - if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) && - (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) && - (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) && - (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) && - (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5))) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - } - - /* Search for Anchor Volume Descriptor Pointer (AVDP). */ - while (1) - { - struct grub_udf_avdp avdp; - - if (grub_disk_read (disk, *sblklist << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_avdp), &avdp)) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP) - { - block = U32 (avdp.vds.start); - break; - } - - sblklist++; - if (*sblklist == 0) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - } - - data->npd = data->npm = 0; - /* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD). */ - while (1) - { - struct grub_udf_tag tag; - - if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_tag), &tag)) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - tag.tag_ident = U16 (tag.tag_ident); - if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD) - { - if (data->npd >= GRUB_UDF_MAX_PDS) - { - grub_error (GRUB_ERR_BAD_FS, "too many PDs"); - goto fail; - } - - if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_pd), - &data->pds[data->npd])) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - data->npd++; - } - else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD) - { - int k; - - struct grub_udf_partmap *ppm; - - if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_lvd), - &data->lvd)) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS) - { - grub_error (GRUB_ERR_BAD_FS, "too many partition maps"); - goto fail; - } - - ppm = (struct grub_udf_partmap *) &data->lvd.part_maps; - for (k = U32 (data->lvd.num_part_maps); k > 0; k--) - { - if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1) - { - grub_error (GRUB_ERR_BAD_FS, "partmap type not supported"); - goto fail; - } - - data->pms[data->npm++] = ppm; - ppm = (struct grub_udf_partmap *) ((char *) ppm + - U32 (ppm->length)); - } - } - else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD) - { - grub_error (GRUB_ERR_BAD_FS, "invalid tag ident"); - goto fail; - } - else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD) - break; - - block++; - } - - for (i = 0; i < data->npm; i++) - { - int j; - - for (j = 0; j < data->npd; j++) - if (data->pms[i]->type1.part_num == data->pds[j].part_num) - { - data->pms[i]->type1.part_num = j; - break; - } - - if (j == data->npd) - { - grub_error (GRUB_ERR_BAD_FS, "can\'t find PD"); - goto fail; - } - } - - block = grub_udf_get_block (data, - data->lvd.root_fileset.block.part_ref, - data->lvd.root_fileset.block.block_num); - - if (grub_errno) - goto fail; - - if (grub_disk_read (disk, block << GRUB_UDF_LOG2_BLKSZ, 0, - sizeof (struct grub_udf_fileset), &root_fs)) - { - grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); - goto fail; - } - - if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD) - { - grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor"); - goto fail; - } - - data->root_icb = root_fs.root_icb; - - return data; - -fail: - grub_free (data); - return 0; -} - -static int -grub_udf_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - grub_fshelp_node_t child; - struct grub_udf_file_ident dirent; - grub_uint32_t offset = 0; - - child = grub_malloc (sizeof (struct grub_fshelp_node)); - if (!child) - return 0; - - /* The current directory is not stored. */ - grub_memcpy ((char *) child, (char *) dir, - sizeof (struct grub_fshelp_node)); - - if (hook (".", GRUB_FSHELP_DIR, child)) - return 1; - - while (offset < U64 (dir->fe.file_size)) - { - if (grub_udf_read_file (dir, 0, offset, sizeof (dirent), - (char *) &dirent) != sizeof (dirent)) - return 0; - - if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID) - { - grub_error (GRUB_ERR_BAD_FS, "invalid fid tag"); - return 0; - } - - child = grub_malloc (sizeof (struct grub_fshelp_node)); - if (!child) - return 0; - - if (grub_udf_read_icb (dir->data, &dirent.icb, child)) - return 0; - - offset += sizeof (dirent) + U16 (dirent.imp_use_length); - if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT) - { - /* This is the parent directory. */ - if (hook ("..", GRUB_FSHELP_DIR, child)) - return 1; - } - else - { - enum grub_fshelp_filetype type; - char filename[dirent.file_ident_length + 1]; - - type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ? - (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG)); - - if ((grub_udf_read_file (dir, 0, offset, - dirent.file_ident_length, filename)) - != dirent.file_ident_length) - return 0; - - filename[dirent.file_ident_length] = 0; - if (hook (&filename[1], type, child)) - return 1; - } - - /* Align to dword boundary. */ - offset = (offset + dirent.file_ident_length + 3) & (~3); - } - - return 0; -} - -static grub_err_t -grub_udf_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_udf_data *data = 0; - struct grub_fshelp_node rootnode; - struct grub_fshelp_node *foundnode; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_udf_mount (device->disk); - if (!data) - goto fail; - - if (grub_udf_read_icb (data, &data->root_icb, &rootnode)) - goto fail; - - if (grub_fshelp_find_file (path, &rootnode, - &foundnode, - grub_udf_iterate_dir, 0, GRUB_FSHELP_DIR)) - goto fail; - - grub_udf_iterate_dir (foundnode, iterate); - - if (foundnode != &rootnode) - grub_free (foundnode); - -fail: - grub_free (data); - - grub_dl_unref (my_mod); - - return grub_errno; -} - -static grub_err_t -grub_udf_open (struct grub_file *file, const char *name) -{ - struct grub_udf_data *data; - struct grub_fshelp_node rootnode; - struct grub_fshelp_node *foundnode; - - grub_dl_ref (my_mod); - - data = grub_udf_mount (file->device->disk); - if (!data) - goto fail; - - if (grub_udf_read_icb (data, &data->root_icb, &rootnode)) - goto fail; - - if (grub_fshelp_find_file (name, &rootnode, - &foundnode, - grub_udf_iterate_dir, 0, GRUB_FSHELP_REG)) - goto fail; - - file->data = foundnode; - file->offset = 0; - file->size = U64 (foundnode->fe.file_size); - - return 0; - -fail: - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - -static grub_ssize_t -grub_udf_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data; - - return grub_udf_read_file (node, file->read_hook, file->offset, len, buf); -} - -static grub_err_t -grub_udf_close (grub_file_t file) -{ - if (file->data) - { - struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data; - - grub_free (node->data); - grub_free (node); - } - - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - -static grub_err_t -grub_udf_label (grub_device_t device, char **label) -{ - struct grub_udf_data *data; - data = grub_udf_mount (device->disk); - - if (data) - { - *label = grub_strdup ((char *) &data->lvd.ident[1]); - grub_free (data); - } - else - *label = 0; - - return grub_errno; -} - -static struct grub_fs grub_udf_fs = { - .name = "udf", - .dir = grub_udf_dir, - .open = grub_udf_open, - .read = grub_udf_read, - .close = grub_udf_close, - .label = grub_udf_label, - .next = 0 -}; - -GRUB_MOD_INIT (udf) -{ - grub_fs_register (&grub_udf_fs); - my_mod = mod; -} - -GRUB_MOD_FINI (udf) -{ - grub_fs_unregister (&grub_udf_fs); -} diff --git a/fs/xfs.c b/fs/xfs.c deleted file mode 100644 index 9dffe31d1..000000000 --- a/fs/xfs.c +++ /dev/null @@ -1,823 +0,0 @@ -/* xfs.c - XFS. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define XFS_INODE_EXTENTS 9 - -#define XFS_INODE_FORMAT_INO 1 -#define XFS_INODE_FORMAT_EXT 2 -#define XFS_INODE_FORMAT_BTREE 3 - - -struct grub_xfs_sblock -{ - grub_uint8_t magic[4]; - grub_uint32_t bsize; - grub_uint8_t unused1[24]; - grub_uint16_t uuid[8]; - grub_uint8_t unused2[8]; - grub_uint64_t rootino; - grub_uint8_t unused3[20]; - grub_uint32_t agsize; - grub_uint8_t unused4[20]; - grub_uint8_t label[12]; - grub_uint8_t log2_bsize; - grub_uint8_t log2_sect; - grub_uint8_t log2_inode; - grub_uint8_t log2_inop; - grub_uint8_t log2_agblk; - grub_uint8_t unused6[67]; - grub_uint8_t log2_dirblk; -} __attribute__ ((packed)); - -struct grub_xfs_dir_header -{ - grub_uint8_t count; - grub_uint8_t smallino; - union - { - grub_uint32_t i4; - grub_uint64_t i8; - } parent __attribute__ ((packed)); -} __attribute__ ((packed)); - -struct grub_xfs_dir_entry -{ - grub_uint8_t len; - grub_uint16_t offset; - char name[1]; - /* Inode number follows, 32 bits. */ -} __attribute__ ((packed)); - -struct grub_xfs_dir2_entry -{ - grub_uint64_t inode; - grub_uint8_t len; -} __attribute__ ((packed)); - -typedef grub_uint32_t grub_xfs_extent[4]; - -struct grub_xfs_btree_node -{ - grub_uint8_t magic[4]; - grub_uint16_t level; - grub_uint16_t numrecs; - grub_uint64_t left; - grub_uint64_t right; - grub_uint64_t keys[1]; -} __attribute__ ((packed)); - -struct grub_xfs_btree_root -{ - grub_uint16_t level; - grub_uint16_t numrecs; - grub_uint64_t keys[1]; -} __attribute__ ((packed)); - -struct grub_xfs_inode -{ - grub_uint8_t magic[2]; - grub_uint16_t mode; - grub_uint8_t version; - grub_uint8_t format; - grub_uint8_t unused2[50]; - grub_uint64_t size; - grub_uint64_t nblocks; - grub_uint32_t extsize; - grub_uint32_t nextents; - grub_uint8_t unused3[20]; - union - { - char raw[156]; - struct dir - { - struct grub_xfs_dir_header dirhead; - struct grub_xfs_dir_entry direntry[1]; - } dir; - grub_xfs_extent extents[XFS_INODE_EXTENTS]; - struct grub_xfs_btree_root btree; - } data __attribute__ ((packed)); -} __attribute__ ((packed)); - -struct grub_xfs_dirblock_tail -{ - grub_uint32_t leaf_count; - grub_uint32_t leaf_stale; -} __attribute__ ((packed)); - -struct grub_fshelp_node -{ - struct grub_xfs_data *data; - grub_uint64_t ino; - int inode_read; - struct grub_xfs_inode inode; -}; - -struct grub_xfs_data -{ - struct grub_xfs_sblock sblock; - grub_disk_t disk; - int pos; - int bsize; - int agsize; - struct grub_fshelp_node diropen; -}; - -static grub_dl_t my_mod; - - - -/* Filetype information as used in inodes. */ -#define FILETYPE_INO_MASK 0170000 -#define FILETYPE_INO_REG 0100000 -#define FILETYPE_INO_DIRECTORY 0040000 -#define FILETYPE_INO_SYMLINK 0120000 - -#define GRUB_XFS_INO_AGBITS(data) \ - ((data)->sblock.log2_agblk + (data)->sblock.log2_inop) -#define GRUB_XFS_INO_INOINAG(data, ino) \ - (grub_be_to_cpu64 (ino) & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1)) -#define GRUB_XFS_INO_AG(data,ino) \ - (grub_be_to_cpu64 (ino) >> GRUB_XFS_INO_AGBITS (data)) - -#define GRUB_XFS_FSB_TO_BLOCK(data, fsb) \ - (((fsb) >> (data)->sblock.log2_agblk) * (data)->agsize \ - + ((fsb) & ((1LL << (data)->sblock.log2_agblk) - 1))) - -#define GRUB_XFS_EXTENT_OFFSET(exts,ex) \ - ((grub_be_to_cpu32 (exts[ex][0]) & ~(1 << 31)) << 23 \ - | grub_be_to_cpu32 (exts[ex][1]) >> 9) - -#define GRUB_XFS_EXTENT_BLOCK(exts,ex) \ - ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex][1]) \ - & (0x1ff)) << 43 \ - | (grub_uint64_t) grub_be_to_cpu32 (exts[ex][2]) << 11 \ - | grub_be_to_cpu32 (exts[ex][3]) >> 21) - -#define GRUB_XFS_EXTENT_SIZE(exts,ex) \ - (grub_be_to_cpu32 (exts[ex][3]) & ((1 << 20) - 1)) - -#define GRUB_XFS_ROUND_TO_DIRENT(pos) ((((pos) + 8 - 1) / 8) * 8) -#define GRUB_XFS_NEXT_DIRENT(pos,len) \ - (pos) + GRUB_XFS_ROUND_TO_DIRENT (8 + 1 + len + 2) - -static inline grub_uint64_t -grub_xfs_inode_block (struct grub_xfs_data *data, - grub_uint64_t ino) -{ - long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino); - long long ag = GRUB_XFS_INO_AG (data, ino); - long long block; - - block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize; - block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS); - return block; -} - - -static inline int -grub_xfs_inode_offset (struct grub_xfs_data *data, - grub_uint64_t ino) -{ - int inoag = GRUB_XFS_INO_INOINAG (data, ino); - return ((inoag & ((1 << data->sblock.log2_inop) - 1)) << - data->sblock.log2_inode); -} - - -static grub_err_t -grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino, - struct grub_xfs_inode *inode) -{ - grub_uint64_t block = grub_xfs_inode_block (data, ino); - int offset = grub_xfs_inode_offset (data, ino); - - /* Read the inode. */ - if (grub_disk_read (data->disk, block, offset, - 1 << data->sblock.log2_inode, inode)) - return grub_errno; - - if (grub_strncmp ((char *) inode->magic, "IN", 2)) - return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode"); - - return 0; -} - - -static grub_disk_addr_t -grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) -{ - struct grub_xfs_btree_node *leaf = 0; - int ex, nrec; - grub_xfs_extent *exts; - grub_uint64_t ret = 0; - - if (node->inode.format == XFS_INODE_FORMAT_BTREE) - { - grub_uint64_t *keys; - - leaf = grub_malloc (node->data->sblock.bsize); - if (leaf == 0) - return 0; - - nrec = grub_be_to_cpu16 (node->inode.data.btree.numrecs); - keys = &node->inode.data.btree.keys[0]; - do - { - int i; - - for (i = 0; i < nrec; i++) - { - if (fileblock < grub_be_to_cpu64 (keys[i])) - break; - } - - /* Sparse block. */ - if (i == 0) - { - grub_free (leaf); - return 0; - } - - if (grub_disk_read (node->data->disk, - grub_be_to_cpu64 (keys[i - 1 + nrec]) - << (node->data->sblock.log2_bsize - - GRUB_DISK_SECTOR_BITS), - 0, node->data->sblock.bsize, leaf)) - return 0; - - if (grub_strncmp ((char *) leaf->magic, "BMAP", 4)) - { - grub_free (leaf); - grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node"); - return 0; - } - - nrec = grub_be_to_cpu16 (leaf->numrecs); - keys = &leaf->keys[0]; - } while (leaf->level); - exts = (grub_xfs_extent *) keys; - } - else if (node->inode.format == XFS_INODE_FORMAT_EXT) - { - nrec = grub_be_to_cpu32 (node->inode.nextents); - exts = &node->inode.data.extents[0]; - } - else - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "XFS does not support inode format %d yet", - node->inode.format); - return 0; - } - - /* Iterate over each extent to figure out which extent has - the block we are looking for. */ - for (ex = 0; ex < nrec; ex++) - { - grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex); - grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex); - grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex); - - /* Sparse block. */ - if (fileblock < offset) - break; - else if (fileblock < offset + size) - { - ret = (fileblock - offset + start); - break; - } - } - - if (leaf) - grub_free (leaf); - - return GRUB_XFS_FSB_TO_BLOCK(node->data, ret); -} - - -/* Read LEN bytes from the file described by DATA starting with byte - POS. Return the amount of read bytes in READ. */ -static grub_ssize_t -grub_xfs_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) -{ - return grub_fshelp_read_file (node->data->disk, node, read_hook, - pos, len, buf, grub_xfs_read_block, - grub_be_to_cpu64 (node->inode.size), - node->data->sblock.log2_bsize - - GRUB_DISK_SECTOR_BITS); -} - - -static char * -grub_xfs_read_symlink (grub_fshelp_node_t node) -{ - int size = grub_be_to_cpu64 (node->inode.size); - - switch (node->inode.format) - { - case XFS_INODE_FORMAT_INO: - return grub_strndup (node->inode.data.raw, size); - - case XFS_INODE_FORMAT_EXT: - { - char *symlink; - grub_ssize_t numread; - - symlink = grub_malloc (size + 1); - if (!symlink) - return 0; - - numread = grub_xfs_read_file (node, 0, 0, size, symlink); - if (numread != size) - { - grub_free (symlink); - return 0; - } - symlink[size] = '\0'; - return symlink; - } - } - - return 0; -} - - -static enum grub_fshelp_filetype -grub_xfs_mode_to_filetype (grub_uint16_t mode) -{ - if ((grub_be_to_cpu16 (mode) - & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) - return GRUB_FSHELP_DIR; - else if ((grub_be_to_cpu16 (mode) - & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) - return GRUB_FSHELP_SYMLINK; - else if ((grub_be_to_cpu16 (mode) - & FILETYPE_INO_MASK) == FILETYPE_INO_REG) - return GRUB_FSHELP_REG; - return GRUB_FSHELP_UNKNOWN; -} - - -static int -grub_xfs_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) -{ - struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; - auto int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename); - - int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, char *filename) - { - struct grub_fshelp_node *fdiro; - - fdiro = grub_malloc (sizeof (struct grub_fshelp_node) - - sizeof (struct grub_xfs_inode) - + (1 << diro->data->sblock.log2_inode)); - if (!fdiro) - return 0; - - /* The inode should be read, otherwise the filetype can - not be determined. */ - fdiro->ino = ino; - fdiro->inode_read = 1; - fdiro->data = diro->data; - grub_xfs_read_inode (diro->data, ino, &fdiro->inode); - - return hook (filename, - grub_xfs_mode_to_filetype (fdiro->inode.mode), - fdiro); - } - - switch (diro->inode.format) - { - case XFS_INODE_FORMAT_INO: - { - struct grub_xfs_dir_entry *de = &diro->inode.data.dir.direntry[0]; - int smallino = !diro->inode.data.dir.dirhead.smallino; - int i; - grub_uint64_t parent; - - /* If small inode numbers are used to pack the direntry, the - parent inode number is small too. */ - if (smallino) - { - parent = grub_be_to_cpu32 (diro->inode.data.dir.dirhead.parent.i4); - parent = grub_cpu_to_be64 (parent); - /* The header is a bit smaller than usual. */ - de = (struct grub_xfs_dir_entry *) ((char *) de - 4); - } - else - { - parent = diro->inode.data.dir.dirhead.parent.i8; - } - - /* Synthesize the direntries for `.' and `..'. */ - if (call_hook (diro->ino, ".")) - return 1; - - if (call_hook (parent, "..")) - return 1; - - for (i = 0; i < diro->inode.data.dir.dirhead.count; i++) - { - grub_uint64_t ino; - void *inopos = (((char *) de) - + sizeof (struct grub_xfs_dir_entry) - + de->len - 1); - char name[de->len + 1]; - - if (smallino) - { - ino = grub_be_to_cpu32 (*(grub_uint32_t *) inopos); - ino = grub_cpu_to_be64 (ino); - } - else - ino = *(grub_uint64_t *) inopos; - - grub_memcpy (name, de->name, de->len); - name[de->len] = '\0'; - if (call_hook (ino, name)) - return 1; - - de = ((struct grub_xfs_dir_entry *) - (((char *) de)+ sizeof (struct grub_xfs_dir_entry) + de->len - + ((smallino ? sizeof (grub_uint32_t) - : sizeof (grub_uint64_t))) - 1)); - } - break; - } - - case XFS_INODE_FORMAT_BTREE: - case XFS_INODE_FORMAT_EXT: - { - grub_ssize_t numread; - char *dirblock; - grub_uint64_t blk; - int dirblk_size, dirblk_log2; - - dirblk_log2 = (dir->data->sblock.log2_bsize - + dir->data->sblock.log2_dirblk); - dirblk_size = 1 << dirblk_log2; - - dirblock = grub_malloc (dirblk_size); - if (! dirblock) - return 0; - - /* Iterate over every block the directory has. */ - for (blk = 0; - blk < (grub_be_to_cpu64 (dir->inode.size) - >> dirblk_log2); - blk++) - { - /* The header is skipped, the first direntry is stored - from byte 16. */ - int pos = 16; - int entries; - int tail_start = (dirblk_size - - sizeof (struct grub_xfs_dirblock_tail)); - - struct grub_xfs_dirblock_tail *tail; - tail = (struct grub_xfs_dirblock_tail *) &dirblock[tail_start]; - - numread = grub_xfs_read_file (dir, 0, - blk << dirblk_log2, - dirblk_size, dirblock); - if (numread != dirblk_size) - return 0; - - entries = (grub_be_to_cpu32 (tail->leaf_count) - - grub_be_to_cpu32 (tail->leaf_stale)); - - /* Iterate over all entries within this block. */ - while (pos < (dirblk_size - - (int) sizeof (struct grub_xfs_dir2_entry))) - { - struct grub_xfs_dir2_entry *direntry; - grub_uint16_t *freetag; - char *filename; - - direntry = (struct grub_xfs_dir2_entry *) &dirblock[pos]; - freetag = (grub_uint16_t *) direntry; - - if (*freetag == 0XFFFF) - { - grub_uint16_t *skip = (grub_uint16_t *) (freetag + 1); - - /* This entry is not used, go to the next one. */ - pos += grub_be_to_cpu16 (*skip); - - continue; - } - - filename = &dirblock[pos + sizeof (*direntry)]; - /* The byte after the filename is for the tag, which - is not used by GRUB. So it can be overwritten. */ - filename[direntry->len] = '\0'; - - if (call_hook (direntry->inode, filename)) - { - grub_free (dirblock); - return 1; - } - - /* Check if last direntry in this block is - reached. */ - entries--; - if (!entries) - break; - - /* Select the next directory entry. */ - pos = GRUB_XFS_NEXT_DIRENT (pos, direntry->len); - pos = GRUB_XFS_ROUND_TO_DIRENT (pos); - } - } - grub_free (dirblock); - break; - } - - default: - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "XFS does not support inode format %d yet", - diro->inode.format); - } - return 0; -} - - -static struct grub_xfs_data * -grub_xfs_mount (grub_disk_t disk) -{ - struct grub_xfs_data *data = 0; - - data = grub_zalloc (sizeof (struct grub_xfs_data)); - if (!data) - return 0; - - /* Read the superblock. */ - if (grub_disk_read (disk, 0, 0, - sizeof (struct grub_xfs_sblock), &data->sblock)) - goto fail; - - if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4)) - { - grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem"); - goto fail; - } - - data = grub_realloc (data, - sizeof (struct grub_xfs_data) - - sizeof (struct grub_xfs_inode) - + (1 << data->sblock.log2_inode)); - - if (! data) - goto fail; - - data->diropen.data = data; - data->diropen.ino = data->sblock.rootino; - data->diropen.inode_read = 1; - data->bsize = grub_be_to_cpu32 (data->sblock.bsize); - data->agsize = grub_be_to_cpu32 (data->sblock.agsize); - - data->disk = disk; - data->pos = 0; - - grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode); - - return data; - fail: - - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) - grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem"); - - grub_free (data); - - return 0; -} - - -static grub_err_t -grub_xfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) -{ - struct grub_xfs_data *data = 0; - struct grub_fshelp_node *fdiro = 0; - - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - - grub_dl_ref (my_mod); - - data = grub_xfs_mount (device->disk); - if (!data) - goto mount_fail; - - grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir, - grub_xfs_read_symlink, GRUB_FSHELP_DIR); - if (grub_errno) - goto fail; - - grub_xfs_iterate_dir (fdiro, iterate); - - fail: - if (fdiro != &data->diropen) - grub_free (fdiro); - grub_free (data); - - mount_fail: - - grub_dl_unref (my_mod); - - return grub_errno; -} - - -/* Open a file named NAME and initialize FILE. */ -static grub_err_t -grub_xfs_open (struct grub_file *file, const char *name) -{ - struct grub_xfs_data *data; - struct grub_fshelp_node *fdiro = 0; - - grub_dl_ref (my_mod); - - data = grub_xfs_mount (file->device->disk); - if (!data) - goto mount_fail; - - grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir, - grub_xfs_read_symlink, GRUB_FSHELP_REG); - if (grub_errno) - goto fail; - - if (!fdiro->inode_read) - { - grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode); - if (grub_errno) - goto fail; - } - - if (fdiro != &data->diropen) - grub_memcpy (&data->diropen, fdiro, - sizeof (struct grub_fshelp_node) - - sizeof (struct grub_xfs_inode) - + (1 << data->sblock.log2_inode)); - - file->size = grub_be_to_cpu64 (data->diropen.inode.size); - file->data = data; - file->offset = 0; - - return 0; - - fail: - if (fdiro != &data->diropen) - grub_free (fdiro); - grub_free (data); - - mount_fail: - grub_dl_unref (my_mod); - - return grub_errno; -} - - -static grub_ssize_t -grub_xfs_read (grub_file_t file, char *buf, grub_size_t len) -{ - struct grub_xfs_data *data = - (struct grub_xfs_data *) file->data; - - return grub_xfs_read_file (&data->diropen, file->read_hook, - file->offset, len, buf); -} - - -static grub_err_t -grub_xfs_close (grub_file_t file) -{ - grub_free (file->data); - - grub_dl_unref (my_mod); - - return GRUB_ERR_NONE; -} - - -static grub_err_t -grub_xfs_label (grub_device_t device, char **label) -{ - struct grub_xfs_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_xfs_mount (disk); - if (data) - *label = grub_strndup ((char *) (data->sblock.label), 12); - else - *label = 0; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - -static grub_err_t -grub_xfs_uuid (grub_device_t device, char **uuid) -{ - struct grub_xfs_data *data; - grub_disk_t disk = device->disk; - - grub_dl_ref (my_mod); - - data = grub_xfs_mount (disk); - if (data) - { - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->sblock.uuid[0]), - grub_be_to_cpu16 (data->sblock.uuid[1]), - grub_be_to_cpu16 (data->sblock.uuid[2]), - grub_be_to_cpu16 (data->sblock.uuid[3]), - grub_be_to_cpu16 (data->sblock.uuid[4]), - grub_be_to_cpu16 (data->sblock.uuid[5]), - grub_be_to_cpu16 (data->sblock.uuid[6]), - grub_be_to_cpu16 (data->sblock.uuid[7])); - } - else - *uuid = NULL; - - grub_dl_unref (my_mod); - - grub_free (data); - - return grub_errno; -} - - - -static struct grub_fs grub_xfs_fs = - { - .name = "xfs", - .dir = grub_xfs_dir, - .open = grub_xfs_open, - .read = grub_xfs_read, - .close = grub_xfs_close, - .label = grub_xfs_label, - .uuid = grub_xfs_uuid, - .next = 0 - }; - -GRUB_MOD_INIT(xfs) -{ - grub_fs_register (&grub_xfs_fs); - my_mod = mod; -} - -GRUB_MOD_FINI(xfs) -{ - grub_fs_unregister (&grub_xfs_fs); -} diff --git a/gencmdlist.sh b/gencmdlist.sh deleted file mode 100644 index ed5965f07..000000000 --- a/gencmdlist.sh +++ /dev/null @@ -1,22 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2005,2009 Free Software Foundation, Inc. -# -# This gensymlist.sh is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect command names. - -module=$1 - -grep -v "^#" | sed -n \ - -e "/grub_register_command *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $module/;p;}" \ - -e "/grub_register_extcmd *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $module/;p;}" \ - -e "/grub_register_command_p1 *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $module/;p;}" - diff --git a/gendistlist.sh b/gendistlist.sh deleted file mode 100755 index 102c0c11c..000000000 --- a/gendistlist.sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc. -# -# This gendistlist.sh is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Generate a list of distributed files. - -EXTRA_DISTFILES="AUTHORS COPYING ChangeLog DISTLIST INSTALL NEWS README \ - THANKS TODO Makefile.in aclocal.m4 autogen.sh config.guess \ - config.h.in config.sub configure configure.ac gencmdlist.sh \ - gendistlist.sh genfslist.sh genhandlerlist.sh geninit.sh \ - geninitheader.sh genkernsyms.sh.in genmk.rb genmoddep.awk \ - genmodsrc.sh genpartmaplist.sh genparttoollist.sh \ - genvideolist.sh \ - gensymlist.sh.in install-sh mkinstalldirs stamp-h.in" - -DISTDIRS="boot bus commands conf disk docs efiemu font fs hello hook include io \ - kern lib loader mmap normal partmap parttool script term util video" - -LC_COLLATE=C -export LC_COLLATE - -for f in $EXTRA_DISTFILES; do - echo $f -done - -dir=`dirname $0` -cd $dir - -for dir in $DISTDIRS; do - for d in `find $dir -type d ! -name .svn ! -name .bzr | sort`; do - find $d -maxdepth 1 -name '*.[chSy]' -o -name '*.mk' -o -name '*.rmk' \ - -o -name '*.rb' -o -name '*.in' -o -name '*.tex' -o -name '*.texi' \ - -o -name '*.info' -o -name 'grub.cfg' -o -name 'README' \ - -o -name '*.sc' -o -name 'mdate-sh' -o -name '*.sh' \ - -o -name 'grub-dumpdevtree' -o -name '*.lua' | sort - done -done diff --git a/genfslist.sh b/genfslist.sh deleted file mode 100644 index 6fa7e927d..000000000 --- a/genfslist.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2005,2008 Free Software Foundation, Inc. -# -# This gensymlist.sh is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect fs names. - -module=$1 - -# Ignore kernel.mod. -if test $module = kernel; then - exit -fi - -# For now, this emits only a module name, if the module registers a filesystem. -if grep -v "^#" | grep '^ *grub_fs_register' >/dev/null 2>&1; then - echo $module -fi diff --git a/genhandlerlist.sh b/genhandlerlist.sh deleted file mode 100644 index e4cb0d9de..000000000 --- a/genhandlerlist.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2009 Free Software Foundation, Inc. -# -# This script is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect command names. - -module=$1 - -grep -v "^#" | sed -n \ - -e "/grub_parser_register *( *\"/{s/.*( *\"\([^\"]*\)\".*/parser.\1: $module/;p;}" diff --git a/geninit.sh b/geninit.sh index 43d2d1640..f0810120f 100644 --- a/geninit.sh +++ b/geninit.sh @@ -11,11 +11,6 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. -lst="$1" -shift - -header=`echo "${lst}" | sed -e "s/\.lst$/.h/g"` - cat <. */ -#include <$header> +#include EOF +for mod in "$@"; do + echo "extern void grub_${mod}_init (void);" + echo "extern void grub_${mod}_fini (void);" +done + cat </dev/null; then - echo $line | sed -e 's/.*GRUB_MOD_INIT *(\([a-zA-Z0-9_]*\)).*/ grub_\1_init ();/' - fi -done < ${lst} +for mod in "$@"; do + echo "grub_${mod}_init ();" +done cat </dev/null; then - echo $line | sed -e 's/.*GRUB_MOD_INIT *(\([a-zA-Z0-9_]*\)).*/ grub_\1_fini ();/' - fi -done < ${lst} +for mod in "$@"; do + echo "grub_${mod}_fini ();" +done cat </dev/null 2>&1 && u="_" - -$CC @TARGET_CFLAGS@ -DGRUB_SYMBOL_GENERATOR=1 -E -I. -Iinclude -I"$srcdir/include" $* \ - | 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 diff --git a/genmk.rb b/genmk.rb deleted file mode 100644 index efea16412..000000000 --- a/genmk.rb +++ /dev/null @@ -1,461 +0,0 @@ -#! /usr/bin/ruby -w -# -# Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. -# -# This genmk.rb is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -module Enumerable - def collect_with_index - ret = [] - self.each_with_index do |item, index| - ret.push(yield(item, index)) - end - ret - end -end - -class String - def to_var - self.gsub(/[^a-zA-Z0-9_@]/, '_') - end - - def suffix(str) - self.sub(/\.[^\.]*$/, '') + '.' + str - end - - def to_obj - self.sub(/\.[^\.]*$/, '').to_var + '.o' - end -end - -class Image - def initialize(dir, name) - @dir = dir - @name = name - @rule_count = 0 - end - attr_reader :dir, :name - - def rule(sources) - prefix = @name.to_var - @rule_count += 1 - exe = @name.suffix('exec') - objs = sources.collect do |src| - raise "unknown source file `#{src}'" if /\.[cS]$/ !~ src - prefix + '-' + src.to_obj - end - objs_str = objs.join(' ') - deps = objs.collect {|obj| obj.suffix('d')} - deps_str = deps.join(' ') - -" -clean-image-#{@name}.#{@rule_count}: - rm -f #{@name} #{exe} #{objs_str} - -CLEAN_IMAGE_TARGETS += clean-image-#{@name}.#{@rule_count} - -mostlyclean-image-#{@name}.#{@rule_count}: - rm -f #{deps_str} - -MOSTLYCLEAN_IMAGE_TARGETS += mostlyclean-image-#{@name}.#{@rule_count} - -ifneq ($(TARGET_APPLE_CC),1) -#{@name}: #{exe} - $(OBJCOPY) -O $(#{prefix}_FORMAT) --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .reginfo -R .rel.dyn $< $@ -else -ifneq (#{exe},kernel.exec) -#{@name}: #{exe} ./grub-macho2img - ./grub-macho2img $< $@ -else -#{@name}: #{exe} ./grub-macho2img - ./grub-macho2img --bss $< $@ -endif -endif - -#{exe}: #{objs_str} - $(TARGET_CC) -o $@ $^ $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) - -" + objs.collect_with_index do |obj, i| - src = sources[i] - fake_obj = File.basename(src).suffix('o') - dep = deps[i] - flag = if /\.c$/ =~ src then 'CFLAGS' else 'ASFLAGS' end - extra_flags = if /\.S$/ =~ src then '-DASM_FILE=1' else '' end - dir = File.dirname(src) - - "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< --include #{dep} - -" - end.join('') - end -end - -# Use PModule instead Module, to avoid name conflicting. -class PModule - def initialize(dir, name) - @dir = dir - @name = name - @rule_count = 0 - end - attr_reader :dir, :name - - def rule(sources) - prefix = @name.to_var - @rule_count += 1 - objs = sources.collect do |src| - raise "unknown source file `#{src}'" if /\.[cS]$/ !~ src - prefix + '-' + src.to_obj - end - objs_str = objs.join(' ') - deps = objs.collect {|obj| obj.suffix('d')} - deps_str = deps.join(' ') - pre_obj = 'pre-' + @name.suffix('o') - mod_src = 'mod-' + @name.suffix('c') - mod_obj = mod_src.suffix('o') - defsym = 'def-' + @name.suffix('lst') - undsym = 'und-' + @name.suffix('lst') - mod_name = File.basename(@name, '.mod') - symbolic_name = mod_name.sub(/\.[^\.]*$/, '') - -" -clean-module-#{@name}.#{@rule_count}: - rm -f #{@name} #{mod_obj} #{mod_src} #{pre_obj} #{objs_str} #{undsym} - -CLEAN_MODULE_TARGETS += clean-module-#{@name}.#{@rule_count} - -clean-module-#{@name}-symbol.#{@rule_count}: - rm -f #{defsym} - -CLEAN_MODULE_TARGETS += clean-module-#{@name}-symbol.#{@rule_count} -DEFSYMFILES += #{defsym} -mostlyclean-module-#{@name}.#{@rule_count}: - rm -f #{deps_str} - -MOSTLYCLEAN_MODULE_TARGETS += mostlyclean-module-#{@name}.#{@rule_count} -UNDSYMFILES += #{undsym} - -ifneq ($(TARGET_APPLE_CC),1) -#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF) - -rm -f $@ - $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{pre_obj} #{mod_obj} - if test ! -z \"$(TARGET_OBJ2ELF)\"; then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi - $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ -else -#{@name}: #{pre_obj} #{mod_obj} $(TARGET_OBJ2ELF) - -rm -f $@ - -rm -f $@.bin - $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@.bin #{pre_obj} #{mod_obj} - $(OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -wd1106 -nu -nd $@.bin $@ - -rm -f $@.bin -endif - -#{pre_obj}: $(#{prefix}_DEPENDENCIES) #{objs_str} - -rm -f $@ - $(TARGET_CC) $(#{prefix}_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ #{objs_str} - -#{mod_obj}: #{mod_src} - $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -c -o $@ $< - -#{mod_src}: $(builddir)/moddep.lst $(srcdir)/genmodsrc.sh - sh $(srcdir)/genmodsrc.sh '#{mod_name}' $< > $@ || (rm -f $@; exit 1) - -ifneq ($(TARGET_APPLE_CC),1) -#{defsym}: #{pre_obj} - $(NM) -g --defined-only -P -p $< | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@ -else -#{defsym}: #{pre_obj} - $(NM) -g -P -p $< | grep -E '^[a-zA-Z0-9_]* [TDS]' | sed 's/^\\([^ ]*\\).*/\\1 #{mod_name}/' > $@ -endif - -#{undsym}: #{pre_obj} - echo '#{mod_name}' > $@ - $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ - -" + objs.collect_with_index do |obj, i| - src = sources[i] - fake_obj = File.basename(src).suffix('o') - extra_target = obj.sub(/\.[^\.]*$/, '') + '-extra' - command = 'cmd-' + obj.suffix('lst') - fs = 'fs-' + obj.suffix('lst') - partmap = 'partmap-' + obj.suffix('lst') - handler = 'handler-' + obj.suffix('lst') - terminal = 'terminal-' + obj.suffix('lst') - parttool = 'parttool-' + obj.suffix('lst') - video = 'video-' + obj.suffix('lst') - dep = deps[i] - flag = if /\.c$/ =~ src then 'CFLAGS' else 'ASFLAGS' end - extra_flags = if /\.S$/ =~ src then '-DASM_FILE=1' else '' end - dir = File.dirname(src) - - "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< --include #{dep} - -clean-module-#{extra_target}.#{@rule_count}: - rm -f #{command} #{fs} #{partmap} #{handler} #{parttool} #{video} #{terminal} - -CLEAN_MODULE_TARGETS += clean-module-#{extra_target}.#{@rule_count} - -COMMANDFILES += #{command} -FSFILES += #{fs} -PARTTOOLFILES += #{parttool} -PARTMAPFILES += #{partmap} -HANDLERFILES += #{handler} -TERMINALFILES += #{terminal} -VIDEOFILES += #{video} - -#{command}: #{src} $(#{src}_DEPENDENCIES) gencmdlist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/gencmdlist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{fs}: #{src} $(#{src}_DEPENDENCIES) genfslist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genfslist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{parttool}: #{src} $(#{src}_DEPENDENCIES) genparttoollist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genparttoollist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{partmap}: #{src} $(#{src}_DEPENDENCIES) genpartmaplist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genpartmaplist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{handler}: #{src} $(#{src}_DEPENDENCIES) genhandlerlist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genhandlerlist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{terminal}: #{src} $(#{src}_DEPENDENCIES) genterminallist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genterminallist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -#{video}: #{src} $(#{src}_DEPENDENCIES) genvideolist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ - | sh $(srcdir)/genvideolist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) - -" - end.join('') - end -end - -class Utility - def initialize(dir, name) - @dir = dir - @name = name - @rule_count = 0 - end - def print_tail() - prefix = @name.to_var - print "#{@name}: $(#{prefix}_DEPENDENCIES) $(#{prefix}_OBJECTS) - $(CC) -o $@ $(#{prefix}_OBJECTS) $(LDFLAGS) $(#{prefix}_LDFLAGS) - -" - end - attr_reader :dir, :name - - def rule(sources) - prefix = @name.to_var - @rule_count += 1 - objs = sources.collect do |src| - raise "unknown source file `#{src}'" if /\.[cS]$/ !~ src - prefix + '-' + src.to_obj - end - objs_str = objs.join(' '); - deps = objs.collect {|obj| obj.suffix('d')} - deps_str = deps.join(' '); - - " -clean-utility-#{@name}.#{@rule_count}: - rm -f #{@name}$(EXEEXT) #{objs_str} - -CLEAN_UTILITY_TARGETS += clean-utility-#{@name}.#{@rule_count} - -mostlyclean-utility-#{@name}.#{@rule_count}: - rm -f #{deps_str} - -MOSTLYCLEAN_UTILITY_TARGETS += mostlyclean-utility-#{@name}.#{@rule_count} - -#{prefix}_OBJECTS += #{objs_str} - -" + objs.collect_with_index do |obj, i| - src = sources[i] - fake_obj = File.basename(src).suffix('o') - dep = deps[i] - dir = File.dirname(src) - - "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(CC) -I#{dir} -I$(srcdir)/#{dir} $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(#{prefix}_CFLAGS) -MD -c -o $@ $< --include #{dep} - -" - end.join('') - end -end - -class Program - def initialize(dir, name) - @dir = dir - @name = name - end - attr_reader :dir, :name - - def rule(sources) - prefix = @name.to_var - objs = sources.collect do |src| - raise "unknown source file `#{src}'" if /\.[cS]$/ !~ src - prefix + '-' + src.to_obj - end - objs_str = objs.join(' '); - deps = objs.collect {|obj| obj.suffix('d')} - deps_str = deps.join(' '); - - "CLEANFILES += #{@name} #{objs_str} -MOSTLYCLEANFILES += #{deps_str} - -ifeq ($(#{prefix}_RELOCATABLE),yes) -#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} - $(TARGET_CC) -Wl,-r,-d -o $@ #{objs_str} $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) - $(STRIP) --strip-unneeded -K start -R .note -R .comment $@ -else -#{@name}: $(#{prefix}_DEPENDENCIES) #{objs_str} - $(TARGET_CC) -o $@ #{objs_str} $(TARGET_LDFLAGS) $(#{prefix}_LDFLAGS) - $(STRIP) -R .rel.dyn -R .reginfo -R .note -R .comment $@ -endif - -" + objs.collect_with_index do |obj, i| - src = sources[i] - fake_obj = File.basename(src).suffix('o') - dep = deps[i] - flag = if /\.c$/ =~ src then 'CFLAGS' else 'ASFLAGS' end - extra_flags = if /\.S$/ =~ src then '-DASM_FILE=1' else '' end - dir = File.dirname(src) - - "#{obj}: #{src} $(#{src}_DEPENDENCIES) - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) #{extra_flags} $(TARGET_#{flag}) $(#{prefix}_#{flag}) -MD -c -o $@ $< - --include #{dep} - -" - end.join('') - end -end - -class Script - def initialize(dir, name) - @dir = dir - @name = name - end - attr_reader :dir, :name - - def rule(sources) - if sources.length != 1 - raise "only a single source file must be specified for a script" - end - src = sources[0] - if /\.in$/ !~ src - raise "unknown source file `#{src}'" - end - - "CLEANFILES += #{@name} - -#{@name}: #{src} $(#{src}_DEPENDENCIES) config.status - ./config.status --file=-:#{src} | sed -e 's,@pkglib_DATA@,$(pkglib_DATA),g' > $@ - chmod +x $@ - -" - end -end - -images = [] -utils = [] -pmodules = [] -programs = [] -scripts = [] - -l = gets -print l -print "# Generated by genmk.rb, please don't edit!\n" - -cont = false -str = nil -while l = gets - if cont - str += l - else - str = l - end - - print l - cont = (/\\$/ =~ l) - unless cont - str.gsub!(/\\\n/, ' ') - - if /^([a-zA-Z0-9_]+)\s*\+?=\s*(.*?)\s*$/ =~ str - var, args = $1, $2 - - if var =~ /^([a-zA-Z0-9_]+)_([A-Z]+)$/ - prefix, type = $1, $2 - - case type - when 'IMAGES' - images += args.split(/\s+/).collect do |img| - Image.new(prefix, img) - end - - when 'MODULES' - pmodules += args.split(/\s+/).collect do |pmod| - PModule.new(prefix, pmod) - end - - when 'UTILITIES' - utils += args.split(/\s+/).collect do |util| - Utility.new(prefix, util) - end - - when 'PROGRAMS' - programs += args.split(/\s+/).collect do |prog| - Program.new(prefix, prog) - end - - when 'SCRIPTS' - scripts += args.split(/\s+/).collect do |script| - Script.new(prefix, script) - end - - when 'SOURCES' - if img = images.detect() {|i| i.name.to_var == prefix} - print img.rule(args.split(/\s+/)) - elsif pmod = pmodules.detect() {|m| m.name.to_var == prefix} - print pmod.rule(args.split(/\s+/)) - elsif util = utils.detect() {|u| u.name.to_var == prefix} - print util.rule(args.split(/\s+/)) - elsif program = programs.detect() {|u| u.name.to_var == prefix} - print program.rule(args.split(/\s+/)) - elsif script = scripts.detect() {|s| s.name.to_var == prefix} - print script.rule(args.split(/\s+/)) - end - end - end - - end - - end - -end -utils.each {|util| util.print_tail()} - diff --git a/genmoddep.awk b/genmoddep.awk deleted file mode 100644 index 19ac80c71..000000000 --- a/genmoddep.awk +++ /dev/null @@ -1,62 +0,0 @@ -#! /usr/bin/awk -f -# -# Copyright (C) 2006 Free Software Foundation, Inc. -# -# This genmoddep.awk is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read defined symbols from stdin. -BEGIN { - while (getline <"/dev/stdin") { - symtab[$1] = $2 - } -} - -# The first line contains a module name. -FNR == 1 { - module = $1 - next -}; - -# The rest is undefined symbols. -{ - if ($1 in symtab) { - modtab[module] = modtab[module] " " symtab[$1]; - } - else if ($1 != "__gnu_local_gp") { - printf "%s in %s is not defined\n", $1, module >"/dev/stderr"; - error++; - exit; - } -} - -# Output the result. -END { - if (error == 1) - exit 1; - - for (mod in modtab) { - # Remove duplications. - split(modtab[mod], depmods, " "); - for (depmod in uniqmods) { - delete uniqmods[depmod]; - } - for (i in depmods) { - depmod = depmods[i]; - # Ignore kernel, as always loaded. - if (depmod != "kernel" && depmod != mod) - uniqmods[depmod] = 1; - } - modlist = "" - for (depmod in uniqmods) { - modlist = modlist " " depmod; - } - printf "%s:%s\n", mod, modlist; - } -} diff --git a/genpartmaplist.sh b/genpartmaplist.sh deleted file mode 100644 index fceb0f869..000000000 --- a/genpartmaplist.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2005, 2008 Free Software Foundation, Inc. -# -# This script is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect partmap names. - -module=$1 - -# Ignore kernel.mod. -if test $module = kernel; then - exit -fi - -# For now, this emits only a module name, if the module registers a partition map. -if grep -v "^#" | grep '^ *grub_partition_map_register' >/dev/null 2>&1; then - echo $module -fi diff --git a/genparttoollist.sh b/genparttoollist.sh deleted file mode 100644 index 48a0efe55..000000000 --- a/genparttoollist.sh +++ /dev/null @@ -1,19 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2009 Free Software Foundation, Inc. -# -# This gensymlist.sh is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect parttool names. - -module=$1 - -grep -v "^#" | sed -n \ - -e "/grub_parttool_register *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $module/;p;}" diff --git a/genterminallist.sh b/genterminallist.sh deleted file mode 100644 index 60f5b9105..000000000 --- a/genterminallist.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2009,2010 Free Software Foundation, Inc. -# -# This script is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect command names. - -module=$1 - -grep -v "^#" | sed -n \ - -e "/grub_term_register_input *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $module/;p;}" \ - -e "/grub_term_register_output *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $module/;p;}" \ diff --git a/gentpl.py b/gentpl.py new file mode 100644 index 000000000..d8c6965d8 --- /dev/null +++ b/gentpl.py @@ -0,0 +1,915 @@ +#! /usr/bin/python +# GRUB -- GRand Unified Bootloader +# Copyright (C) 2010,2011,2012,2013 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 . + +from __future__ import print_function + +__metaclass__ = type + +from optparse import OptionParser +import re + +# +# This is the python script used to generate Makefile.*.am +# + +GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", + "i386_multiboot", "i386_ieee1275", "x86_64_efi", + "i386_xen", "x86_64_xen", "i386_xen_pvh", + "mips_loongson", "sparc64_ieee1275", + "powerpc_ieee1275", "mips_arc", "ia64_efi", + "mips_qemu_mips", "arm_uboot", "arm_efi", "arm64_efi", + "arm_coreboot", "loongarch64_efi", "riscv32_efi", "riscv64_efi" ] + +GROUPS = {} + +GROUPS["common"] = GRUB_PLATFORMS[:] + +# Groups based on CPU +GROUPS["i386"] = [ "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot", "i386_multiboot", "i386_ieee1275" ] +GROUPS["x86_64"] = [ "x86_64_efi" ] +GROUPS["x86"] = GROUPS["i386"] + GROUPS["x86_64"] +GROUPS["mips"] = [ "mips_loongson", "mips_qemu_mips", "mips_arc" ] +GROUPS["sparc64"] = [ "sparc64_ieee1275" ] +GROUPS["powerpc"] = [ "powerpc_ieee1275" ] +GROUPS["arm"] = [ "arm_uboot", "arm_efi", "arm_coreboot" ] +GROUPS["arm64"] = [ "arm64_efi" ] +GROUPS["loongarch64"] = [ "loongarch64_efi" ] +GROUPS["riscv32"] = [ "riscv32_efi" ] +GROUPS["riscv64"] = [ "riscv64_efi" ] + +# Groups based on firmware +GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi", + "loongarch64_efi", "riscv32_efi", "riscv64_efi" ] +GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] +GROUPS["uboot"] = [ "arm_uboot" ] +GROUPS["xen"] = [ "i386_xen", "x86_64_xen" ] +GROUPS["coreboot"] = [ "i386_coreboot", "arm_coreboot" ] + +# emu is a special case so many core functionality isn't needed on this platform +GROUPS["noemu"] = GRUB_PLATFORMS[:]; GROUPS["noemu"].remove("emu") + +# Groups based on hardware features +GROUPS["cmos"] = GROUPS["x86"][:] + ["mips_loongson", "mips_qemu_mips", + "sparc64_ieee1275", "powerpc_ieee1275"] +GROUPS["cmos"].remove("i386_efi"); GROUPS["cmos"].remove("x86_64_efi"); +GROUPS["pci"] = GROUPS["x86"] + ["mips_loongson"] +GROUPS["usb"] = GROUPS["pci"] + ["arm_coreboot"] + +# If gfxterm is main output console integrate it into kernel +GROUPS["videoinkernel"] = ["mips_loongson", "i386_coreboot", "arm_coreboot" ] +GROUPS["videomodules"] = GRUB_PLATFORMS[:]; +for i in GROUPS["videoinkernel"]: GROUPS["videomodules"].remove(i) + +# Similar for terminfo +GROUPS["terminfoinkernel"] = [ "emu", "mips_loongson", "mips_arc", "mips_qemu_mips", "i386_xen_pvh" ] + GROUPS["xen"] + GROUPS["ieee1275"] + GROUPS["uboot"]; +GROUPS["terminfomodule"] = GRUB_PLATFORMS[:]; +for i in GROUPS["terminfoinkernel"]: GROUPS["terminfomodule"].remove(i) + +# Flattened Device Trees (FDT) +GROUPS["fdt"] = [ "arm64_efi", "arm_uboot", "arm_efi", "loongarch64_efi", "riscv32_efi", "riscv64_efi" ] + +# Needs software helpers for division +# Must match GRUB_DIVISION_IN_SOFTWARE in misc.h +GROUPS["softdiv"] = GROUPS["arm"] + ["ia64_efi"] + GROUPS["riscv32"] +GROUPS["no_softdiv"] = GRUB_PLATFORMS[:] +for i in GROUPS["softdiv"]: GROUPS["no_softdiv"].remove(i) + +# Miscellaneous groups scheduled to disappear in future +GROUPS["i386_coreboot_multiboot_qemu"] = ["i386_coreboot", "i386_multiboot", "i386_qemu"] +GROUPS["nopc"] = GRUB_PLATFORMS[:]; GROUPS["nopc"].remove("i386_pc") + +# +# Create platform => groups reverse map, where groups covering that +# platform are ordered by their sizes +# +RMAP = {} +for platform in GRUB_PLATFORMS: + # initialize with platform itself as a group + RMAP[platform] = [ platform ] + + for k in GROUPS.keys(): + v = GROUPS[k] + # skip groups that don't cover this platform + if platform not in v: continue + + bigger = [] + smaller = [] + # partition currently known groups based on their size + for group in RMAP[platform]: + if group in GRUB_PLATFORMS: smaller.append(group) + elif len(GROUPS[group]) < len(v): smaller.append(group) + else: bigger.append(group) + # insert in the middle + RMAP[platform] = smaller + [ k ] + bigger + +# +# Input +# + +# We support a subset of the AutoGen definitions file syntax. Specifically, +# compound names are disallowed; some preprocessing directives are +# disallowed (though #if/#endif are allowed; note that, like AutoGen, #if +# skips everything to the next #endif regardless of the value of the +# conditional); and shell-generated strings, Scheme-generated strings, and +# here strings are disallowed. + +class AutogenToken: + (autogen, definitions, eof, var_name, other_name, string, number, + semicolon, equals, comma, lbrace, rbrace, lbracket, rbracket) = range(14) + +class AutogenState: + (init, need_def, need_tpl, need_semi, need_name, have_name, need_value, + need_idx, need_rbracket, indx_name, have_value, done) = range(12) + +class AutogenParseError(Exception): + def __init__(self, message, path, line): + super(AutogenParseError, self).__init__(message) + self.path = path + self.line = line + + def __str__(self): + return ( + super(AutogenParseError, self).__str__() + + " at file %s line %d" % (self.path, self.line)) + +class AutogenDefinition(list): + def __getitem__(self, key): + try: + return super(AutogenDefinition, self).__getitem__(key) + except TypeError: + for name, value in self: + if name == key: + return value + + def __contains__(self, key): + for name, value in self: + if name == key: + return True + return False + + def get(self, key, default): + for name, value in self: + if name == key: + return value + else: + return default + + def find_all(self, key): + for name, value in self: + if name == key: + yield value + +class AutogenParser: + def __init__(self): + self.definitions = AutogenDefinition() + self.def_stack = [("", self.definitions)] + self.curdef = None + self.new_name = None + self.cur_path = None + self.cur_line = 0 + + @staticmethod + def is_unquotable_char(c): + return (ord(c) in range(ord("!"), ord("~") + 1) and + c not in "#,;<=>[\\]`{}?*'\"()") + + @staticmethod + def is_value_name_char(c): + return c in ":^-_" or c.isalnum() + + def error(self, message): + raise AutogenParseError(message, self.cur_file, self.cur_line) + + def read_tokens(self, f): + data = f.read() + end = len(data) + offset = 0 + while offset < end: + while offset < end and data[offset].isspace(): + if data[offset] == "\n": + self.cur_line += 1 + offset += 1 + if offset >= end: + break + c = data[offset] + if c == "#": + offset += 1 + try: + end_directive = data.index("\n", offset) + directive = data[offset:end_directive] + offset = end_directive + except ValueError: + directive = data[offset:] + offset = end + name, value = directive.split(None, 1) + if name == "if": + try: + end_if = data.index("\n#endif", offset) + new_offset = end_if + len("\n#endif") + self.cur_line += data[offset:new_offset].count("\n") + offset = new_offset + except ValueError: + self.error("#if without matching #endif") + else: + self.error("Unhandled directive '#%s'" % name) + elif c == "{": + yield AutogenToken.lbrace, c + offset += 1 + elif c == "=": + yield AutogenToken.equals, c + offset += 1 + elif c == "}": + yield AutogenToken.rbrace, c + offset += 1 + elif c == "[": + yield AutogenToken.lbracket, c + offset += 1 + elif c == "]": + yield AutogenToken.rbracket, c + offset += 1 + elif c == ";": + yield AutogenToken.semicolon, c + offset += 1 + elif c == ",": + yield AutogenToken.comma, c + offset += 1 + elif c in ("'", '"'): + s = [] + while True: + offset += 1 + if offset >= end: + self.error("EOF in quoted string") + if data[offset] == "\n": + self.cur_line += 1 + if data[offset] == "\\": + offset += 1 + if offset >= end: + self.error("EOF in quoted string") + if data[offset] == "\n": + self.cur_line += 1 + # Proper escaping unimplemented; this can be filled + # out if needed. + s.append("\\") + s.append(data[offset]) + elif data[offset] == c: + offset += 1 + break + else: + s.append(data[offset]) + yield AutogenToken.string, "".join(s) + elif c == "/": + offset += 1 + if data[offset] == "*": + offset += 1 + try: + end_comment = data.index("*/", offset) + new_offset = end_comment + len("*/") + self.cur_line += data[offset:new_offset].count("\n") + offset = new_offset + except ValueError: + self.error("/* without matching */") + elif data[offset] == "/": + try: + offset = data.index("\n", offset) + except ValueError: + pass + elif (c.isdigit() or + (c == "-" and offset < end - 1 and + data[offset + 1].isdigit())): + end_number = offset + 1 + while end_number < end and data[end_number].isdigit(): + end_number += 1 + yield AutogenToken.number, data[offset:end_number] + offset = end_number + elif self.is_unquotable_char(c): + end_name = offset + while (end_name < end and + self.is_value_name_char(data[end_name])): + end_name += 1 + if end_name < end and self.is_unquotable_char(data[end_name]): + while (end_name < end and + self.is_unquotable_char(data[end_name])): + end_name += 1 + yield AutogenToken.other_name, data[offset:end_name] + offset = end_name + else: + s = data[offset:end_name] + if s.lower() == "autogen": + yield AutogenToken.autogen, s + elif s.lower() == "definitions": + yield AutogenToken.definitions, s + else: + yield AutogenToken.var_name, s + offset = end_name + else: + self.error("Invalid input character '%s'" % c) + yield AutogenToken.eof, None + + def do_need_name_end(self, token): + if len(self.def_stack) > 1: + self.error("Definition blocks were left open") + + def do_need_name_var_name(self, token): + self.new_name = token + + def do_end_block(self, token): + if len(self.def_stack) <= 1: + self.error("Too many close braces") + new_name, parent_def = self.def_stack.pop() + parent_def.append((new_name, self.curdef)) + self.curdef = parent_def + + def do_empty_val(self, token): + self.curdef.append((self.new_name, "")) + + def do_str_value(self, token): + self.curdef.append((self.new_name, token)) + + def do_start_block(self, token): + self.def_stack.append((self.new_name, self.curdef)) + self.curdef = AutogenDefinition() + + def do_indexed_name(self, token): + self.new_name = token + + def read_definitions_file(self, f): + self.curdef = self.definitions + self.cur_line = 0 + state = AutogenState.init + + # The following transition table was reduced from the Autogen + # documentation: + # info -f autogen -n 'Full Syntax' + transitions = { + AutogenState.init: { + AutogenToken.autogen: (AutogenState.need_def, None), + }, + AutogenState.need_def: { + AutogenToken.definitions: (AutogenState.need_tpl, None), + }, + AutogenState.need_tpl: { + AutogenToken.var_name: (AutogenState.need_semi, None), + AutogenToken.other_name: (AutogenState.need_semi, None), + AutogenToken.string: (AutogenState.need_semi, None), + }, + AutogenState.need_semi: { + AutogenToken.semicolon: (AutogenState.need_name, None), + }, + AutogenState.need_name: { + AutogenToken.autogen: (AutogenState.need_def, None), + AutogenToken.eof: (AutogenState.done, self.do_need_name_end), + AutogenToken.var_name: ( + AutogenState.have_name, self.do_need_name_var_name), + AutogenToken.rbrace: ( + AutogenState.have_value, self.do_end_block), + }, + AutogenState.have_name: { + AutogenToken.semicolon: ( + AutogenState.need_name, self.do_empty_val), + AutogenToken.equals: (AutogenState.need_value, None), + AutogenToken.lbracket: (AutogenState.need_idx, None), + }, + AutogenState.need_value: { + AutogenToken.var_name: ( + AutogenState.have_value, self.do_str_value), + AutogenToken.other_name: ( + AutogenState.have_value, self.do_str_value), + AutogenToken.string: ( + AutogenState.have_value, self.do_str_value), + AutogenToken.number: ( + AutogenState.have_value, self.do_str_value), + AutogenToken.lbrace: ( + AutogenState.need_name, self.do_start_block), + }, + AutogenState.need_idx: { + AutogenToken.var_name: ( + AutogenState.need_rbracket, self.do_indexed_name), + AutogenToken.number: ( + AutogenState.need_rbracket, self.do_indexed_name), + }, + AutogenState.need_rbracket: { + AutogenToken.rbracket: (AutogenState.indx_name, None), + }, + AutogenState.indx_name: { + AutogenToken.semicolon: ( + AutogenState.need_name, self.do_empty_val), + AutogenToken.equals: (AutogenState.need_value, None), + }, + AutogenState.have_value: { + AutogenToken.semicolon: (AutogenState.need_name, None), + AutogenToken.comma: (AutogenState.need_value, None), + }, + } + + for code, token in self.read_tokens(f): + if code in transitions[state]: + state, handler = transitions[state][code] + if handler is not None: + handler(token) + else: + self.error( + "Parse error in state %s: unexpected token '%s'" % ( + state, token)) + if state == AutogenState.done: + break + + def read_definitions(self, path): + self.cur_file = path + with open(path) as f: + self.read_definitions_file(f) + +defparser = AutogenParser() + +# +# Output +# + +outputs = {} + +def output(s, section=''): + if s == "": + return + outputs.setdefault(section, []) + outputs[section].append(s) + +def write_output(section=''): + for s in outputs.get(section, []): + print(s, end='') + +# +# Global variables +# + +def gvar_add(var, value): + output(var + " += " + value + "\n") + +# +# Per PROGRAM/SCRIPT variables +# + +seen_vars = set() + +def vars_init(defn, *var_list): + name = defn['name'] + + if name not in seen_target and name not in seen_vars: + for var in var_list: + output(var + " = \n", section='decl') + seen_vars.add(name) + +def var_set(var, value): + output(var + " = " + value + "\n") + +def var_add(var, value): + output(var + " += " + value + "\n") + +# +# Variable names and rules +# + +canonical_name_re = re.compile(r'[^0-9A-Za-z@_]') +canonical_name_suffix = "" + +def set_canonical_name_suffix(suffix): + global canonical_name_suffix + canonical_name_suffix = suffix + +def cname(defn): + return canonical_name_re.sub('_', defn['name'] + canonical_name_suffix) + +def rule(target, source, cmd): + if cmd[0] == "\n": + output("\n" + target + ": " + source + cmd.replace("\n", "\n\t") + "\n") + else: + output("\n" + target + ": " + source + "\n\t" + cmd.replace("\n", "\n\t") + "\n") + +# +# Handle keys with platform names as values, for example: +# +# kernel = { +# nostrip = emu; +# ... +# } +# +def platform_tagged(defn, platform, tag): + for value in defn.find_all(tag): + for group in RMAP[platform]: + if value == group: + return True + return False + +def if_platform_tagged(defn, platform, tag, snippet_if, snippet_else=None): + if platform_tagged(defn, platform, tag): + return snippet_if + elif snippet_else is not None: + return snippet_else + +# +# Handle tagged values +# +# module = { +# extra_dist = ... +# extra_dist = ... +# ... +# }; +# +def foreach_value(defn, tag, closure): + r = [] + for value in defn.find_all(tag): + r.append(closure(value)) + return ''.join(r) + +# +# Handle best matched values for a platform, for example: +# +# module = { +# cflags = '-Wall'; +# emu_cflags = '-Wall -DGRUB_EMU=1'; +# ... +# } +# +def foreach_platform_specific_value(defn, platform, suffix, nonetag, closure): + r = [] + for group in RMAP[platform]: + values = list(defn.find_all(group + suffix)) + if values: + for value in values: + r.append(closure(value)) + break + else: + for value in defn.find_all(nonetag): + r.append(closure(value)) + return ''.join(r) + +# +# Handle values from sum of all groups for a platform, for example: +# +# module = { +# common = kern/misc.c; +# emu = kern/emu/misc.c; +# ... +# } +# +def foreach_platform_value(defn, platform, suffix, closure): + r = [] + for group in RMAP[platform]: + for value in defn.find_all(group + suffix): + r.append(closure(value)) + r.sort() + return ''.join(r) + +def platform_conditional(platform, closure): + output("\nif COND_" + platform + "\n") + closure(platform) + output("endif\n") + +# +# Handle guarding with platform-specific "enable" keys, for example: +# +# 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; +# }; +# +def foreach_enabled_platform(defn, closure): + if 'enable' in defn: + for platform in GRUB_PLATFORMS: + if platform_tagged(defn, platform, "enable"): + platform_conditional(platform, closure) + else: + for platform in GRUB_PLATFORMS: + platform_conditional(platform, closure) + +# +# Handle guarding with platform-specific automake conditionals, for example: +# +# module = { +# name = usb; +# common = bus/usb/usb.c; +# noemu = bus/usb/usbtrans.c; +# noemu = bus/usb/usbhub.c; +# enable = emu; +# enable = i386; +# enable = mips_loongson; +# emu_condition = COND_GRUB_EMU_SDL; +# }; +# +def under_platform_specific_conditionals(defn, platform, closure): + output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "if " + cond + "\n")) + closure(defn, platform) + output(foreach_platform_specific_value(defn, platform, "_condition", "condition", lambda cond: "endif " + cond + "\n")) + +def platform_specific_values(defn, platform, suffix, nonetag): + return foreach_platform_specific_value(defn, platform, suffix, nonetag, + lambda value: value + " ") + +def platform_values(defn, platform, suffix): + return foreach_platform_value(defn, platform, suffix, lambda value: value + " ") + +def extra_dist(defn): + return foreach_value(defn, "extra_dist", lambda value: value + " ") + +def extra_dep(defn): + return foreach_value(defn, "depends", lambda value: value + " ") + +def platform_sources(defn, p): return platform_values(defn, p, "_head") + platform_values(defn, p, "") +def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist") + +def platform_startup(defn, p): return platform_specific_values(defn, p, "_startup", "startup") +def platform_ldadd(defn, p): return platform_specific_values(defn, p, "_ldadd", "ldadd") +def platform_dependencies(defn, p): return platform_specific_values(defn, p, "_dependencies", "dependencies") +def platform_cflags(defn, p): return platform_specific_values(defn, p, "_cflags", "cflags") +def platform_ldflags(defn, p): return platform_specific_values(defn, p, "_ldflags", "ldflags") +def platform_cppflags(defn, p): return platform_specific_values(defn, p, "_cppflags", "cppflags") +def platform_ccasflags(defn, p): return platform_specific_values(defn, p, "_ccasflags", "ccasflags") +def platform_stripflags(defn, p): return platform_specific_values(defn, p, "_stripflags", "stripflags") +def platform_objcopyflags(defn, p): return platform_specific_values(defn, p, "_objcopyflags", "objcopyflags") + +# +# Emit snippet only the first time through for the current name. +# +seen_target = set() + +def first_time(defn, snippet): + if defn['name'] not in seen_target: + return snippet + return '' + +def is_platform_independent(defn): + if 'enable' in defn: + return False + for suffix in [ "", "_head", "_nodist" ]: + template = platform_values(defn, GRUB_PLATFORMS[0], suffix) + for platform in GRUB_PLATFORMS[1:]: + if template != platform_values(defn, platform, suffix): + return False + + for suffix in [ "startup", "ldadd", "dependencies", "cflags", "ldflags", "cppflags", "ccasflags", "stripflags", "objcopyflags", "condition" ]: + template = platform_specific_values(defn, GRUB_PLATFORMS[0], "_" + suffix, suffix) + for platform in GRUB_PLATFORMS[1:]: + if template != platform_specific_values(defn, platform, "_" + suffix, suffix): + return False + for tag in [ "nostrip" ]: + template = platform_tagged(defn, GRUB_PLATFORMS[0], tag) + for platform in GRUB_PLATFORMS[1:]: + if template != platform_tagged(defn, platform, tag): + return False + + return True + +def module(defn, platform): + name = defn['name'] + set_canonical_name_suffix(".module") + + gvar_add("platform_PROGRAMS", name + ".module") + gvar_add("MODULE_FILES", name + ".module$(EXEEXT)") + + var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources") + var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources") + var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform)) + + gvar_add("dist_noinst_DATA", extra_dist(defn)) + gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)") + gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)") + + gvar_add("MOD_FILES", name + ".mod") + gvar_add("MARKER_FILES", name + ".marker") + gvar_add("CLEANFILES", name + ".marker") + + for dep in defn.find_all("depends"): + gvar_add("EXTRA_DEPS", "depends " + name + " " + dep + ":") + + output(""" +""" + name + """.marker: $(""" + cname(defn) + """_SOURCES) $(nodist_""" + cname(defn) + """_SOURCES) + $(TARGET_CPP) -DGRUB_LST_GENERATOR $(CPPFLAGS_MARKER) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(""" + cname(defn) + """_CPPFLAGS) $(CPPFLAGS) $^ > $@.new || (rm -f $@; exit 1) + grep 'MARKER' $@.new | grep -v '^#' > $@; rm -f $@.new +""") + +def kernel(defn, platform): + name = defn['name'] + set_canonical_name_suffix(".exec") + gvar_add("platform_PROGRAMS", name + ".exec") + var_set(cname(defn) + "_SOURCES", platform_startup(defn, platform)) + var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform)) + var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources") + var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_KERNEL) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_KERNEL) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_KERNEL) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_KERNEL) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_STRIPFLAGS", "$(AM_STRIPFLAGS) $(STRIPFLAGS_KERNEL) " + platform_stripflags(defn, platform)) + var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF)") + + gvar_add("dist_noinst_DATA", extra_dist(defn)) + gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)") + gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)") + + gvar_add("platform_DATA", name + ".img") + gvar_add("CLEANFILES", name + ".img") + rule(name + ".img", name + ".exec$(EXEEXT)", + if_platform_tagged(defn, platform, "nostrip", +"""if test x$(TARGET_APPLE_LINKER) = x1; then \ + $(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -wd1106 -nu -nd $< $@; \ + elif test ! -z '$(TARGET_OBJ2ELF)'; then \ + $(TARGET_OBJ2ELF) $< $@ || (rm -f $@; exit 1); \ + else cp $< $@; fi""", +"""if test x$(TARGET_APPLE_LINKER) = x1; then \ + $(TARGET_STRIP) -S -x $(""" + cname(defn) + """) -o $@.bin $<; \ + $(TARGET_OBJCONV) -f$(TARGET_MODULE_FORMAT) -nr:_grub_mod_init:grub_mod_init -nr:_grub_mod_fini:grub_mod_fini -ed2022 -ed2016 -wd1106 -nu -nd $@.bin $@; \ + rm -f $@.bin; \ + elif test ! -z '$(TARGET_OBJ2ELF)'; then \ + """ + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o $@.bin $< && \ + $(TARGET_OBJ2ELF) $@.bin $@ || (rm -f $@; rm -f $@.bin; exit 1); \ + rm -f $@.bin; \ +else """ + "$(TARGET_STRIP) $(" + cname(defn) + "_STRIPFLAGS) -o $@ $<; \ +fi""")) + +def image(defn, platform): + name = defn['name'] + set_canonical_name_suffix(".image") + gvar_add("platform_PROGRAMS", name + ".image") + var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform)) + var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + "## platform nodist sources") + var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_IMAGE) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_IMAGE) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_IMAGE) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_IMAGE) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_OBJCOPYFLAGS", "$(OBJCOPYFLAGS_IMAGE) " + platform_objcopyflags(defn, platform)) + # var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform)) + + gvar_add("dist_noinst_DATA", extra_dist(defn)) + gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)") + gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)") + + gvar_add("platform_DATA", name + ".img") + gvar_add("CLEANFILES", name + ".img") + rule(name + ".img", name + ".image$(EXEEXT)", """ +if test x$(TARGET_APPLE_LINKER) = x1; then \ + $(MACHO2IMG) $< $@; \ +else \ + $(TARGET_OBJCOPY) $(""" + cname(defn) + """_OBJCOPYFLAGS) --strip-unneeded -R .note -R .comment -R .note.gnu.build-id -R .MIPS.abiflags -R .reginfo -R .rel.dyn -R .note.gnu.gold-version -R .note.gnu.property -R .ARM.exidx -R .interp $< $@; \ +fi +""") + +def library(defn, platform): + name = defn['name'] + set_canonical_name_suffix("") + + vars_init(defn, + cname(defn) + "_SOURCES", + "nodist_" + cname(defn) + "_SOURCES", + cname(defn) + "_CFLAGS", + cname(defn) + "_CPPFLAGS", + cname(defn) + "_CCASFLAGS") + # cname(defn) + "_DEPENDENCIES") + + if name not in seen_target: + gvar_add("noinst_LIBRARIES", name) + var_add(cname(defn) + "_SOURCES", platform_sources(defn, platform)) + var_add("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform)) + var_add(cname(defn) + "_CFLAGS", first_time(defn, "$(AM_CFLAGS) $(CFLAGS_LIBRARY) ") + platform_cflags(defn, platform)) + var_add(cname(defn) + "_CPPFLAGS", first_time(defn, "$(AM_CPPFLAGS) $(CPPFLAGS_LIBRARY) ") + platform_cppflags(defn, platform)) + var_add(cname(defn) + "_CCASFLAGS", first_time(defn, "$(AM_CCASFLAGS) $(CCASFLAGS_LIBRARY) ") + platform_ccasflags(defn, platform)) + # var_add(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform)) + + gvar_add("dist_noinst_DATA", extra_dist(defn)) + if name not in seen_target: + gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)") + gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)") + +def installdir(defn, default="bin"): + return defn.get('installdir', default) + +def manpage(defn, adddeps): + name = defn['name'] + mansection = defn['mansection'] + + output("if COND_MAN_PAGES\n") + gvar_add("man_MANS", name + "." + mansection) + rule(name + "." + mansection, name + " " + adddeps, """ +chmod a+x """ + name + """ +PATH=$(builddir):$$PATH pkgdatadir=$(builddir) $(HELP2MAN) --section=""" + mansection + """ -i $(top_srcdir)/docs/man/""" + name + """.h2m -o $@ """ + name + """ +""") + gvar_add("CLEANFILES", name + "." + mansection) + output("endif\n") + +def program(defn, platform, test=False): + name = defn['name'] + set_canonical_name_suffix("") + + if 'testcase' in defn: + gvar_add("check_PROGRAMS_" + defn['testcase'], name) + else: + var_add(installdir(defn) + "_PROGRAMS", name) + if 'mansection' in defn: + manpage(defn, "") + + var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform)) + var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform)) + var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_PROGRAM) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_PROGRAM) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_PROGRAM) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_PROGRAM) " + platform_ccasflags(defn, platform)) + # var_set(cname(defn) + "_DEPENDENCIES", platform_dependencies(defn, platform) + " " + platform_ldadd(defn, platform)) + + gvar_add("dist_noinst_DATA", extra_dist(defn)) + gvar_add("BUILT_SOURCES", "$(nodist_" + cname(defn) + "_SOURCES)") + gvar_add("CLEANFILES", "$(nodist_" + cname(defn) + "_SOURCES)") + +def data(defn, platform): + var_add("dist_" + installdir(defn) + "_DATA", platform_sources(defn, platform)) + gvar_add("dist_noinst_DATA", extra_dist(defn)) + +def transform_data(defn, platform): + name = defn['name'] + + var_add(installdir(defn) + "_DATA", name) + + rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """ +(for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:- +chmod a+x """ + name + """ +""") + + gvar_add("CLEANFILES", name) + gvar_add("EXTRA_DIST", extra_dist(defn)) + gvar_add("dist_noinst_DATA", platform_sources(defn, platform)) + +def script(defn, platform): + name = defn['name'] + + if 'testcase' in defn: + gvar_add("check_SCRIPTS_" + defn['testcase'], name) + else: + var_add(installdir(defn) + "_SCRIPTS", name) + if 'mansection' in defn: + manpage(defn, "grub-mkconfig_lib") + + rule(name, "$(top_builddir)/config.status " + platform_sources(defn, platform) + platform_dependencies(defn, platform), """ +(for x in """ + platform_sources(defn, platform) + """; do cat $(srcdir)/"$$x"; done) | $(top_builddir)/config.status --file=$@:- +chmod a+x """ + name + """ +""") + + gvar_add("CLEANFILES", name) + gvar_add("EXTRA_DIST", extra_dist(defn)) + gvar_add("dist_noinst_DATA", platform_sources(defn, platform)) + +def rules(target, closure): + seen_target.clear() + seen_vars.clear() + + for defn in defparser.definitions.find_all(target): + if is_platform_independent(defn): + under_platform_specific_conditionals(defn, GRUB_PLATFORMS[0], closure) + else: + foreach_enabled_platform( + defn, + lambda p: under_platform_specific_conditionals(defn, p, closure)) + # Remember that we've seen this target. + seen_target.add(defn['name']) + +parser = OptionParser(usage="%prog DEFINITION-FILES") +_, args = parser.parse_args() + +for arg in args: + defparser.read_definitions(arg) + +rules("module", module) +rules("kernel", kernel) +rules("image", image) +rules("library", library) +rules("program", program) +rules("script", script) +rules("data", data) +rules("transform_data", transform_data) + +write_output(section='decl') +write_output() diff --git a/genvideolist.sh b/genvideolist.sh deleted file mode 100644 index b208fa25c..000000000 --- a/genvideolist.sh +++ /dev/null @@ -1,26 +0,0 @@ -#! /bin/sh -# -# Copyright (C) 2005,2008,2009 Free Software Foundation, Inc. -# -# This script is free software; the author -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY, to the extent permitted by law; without -# even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. - -# Read source code from stdin and detect partmap names. - -module=$1 - -# Ignore video.mod. -if test $module = video; then - exit -fi - -# For now, this emits only a module name, if the module registers a partition map. -if grep -v "^#" | grep '^ *grub_video_register' >/dev/null 2>&1; then - echo $module -fi diff --git a/gettext/gettext.c b/gettext/gettext.c deleted file mode 100644 index 0aa8decbd..000000000 --- a/gettext/gettext.c +++ /dev/null @@ -1,377 +0,0 @@ -/* gettext.c - gettext module */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - .mo file information from: - http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html . -*/ - - -static grub_file_t fd_mo; - -static int grub_gettext_offsetoriginal; -static int grub_gettext_max; - -static const char *(*grub_gettext_original) (const char *s); - -struct grub_gettext_msg -{ - struct grub_gettext_msg *next; - const char *name; - - const char *translated; -}; - -struct grub_gettext_msg *grub_gettext_msg_list = NULL; - -#define GETTEXT_MAGIC_NUMBER 0 -#define GETTEXT_FILE_FORMAT 4 -#define GETTEXT_NUMBER_OF_STRINGS 8 -#define GETTEXT_OFFSET_ORIGINAL 12 -#define GETTEXT_OFFSET_TRANSLATION 16 - -#define MO_MAGIC_NUMBER 0x950412de - -static grub_ssize_t -grub_gettext_pread (grub_file_t file, void *buf, grub_size_t len, - grub_off_t offset) -{ - if (grub_file_seek (file, offset) == (grub_off_t) - 1) - { - return -1; - } - return grub_file_read (file, buf, len); -} - -static grub_uint32_t -grub_gettext_get_info (int offset) -{ - grub_uint32_t value; - - grub_gettext_pread (fd_mo, (char *) &value, 4, offset); - - value = grub_cpu_to_le32 (value); - return value; -} - -static void -grub_gettext_getstring_from_offset (grub_uint32_t offset, - grub_uint32_t length, char *translation) -{ - grub_gettext_pread (fd_mo, translation, length, offset); - translation[length] = '\0'; -} - -static const char * -grub_gettext_gettranslation_from_position (int position) -{ - int offsettranslation; - int internal_position; - grub_uint32_t length, offset; - char *translation; - - offsettranslation = grub_gettext_get_info (GETTEXT_OFFSET_TRANSLATION); - - internal_position = offsettranslation + position * 8; - - grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position); - length = grub_cpu_to_le32 (length); - - grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4); - offset = grub_cpu_to_le32 (offset); - - translation = grub_malloc (length + 1); - grub_gettext_getstring_from_offset (offset, length, translation); - - return translation; -} - -static char * -grub_gettext_getstring_from_position (int position) -{ - int internal_position; - int length, offset; - char *original; - - /* Get position for string i. */ - internal_position = grub_gettext_offsetoriginal + (position * 8); - - /* Get the length of the string i. */ - grub_gettext_pread (fd_mo, (char *) &length, 4, internal_position); - - /* Get the offset of the string i. */ - grub_gettext_pread (fd_mo, (char *) &offset, 4, internal_position + 4); - - /* Get the string i. */ - original = grub_malloc (length + 1); - grub_gettext_getstring_from_offset (offset, length, original); - - return original; -} - -static const char * -grub_gettext_translate (const char *orig) -{ - char *current_string; - const char *ret; - - int min, max, current; - int found = 0; - - struct grub_gettext_msg *cur; - - /* Make sure we can use grub_gettext_translate for error messages. Push - active error message to error stack and reset error message. */ - grub_error_push (); - - cur = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_gettext_msg_list), - orig); - - if (cur) - { - grub_error_pop (); - return cur->translated; - } - - if (fd_mo == 0) - { - grub_error_pop (); - return orig; - } - - min = 0; - max = grub_gettext_max; - - current = (max + min) / 2; - - while (current != min && current != max && found == 0) - { - current_string = grub_gettext_getstring_from_position (current); - - /* Search by bisection. */ - if (grub_strcmp (current_string, orig) < 0) - { - grub_free (current_string); - min = current; - } - else if (grub_strcmp (current_string, orig) > 0) - { - grub_free (current_string); - max = current; - } - else if (grub_strcmp (current_string, orig) == 0) - { - grub_free (current_string); - found = 1; - } - current = (max + min) / 2; - } - - ret = found ? grub_gettext_gettranslation_from_position (current) : orig; - - if (found) - { - cur = grub_zalloc (sizeof (*cur)); - - if (cur) - { - cur->name = grub_strdup (orig); - if (cur->name) - { - cur->translated = ret; - grub_list_push (GRUB_AS_LIST_P (&grub_gettext_msg_list), - GRUB_AS_LIST (cur)); - } - } - else - grub_errno = GRUB_ERR_NONE; - } - - grub_error_pop (); - return ret; -} - -/* This is similar to grub_gzfile_open. */ -static grub_file_t -grub_mofile_open (const char *filename) -{ - int unsigned magic; - int version; - - /* Using fd_mo and not another variable because - it's needed for grub_gettext_get_info. */ - - fd_mo = grub_gzfile_open (filename, 1); - grub_errno = GRUB_ERR_NONE; - - if (!fd_mo) - { - grub_dprintf ("gettext", "Cannot read %s\n", filename); - return 0; - } - - magic = grub_gettext_get_info (GETTEXT_MAGIC_NUMBER); - - if (magic != MO_MAGIC_NUMBER) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "mo: invalid mo file: %s", - filename); - grub_file_close (fd_mo); - fd_mo = 0; - return 0; - } - - version = grub_gettext_get_info (GETTEXT_FILE_FORMAT); - - if (version != 0) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, - "mo: invalid mo version in file: %s", filename); - fd_mo = 0; - return 0; - } - - return fd_mo; -} - -static void -grub_gettext_init_ext (const char *lang) -{ - char *mo_file; - char *locale_dir; - - locale_dir = grub_env_get ("locale_dir"); - if (locale_dir == NULL) - { - grub_dprintf ("gettext", "locale_dir variable is not set up.\n"); - return; - } - - fd_mo = NULL; - - /* mo_file e.g.: /boot/grub/locale/ca.mo */ - - mo_file = grub_xasprintf ("%s/%s.mo", locale_dir, lang); - if (!mo_file) - return; - - fd_mo = grub_mofile_open (mo_file); - - /* Will try adding .gz as well. */ - if (fd_mo == NULL) - { - grub_free (mo_file); - mo_file = grub_xasprintf ("%s.gz", mo_file); - if (!mo_file) - return; - fd_mo = grub_mofile_open (mo_file); - } - - if (fd_mo) - { - grub_gettext_offsetoriginal = - grub_gettext_get_info (GETTEXT_OFFSET_ORIGINAL); - grub_gettext_max = grub_gettext_get_info (GETTEXT_NUMBER_OF_STRINGS); - - grub_gettext_original = grub_gettext; - grub_gettext = grub_gettext_translate; - } -} - -static void -grub_gettext_delete_list (void) -{ - struct grub_gettext_msg *item; - - while ((item = - grub_list_pop (GRUB_AS_LIST_P (&grub_gettext_msg_list))) != 0) - { - char *original = (char *) ((struct grub_gettext_msg *) item)->name; - grub_free (original); - - /* Don't delete the translated message because could be in use. */ - } -} - -static char * -grub_gettext_env_write_lang (struct grub_env_var *var - __attribute__ ((unused)), const char *val) -{ - grub_gettext_init_ext (val); - - grub_gettext_delete_list (); - - return grub_strdup (val); -} - -static grub_err_t -grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) -{ - if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "text to translate required"); - - const char *translation; - translation = grub_gettext_translate (args[0]); - grub_printf ("%s\n", translation); - return 0; -} - -GRUB_MOD_INIT (gettext) -{ - (void) mod; /* To stop warning. */ - - const char *lang; - - lang = grub_env_get ("lang"); - - grub_gettext_init_ext (lang); - - grub_register_command_p1 ("gettext", grub_cmd_translate, - N_("STRING"), - N_("Translates the string with the current settings.")); - - /* Reload .mo file information if lang changes. */ - grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang); - - /* Preserve hooks after context changes. */ - grub_env_export ("lang"); -} - -GRUB_MOD_FINI (gettext) -{ - if (fd_mo != 0) - grub_file_close (fd_mo); - - grub_gettext_delete_list (); - - grub_gettext = grub_gettext_original; -} diff --git a/gfxmenu/gui_list.c b/gfxmenu/gui_list.c deleted file mode 100644 index 0d771413f..000000000 --- a/gfxmenu/gui_list.c +++ /dev/null @@ -1,612 +0,0 @@ -/* gui_list.c - GUI component to display a selectable list of items. */ -/* - * 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 . - */ - -#include -#include -#include -#include -#include -#include - -struct grub_gui_list_impl -{ - struct grub_gui_list list; - - grub_gui_container_t parent; - grub_video_rect_t bounds; - char *id; - int visible; - - int icon_width; - int icon_height; - int item_height; - int item_padding; - int item_icon_space; - int item_spacing; - grub_font_t item_font; - grub_font_t selected_item_font; - grub_gui_color_t item_color; - int selected_item_color_set; - grub_gui_color_t selected_item_color; - - int draw_scrollbar; - int need_to_recreate_scrollbar; - char *scrollbar_frame_pattern; - char *scrollbar_thumb_pattern; - grub_gfxmenu_box_t scrollbar_frame; - grub_gfxmenu_box_t scrollbar_thumb; - int scrollbar_width; - - int first_shown_index; - - int need_to_recreate_boxes; - char *theme_dir; - char *menu_box_pattern; - char *selected_item_box_pattern; - grub_gfxmenu_box_t menu_box; - grub_gfxmenu_box_t selected_item_box; - - grub_gfxmenu_icon_manager_t icon_manager; - - grub_gfxmenu_view_t view; -}; - -typedef struct grub_gui_list_impl *list_impl_t; - -static void -list_destroy (void *vself) -{ - list_impl_t self = vself; - - grub_free (self->theme_dir); - grub_free (self->menu_box_pattern); - grub_free (self->selected_item_box_pattern); - if (self->menu_box) - self->menu_box->destroy (self->menu_box); - if (self->selected_item_box) - self->selected_item_box->destroy (self->selected_item_box); - if (self->icon_manager) - grub_gfxmenu_icon_manager_destroy (self->icon_manager); - - grub_free (self); -} - -static int -get_num_shown_items (list_impl_t self) -{ - int boxpad = self->item_padding; - int item_vspace = self->item_spacing; - int item_height = self->item_height; - - grub_gfxmenu_box_t box = self->menu_box; - int box_top_pad = box->get_top_pad (box); - int box_bottom_pad = box->get_bottom_pad (box); - - return (self->bounds.height + item_vspace - 2 * boxpad - - box_top_pad - box_bottom_pad) / (item_height + item_vspace); -} - -static int -check_boxes (list_impl_t self) -{ - if (self->need_to_recreate_boxes) - { - grub_gui_recreate_box (&self->menu_box, - self->menu_box_pattern, - self->theme_dir); - - grub_gui_recreate_box (&self->selected_item_box, - self->selected_item_box_pattern, - self->theme_dir); - - self->need_to_recreate_boxes = 0; - } - - return (self->menu_box != 0 && self->selected_item_box != 0); -} - -static int -check_scrollbar (list_impl_t self) -{ - if (self->need_to_recreate_scrollbar) - { - grub_gui_recreate_box (&self->scrollbar_frame, - self->scrollbar_frame_pattern, - self->theme_dir); - - grub_gui_recreate_box (&self->scrollbar_thumb, - self->scrollbar_thumb_pattern, - self->theme_dir); - - self->need_to_recreate_scrollbar = 0; - } - - return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 0); -} - -static const char * -list_get_id (void *vself) -{ - list_impl_t self = vself; - return self->id; -} - -static int -list_is_instance (void *vself __attribute__((unused)), const char *type) -{ - return (grub_strcmp (type, "component") == 0 - || grub_strcmp (type, "list") == 0); -} - -static struct grub_video_bitmap * -get_item_icon (list_impl_t self, int item_index) -{ - grub_menu_entry_t entry; - entry = grub_menu_get_entry (self->view->menu, item_index); - if (! entry) - return 0; - - return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry); -} - -static void -make_selected_item_visible (list_impl_t self) -{ - int selected_index = self->view->selected; - if (selected_index < 0) - return; /* No item is selected. */ - int num_shown_items = get_num_shown_items (self); - int last_shown_index = self->first_shown_index + (num_shown_items - 1); - if (selected_index < self->first_shown_index) - self->first_shown_index = selected_index; - else if (selected_index > last_shown_index) - self->first_shown_index = selected_index - (num_shown_items - 1); -} - -/* Draw a scrollbar on the menu. */ -static void -draw_scrollbar (list_impl_t self, - int value, int extent, int min, int max, - int rightx, int topy, int height) -{ - grub_gfxmenu_box_t frame = self->scrollbar_frame; - grub_gfxmenu_box_t thumb = self->scrollbar_thumb; - int frame_vertical_pad = (frame->get_top_pad (frame) - + frame->get_bottom_pad (frame)); - int frame_horizontal_pad = (frame->get_left_pad (frame) - + frame->get_right_pad (frame)); - int tracktop = topy + frame->get_top_pad (frame); - int tracklen = height - frame_vertical_pad; - frame->set_content_size (frame, self->scrollbar_width, tracklen); - int thumby = tracktop + tracklen * (value - min) / (max - min); - int thumbheight = tracklen * extent / (max - min) + 1; - thumb->set_content_size (thumb, - self->scrollbar_width - frame_horizontal_pad, - thumbheight - (thumb->get_top_pad (thumb) - + thumb->get_bottom_pad (thumb))); - frame->draw (frame, - rightx - (self->scrollbar_width + frame_horizontal_pad), - topy); - thumb->draw (thumb, - rightx - (self->scrollbar_width - frame->get_right_pad (frame)), - thumby); -} - -/* Draw the list of items. */ -static void -draw_menu (list_impl_t self, int width, int drawing_scrollbar, - int num_shown_items) -{ - if (! self->menu_box || ! self->selected_item_box) - return; - - int boxpad = self->item_padding; - int icon_text_space = self->item_icon_space; - int item_vspace = self->item_spacing; - - int ascent = grub_font_get_ascent (self->item_font); - int descent = grub_font_get_descent (self->item_font); - int item_height = self->item_height; - - make_selected_item_visible (self); - - int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0; - - grub_gfxmenu_box_t selbox = self->selected_item_box; - int sel_leftpad = selbox->get_left_pad (selbox); - int item_top = boxpad; - int item_left = boxpad + sel_leftpad; - int menu_index; - int visible_index; - - for (visible_index = 0, menu_index = self->first_shown_index; - visible_index < num_shown_items && menu_index < self->view->menu->size; - visible_index++, menu_index++) - { - int is_selected = (menu_index == self->view->selected); - - if (is_selected) - { - int sel_toppad = selbox->get_top_pad (selbox); - selbox->set_content_size (selbox, - (width - 2 * boxpad - - scrollbar_h_space), - item_height); - selbox->draw (selbox, - item_left - sel_leftpad, - item_top - sel_toppad); - } - - struct grub_video_bitmap *icon; - if ((icon = get_item_icon (self, menu_index)) != 0) - grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND, - item_left, - item_top + (item_height - self->icon_height) / 2, - 0, 0, self->icon_width, self->icon_height); - - const char *item_title = - grub_menu_get_entry (self->view->menu, menu_index)->title; - grub_font_t font = - (is_selected && self->selected_item_font - ? self->selected_item_font - : self->item_font); - grub_gui_color_t text_color = - ((is_selected && self->selected_item_color_set) - ? self->selected_item_color - : self->item_color); - grub_font_draw_string (item_title, - font, - grub_gui_map_color (text_color), - item_left + self->icon_width + icon_text_space, - (item_top + (item_height - (ascent + descent)) - / 2 + ascent)); - - item_top += item_height + item_vspace; - } -} - -static void -list_paint (void *vself, const grub_video_rect_t *region) -{ - list_impl_t self = vself; - grub_video_rect_t vpsave; - - if (! self->visible) - return; - if (!grub_video_have_common_points (region, &self->bounds)) - return; - - check_boxes (self); - - if (! self->menu_box || ! self->selected_item_box) - return; - - grub_gui_set_viewport (&self->bounds, &vpsave); - { - grub_gfxmenu_box_t box = self->menu_box; - int box_left_pad = box->get_left_pad (box); - int box_top_pad = box->get_top_pad (box); - int box_right_pad = box->get_right_pad (box); - int box_bottom_pad = box->get_bottom_pad (box); - grub_video_rect_t vpsave2, content_rect; - int num_shown_items = get_num_shown_items (self); - int drawing_scrollbar = (self->draw_scrollbar - && (num_shown_items < self->view->menu->size) - && check_scrollbar (self)); - - content_rect.x = box_left_pad; - content_rect.y = box_top_pad; - content_rect.width = self->bounds.width - box_left_pad - box_right_pad; - content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad; - - box->set_content_size (box, content_rect.width, content_rect.height); - - box->draw (box, 0, 0); - - grub_gui_set_viewport (&content_rect, &vpsave2); - draw_menu (self, content_rect.width, drawing_scrollbar, num_shown_items); - grub_gui_restore_viewport (&vpsave2); - - if (drawing_scrollbar) - draw_scrollbar (self, - self->first_shown_index, num_shown_items, - 0, self->view->menu->size, - self->bounds.width - box_right_pad - + self->scrollbar_width, - box_top_pad + self->item_padding, - self->bounds.height - box_top_pad - box_bottom_pad); - } - - grub_gui_restore_viewport (&vpsave); -} - -static void -list_set_parent (void *vself, grub_gui_container_t parent) -{ - list_impl_t self = vself; - self->parent = parent; -} - -static grub_gui_container_t -list_get_parent (void *vself) -{ - list_impl_t self = vself; - return self->parent; -} - -static void -list_set_bounds (void *vself, const grub_video_rect_t *bounds) -{ - list_impl_t self = vself; - self->bounds = *bounds; -} - -static void -list_get_bounds (void *vself, grub_video_rect_t *bounds) -{ - list_impl_t self = vself; - *bounds = self->bounds; -} - -static void -list_get_minimal_size (void *vself, unsigned *width, unsigned *height) -{ - list_impl_t self = vself; - - if (check_boxes (self)) - { - int boxpad = self->item_padding; - int item_vspace = self->item_spacing; - int item_height = self->item_height; - int num_items = 3; - - grub_gfxmenu_box_t box = self->menu_box; - int box_left_pad = box->get_left_pad (box); - int box_top_pad = box->get_top_pad (box); - int box_right_pad = box->get_right_pad (box); - int box_bottom_pad = box->get_bottom_pad (box); - unsigned width_s; - - *width = grub_font_get_string_width (self->item_font, "Typical OS"); - width_s = grub_font_get_string_width (self->selected_item_font, - "Typical OS"); - if (*width < width_s) - *width = width_s; - - *width += 2 * boxpad + box_left_pad + box_right_pad; - - /* Set the menu box height to fit the items. */ - *height = (item_height * num_items - + item_vspace * (num_items - 1) - + 2 * boxpad - + box_top_pad + box_bottom_pad); - } - else - { - *width = 0; - *height = 0; - } -} - -static grub_err_t -list_set_property (void *vself, const char *name, const char *value) -{ - list_impl_t self = vself; - if (grub_strcmp (name, "item_font") == 0) - { - self->item_font = grub_font_get (value); - } - else if (grub_strcmp (name, "selected_item_font") == 0) - { - if (! value || grub_strcmp (value, "inherit") == 0) - self->selected_item_font = 0; - else - self->selected_item_font = grub_font_get (value); - } - else if (grub_strcmp (name, "item_color") == 0) - { - grub_gui_parse_color (value, &self->item_color); - } - else if (grub_strcmp (name, "selected_item_color") == 0) - { - if (! value || grub_strcmp (value, "inherit") == 0) - { - self->selected_item_color_set = 0; - } - else - { - if (grub_gui_parse_color (value, &self->selected_item_color) - == GRUB_ERR_NONE) - self->selected_item_color_set = 1; - } - } - else if (grub_strcmp (name, "icon_width") == 0) - { - self->icon_width = grub_strtol (value, 0, 10); - grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, - self->icon_width, - self->icon_height); - } - else if (grub_strcmp (name, "icon_height") == 0) - { - self->icon_height = grub_strtol (value, 0, 10); - grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, - self->icon_width, - self->icon_height); - } - else if (grub_strcmp (name, "item_height") == 0) - { - self->item_height = grub_strtol (value, 0, 10); - } - else if (grub_strcmp (name, "item_padding") == 0) - { - self->item_padding = grub_strtol (value, 0, 10); - } - else if (grub_strcmp (name, "item_icon_space") == 0) - { - self->item_icon_space = grub_strtol (value, 0, 10); - } - else if (grub_strcmp (name, "item_spacing") == 0) - { - self->item_spacing = grub_strtol (value, 0, 10); - } - else if (grub_strcmp (name, "visible") == 0) - { - self->visible = grub_strcmp (value, "false") != 0; - } - else if (grub_strcmp (name, "menu_pixmap_style") == 0) - { - self->need_to_recreate_boxes = 1; - grub_free (self->menu_box_pattern); - self->menu_box_pattern = value ? grub_strdup (value) : 0; - } - else if (grub_strcmp (name, "selected_item_pixmap_style") == 0) - { - self->need_to_recreate_boxes = 1; - grub_free (self->selected_item_box_pattern); - self->selected_item_box_pattern = value ? grub_strdup (value) : 0; - } - else if (grub_strcmp (name, "scrollbar_frame") == 0) - { - self->need_to_recreate_scrollbar = 1; - grub_free (self->scrollbar_frame_pattern); - self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0; - } - else if (grub_strcmp (name, "scrollbar_thumb") == 0) - { - self->need_to_recreate_scrollbar = 1; - grub_free (self->scrollbar_thumb_pattern); - self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0; - } - else if (grub_strcmp (name, "scrollbar_width") == 0) - { - self->scrollbar_width = grub_strtol (value, 0, 10); - } - else if (grub_strcmp (name, "scrollbar") == 0) - { - self->draw_scrollbar = grub_strcmp (value, "false") != 0; - } - else if (grub_strcmp (name, "theme_dir") == 0) - { - self->need_to_recreate_boxes = 1; - grub_free (self->theme_dir); - self->theme_dir = value ? grub_strdup (value) : 0; - } - else if (grub_strcmp (name, "id") == 0) - { - grub_free (self->id); - if (value) - self->id = grub_strdup (value); - else - self->id = 0; - } - return grub_errno; -} - -/* Set necessary information that the gfxmenu view provides. */ -static void -list_set_view_info (void *vself, - grub_gfxmenu_view_t view) -{ - list_impl_t self = vself; - grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, - view->theme_path); - self->view = view; -} - -static struct grub_gui_component_ops list_comp_ops = - { - .destroy = list_destroy, - .get_id = list_get_id, - .is_instance = list_is_instance, - .paint = list_paint, - .set_parent = list_set_parent, - .get_parent = list_get_parent, - .set_bounds = list_set_bounds, - .get_bounds = list_get_bounds, - .get_minimal_size = list_get_minimal_size, - .set_property = list_set_property - }; - -static struct grub_gui_list_ops list_ops = -{ - .set_view_info = list_set_view_info -}; - -grub_gui_component_t -grub_gui_list_new (void) -{ - list_impl_t self; - grub_font_t default_font; - grub_gui_color_t default_fg_color; - grub_gui_color_t default_bg_color; - - self = grub_zalloc (sizeof (*self)); - if (! self) - return 0; - - self->list.ops = &list_ops; - self->list.component.ops = &list_comp_ops; - - self->visible = 1; - - default_font = grub_font_get ("Unknown Regular 16"); - default_fg_color = grub_gui_color_rgb (0, 0, 0); - default_bg_color = grub_gui_color_rgb (255, 255, 255); - - self->icon_width = 32; - self->icon_height = 32; - self->item_height = 42; - self->item_padding = 14; - self->item_icon_space = 4; - self->item_spacing = 16; - self->item_font = default_font; - self->selected_item_font = 0; /* Default to using the item_font. */ - self->item_color = default_fg_color; - self->selected_item_color_set = 0; /* Default to using the item_color. */ - self->selected_item_color = default_fg_color; - - self->draw_scrollbar = 1; - self->need_to_recreate_scrollbar = 1; - self->scrollbar_frame = 0; - self->scrollbar_thumb = 0; - self->scrollbar_frame_pattern = 0; - self->scrollbar_thumb_pattern = 0; - self->scrollbar_width = 16; - - self->first_shown_index = 0; - - self->need_to_recreate_boxes = 0; - self->theme_dir = 0; - self->menu_box_pattern = 0; - self->selected_item_box_pattern = 0; - self->menu_box = grub_gfxmenu_create_box (0, 0); - self->selected_item_box = grub_gfxmenu_create_box (0, 0); - - self->icon_manager = grub_gfxmenu_icon_manager_new (); - if (! self->icon_manager) - { - self->list.component.ops->destroy (self); - return 0; - } - grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, - self->icon_width, - self->icon_height); - return (grub_gui_component_t) self; -} diff --git a/gnulib/alloca.h b/gnulib/alloca.h deleted file mode 100644 index 5d16e08b7..000000000 --- a/gnulib/alloca.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Memory allocation on the stack. - - Copyright (C) 1995, 1999, 2001-2004, 2006-2008 Free Software - Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - USA. */ - -/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H - means there is a real alloca function. */ -#ifndef _GL_ALLOCA_H -#define _GL_ALLOCA_H - -/* alloca (N) returns a pointer to N bytes of memory - allocated on the stack, which will last until the function returns. - Use of alloca should be avoided: - - inside arguments of function calls - undefined behaviour, - - in inline functions - the allocation may actually last until the - calling function returns, - - for huge N (say, N >= 65536) - you never know how large (or small) - the stack is, and when the stack cannot fulfill the memory allocation - request, the program just crashes. - */ - -#ifndef alloca -# ifdef __GNUC__ -# define alloca __builtin_alloca -# elif defined _AIX -# define alloca __alloca -# elif defined _MSC_VER -# include -# define alloca _alloca -# elif defined __DECC && defined __VMS -# define alloca __ALLOCA -# else -# include -# ifdef __cplusplus -extern "C" -# endif -void *alloca (size_t); -# endif -#endif - -#endif /* _GL_ALLOCA_H */ diff --git a/gnulib/error.c b/gnulib/error.c deleted file mode 100644 index af2287b27..000000000 --- a/gnulib/error.c +++ /dev/null @@ -1,352 +0,0 @@ -/* Error handler for noninteractive utilities - Copyright (C) 1990-1998, 2000-2007, 2009 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -/* Written by David MacKenzie . */ - -#if !_LIBC -# include -#endif - -#include "error.h" - -#include -#include -#include -#include - -#if !_LIBC && ENABLE_NLS -# include "gettext.h" -# define _(msgid) gettext (msgid) -#endif - -#ifdef _LIBC -# include -# include -# include -# include -# define mbsrtowcs __mbsrtowcs -#endif - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -#endif - -#ifndef _ -# define _(String) String -#endif - -/* If NULL, error will flush stdout, then print on stderr the program - name, a colon and a space. Otherwise, error will call this - function without parameters instead. */ -void (*error_print_progname) (void); - -/* This variable is incremented each time `error' is called. */ -unsigned int error_message_count; - -#ifdef _LIBC -/* In the GNU C library, there is a predefined variable for this. */ - -# define program_name program_invocation_name -# include -# include -# include - -/* In GNU libc we want do not want to use the common name `error' directly. - Instead make it a weak alias. */ -extern void __error (int status, int errnum, const char *message, ...) - __attribute__ ((__format__ (__printf__, 3, 4))); -extern void __error_at_line (int status, int errnum, const char *file_name, - unsigned int line_number, const char *message, - ...) - __attribute__ ((__format__ (__printf__, 5, 6)));; -# define error __error -# define error_at_line __error_at_line - -# include -# define fflush(s) INTUSE(_IO_fflush) (s) -# undef putc -# define putc(c, fp) INTUSE(_IO_putc) (c, fp) - -# include - -#else /* not _LIBC */ - -# include - -# if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P -# ifndef HAVE_DECL_STRERROR_R -"this configure-time declaration test was not run" -# endif -char *strerror_r (); -# endif - -/* The calling program should define program_name and set it to the - name of the executing program. */ -extern char *program_name; - -# if HAVE_STRERROR_R || defined strerror_r -# define __strerror_r strerror_r -# endif /* HAVE_STRERROR_R || defined strerror_r */ -#endif /* not _LIBC */ - -static void -print_errno_message (int errnum) -{ - char const *s; - -#if defined HAVE_STRERROR_R || _LIBC - char errbuf[1024]; -# if STRERROR_R_CHAR_P || _LIBC - s = __strerror_r (errnum, errbuf, sizeof errbuf); -# else - if (__strerror_r (errnum, errbuf, sizeof errbuf) == 0) - s = errbuf; - else - s = 0; -# endif -#else - s = strerror (errnum); -#endif - -#if !_LIBC - if (! s) - s = _("Unknown system error"); -#endif - -#if _LIBC - __fxprintf (NULL, ": %s", s); -#else - fprintf (stderr, ": %s", s); -#endif -} - -static void -error_tail (int status, int errnum, const char *message, va_list args) -{ -#if _LIBC - if (_IO_fwide (stderr, 0) > 0) - { -# define ALLOCA_LIMIT 2000 - size_t len = strlen (message) + 1; - wchar_t *wmessage = NULL; - mbstate_t st; - size_t res; - const char *tmp; - bool use_malloc = false; - - while (1) - { - if (__libc_use_alloca (len * sizeof (wchar_t))) - wmessage = (wchar_t *) alloca (len * sizeof (wchar_t)); - else - { - if (!use_malloc) - wmessage = NULL; - - wchar_t *p = (wchar_t *) realloc (wmessage, - len * sizeof (wchar_t)); - if (p == NULL) - { - free (wmessage); - fputws_unlocked (L"out of memory\n", stderr); - return; - } - wmessage = p; - use_malloc = true; - } - - memset (&st, '\0', sizeof (st)); - tmp = message; - - res = mbsrtowcs (wmessage, &tmp, len, &st); - if (res != len) - break; - - if (__builtin_expect (len >= SIZE_MAX / 2, 0)) - { - /* This really should not happen if everything is fine. */ - res = (size_t) -1; - break; - } - - len *= 2; - } - - if (res == (size_t) -1) - { - /* The string cannot be converted. */ - if (use_malloc) - { - free (wmessage); - use_malloc = false; - } - wmessage = (wchar_t *) L"???"; - } - - __vfwprintf (stderr, wmessage, args); - - if (use_malloc) - free (wmessage); - } - else -#endif - vfprintf (stderr, message, args); - va_end (args); - - ++error_message_count; - if (errnum) - print_errno_message (errnum); -#if _LIBC - __fxprintf (NULL, "\n"); -#else - putc ('\n', stderr); -#endif - fflush (stderr); - if (status) - exit (status); -} - - -/* Print the program name and error message MESSAGE, which is a printf-style - format string with optional args. - If ERRNUM is nonzero, print its corresponding system error message. - Exit with status STATUS if it is nonzero. */ -void -error (int status, int errnum, const char *message, ...) -{ - va_list args; - -#if defined _LIBC && defined __libc_ptf_call - /* We do not want this call to be cut short by a thread - cancellation. Therefore disable cancellation for now. */ - int state = PTHREAD_CANCEL_ENABLE; - __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), - 0); -#endif - -#if !_LIBC && defined F_GETFL - /* POSIX states that fflush (stdout) after fclose is unspecified; it - is safe in glibc, but not on all other platforms. fflush (NULL) - is always defined, but too draconian. */ - if (0 <= fcntl (1, F_GETFL)) -#endif - fflush (stdout); -#ifdef _LIBC - _IO_flockfile (stderr); -#endif - if (error_print_progname) - (*error_print_progname) (); - else - { -#if _LIBC - __fxprintf (NULL, "%s: ", program_name); -#else - fprintf (stderr, "%s: ", program_name); -#endif - } - - va_start (args, message); - error_tail (status, errnum, message, args); - -#ifdef _LIBC - _IO_funlockfile (stderr); -# ifdef __libc_ptf_call - __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0); -# endif -#endif -} - -/* Sometimes we want to have at most one error per line. This - variable controls whether this mode is selected or not. */ -int error_one_per_line; - -void -error_at_line (int status, int errnum, const char *file_name, - unsigned int line_number, const char *message, ...) -{ - va_list args; - - if (error_one_per_line) - { - static const char *old_file_name; - static unsigned int old_line_number; - - if (old_line_number == line_number - && (file_name == old_file_name - || strcmp (old_file_name, file_name) == 0)) - /* Simply return and print nothing. */ - return; - - old_file_name = file_name; - old_line_number = line_number; - } - -#if defined _LIBC && defined __libc_ptf_call - /* We do not want this call to be cut short by a thread - cancellation. Therefore disable cancellation for now. */ - int state = PTHREAD_CANCEL_ENABLE; - __libc_ptf_call (pthread_setcancelstate, (PTHREAD_CANCEL_DISABLE, &state), - 0); -#endif - -#if !_LIBC && defined F_GETFL - /* POSIX states that fflush (stdout) after fclose is unspecified; it - is safe in glibc, but not on all other platforms. fflush (NULL) - is always defined, but too draconian. */ - if (0 <= fcntl (1, F_GETFL)) -#endif - fflush (stdout); -#ifdef _LIBC - _IO_flockfile (stderr); -#endif - if (error_print_progname) - (*error_print_progname) (); - else - { -#if _LIBC - __fxprintf (NULL, "%s:", program_name); -#else - fprintf (stderr, "%s:", program_name); -#endif - } - -#if _LIBC - __fxprintf (NULL, file_name != NULL ? "%s:%d: " : " ", - file_name, line_number); -#else - fprintf (stderr, file_name != NULL ? "%s:%d: " : " ", - file_name, line_number); -#endif - - va_start (args, message); - error_tail (status, errnum, message, args); - -#ifdef _LIBC - _IO_funlockfile (stderr); -# ifdef __libc_ptf_call - __libc_ptf_call (pthread_setcancelstate, (state, NULL), 0); -# endif -#endif -} - -#ifdef _LIBC -/* Make the weak alias. */ -# undef error -# undef error_at_line -weak_alias (__error, error) -weak_alias (__error_at_line, error_at_line) -#endif diff --git a/gnulib/error.h b/gnulib/error.h deleted file mode 100644 index 6d4968114..000000000 --- a/gnulib/error.h +++ /dev/null @@ -1,65 +0,0 @@ -/* Declaration for error-reporting function - Copyright (C) 1995, 1996, 1997, 2003, 2006, 2008 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _ERROR_H -#define _ERROR_H 1 - -#ifndef __attribute__ -/* This feature is available in gcc versions 2.5 and later. */ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) -# define __attribute__(Spec) /* empty */ -# endif -/* The __-protected variants of `format' and `printf' attributes - are accepted by gcc versions 2.6.4 (effectively 2.7) and later. */ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) -# define __format__ format -# define __printf__ printf -# endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Print a message with `fprintf (stderr, FORMAT, ...)'; - if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM). - If STATUS is nonzero, terminate the program with `exit (STATUS)'. */ - -extern void error (int __status, int __errnum, const char *__format, ...) - __attribute__ ((__format__ (__printf__, 3, 4))); - -extern void error_at_line (int __status, int __errnum, const char *__fname, - unsigned int __lineno, const char *__format, ...) - __attribute__ ((__format__ (__printf__, 5, 6))); - -/* If NULL, error will flush stdout, then print on stderr the program - name, a colon and a space. Otherwise, error will call this - function without parameters instead. */ -extern void (*error_print_progname) (void); - -/* This variable is incremented each time `error' is called. */ -extern unsigned int error_message_count; - -/* Sometimes we want to have at most one error per line. This - variable controls whether this mode is selected or not. */ -extern int error_one_per_line; - -#ifdef __cplusplus -} -#endif - -#endif /* error.h */ diff --git a/gnulib/fnmatch.c b/gnulib/fnmatch.c deleted file mode 100644 index 48bc8b5d2..000000000 --- a/gnulib/fnmatch.c +++ /dev/null @@ -1,354 +0,0 @@ -/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007 - Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#ifndef _LIBC -# include -#endif - -/* Enable GNU extensions in fnmatch.h. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#if ! defined __builtin_expect && __GNUC__ < 3 -# define __builtin_expect(expr, expected) (expr) -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define WIDE_CHAR_SUPPORT \ - (HAVE_WCTYPE_H && HAVE_BTOWC && HAVE_ISWCTYPE \ - && HAVE_WMEMCHR && (HAVE_WMEMCPY || HAVE_WMEMPCPY)) - -/* For platform which support the ISO C amendement 1 functionality we - support user defined character classes. */ -#if defined _LIBC || WIDE_CHAR_SUPPORT -# include -# include -#endif - -/* We need some of the locale data (the collation sequence information) - but there is no interface to get this information in general. Therefore - we support a correct implementation only in glibc. */ -#ifdef _LIBC -# include "../locale/localeinfo.h" -# include "../locale/elem-hash.h" -# include "../locale/coll-lookup.h" -# include - -# define CONCAT(a,b) __CONCAT(a,b) -# define mbsrtowcs __mbsrtowcs -# define fnmatch __fnmatch -extern int fnmatch (const char *pattern, const char *string, int flags); -#endif - -#ifndef SIZE_MAX -# define SIZE_MAX ((size_t) -1) -#endif - -/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set. */ -#define NO_LEADING_PERIOD(flags) \ - ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD)) - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself, and have not detected a bug - in the library. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#if defined _LIBC || !defined __GNU_LIBRARY__ || !HAVE_FNMATCH_GNU - - -# if ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK)) -# define isblank(c) ((c) == ' ' || (c) == '\t') -# endif - -# define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) - -# if defined _LIBC || WIDE_CHAR_SUPPORT -/* The GNU C library provides support for user-defined character classes - and the functions from ISO C amendement 1. */ -# ifdef CHARCLASS_NAME_MAX -# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX -# else -/* This shouldn't happen but some implementation might still have this - problem. Use a reasonable default value. */ -# define CHAR_CLASS_MAX_LENGTH 256 -# endif - -# ifdef _LIBC -# define IS_CHAR_CLASS(string) __wctype (string) -# else -# define IS_CHAR_CLASS(string) wctype (string) -# endif - -# ifdef _LIBC -# define ISWCTYPE(WC, WT) __iswctype (WC, WT) -# else -# define ISWCTYPE(WC, WT) iswctype (WC, WT) -# endif - -# if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC -/* In this case we are implementing the multibyte character handling. */ -# define HANDLE_MULTIBYTE 1 -# endif - -# else -# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ - -# define IS_CHAR_CLASS(string) \ - (STREQ (string, "alpha") || STREQ (string, "upper") \ - || STREQ (string, "lower") || STREQ (string, "digit") \ - || STREQ (string, "alnum") || STREQ (string, "xdigit") \ - || STREQ (string, "space") || STREQ (string, "print") \ - || STREQ (string, "punct") || STREQ (string, "graph") \ - || STREQ (string, "cntrl") || STREQ (string, "blank")) -# endif - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -/* Global variable. */ -static int posixly_correct; - -# ifndef internal_function -/* Inside GNU libc we mark some function in a special way. In other - environments simply ignore the marking. */ -# define internal_function -# endif - -/* Note that this evaluates C many times. */ -# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c)) -# define CHAR char -# define UCHAR unsigned char -# define INT int -# define FCT internal_fnmatch -# define EXT ext_match -# define END end_pattern -# define L_(CS) CS -# ifdef _LIBC -# define BTOWC(C) __btowc (C) -# else -# define BTOWC(C) btowc (C) -# endif -# define STRLEN(S) strlen (S) -# define STRCAT(D, S) strcat (D, S) -# ifdef _LIBC -# define MEMPCPY(D, S, N) __mempcpy (D, S, N) -# else -# if HAVE_MEMPCPY -# define MEMPCPY(D, S, N) mempcpy (D, S, N) -# else -# define MEMPCPY(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N))) -# endif -# endif -# define MEMCHR(S, C, N) memchr (S, C, N) -# define STRCOLL(S1, S2) strcoll (S1, S2) -# include "fnmatch_loop.c" - - -# if HANDLE_MULTIBYTE -# define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c)) -# define CHAR wchar_t -# define UCHAR wint_t -# define INT wint_t -# define FCT internal_fnwmatch -# define EXT ext_wmatch -# define END end_wpattern -# define L_(CS) L##CS -# define BTOWC(C) (C) -# ifdef _LIBC -# define STRLEN(S) __wcslen (S) -# define STRCAT(D, S) __wcscat (D, S) -# define MEMPCPY(D, S, N) __wmempcpy (D, S, N) -# else -# define STRLEN(S) wcslen (S) -# define STRCAT(D, S) wcscat (D, S) -# if HAVE_WMEMPCPY -# define MEMPCPY(D, S, N) wmempcpy (D, S, N) -# else -# define MEMPCPY(D, S, N) (wmemcpy (D, S, N) + (N)) -# endif -# endif -# define MEMCHR(S, C, N) wmemchr (S, C, N) -# define STRCOLL(S1, S2) wcscoll (S1, S2) -# define WIDE_CHAR_VERSION 1 - -# undef IS_CHAR_CLASS -/* We have to convert the wide character string in a multibyte string. But - we know that the character class names consist of alphanumeric characters - from the portable character set, and since the wide character encoding - for a member of the portable character set is the same code point as - its single-byte encoding, we can use a simplified method to convert the - string to a multibyte character string. */ -static wctype_t -is_char_class (const wchar_t *wcs) -{ - char s[CHAR_CLASS_MAX_LENGTH + 1]; - char *cp = s; - - do - { - /* Test for a printable character from the portable character set. */ -# ifdef _LIBC - if (*wcs < 0x20 || *wcs > 0x7e - || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60) - return (wctype_t) 0; -# else - switch (*wcs) - { - case L' ': case L'!': case L'"': case L'#': case L'%': - case L'&': case L'\'': case L'(': case L')': case L'*': - case L'+': case L',': case L'-': case L'.': case L'/': - case L'0': case L'1': case L'2': case L'3': case L'4': - case L'5': case L'6': case L'7': case L'8': case L'9': - case L':': case L';': case L'<': case L'=': case L'>': - case L'?': - case L'A': case L'B': case L'C': case L'D': case L'E': - case L'F': case L'G': case L'H': case L'I': case L'J': - case L'K': case L'L': case L'M': case L'N': case L'O': - case L'P': case L'Q': case L'R': case L'S': case L'T': - case L'U': case L'V': case L'W': case L'X': case L'Y': - case L'Z': - case L'[': case L'\\': case L']': case L'^': case L'_': - case L'a': case L'b': case L'c': case L'd': case L'e': - case L'f': case L'g': case L'h': case L'i': case L'j': - case L'k': case L'l': case L'm': case L'n': case L'o': - case L'p': case L'q': case L'r': case L's': case L't': - case L'u': case L'v': case L'w': case L'x': case L'y': - case L'z': case L'{': case L'|': case L'}': case L'~': - break; - default: - return (wctype_t) 0; - } -# endif - - /* Avoid overrunning the buffer. */ - if (cp == s + CHAR_CLASS_MAX_LENGTH) - return (wctype_t) 0; - - *cp++ = (char) *wcs++; - } - while (*wcs != L'\0'); - - *cp = '\0'; - -# ifdef _LIBC - return __wctype (s); -# else - return wctype (s); -# endif -} -# define IS_CHAR_CLASS(string) is_char_class (string) - -# include "fnmatch_loop.c" -# endif - - -int -fnmatch (const char *pattern, const char *string, int flags) -{ -# if HANDLE_MULTIBYTE -# define ALLOCA_LIMIT 2000 - if (__builtin_expect (MB_CUR_MAX, 1) != 1) - { - mbstate_t ps; - size_t patsize; - size_t strsize; - size_t totsize; - wchar_t *wpattern; - wchar_t *wstring; - int res; - - /* Calculate the size needed to convert the strings to - wide characters. */ - memset (&ps, '\0', sizeof (ps)); - patsize = mbsrtowcs (NULL, &pattern, 0, &ps) + 1; - if (__builtin_expect (patsize != 0, 1)) - { - assert (mbsinit (&ps)); - strsize = mbsrtowcs (NULL, &string, 0, &ps) + 1; - if (__builtin_expect (strsize != 0, 1)) - { - assert (mbsinit (&ps)); - totsize = patsize + strsize; - if (__builtin_expect (! (patsize <= totsize - && totsize <= SIZE_MAX / sizeof (wchar_t)), - 0)) - { - errno = ENOMEM; - return -1; - } - - /* Allocate room for the wide characters. */ - if (__builtin_expect (totsize < ALLOCA_LIMIT, 1)) - wpattern = (wchar_t *) alloca (totsize * sizeof (wchar_t)); - else - { - wpattern = malloc (totsize * sizeof (wchar_t)); - if (__builtin_expect (! wpattern, 0)) - { - errno = ENOMEM; - return -1; - } - } - wstring = wpattern + patsize; - - /* Convert the strings into wide characters. */ - mbsrtowcs (wpattern, &pattern, patsize, &ps); - assert (mbsinit (&ps)); - mbsrtowcs (wstring, &string, strsize, &ps); - - res = internal_fnwmatch (wpattern, wstring, wstring + strsize - 1, - flags & FNM_PERIOD, flags); - - if (__builtin_expect (! (totsize < ALLOCA_LIMIT), 0)) - free (wpattern); - return res; - } - } - } - -# endif /* HANDLE_MULTIBYTE */ - - return internal_fnmatch (pattern, string, string + strlen (string), - flags & FNM_PERIOD, flags); -} - -# ifdef _LIBC -# undef fnmatch -versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3); -# if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3) -strong_alias (__fnmatch, __fnmatch_old) -compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0); -# endif -libc_hidden_ver (__fnmatch, fnmatch) -# endif - -#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/gnulib/fnmatch.h b/gnulib/fnmatch.h deleted file mode 100644 index b086b45aa..000000000 --- a/gnulib/fnmatch.h +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003, - 2005, 2007 Free Software Foundation, Inc. - - This file is part of the GNU C Library. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#ifndef _FNMATCH_H -#define _FNMATCH_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* We #undef these before defining them because some losing systems - (HP-UX A.08.07 for example) define these in . */ -#undef FNM_PATHNAME -#undef FNM_NOESCAPE -#undef FNM_PERIOD - -/* Bits set in the FLAGS argument to `fnmatch'. */ -#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ -#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ -#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ - -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE -# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ -# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ -# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ -# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */ -#endif - -/* Value returned by `fnmatch' if STRING does not match PATTERN. */ -#define FNM_NOMATCH 1 - -/* This value is returned if the implementation does not support - `fnmatch'. Since this is not the case here it will never be - returned but the conformance test suites still require the symbol - to be defined. */ -#ifdef _XOPEN_SOURCE -# define FNM_NOSYS (-1) -#endif - -/* Match NAME against the file name pattern PATTERN, - returning zero if it matches, FNM_NOMATCH if not. */ -extern int fnmatch (const char *__pattern, const char *__name, - int __flags); - -#ifdef __cplusplus -} -#endif - -#endif /* fnmatch.h */ diff --git a/gnulib/fnmatch_loop.c b/gnulib/fnmatch_loop.c deleted file mode 100644 index c2182ffb0..000000000 --- a/gnulib/fnmatch_loop.c +++ /dev/null @@ -1,1211 +0,0 @@ -/* Copyright (C) 1991,1992,1993,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Match STRING against the file name pattern PATTERN, returning zero if - it matches, nonzero if not. */ -static int EXT (INT opt, const CHAR *pattern, const CHAR *string, - const CHAR *string_end, bool no_leading_period, int flags) - internal_function; -static const CHAR *END (const CHAR *patternp) internal_function; - -static int -internal_function -FCT (const CHAR *pattern, const CHAR *string, const CHAR *string_end, - bool no_leading_period, int flags) -{ - register const CHAR *p = pattern, *n = string; - register UCHAR c; -#ifdef _LIBC -# if WIDE_CHAR_VERSION - const char *collseq = (const char *) - _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQWC); -# else - const UCHAR *collseq = (const UCHAR *) - _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQMB); -# endif -#endif - - while ((c = *p++) != L_('\0')) - { - bool new_no_leading_period = false; - c = FOLD (c); - - switch (c) - { - case L_('?'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; - - res = EXT (c, p, n, string_end, no_leading_period, - flags); - if (res != -1) - return res; - } - - if (n == string_end) - return FNM_NOMATCH; - else if (*n == L_('/') && (flags & FNM_FILE_NAME)) - return FNM_NOMATCH; - else if (*n == L_('.') && no_leading_period) - return FNM_NOMATCH; - break; - - case L_('\\'): - if (!(flags & FNM_NOESCAPE)) - { - c = *p++; - if (c == L_('\0')) - /* Trailing \ loses. */ - return FNM_NOMATCH; - c = FOLD (c); - } - if (n == string_end || FOLD ((UCHAR) *n) != c) - return FNM_NOMATCH; - break; - - case L_('*'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; - - res = EXT (c, p, n, string_end, no_leading_period, - flags); - if (res != -1) - return res; - } - - if (n != string_end && *n == L_('.') && no_leading_period) - return FNM_NOMATCH; - - for (c = *p++; c == L_('?') || c == L_('*'); c = *p++) - { - if (*p == L_('(') && (flags & FNM_EXTMATCH) != 0) - { - const CHAR *endp = END (p); - if (endp != p) - { - /* This is a pattern. Skip over it. */ - p = endp; - continue; - } - } - - if (c == L_('?')) - { - /* A ? needs to match one character. */ - if (n == string_end) - /* There isn't another character; no match. */ - return FNM_NOMATCH; - else if (*n == L_('/') - && __builtin_expect (flags & FNM_FILE_NAME, 0)) - /* A slash does not match a wildcard under - FNM_FILE_NAME. */ - return FNM_NOMATCH; - else - /* One character of the string is consumed in matching - this ? wildcard, so *??? won't match if there are - less than three characters. */ - ++n; - } - } - - if (c == L_('\0')) - /* The wildcard(s) is/are the last element of the pattern. - If the name is a file name and contains another slash - this means it cannot match, unless the FNM_LEADING_DIR - flag is set. */ - { - int result = (flags & FNM_FILE_NAME) == 0 ? 0 : FNM_NOMATCH; - - if (flags & FNM_FILE_NAME) - { - if (flags & FNM_LEADING_DIR) - result = 0; - else - { - if (MEMCHR (n, L_('/'), string_end - n) == NULL) - result = 0; - } - } - - return result; - } - else - { - const CHAR *endp; - - endp = MEMCHR (n, (flags & FNM_FILE_NAME) ? L_('/') : L_('\0'), - string_end - n); - if (endp == NULL) - endp = string_end; - - if (c == L_('[') - || (__builtin_expect (flags & FNM_EXTMATCH, 0) != 0 - && (c == L_('@') || c == L_('+') || c == L_('!')) - && *p == L_('('))) - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - bool no_leading_period2 = no_leading_period; - - for (--p; n < endp; ++n, no_leading_period2 = false) - if (FCT (p, n, string_end, no_leading_period2, flags2) - == 0) - return 0; - } - else if (c == L_('/') && (flags & FNM_FILE_NAME)) - { - while (n < string_end && *n != L_('/')) - ++n; - if (n < string_end && *n == L_('/') - && (FCT (p, n + 1, string_end, flags & FNM_PERIOD, flags) - == 0)) - return 0; - } - else - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - int no_leading_period2 = no_leading_period; - - if (c == L_('\\') && !(flags & FNM_NOESCAPE)) - c = *p; - c = FOLD (c); - for (--p; n < endp; ++n, no_leading_period2 = false) - if (FOLD ((UCHAR) *n) == c - && (FCT (p, n, string_end, no_leading_period2, flags2) - == 0)) - return 0; - } - } - - /* If we come here no match is possible with the wildcard. */ - return FNM_NOMATCH; - - case L_('['): - { - /* Nonzero if the sense of the character class is inverted. */ - register bool not; - CHAR cold; - UCHAR fn; - - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - - if (n == string_end) - return FNM_NOMATCH; - - if (*n == L_('.') && no_leading_period) - return FNM_NOMATCH; - - if (*n == L_('/') && (flags & FNM_FILE_NAME)) - /* `/' cannot be matched. */ - return FNM_NOMATCH; - - not = (*p == L_('!') || (posixly_correct < 0 && *p == L_('^'))); - if (not) - ++p; - - fn = FOLD ((UCHAR) *n); - - c = *p++; - for (;;) - { - if (!(flags & FNM_NOESCAPE) && c == L_('\\')) - { - if (*p == L_('\0')) - return FNM_NOMATCH; - c = FOLD ((UCHAR) *p); - ++p; - - goto normal_bracket; - } - else if (c == L_('[') && *p == L_(':')) - { - /* Leave room for the null. */ - CHAR str[CHAR_CLASS_MAX_LENGTH + 1]; - size_t c1 = 0; -#if defined _LIBC || WIDE_CHAR_SUPPORT - wctype_t wt; -#endif - const CHAR *startp = p; - - for (;;) - { - if (c1 == CHAR_CLASS_MAX_LENGTH) - /* The name is too long and therefore the pattern - is ill-formed. */ - return FNM_NOMATCH; - - c = *++p; - if (c == L_(':') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c < L_('a') || c >= L_('z')) - { - /* This cannot possibly be a character class name. - Match it as a normal range. */ - p = startp; - c = L_('['); - goto normal_bracket; - } - str[c1++] = c; - } - str[c1] = L_('\0'); - -#if defined _LIBC || WIDE_CHAR_SUPPORT - wt = IS_CHAR_CLASS (str); - if (wt == 0) - /* Invalid character class name. */ - return FNM_NOMATCH; - -# if defined _LIBC && ! WIDE_CHAR_VERSION - /* The following code is glibc specific but does - there a good job in speeding up the code since - we can avoid the btowc() call. */ - if (_ISCTYPE ((UCHAR) *n, wt)) - goto matched; -# else - if (ISWCTYPE (BTOWC ((UCHAR) *n), wt)) - goto matched; -# endif -#else - if ((STREQ (str, L_("alnum")) && isalnum ((UCHAR) *n)) - || (STREQ (str, L_("alpha")) && isalpha ((UCHAR) *n)) - || (STREQ (str, L_("blank")) && isblank ((UCHAR) *n)) - || (STREQ (str, L_("cntrl")) && iscntrl ((UCHAR) *n)) - || (STREQ (str, L_("digit")) && isdigit ((UCHAR) *n)) - || (STREQ (str, L_("graph")) && isgraph ((UCHAR) *n)) - || (STREQ (str, L_("lower")) && islower ((UCHAR) *n)) - || (STREQ (str, L_("print")) && isprint ((UCHAR) *n)) - || (STREQ (str, L_("punct")) && ispunct ((UCHAR) *n)) - || (STREQ (str, L_("space")) && isspace ((UCHAR) *n)) - || (STREQ (str, L_("upper")) && isupper ((UCHAR) *n)) - || (STREQ (str, L_("xdigit")) && isxdigit ((UCHAR) *n))) - goto matched; -#endif - c = *p++; - } -#ifdef _LIBC - else if (c == L_('[') && *p == L_('=')) - { - UCHAR str[1]; - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); - const CHAR *startp = p; - - c = *++p; - if (c == L_('\0')) - { - p = startp; - c = L_('['); - goto normal_bracket; - } - str[0] = c; - - c = *++p; - if (c != L_('=') || p[1] != L_(']')) - { - p = startp; - c = L_('['); - goto normal_bracket; - } - p += 2; - - if (nrules == 0) - { - if ((UCHAR) *n == str[0]) - goto matched; - } - else - { - const int32_t *table; -# if WIDE_CHAR_VERSION - const int32_t *weights; - const int32_t *extra; -# else - const unsigned char *weights; - const unsigned char *extra; -# endif - const int32_t *indirect; - int32_t idx; - const UCHAR *cp = (const UCHAR *) str; - - /* This #include defines a local function! */ -# if WIDE_CHAR_VERSION -# include -# else -# include -# endif - -# if WIDE_CHAR_VERSION - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC); - weights = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC); - extra = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC); -# else - table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); - weights = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); - indirect = (const int32_t *) - _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); -# endif - - idx = findidx (&cp); - if (idx != 0) - { - /* We found a table entry. Now see whether the - character we are currently at has the same - equivalance class value. */ - int len = weights[idx]; - int32_t idx2; - const UCHAR *np = (const UCHAR *) n; - - idx2 = findidx (&np); - if (idx2 != 0 && len == weights[idx2]) - { - int cnt = 0; - - while (cnt < len - && (weights[idx + 1 + cnt] - == weights[idx2 + 1 + cnt])) - ++cnt; - - if (cnt == len) - goto matched; - } - } - } - - c = *p++; - } -#endif - else if (c == L_('\0')) - /* [ (unterminated) loses. */ - return FNM_NOMATCH; - else - { - bool is_range = false; - -#ifdef _LIBC - bool is_seqval = false; - - if (c == L_('[') && *p == L_('.')) - { - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); - const CHAR *startp = p; - size_t c1 = 0; - - while (1) - { - c = *++p; - if (c == L_('.') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c == '\0') - return FNM_NOMATCH; - ++c1; - } - - /* We have to handling the symbols differently in - ranges since then the collation sequence is - important. */ - is_range = *p == L_('-') && p[1] != L_('\0'); - - if (nrules == 0) - { - /* There are no names defined in the collation - data. Therefore we only accept the trivial - names consisting of the character itself. */ - if (c1 != 1) - return FNM_NOMATCH; - - if (!is_range && *n == startp[1]) - goto matched; - - cold = startp[1]; - c = *p++; - } - else - { - int32_t table_size; - const int32_t *symb_table; -# ifdef WIDE_CHAR_VERSION - char str[c1]; - size_t strcnt; -# else -# define str (startp + 1) -# endif - const unsigned char *extra; - int32_t idx; - int32_t elem; - int32_t second; - int32_t hash; - -# ifdef WIDE_CHAR_VERSION - /* We have to convert the name to a single-byte - string. This is possible since the names - consist of ASCII characters and the internal - representation is UCS4. */ - for (strcnt = 0; strcnt < c1; ++strcnt) - str[strcnt] = startp[1 + strcnt]; -# endif - - table_size = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_SYMB_HASH_SIZEMB); - symb_table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_TABLEMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_EXTRAMB); - - /* Locate the character in the hashing table. */ - hash = elem_hash (str, c1); - - idx = 0; - elem = hash % table_size; - if (symb_table[2 * elem] != 0) - { - second = hash % (table_size - 2) + 1; - - do - { - /* First compare the hashing value. */ - if (symb_table[2 * elem] == hash - && (c1 - == extra[symb_table[2 * elem + 1]]) - && memcmp (str, - &extra[symb_table[2 * elem - + 1] - + 1], c1) == 0) - { - /* Yep, this is the entry. */ - idx = symb_table[2 * elem + 1]; - idx += 1 + extra[idx]; - break; - } - - /* Next entry. */ - elem += second; - } - while (symb_table[2 * elem] != 0); - } - - if (symb_table[2 * elem] != 0) - { - /* Compare the byte sequence but only if - this is not part of a range. */ -# ifdef WIDE_CHAR_VERSION - int32_t *wextra; - - idx += 1 + extra[idx]; - /* Adjust for the alignment. */ - idx = (idx + 3) & ~3; - - wextra = (int32_t *) &extra[idx + 4]; -# endif - - if (! is_range) - { -# ifdef WIDE_CHAR_VERSION - for (c1 = 0; - (int32_t) c1 < wextra[idx]; - ++c1) - if (n[c1] != wextra[1 + c1]) - break; - - if ((int32_t) c1 == wextra[idx]) - goto matched; -# else - for (c1 = 0; c1 < extra[idx]; ++c1) - if (n[c1] != extra[1 + c1]) - break; - - if (c1 == extra[idx]) - goto matched; -# endif - } - - /* Get the collation sequence value. */ - is_seqval = true; -# ifdef WIDE_CHAR_VERSION - cold = wextra[1 + wextra[idx]]; -# else - /* Adjust for the alignment. */ - idx += 1 + extra[idx]; - idx = (idx + 3) & ~4; - cold = *((int32_t *) &extra[idx]); -# endif - - c = *p++; - } - else if (c1 == 1) - { - /* No valid character. Match it as a - single byte. */ - if (!is_range && *n == str[0]) - goto matched; - - cold = str[0]; - c = *p++; - } - else - return FNM_NOMATCH; - } - } - else -# undef str -#endif - { - c = FOLD (c); - normal_bracket: - - /* We have to handling the symbols differently in - ranges since then the collation sequence is - important. */ - is_range = (*p == L_('-') && p[1] != L_('\0') - && p[1] != L_(']')); - - if (!is_range && c == fn) - goto matched; - -#if _LIBC - /* This is needed if we goto normal_bracket; from - outside of is_seqval's scope. */ - is_seqval = false; -#endif - - cold = c; - c = *p++; - } - - if (c == L_('-') && *p != L_(']')) - { -#if _LIBC - /* We have to find the collation sequence - value for C. Collation sequence is nothing - we can regularly access. The sequence - value is defined by the order in which the - definitions of the collation values for the - various characters appear in the source - file. A strange concept, nowhere - documented. */ - uint32_t fcollseq; - uint32_t lcollseq; - UCHAR cend = *p++; - -# ifdef WIDE_CHAR_VERSION - /* Search in the `names' array for the characters. */ - fcollseq = __collseq_table_lookup (collseq, fn); - if (fcollseq == ~((uint32_t) 0)) - /* XXX We don't know anything about the character - we are supposed to match. This means we are - failing. */ - goto range_not_matched; - - if (is_seqval) - lcollseq = cold; - else - lcollseq = __collseq_table_lookup (collseq, cold); -# else - fcollseq = collseq[fn]; - lcollseq = is_seqval ? cold : collseq[(UCHAR) cold]; -# endif - - is_seqval = false; - if (cend == L_('[') && *p == L_('.')) - { - uint32_t nrules = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_NRULES); - const CHAR *startp = p; - size_t c1 = 0; - - while (1) - { - c = *++p; - if (c == L_('.') && p[1] == L_(']')) - { - p += 2; - break; - } - if (c == '\0') - return FNM_NOMATCH; - ++c1; - } - - if (nrules == 0) - { - /* There are no names defined in the - collation data. Therefore we only - accept the trivial names consisting - of the character itself. */ - if (c1 != 1) - return FNM_NOMATCH; - - cend = startp[1]; - } - else - { - int32_t table_size; - const int32_t *symb_table; -# ifdef WIDE_CHAR_VERSION - char str[c1]; - size_t strcnt; -# else -# define str (startp + 1) -# endif - const unsigned char *extra; - int32_t idx; - int32_t elem; - int32_t second; - int32_t hash; - -# ifdef WIDE_CHAR_VERSION - /* We have to convert the name to a single-byte - string. This is possible since the names - consist of ASCII characters and the internal - representation is UCS4. */ - for (strcnt = 0; strcnt < c1; ++strcnt) - str[strcnt] = startp[1 + strcnt]; -# endif - - table_size = - _NL_CURRENT_WORD (LC_COLLATE, - _NL_COLLATE_SYMB_HASH_SIZEMB); - symb_table = (const int32_t *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_TABLEMB); - extra = (const unsigned char *) - _NL_CURRENT (LC_COLLATE, - _NL_COLLATE_SYMB_EXTRAMB); - - /* Locate the character in the hashing - table. */ - hash = elem_hash (str, c1); - - idx = 0; - elem = hash % table_size; - if (symb_table[2 * elem] != 0) - { - second = hash % (table_size - 2) + 1; - - do - { - /* First compare the hashing value. */ - if (symb_table[2 * elem] == hash - && (c1 - == extra[symb_table[2 * elem + 1]]) - && memcmp (str, - &extra[symb_table[2 * elem + 1] - + 1], c1) == 0) - { - /* Yep, this is the entry. */ - idx = symb_table[2 * elem + 1]; - idx += 1 + extra[idx]; - break; - } - - /* Next entry. */ - elem += second; - } - while (symb_table[2 * elem] != 0); - } - - if (symb_table[2 * elem] != 0) - { - /* Compare the byte sequence but only if - this is not part of a range. */ -# ifdef WIDE_CHAR_VERSION - int32_t *wextra; - - idx += 1 + extra[idx]; - /* Adjust for the alignment. */ - idx = (idx + 3) & ~4; - - wextra = (int32_t *) &extra[idx + 4]; -# endif - /* Get the collation sequence value. */ - is_seqval = true; -# ifdef WIDE_CHAR_VERSION - cend = wextra[1 + wextra[idx]]; -# else - /* Adjust for the alignment. */ - idx += 1 + extra[idx]; - idx = (idx + 3) & ~4; - cend = *((int32_t *) &extra[idx]); -# endif - } - else if (symb_table[2 * elem] != 0 && c1 == 1) - { - cend = str[0]; - c = *p++; - } - else - return FNM_NOMATCH; - } -# undef str - } - else - { - if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) - cend = *p++; - if (cend == L_('\0')) - return FNM_NOMATCH; - cend = FOLD (cend); - } - - /* XXX It is not entirely clear to me how to handle - characters which are not mentioned in the - collation specification. */ - if ( -# ifdef WIDE_CHAR_VERSION - lcollseq == 0xffffffff || -# endif - lcollseq <= fcollseq) - { - /* We have to look at the upper bound. */ - uint32_t hcollseq; - - if (is_seqval) - hcollseq = cend; - else - { -# ifdef WIDE_CHAR_VERSION - hcollseq = - __collseq_table_lookup (collseq, cend); - if (hcollseq == ~((uint32_t) 0)) - { - /* Hum, no information about the upper - bound. The matching succeeds if the - lower bound is matched exactly. */ - if (lcollseq != fcollseq) - goto range_not_matched; - - goto matched; - } -# else - hcollseq = collseq[cend]; -# endif - } - - if (lcollseq <= hcollseq && fcollseq <= hcollseq) - goto matched; - } -# ifdef WIDE_CHAR_VERSION - range_not_matched: -# endif -#else - /* We use a boring value comparison of the character - values. This is better than comparing using - `strcoll' since the latter would have surprising - and sometimes fatal consequences. */ - UCHAR cend = *p++; - - if (!(flags & FNM_NOESCAPE) && cend == L_('\\')) - cend = *p++; - if (cend == L_('\0')) - return FNM_NOMATCH; - - /* It is a range. */ - if (cold <= fn && fn <= cend) - goto matched; -#endif - - c = *p++; - } - } - - if (c == L_(']')) - break; - } - - if (!not) - return FNM_NOMATCH; - break; - - matched: - /* Skip the rest of the [...] that already matched. */ - do - { - ignore_next: - c = *p++; - - if (c == L_('\0')) - /* [... (unterminated) loses. */ - return FNM_NOMATCH; - - if (!(flags & FNM_NOESCAPE) && c == L_('\\')) - { - if (*p == L_('\0')) - return FNM_NOMATCH; - /* XXX 1003.2d11 is unclear if this is right. */ - ++p; - } - else if (c == L_('[') && *p == L_(':')) - { - int c1 = 0; - const CHAR *startp = p; - - while (1) - { - c = *++p; - if (++c1 == CHAR_CLASS_MAX_LENGTH) - return FNM_NOMATCH; - - if (*p == L_(':') && p[1] == L_(']')) - break; - - if (c < L_('a') || c >= L_('z')) - { - p = startp; - goto ignore_next; - } - } - p += 2; - c = *p++; - } - else if (c == L_('[') && *p == L_('=')) - { - c = *++p; - if (c == L_('\0')) - return FNM_NOMATCH; - c = *++p; - if (c != L_('=') || p[1] != L_(']')) - return FNM_NOMATCH; - p += 2; - c = *p++; - } - else if (c == L_('[') && *p == L_('.')) - { - ++p; - while (1) - { - c = *++p; - if (c == '\0') - return FNM_NOMATCH; - - if (*p == L_('.') && p[1] == L_(']')) - break; - } - p += 2; - c = *p++; - } - } - while (c != L_(']')); - if (not) - return FNM_NOMATCH; - } - break; - - case L_('+'): - case L_('@'): - case L_('!'): - if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(') - { - int res; - - res = EXT (c, p, n, string_end, no_leading_period, flags); - if (res != -1) - return res; - } - goto normal_match; - - case L_('/'): - if (NO_LEADING_PERIOD (flags)) - { - if (n == string_end || c != (UCHAR) *n) - return FNM_NOMATCH; - - new_no_leading_period = true; - break; - } - /* FALLTHROUGH */ - default: - normal_match: - if (n == string_end || c != FOLD ((UCHAR) *n)) - return FNM_NOMATCH; - } - - no_leading_period = new_no_leading_period; - ++n; - } - - if (n == string_end) - return 0; - - if ((flags & FNM_LEADING_DIR) && n != string_end && *n == L_('/')) - /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ - return 0; - - return FNM_NOMATCH; -} - - -static const CHAR * -internal_function -END (const CHAR *pattern) -{ - const CHAR *p = pattern; - - while (1) - if (*++p == L_('\0')) - /* This is an invalid pattern. */ - return pattern; - else if (*p == L_('[')) - { - /* Handle brackets special. */ - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - - /* Skip the not sign. We have to recognize it because of a possibly - following ']'. */ - if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) - ++p; - /* A leading ']' is recognized as such. */ - if (*p == L_(']')) - ++p; - /* Skip over all characters of the list. */ - while (*p != L_(']')) - if (*p++ == L_('\0')) - /* This is no valid pattern. */ - return pattern; - } - else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == L_('@') - || *p == L_('!')) && p[1] == L_('(')) - p = END (p + 1); - else if (*p == L_(')')) - break; - - return p + 1; -} - - -static int -internal_function -EXT (INT opt, const CHAR *pattern, const CHAR *string, const CHAR *string_end, - bool no_leading_period, int flags) -{ - const CHAR *startp; - size_t level; - struct patternlist - { - struct patternlist *next; - CHAR str[1]; - } *list = NULL; - struct patternlist **lastp = &list; - size_t pattern_len = STRLEN (pattern); - const CHAR *p; - const CHAR *rs; - enum { ALLOCA_LIMIT = 8000 }; - - /* Parse the pattern. Store the individual parts in the list. */ - level = 0; - for (startp = p = pattern + 1; ; ++p) - if (*p == L_('\0')) - /* This is an invalid pattern. */ - return -1; - else if (*p == L_('[')) - { - /* Handle brackets special. */ - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - - /* Skip the not sign. We have to recognize it because of a possibly - following ']'. */ - if (*++p == L_('!') || (posixly_correct < 0 && *p == L_('^'))) - ++p; - /* A leading ']' is recognized as such. */ - if (*p == L_(']')) - ++p; - /* Skip over all characters of the list. */ - while (*p != L_(']')) - if (*p++ == L_('\0')) - /* This is no valid pattern. */ - return -1; - } - else if ((*p == L_('?') || *p == L_('*') || *p == L_('+') || *p == L_('@') - || *p == L_('!')) && p[1] == L_('(')) - /* Remember the nesting level. */ - ++level; - else if (*p == L_(')')) - { - if (level-- == 0) - { - /* This means we found the end of the pattern. */ -#define NEW_PATTERN \ - struct patternlist *newp; \ - size_t plen; \ - size_t plensize; \ - size_t newpsize; \ - \ - assert (p > startp); \ - plen = (opt == L_('?') || opt == L_('@') \ - ? pattern_len \ - : (unsigned) (p - startp) + 1); \ - plensize = plen * sizeof (CHAR); \ - newpsize = offsetof (struct patternlist, str) + plensize; \ - if ((size_t) -1 / sizeof (CHAR) < plen \ - || newpsize < offsetof (struct patternlist, str) \ - || ALLOCA_LIMIT <= newpsize) \ - return -1; \ - newp = (struct patternlist *) alloca (newpsize); \ - *((CHAR *) MEMPCPY (newp->str, startp, p - startp)) = L_('\0'); \ - newp->next = NULL; \ - *lastp = newp; \ - lastp = &newp->next - NEW_PATTERN; - break; - } - } - else if (*p == L_('|')) - { - if (level == 0) - { - NEW_PATTERN; - startp = p + 1; - } - } - assert (list != NULL); - assert (p[-1] == L_(')')); -#undef NEW_PATTERN - - switch (opt) - { - case L_('*'): - if (FCT (p, string, string_end, no_leading_period, flags) == 0) - return 0; - /* FALLTHROUGH */ - - case L_('+'): - do - { - for (rs = string; rs <= string_end; ++rs) - /* First match the prefix with the current pattern with the - current pattern. */ - if (FCT (list->str, string, rs, no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0 - /* This was successful. Now match the rest with the rest - of the pattern. */ - && (FCT (p, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME - ? flags : flags & ~FNM_PERIOD) == 0 - /* This didn't work. Try the whole pattern. */ - || (rs != string - && FCT (pattern - 1, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME - ? flags : flags & ~FNM_PERIOD) == 0))) - /* It worked. Signal success. */ - return 0; - } - while ((list = list->next) != NULL); - - /* None of the patterns lead to a match. */ - return FNM_NOMATCH; - - case L_('?'): - if (FCT (p, string, string_end, no_leading_period, flags) == 0) - return 0; - /* FALLTHROUGH */ - - case L_('@'): - do - /* I cannot believe it but `strcat' is actually acceptable - here. Match the entire string with the prefix from the - pattern list and the rest of the pattern following the - pattern list. */ - if (FCT (STRCAT (list->str, p), string, string_end, - no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) - /* It worked. Signal success. */ - return 0; - while ((list = list->next) != NULL); - - /* None of the patterns lead to a match. */ - return FNM_NOMATCH; - - case L_('!'): - for (rs = string; rs <= string_end; ++rs) - { - struct patternlist *runp; - - for (runp = list; runp != NULL; runp = runp->next) - if (FCT (runp->str, string, rs, no_leading_period, - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) == 0) - break; - - /* If none of the patterns matched see whether the rest does. */ - if (runp == NULL - && (FCT (p, rs, string_end, - rs == string - ? no_leading_period - : rs[-1] == '/' && NO_LEADING_PERIOD (flags), - flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD) - == 0)) - /* This is successful. */ - return 0; - } - - /* None of the patterns together with the rest of the pattern - lead to a match. */ - return FNM_NOMATCH; - - default: - assert (! "Invalid extended matching operator"); - break; - } - - return -1; -} - - -#undef FOLD -#undef CHAR -#undef UCHAR -#undef INT -#undef FCT -#undef EXT -#undef END -#undef MEMPCPY -#undef MEMCHR -#undef STRCOLL -#undef STRLEN -#undef STRCAT -#undef L_ -#undef BTOWC diff --git a/gnulib/getdelim.c b/gnulib/getdelim.c deleted file mode 100644 index 85818b565..000000000 --- a/gnulib/getdelim.c +++ /dev/null @@ -1,134 +0,0 @@ -/* getdelim.c --- Implementation of replacement getdelim function. - Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005, 2006, 2007, - 2008, 2009 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ - -/* Ported from glibc by Simon Josefsson. */ - -#include - -#include - -#include -#include -#include -#include -#include - -#ifndef SSIZE_MAX -# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) -#endif - -#if USE_UNLOCKED_IO -# include "unlocked-io.h" -# define getc_maybe_unlocked(fp) getc(fp) -#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED -# undef flockfile -# undef funlockfile -# define flockfile(x) ((void) 0) -# define funlockfile(x) ((void) 0) -# define getc_maybe_unlocked(fp) getc(fp) -#else -# define getc_maybe_unlocked(fp) getc_unlocked(fp) -#endif - -/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and - NUL-terminate it). *LINEPTR is a pointer returned from malloc (or - NULL), pointing to *N characters of space. It is realloc'ed as - necessary. Returns the number of characters read (not including - the null terminator), or -1 on error or EOF. */ - -ssize_t -getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp) -{ - ssize_t result; - size_t cur_len = 0; - - if (lineptr == NULL || n == NULL || fp == NULL) - { - errno = EINVAL; - return -1; - } - - flockfile (fp); - - if (*lineptr == NULL || *n == 0) - { - char *new_lineptr; - *n = 120; - new_lineptr = (char *) realloc (*lineptr, *n); - if (new_lineptr == NULL) - { - result = -1; - goto unlock_return; - } - *lineptr = new_lineptr; - } - - for (;;) - { - int i; - - i = getc_maybe_unlocked (fp); - if (i == EOF) - { - result = -1; - break; - } - - /* Make enough space for len+1 (for final NUL) bytes. */ - if (cur_len + 1 >= *n) - { - size_t needed_max = - SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; - size_t needed = 2 * *n + 1; /* Be generous. */ - char *new_lineptr; - - if (needed_max < needed) - needed = needed_max; - if (cur_len + 1 >= needed) - { - result = -1; - errno = EOVERFLOW; - goto unlock_return; - } - - new_lineptr = (char *) realloc (*lineptr, needed); - if (new_lineptr == NULL) - { - result = -1; - goto unlock_return; - } - - *lineptr = new_lineptr; - *n = needed; - } - - (*lineptr)[cur_len] = i; - cur_len++; - - if (i == delimiter) - break; - } - (*lineptr)[cur_len] = '\0'; - result = cur_len ? cur_len : result; - - unlock_return: - funlockfile (fp); /* doesn't set errno */ - - return result; -} diff --git a/gnulib/getline.c b/gnulib/getline.c deleted file mode 100644 index eb8f055a8..000000000 --- a/gnulib/getline.c +++ /dev/null @@ -1,30 +0,0 @@ -/* getline.c --- Implementation of replacement getline function. - Copyright (C) 2005, 2006, 2007 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2, or (at - your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ - -/* Written by Simon Josefsson. */ - -#include - -#include -#include - -ssize_t -getline (char **lineptr, size_t *n, FILE *stream) -{ - return getdelim (lineptr, n, '\n', stream); -} diff --git a/gnulib/getopt.c b/gnulib/getopt.c deleted file mode 100644 index f1e6d1f7c..000000000 --- a/gnulib/getopt.c +++ /dev/null @@ -1,1186 +0,0 @@ -/* Getopt for GNU. - NOTE: getopt is now part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to drepper@gnu.org - before changing it! - Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002,2003,2004,2006,2008 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _LIBC -# include -#endif - -#include "getopt.h" - -#include -#include -#include -#include - -#ifdef _LIBC -# include -#else -# include "gettext.h" -# define _(msgid) gettext (msgid) -#endif - -#if defined _LIBC && defined USE_IN_LIBIO -# include -#endif - -#ifndef attribute_hidden -# define attribute_hidden -#endif - -/* Unlike standard Unix `getopt', functions like `getopt_long' - let the user intersperse the options with the other arguments. - - As `getopt_long' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Using `getopt' or setting the environment variable POSIXLY_CORRECT - disables permutation. - Then the application's behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt_int.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Keep a global copy of all internal members of getopt_data. */ - -static struct _getopt_data getopt_data; - - -#if defined HAVE_DECL_GETENV && !HAVE_DECL_GETENV -extern char *getenv (); -#endif - -#ifdef _LIBC -/* Stored original parameters. - XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ -extern int __libc_argc; -extern char **__libc_argv; - -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -# ifdef USE_NONOPTION_FLAGS -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; -# endif - -# ifdef USE_NONOPTION_FLAGS -# define SWAP_FLAGS(ch1, ch2) \ - if (d->__nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -# else -# define SWAP_FLAGS(ch1, ch2) -# endif -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -static void -exchange (char **argv, struct _getopt_data *d) -{ - int bottom = d->__first_nonopt; - int middle = d->__last_nonopt; - int top = d->optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; - else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - d->__nonoption_flags_max_len), - '\0', top + 1 - d->__nonoption_flags_max_len); - d->__nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - d->__first_nonopt += (d->optind - d->__last_nonopt); - d->__last_nonopt = d->optind; -} - -/* Initialize the internal data when the first call is made. */ - -static const char * -_getopt_initialize (int argc, char **argv, const char *optstring, - int posixly_correct, struct _getopt_data *d) -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - d->__first_nonopt = d->__last_nonopt = d->optind; - - d->__nextchar = NULL; - - d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - d->__ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - d->__ordering = REQUIRE_ORDER; - ++optstring; - } - else if (d->__posixly_correct) - d->__ordering = REQUIRE_ORDER; - else - d->__ordering = PERMUTE; - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - if (!d->__posixly_correct - && argc == __libc_argc && argv == __libc_argv) - { - if (d->__nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - d->__nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = d->__nonoption_flags_max_len = strlen (orig_str); - if (d->__nonoption_flags_max_len < argc) - d->__nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (d->__nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - d->__nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', d->__nonoption_flags_max_len - len); - } - } - d->__nonoption_flags_len = d->__nonoption_flags_max_len; - } - else - d->__nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. - - If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT - environment variable were set. */ - -int -_getopt_internal_r (int argc, char **argv, const char *optstring, - const struct option *longopts, int *longind, - int long_only, int posixly_correct, struct _getopt_data *d) -{ - int print_errors = d->opterr; - if (optstring[0] == ':') - print_errors = 0; - - if (argc < 1) - return -1; - - d->optarg = NULL; - - if (d->optind == 0 || !d->__initialized) - { - if (d->optind == 0) - d->optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring, - posixly_correct, d); - d->__initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS -# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ - || (d->optind < d->__nonoption_flags_len \ - && __getopt_nonoption_flags[d->optind] == '1')) -#else -# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') -#endif - - if (d->__nextchar == NULL || *d->__nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (d->__last_nonopt > d->optind) - d->__last_nonopt = d->optind; - if (d->__first_nonopt > d->optind) - d->__first_nonopt = d->optind; - - if (d->__ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (d->__first_nonopt != d->__last_nonopt - && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); - else if (d->__last_nonopt != d->optind) - d->__first_nonopt = d->optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (d->optind < argc && NONOPTION_P) - d->optind++; - d->__last_nonopt = d->optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (d->optind != argc && !strcmp (argv[d->optind], "--")) - { - d->optind++; - - if (d->__first_nonopt != d->__last_nonopt - && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); - else if (d->__first_nonopt == d->__last_nonopt) - d->__first_nonopt = d->optind; - d->__last_nonopt = argc; - - d->optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (d->optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (d->__first_nonopt != d->__last_nonopt) - d->optind = d->__first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (d->__ordering == REQUIRE_ORDER) - return -1; - d->optarg = argv[d->optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - d->__nextchar = (argv[d->optind] + 1 - + (longopts != NULL && argv[d->optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[d->optind][1] == '-' - || (long_only && (argv[d->optind][2] - || !strchr (optstring, argv[d->optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) - { - if ((unsigned int) (nameend - d->__nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[d->optind]) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[d->optind]); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - d->optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - d->optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; -#endif - - if (argv[d->optind - 1][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#else - fprintf (stderr, _("\ -%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); -#else - fprintf (stderr, _("\ -%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } - - d->__nextchar += strlen (d->__nextchar); - - d->optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[d->optind][1] == '-' - || strchr (optstring, *d->__nextchar) == NULL) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; -#endif - - if (argv[d->optind][1] == '-') - { - /* --option */ -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"), - argv[0], d->__nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], d->__nextchar); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); -#else - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } - d->__nextchar = (char *) ""; - d->optind++; - d->optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *d->__nextchar++; - char *temp = strchr (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*d->__nextchar == '\0') - ++d->optind; - - if (temp == NULL || c == ':') - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - int n; -#endif - - if (d->__posixly_correct) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: illegal option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); -#endif - } - else - { -#if defined _LIBC && defined USE_IN_LIBIO - n = __asprintf (&buf, _("%s: invalid option -- %c\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); -#endif - } - -#if defined _LIBC && defined USE_IN_LIBIO - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } - d->optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - d->optind++; - } - else if (d->optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, - _("%s: option requires an argument -- %c\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - d->optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `d->optind' once; - increment it again when taking next ARGV-elt as argument. */ - d->optarg = argv[d->optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; - nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) - { - if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[d->optind]) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[d->optind]); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - - d->__nextchar += strlen (d->__nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("\ -%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[d->optind - 1]); -#endif - } - d->__nextchar += strlen (d->__nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - d->__nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - d->optind++; - } - else - d->optarg = NULL; - d->__nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - d->optind++; - } - else if (d->optind == argc) - { - if (print_errors) - { - /* 1003.2 specifies the format of this message. */ -#if defined _LIBC && defined USE_IN_LIBIO - char *buf; - - if (__asprintf (&buf, _("\ -%s: option requires an argument -- %c\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); -#endif - } - d->optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - d->optarg = argv[d->optind++]; - d->__nextchar = NULL; - } - } - return c; - } -} - -int -_getopt_internal (int argc, char **argv, const char *optstring, - const struct option *longopts, int *longind, - int long_only, int posixly_correct) -{ - int result; - - getopt_data.optind = optind; - getopt_data.opterr = opterr; - - result = _getopt_internal_r (argc, argv, optstring, longopts, longind, - long_only, posixly_correct, &getopt_data); - - optind = getopt_data.optind; - optarg = getopt_data.optarg; - optopt = getopt_data.optopt; - - return result; -} - -/* glibc gets a LSB-compliant getopt. - Standalone applications get a POSIX-compliant getopt. */ -#if _LIBC -enum { POSIXLY_CORRECT = 0 }; -#else -enum { POSIXLY_CORRECT = 1 }; -#endif - -int -getopt (int argc, char *const *argv, const char *optstring) -{ - return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0, - POSIXLY_CORRECT); -} - - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int -main (int argc, char **argv) -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - - c = getopt (argc, argv, "abc:d:0123456789"); - if (c == -1) - break; - - switch (c) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/gnulib/getopt.h b/gnulib/getopt.h deleted file mode 100644 index d2d3e6e63..000000000 --- a/gnulib/getopt.h +++ /dev/null @@ -1,225 +0,0 @@ -/* Declarations for getopt. - Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006,2007 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _GETOPT_H - -#ifndef __need_getopt -# define _GETOPT_H 1 -#endif - -/* Standalone applications should #define __GETOPT_PREFIX to an - identifier that prefixes the external functions and variables - defined in this header. When this happens, include the - headers that might declare getopt so that they will not cause - confusion if included after this file. Then systematically rename - identifiers so that they do not collide with the system functions - and variables. Renaming avoids problems with some compilers and - linkers. */ -#if defined __GETOPT_PREFIX && !defined __need_getopt -# include -# include -# include -# undef __need_getopt -# undef getopt -# undef getopt_long -# undef getopt_long_only -# undef optarg -# undef opterr -# undef optind -# undef optopt -# define __GETOPT_CONCAT(x, y) x ## y -# define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y) -# define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y) -# define getopt __GETOPT_ID (getopt) -# define getopt_long __GETOPT_ID (getopt_long) -# define getopt_long_only __GETOPT_ID (getopt_long_only) -# define optarg __GETOPT_ID (optarg) -# define opterr __GETOPT_ID (opterr) -# define optind __GETOPT_ID (optind) -# define optopt __GETOPT_ID (optopt) -#endif - -/* Standalone applications get correct prototypes for getopt_long and - getopt_long_only; they declare "char **argv". libc uses prototypes - with "char *const *argv" that are incorrect because getopt_long and - getopt_long_only can permute argv; this is required for backward - compatibility (e.g., for LSB 2.0.1). - - This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt', - but it caused redefinition warnings if both unistd.h and getopt.h were - included, since unistd.h includes getopt.h having previously defined - __need_getopt. - - The only place where __getopt_argv_const is used is in definitions - of getopt_long and getopt_long_only below, but these are visible - only if __need_getopt is not defined, so it is quite safe to rewrite - the conditional as follows: -*/ -#if !defined __need_getopt -# if defined __GETOPT_PREFIX -# define __getopt_argv_const /* empty */ -# else -# define __getopt_argv_const const -# endif -#endif - -/* If __GNU_LIBRARY__ is not already defined, either we are being used - standalone, or this is the first header included in the source file. - If we are being used with glibc, we need to include , but - that does not exist if we are standalone. So: if __GNU_LIBRARY__ is - not defined, include , which will pull in for us - if it's from glibc. (Why ctype.h? It's guaranteed to exist and it - doesn't flood the namespace with stuff the way some other headers do.) */ -#if !defined __GNU_LIBRARY__ -# include -#endif - -#ifndef __THROW -# ifndef __GNUC_PREREQ -# define __GNUC_PREREQ(maj, min) (0) -# endif -# if defined __cplusplus && __GNUC_PREREQ (2,8) -# define __THROW throw () -# else -# define __THROW -# endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -#ifndef __need_getopt -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 -#endif /* need getopt */ - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `-', then non-option arguments are treated as - arguments to the option '\1'. This behavior is specific to the GNU - `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in - the environment, then do not permute arguments. */ - -extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) - __THROW; - -#ifndef __need_getopt -extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind) - __THROW; -extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind) - __THROW; - -#endif - -#ifdef __cplusplus -} -#endif - -/* Make sure we later can get all the definitions and declarations. */ -#undef __need_getopt - -#endif /* getopt.h */ diff --git a/gnulib/getopt1.c b/gnulib/getopt1.c deleted file mode 100644 index d6a3ecf4e..000000000 --- a/gnulib/getopt1.c +++ /dev/null @@ -1,170 +0,0 @@ -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98,2004,2006 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifdef _LIBC -# include -#else -# include -# include "getopt.h" -#endif -#include "getopt_int.h" - -#include - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int -getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 0, 0); -} - -int -_getopt_long_r (int argc, char **argv, const char *options, - const struct option *long_options, int *opt_index, - struct _getopt_data *d) -{ - return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 0, 0, d); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (int argc, char *__getopt_argv_const *argv, - const char *options, - const struct option *long_options, int *opt_index) -{ - return _getopt_internal (argc, (char **) argv, options, long_options, - opt_index, 1, 0); -} - -int -_getopt_long_only_r (int argc, char **argv, const char *options, - const struct option *long_options, int *opt_index, - struct _getopt_data *d) -{ - return _getopt_internal_r (argc, argv, options, long_options, opt_index, - 1, 0, d); -} - - -#ifdef TEST - -#include - -int -main (int argc, char **argv) -{ - int c; - int digit_optind = 0; - - while (1) - { - int this_option_optind = optind ? optind : 1; - int option_index = 0; - static struct option long_options[] = - { - {"add", 1, 0, 0}, - {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, - {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, - {"file", 1, 0, 0}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "abc:d:0123456789", - long_options, &option_index); - if (c == -1) - break; - - switch (c) - { - case 0: - printf ("option %s", long_options[option_index].name); - if (optarg) - printf (" with arg %s", optarg); - printf ("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 && digit_optind != this_option_optind) - printf ("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf ("option %c\n", c); - break; - - case 'a': - printf ("option a\n"); - break; - - case 'b': - printf ("option b\n"); - break; - - case 'c': - printf ("option c with value `%s'\n", optarg); - break; - - case 'd': - printf ("option d with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf ("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) - { - printf ("non-option ARGV-elements: "); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - } - - exit (0); -} - -#endif /* TEST */ diff --git a/gnulib/getopt_int.h b/gnulib/getopt_int.h deleted file mode 100644 index 3c6628bb9..000000000 --- a/gnulib/getopt_int.h +++ /dev/null @@ -1,130 +0,0 @@ -/* Internal declarations for getopt. - Copyright (C) 1989-1994,1996-1999,2001,2003,2004 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _GETOPT_INT_H -#define _GETOPT_INT_H 1 - -extern int _getopt_internal (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only, int __posixly_correct); - - -/* Reentrant versions which can handle parsing multiple argument - vectors at the same time. */ - -/* Data type for reentrant functions. */ -struct _getopt_data -{ - /* These have exactly the same meaning as the corresponding global - variables, except that they are used for the reentrant - versions of getopt. */ - int optind; - int opterr; - int optopt; - char *optarg; - - /* Internal members. */ - - /* True if the internal members have been initialized. */ - int __initialized; - - /* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - char *__nextchar; - - /* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters, or by calling getopt. - - PERMUTE is the default. We permute the contents of ARGV as we - scan, so that eventually all the non-options are at the end. - This allows options to be given in any order, even with programs - that were not written to expect this. - - RETURN_IN_ORDER is an option available to programs that were - written to expect options and other ARGV-elements in any order - and that care about the ordering of the two. We describe each - non-option ARGV-element as if it were the argument of an option - with character code 1. Using `-' as the first character of the - list of option characters selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - - enum - { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER - } __ordering; - - /* If the POSIXLY_CORRECT environment variable is set - or getopt was called. */ - int __posixly_correct; - - - /* Handle permutation of arguments. */ - - /* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first - of them; `last_nonopt' is the index after the last of them. */ - - int __first_nonopt; - int __last_nonopt; - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - int __nonoption_flags_max_len; - int __nonoption_flags_len; -# endif -}; - -/* The initializer is necessary to set OPTIND and OPTERR to their - default values and to clear the initialization flag. */ -#define _GETOPT_DATA_INITIALIZER { 1, 1 } - -extern int _getopt_internal_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only, int __posixly_correct, - struct _getopt_data *__data); - -extern int _getopt_long_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind, - struct _getopt_data *__data); - -extern int _getopt_long_only_r (int ___argc, char **___argv, - const char *__shortopts, - const struct option *__longopts, - int *__longind, - struct _getopt_data *__data); - -#endif /* getopt_int.h */ diff --git a/gnulib/gettext.h b/gnulib/gettext.h deleted file mode 100644 index 9d76ec9af..000000000 --- a/gnulib/gettext.h +++ /dev/null @@ -1,270 +0,0 @@ -/* Convenience header for conditional use of GNU . - Copyright (C) 1995-1998, 2000-2002, 2004-2006 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -#ifndef _LIBGETTEXT_H -#define _LIBGETTEXT_H 1 - -/* NLS can be disabled through the configure --disable-nls option. */ -#if ENABLE_NLS - -/* Get declarations of GNU message catalog functions. */ -# include - -/* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by - the gettext() and ngettext() macros. This is an alternative to calling - textdomain(), and is useful for libraries. */ -# ifdef DEFAULT_TEXT_DOMAIN -# undef gettext -# define gettext(Msgid) \ - dgettext (DEFAULT_TEXT_DOMAIN, Msgid) -# undef ngettext -# define ngettext(Msgid1, Msgid2, N) \ - dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N) -# endif - -#else - -/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which - chokes if dcgettext is defined as a macro. So include it now, to make - later inclusions of a NOP. We don't include - as well because people using "gettext.h" will not include , - and also including would fail on SunOS 4, whereas - is OK. */ -#if defined(__sun) -# include -#endif - -/* Many header files from the libstdc++ coming with g++ 3.3 or newer include - , which chokes if dcgettext is defined as a macro. So include - it now, to make later inclusions of a NOP. */ -#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3) -# include -# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H -# include -# endif -#endif - -/* Disabled NLS. - The casts to 'const char *' serve the purpose of producing warnings - for invalid uses of the value returned from these functions. - On pre-ANSI systems without 'const', the config.h file is supposed to - contain "#define const". */ -# define gettext(Msgid) ((const char *) (Msgid)) -# define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid)) -# define dcgettext(Domainname, Msgid, Category) \ - ((void) (Category), dgettext (Domainname, Msgid)) -# define ngettext(Msgid1, Msgid2, N) \ - ((N) == 1 \ - ? ((void) (Msgid2), (const char *) (Msgid1)) \ - : ((void) (Msgid1), (const char *) (Msgid2))) -# define dngettext(Domainname, Msgid1, Msgid2, N) \ - ((void) (Domainname), ngettext (Msgid1, Msgid2, N)) -# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ - ((void) (Category), dngettext(Domainname, Msgid1, Msgid2, N)) -# define textdomain(Domainname) ((const char *) (Domainname)) -# define bindtextdomain(Domainname, Dirname) \ - ((void) (Domainname), (const char *) (Dirname)) -# define bind_textdomain_codeset(Domainname, Codeset) \ - ((void) (Domainname), (const char *) (Codeset)) - -#endif - -/* A pseudo function call that serves as a marker for the automated - extraction of messages, but does not call gettext(). The run-time - translation is done at a different place in the code. - The argument, String, should be a literal string. Concatenated strings - and other string expressions won't work. - The macro's expansion is not parenthesized, so that it is suitable as - initializer for static 'char[]' or 'const char[]' variables. */ -#define gettext_noop(String) String - -/* The separator between msgctxt and msgid in a .mo file. */ -#define GETTEXT_CONTEXT_GLUE "\004" - -/* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a - MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be - short and rarely need to change. - The letter 'p' stands for 'particular' or 'special'. */ -#ifdef DEFAULT_TEXT_DOMAIN -# define pgettext(Msgctxt, Msgid) \ - pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) -#else -# define pgettext(Msgctxt, Msgid) \ - pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) -#endif -#define dpgettext(Domainname, Msgctxt, Msgid) \ - pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES) -#define dcpgettext(Domainname, Msgctxt, Msgid, Category) \ - pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category) -#ifdef DEFAULT_TEXT_DOMAIN -# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ - npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) -#else -# define npgettext(Msgctxt, Msgid, MsgidPlural, N) \ - npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) -#endif -#define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ - npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES) -#define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \ - npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category) - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static const char * -pgettext_aux (const char *domain, - const char *msg_ctxt_id, const char *msgid, - int category) -{ - const char *translation = dcgettext (domain, msg_ctxt_id, category); - if (translation == msg_ctxt_id) - return msgid; - else - return translation; -} - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static const char * -npgettext_aux (const char *domain, - const char *msg_ctxt_id, const char *msgid, - const char *msgid_plural, unsigned long int n, - int category) -{ - const char *translation = - dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); - if (translation == msg_ctxt_id || translation == msgid_plural) - return (n == 1 ? msgid : msgid_plural); - else - return translation; -} - -/* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID - can be arbitrary expressions. But for string literals these macros are - less efficient than those above. */ - -#include - -#define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS \ - (((__GNUC__ >= 3 || __GNUG__ >= 2) && !__STRICT_ANSI__) \ - /* || __STDC_VERSION__ >= 199901L */ ) - -#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS -#include -#endif - -#define pgettext_expr(Msgctxt, Msgid) \ - dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES) -#define dpgettext_expr(Domainname, Msgctxt, Msgid) \ - dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES) - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static const char * -dcpgettext_expr (const char *domain, - const char *msgctxt, const char *msgid, - int category) -{ - size_t msgctxt_len = strlen (msgctxt) + 1; - size_t msgid_len = strlen (msgid) + 1; - const char *translation; -#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS - char msg_ctxt_id[msgctxt_len + msgid_len]; -#else - char buf[1024]; - char *msg_ctxt_id = - (msgctxt_len + msgid_len <= sizeof (buf) - ? buf - : (char *) malloc (msgctxt_len + msgid_len)); - if (msg_ctxt_id != NULL) -#endif - { - memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); - msg_ctxt_id[msgctxt_len - 1] = '\004'; - memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); - translation = dcgettext (domain, msg_ctxt_id, category); -#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS - if (msg_ctxt_id != buf) - free (msg_ctxt_id); -#endif - if (translation != msg_ctxt_id) - return translation; - } - return msgid; -} - -#define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \ - dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) -#define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \ - dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES) - -#ifdef __GNUC__ -__inline -#else -#ifdef __cplusplus -inline -#endif -#endif -static const char * -dcnpgettext_expr (const char *domain, - const char *msgctxt, const char *msgid, - const char *msgid_plural, unsigned long int n, - int category) -{ - size_t msgctxt_len = strlen (msgctxt) + 1; - size_t msgid_len = strlen (msgid) + 1; - const char *translation; -#if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS - char msg_ctxt_id[msgctxt_len + msgid_len]; -#else - char buf[1024]; - char *msg_ctxt_id = - (msgctxt_len + msgid_len <= sizeof (buf) - ? buf - : (char *) malloc (msgctxt_len + msgid_len)); - if (msg_ctxt_id != NULL) -#endif - { - memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1); - msg_ctxt_id[msgctxt_len - 1] = '\004'; - memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len); - translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category); -#if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS - if (msg_ctxt_id != buf) - free (msg_ctxt_id); -#endif - if (!(translation == msg_ctxt_id || translation == msgid_plural)) - return translation; - } - return (n == 1 ? msgid : msgid_plural); -} - -#endif /* _LIBGETTEXT_H */ diff --git a/gnulib/progname.c b/gnulib/progname.c deleted file mode 100644 index bfa374a52..000000000 --- a/gnulib/progname.c +++ /dev/null @@ -1,78 +0,0 @@ -/* Program name management. - Copyright (C) 2001-2003, 2005-2009 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - - -#include - -/* Specification. */ -#undef ENABLE_RELOCATABLE /* avoid defining set_program_name as a macro */ -#include "progname.h" - -#include /* get program_invocation_name declaration */ -#include - - -/* String containing name the program is called with. - To be initialized by main(). */ -const char *program_name = NULL; - -/* Set program_name, based on argv[0]. */ -void -set_program_name (const char *argv0) -{ - /* libtool creates a temporary executable whose name is sometimes prefixed - with "lt-" (depends on the platform). It also makes argv[0] absolute. - But the name of the temporary executable is a detail that should not be - visible to the end user and to the test suite. - Remove this "/.libs/" or "/.libs/lt-" prefix here. */ - const char *slash; - const char *base; - - slash = strrchr (argv0, '/'); - base = (slash != NULL ? slash + 1 : argv0); - if (base - argv0 >= 7 && strncmp (base - 7, "/.libs/", 7) == 0) - { - argv0 = base; - if (strncmp (base, "lt-", 3) == 0) - { - argv0 = base + 3; - /* On glibc systems, remove the "lt-" prefix from the variable - program_invocation_short_name. */ -#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME - program_invocation_short_name = (char *) argv0; -#endif - } - } - - /* But don't strip off a leading / in general, because when the user - runs - /some/hidden/place/bin/cp foo foo - he should get the error message - /some/hidden/place/bin/cp: `foo' and `foo' are the same file - not - cp: `foo' and `foo' are the same file - */ - - program_name = argv0; - - /* On glibc systems, the error() function comes from libc and uses the - variable program_invocation_name, not program_name. So set this variable - as well. */ -#if HAVE_DECL_PROGRAM_INVOCATION_NAME - program_invocation_name = (char *) argv0; -#endif -} diff --git a/gnulib/progname.h b/gnulib/progname.h deleted file mode 100644 index 82615c6bc..000000000 --- a/gnulib/progname.h +++ /dev/null @@ -1,60 +0,0 @@ -/* Program name management. - Copyright (C) 2001-2004, 2006 Free Software Foundation, Inc. - Written by Bruno Haible , 2001. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#ifndef _PROGNAME_H -#define _PROGNAME_H - -/* Programs using this file should do the following in main(): - set_program_name (argv[0]); - */ - - -#ifdef __cplusplus -extern "C" { -#endif - - -/* String containing name the program is called with. */ -extern const char *program_name; - -/* Set program_name, based on argv[0]. */ -extern void set_program_name (const char *argv0); - -#if ENABLE_RELOCATABLE - -/* Set program_name, based on argv[0], and original installation prefix and - directory, for relocatability. */ -extern void set_program_name_and_installdir (const char *argv0, - const char *orig_installprefix, - const char *orig_installdir); -#undef set_program_name -#define set_program_name(ARG0) \ - set_program_name_and_installdir (ARG0, INSTALLPREFIX, INSTALLDIR) - -/* Return the full pathname of the current executable, based on the earlier - call to set_program_name_and_installdir. Return NULL if unknown. */ -extern char *get_full_program_name (void); - -#endif - - -#ifdef __cplusplus -} -#endif - - -#endif /* _PROGNAME_H */ diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am new file mode 100644 index 000000000..e50db8106 --- /dev/null +++ b/grub-core/Makefile.am @@ -0,0 +1,522 @@ +AUTOMAKE_OPTIONS = subdir-objects -Wno-portability + +DEPDIR=.deps-core + +include $(top_srcdir)/conf/Makefile.common + +CC=$(TARGET_CC) +CPP=$(TARGET_CC) +CCAS=$(TARGET_CC) +RANLIB=$(TARGET_RANLIB) +STRIP=$(TARGET_STRIP) + +MACHO2IMG=$(top_builddir)/grub-macho2img + +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) + +build-grub-pep2elf$(BUILD_EXEEXT): $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=64 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pep2elf\" $^ +CLEANFILES += build-grub-pep2elf$(BUILD_EXEEXT) + +build-grub-pe2elf$(BUILD_EXEEXT): $(top_srcdir)/util/grub-pe2elf.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_TARGET_WORDSIZE=32 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-pe2elf\" $^ +CLEANFILES += build-grub-pe2elf$(BUILD_EXEEXT) + +# gentrigtables +gentrigtables$(BUILD_EXEEXT): gentrigtables.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) $< $(BUILD_LIBM) +CLEANFILES += gentrigtables$(BUILD_EXEEXT) + +build-grub-module-verifier$(BUILD_EXEEXT): $(top_srcdir)/util/grub-module-verifier.c $(top_srcdir)/util/grub-module-verifier32.c $(top_srcdir)/util/grub-module-verifier64.c $(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c + $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS) $(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_UTIL=1 -DGRUB_BUILD_PROGRAM_NAME=\"build-grub-module-verifier\" $^ +CLEANFILES += build-grub-module-verifier$(BUILD_EXEEXT) + +# trigtables.c +trigtables.c: gentrigtables$(BUILD_EXEEXT) gentrigtables.c $(top_srcdir)/configure.ac + ./gentrigtables$(BUILD_EXEEXT) > $@ +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 + +rs_decoder.h: $(srcdir)/lib/reed_solomon.c + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) -Os -I$(top_builddir) -S -DSTANDALONE -o $@ $< -g0 -mregparm=3 -ffreestanding + +CLEANFILES += grub_script.yy.c grub_script.yy.h + +include $(srcdir)/Makefile.core.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/efi/sb.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/lockdown.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/misc.h +if COND_emu +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/compiler-rt-emu.h +else +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/compiler-rt.h +endif +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.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/key_protector.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.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/verify.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm_private.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/net.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/memory.h + +if COND_i386_pc +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pxe.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +endif + +if COND_i386_xen_pvh +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/int.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/xen/hypercall.h +endif + +if COND_i386_efi +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h +endif + +if COND_i386_coreboot +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/coreboot/lbio.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.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/bufio.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_i386_multiboot +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_i386_qemu +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +endif + +if COND_i386_ieee1275 +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +endif + +if COND_i386_xen +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/xen/hypercall.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +endif + +if COND_x86_64_xen +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/xen.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/x86_64/xen/hypercall.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +endif + +if COND_x86_64_efi +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/tsc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pmtimer.h +endif + +if COND_ia64_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_mips +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/cpu/kernel.h +endif + +if COND_mips_arc +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arc/arc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +endif + +if COND_mips_qemu_mips +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/serial.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +endif + +if COND_mips_loongson +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/time.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.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/bufio.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cs5536.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/pci.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/serial.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/loader.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +endif + +if COND_mips_qemu_mips +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/memory.h +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/machine/kernel.h +endif + +if COND_powerpc_ieee1275 +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/alloc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +endif + +if COND_sparc64_ieee1275 +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sparc64/ieee1275/ieee1275.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/terminfo.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +endif + +if COND_arm_uboot +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/uboot.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/uboot/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/lib/arg.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h +endif + +if COND_arm_coreboot +KERNEL_HEADER_FILES += $(top_builddir)/include/grub/keyboard_layouts.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/video_fb.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/bufio.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdt.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/dma.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/coreboot/kernel.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdtbus.h +endif + +if COND_arm_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/arm/system.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_arm64_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_loongarch64_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_riscv32_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_riscv64_efi +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/acpi.h +endif + +if COND_emu +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/datetime.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/misc.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/net.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostdisk.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/hostfile.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/extcmd.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/emu/exec.h +if COND_GRUB_EMU_SDL +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.h +endif +if COND_GRUB_EMU_SDL2 +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/sdl.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 | $(SHELL) $(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 + +if COND_HAVE_ASM_USCORE +ASM_PREFIX=_ +else +ASM_PREFIX= +endif + +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 + cat kernel_syms.input | grep -v '^#' | sed -n \ + -e '/EXPORT_FUNC *([a-zA-Z0-9_]*)/{s/.*EXPORT_FUNC *(\([a-zA-Z0-9_]*\)).*/defined kernel '"$(ASM_PREFIX)"'\1/;p;}' \ + -e '/EXPORT_VAR *([a-zA-Z0-9_]*)/{s/.*EXPORT_VAR *(\([a-zA-Z0-9_]*\)).*/defined kernel '"$(ASM_PREFIX)"'\1/;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 $(MODULE_FILES) + rm -f $@; echo $(MODULE_FILES) | sh $(srcdir)/genemuinitheader.sh $(TARGET_NM) > $@ +CLEANFILES += grub_emu_init.h + +grub_emu_init.c: grub_emu_init.h genemuinit.sh $(MODULE_FILES) + rm -f $@; echo $(MODULE_FILES) | sh $(srcdir)/genemuinit.sh $(TARGET_NM) > $@ +CLEANFILES += grub_emu_init.c +endif + +# List files + +fs.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + if grep 'FS_LIST_MARKER' $$pp >/dev/null 2>&1; then \ + echo $$b; \ + fi; \ + done) | sort -u > $@ +platform_DATA += fs.lst +CLEANFILES += fs.lst + +command.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + sed -n \ + -e "/EXTCOMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $$b/;p;}" \ + -e "/EXTCOMMAND_LOCKDOWN_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $$b/;p;}" \ + -e "/P1COMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/*\1: $$b/;p;}" \ + -e "/COMMAND_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $$b/;p;}" \ + -e "/COMMAND_LOCKDOWN_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $$b/;p;}" $$pp; \ + done) | sort -u > $@ +platform_DATA += command.lst +CLEANFILES += command.lst + +partmap.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + if grep 'PARTMAP_LIST_MARKER' $$pp >/dev/null 2>&1; then \ + echo $$b; \ + fi; \ + done) | sort -u > $@ +platform_DATA += partmap.lst +CLEANFILES += partmap.lst + +terminal.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + sed -n \ + -e "/INPUT_TERMINAL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $$b/;p;}" \ + -e "/OUTPUT_TERMINAL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $$b/;p;}" $$pp; \ + done) | sort -u > $@ +platform_DATA += terminal.lst +CLEANFILES += terminal.lst + +fdt.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + sed -n \ + -e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $$b/;p;}" \ + -e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $$b/;p;}" $$pp; \ + done) | sort -u > $@ +platform_DATA += fdt.lst +CLEANFILES += fdt.lst + +parttool.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + sed -n \ + -e "/PARTTOOL_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/\1: $$b/;p;}" $$pp; \ + done) | sort -u > $@ +platform_DATA += parttool.lst +CLEANFILES += parttool.lst + +video.lst: $(MARKER_FILES) + (for pp in $^; do \ + b=`basename $$pp .marker`; \ + if grep 'VIDEO_LIST_MARKER' $$pp >/dev/null 2>&1; then \ + echo $$b; \ + fi; \ + done) | sort -u > $@ +platform_DATA += video.lst +CLEANFILES += video.lst + +# but, crypto.lst is simply copied +crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst + cp $^ $@ +platform_DATA += crypto.lst +CLEANFILES += crypto.lst + +extra_deps.lst: + @echo $(EXTRA_DEPS) | sed "s/\s*:\s*/\n/g" > $@ + +syminfo.lst: gensyminfo.sh kernel_syms.lst extra_deps.lst $(MODULE_FILES) + cat kernel_syms.lst extra_deps.lst > $@.new + for m in $(MODULE_FILES); do \ + sh $< $$m >> $@.new || exit 1; \ + done + mv $@.new $@ + +# generate global module dependencies list +moddep.lst: syminfo.lst genmoddep.awk video.lst + cat $< | sort | $(AWK) -f $(srcdir)/genmoddep.awk > $@ || (rm -f $@; exit 1) +platform_DATA += moddep.lst +CLEANFILES += config.log syminfo.lst moddep.lst extra_deps.lst + +$(MOD_FILES): %.mod : genmod.sh moddep.lst %.module$(EXEEXT) build-grub-module-verifier$(BUILD_EXEEXT) + TARGET_OBJ2ELF=@TARGET_OBJ2ELF@ sh $^ $@ +platform_DATA += $(MOD_FILES) +platform_DATA += modinfo.sh +CLEANFILES += $(MOD_FILES) + +if COND_ENABLE_EFIEMU +efiemu32.o: efiemu/runtime/efiemu.c $(TARGET_OBJ2ELF) + -rm -f $@ + -rm -f $@.bin + $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m32 -Wall -Werror -nostdlib -static -O2 -c -o $@.bin $< + if test "x$(TARGET_APPLE_LINKER)" = x1; then \ + $(TARGET_OBJCONV) -felf32 -nu -nd $@.bin $@ || exit 1; \ + rm -f $@.bin ; \ + elif test ! -z "$(TARGET_OBJ2ELF)"; then \ + $(TARGET_OBJ2ELF) $@.bin || (rm -f $@.bin; exit 1); \ + mv $@.bin $@ ; \ + else \ + mv $@.bin $@ ; \ + fi + +# Link format -arch,x86_64 means Apple linker +efiemu64_c.o: efiemu/runtime/efiemu.c + $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m64 -nostdlib -Wall -Werror -O2 -mcmodel=large -mno-red-zone -c -o $@ $< + +efiemu64_s.o: efiemu/runtime/efiemu.S + $(TARGET_CC) $(DEFS) $(INCLUDES) $(CPPFLAGS_EFIEMU) $(CPPFLAGS_DEFAULT) -m64 -Wall -Werror -nostdlib -O2 -mcmodel=large -mno-red-zone -c -o $@ $< + +efiemu64.o: efiemu64_c.o efiemu64_s.o $(TARGET_OBJ2ELEF) + -rm -f $@ + -rm -f $@.bin + $(TARGET_CC) -m64 $(EFIEMU64_LINK_FORMAT) -nostdlib -static -Wl,-r -o $@.bin $^ + if test "x$(EFIEMU64_LINK_FORMAT)" = x-arch,x86_64; then \ + $(TARGET_OBJCONV) -felf64 -nu -nd $@.bin $@ || exit 1; \ + rm -f $@.bin; \ + else \ + mv $@.bin $@ ; \ + fi + +platform_DATA += efiemu32.o efiemu64.o +CLEANFILES += efiemu32.o efiemu64.o efiemu64_c.o efiemu64_s.o +endif + +windowsdir=$(top_builddir)/$(PACKAGE)-$(VERSION)-for-windows +windowsdir: $(PROGRAMS) $(starfield_DATA) $(platform_DATA) + test -d $(windowsdir)/$(target_cpu)-$(platform) || mkdir $(windowsdir)/$(target_cpu)-$(platform) + for x in $(platform_DATA); do \ + cp -fp $$x $(windowsdir)/$(target_cpu)-$(platform)/$$x; \ + done diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def new file mode 100644 index 000000000..f70e02e69 --- /dev/null +++ b/grub-core/Makefile.core.def @@ -0,0 +1,2671 @@ +AutoGen definitions Makefile.tpl; + +transform_data = { + installdir = noinst; + name = gensyminfo.sh; + common = gensyminfo.sh.in; +}; + +transform_data = { + installdir = noinst; + name = genmod.sh; + common = genmod.sh.in; +}; + +transform_data = { + installdir = noinst; + name = modinfo.sh; + common = modinfo.sh.in; +}; + +transform_data = { + installdir = platform; + name = gdb_helper.py; + common = gdb_helper.py.in; +}; + +transform_data = { + installdir = platform; + name = gdb_grub; + common = gdb_grub.in; +}; + +transform_data = { + installdir = platform; + name = grub.chrp; + common = boot/powerpc/grub.chrp.in; + enable = powerpc_ieee1275; +}; + +transform_data = { + installdir = platform; + name = bootinfo.txt; + common = boot/powerpc/bootinfo.txt.in; + enable = powerpc_ieee1275; +}; + +kernel = { + name = kernel; + + nostrip = emu; + + emu_ldflags = '-Wl,-r'; + i386_efi_cflags = '-fshort-wchar'; + i386_efi_ldflags = '-Wl,-r'; + i386_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + x86_64_efi_cflags = '-fshort-wchar'; + x86_64_efi_ldflags = '-Wl,-r'; + x86_64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + + ia64_efi_cflags = '-fshort-wchar -fno-builtin -fpic -minline-int-divide-max-throughput'; + ia64_efi_ldflags = '-Wl,-r'; + ia64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + + arm_efi_cflags = '-fshort-wchar'; + arm_efi_ldflags = '-Wl,-r'; + arm_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + + arm64_efi_cflags = '-fshort-wchar'; + arm64_efi_ldflags = '-Wl,-r'; + arm64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame'; + + loongarch64_efi_cflags = '-fshort-wchar'; + loongarch64_efi_ldflags = '-Wl,-r'; + loongarch64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame'; + + riscv32_efi_cflags = '-fshort-wchar'; + riscv32_efi_ldflags = '-Wl,-r'; + riscv32_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame'; + + riscv64_efi_cflags = '-fshort-wchar'; + riscv64_efi_ldflags = '-Wl,-r'; + riscv64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame'; + + i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x9000'; + i386_qemu_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_qemu_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x9000'; + i386_coreboot_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_coreboot_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x9000'; + i386_multiboot_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_multiboot_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x9000'; + i386_ieee1275_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_ieee1275_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x10000'; + i386_xen_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_xen_ldflags = '$(TARGET_IMG_BASE_LDOPT),0'; + x86_64_xen_ldflags = '$(TARGET_IMG_LDFLAGS)'; + x86_64_xen_ldflags = '$(TARGET_IMG_BASE_LDOPT),0'; + i386_xen_pvh_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_xen_pvh_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x100000'; + + mips_loongson_ldflags = '-Wl,-Ttext,0x80200000'; + powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000'; + sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400'; + mips_arc_ldflags = '-Wl,-Ttext,$(TARGET_LINK_ADDR)'; + mips_qemu_mips_ldflags = '-Wl,-Ttext,0x80200000'; + + mips_arc_cppflags = '-DGRUB_DECOMPRESSOR_LINK_ADDR=$(TARGET_DECOMPRESSOR_LINK_ADDR)'; + i386_qemu_cppflags = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)'; + emu_cflags = '$(CFLAGS_GNULIB)'; + emu_cppflags = '$(CPPFLAGS_GNULIB)'; + arm_uboot_ldflags = '-Wl,-r'; + arm_uboot_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + arm_coreboot_ldflags = '-Wl,-r'; + arm_coreboot_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version'; + + 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_xen_startup = kern/i386/xen/startup.S; + x86_64_xen_startup = kern/x86_64/xen/startup.S; + i386_xen_pvh_startup = kern/i386/xen/startup_pvh.S; + i386_qemu_startup = kern/i386/qemu/startup.S; + i386_ieee1275_startup = kern/i386/ieee1275/startup.S; + i386_coreboot_startup = kern/i386/coreboot/startup.S; + i386_multiboot_startup = kern/i386/coreboot/startup.S; + mips_startup = kern/mips/startup.S; + sparc64_ieee1275_startup = kern/sparc64/ieee1275/crt0.S; + powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S; + arm_uboot_startup = kern/arm/startup.S; + arm_coreboot_startup = kern/arm/startup.S; + arm_efi_startup = kern/arm/efi/startup.S; + arm64_efi_startup = kern/arm64/efi/startup.S; + loongarch64_efi_startup = kern/loongarch64/efi/startup.S; + riscv32_efi_startup = kern/riscv/efi/startup.S; + riscv64_efi_startup = kern/riscv/efi/startup.S; + + common = kern/buffer.c; + 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; + common = kern/verifiers.c; + + noemu = kern/compiler-rt.c; + noemu = kern/mm.c; + noemu = kern/time.c; + noemu = kern/generic/millisleep.c; + + noemu_nodist = symlist.c; + + mips = 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/console.c; + ieee1275 = kern/ieee1275/init.c; + + uboot = disk/uboot/ubootdisk.c; + uboot = kern/uboot/uboot.c; + uboot = kern/uboot/init.c; + uboot = kern/uboot/hw.c; + uboot = term/uboot/console.c; + arm_uboot = kern/arm/uboot/init.c; + arm_uboot = kern/arm/uboot/uboot.S; + + arm_coreboot = kern/arm/coreboot/init.c; + arm_coreboot = kern/arm/coreboot/timer.c; + arm_coreboot = kern/arm/coreboot/coreboot.S; + arm_coreboot = lib/fdt.c; + arm_coreboot = bus/fdt.c; + arm_coreboot = term/ps2.c; + arm_coreboot = term/arm/pl050.c; + arm_coreboot = term/arm/cros.c; + arm_coreboot = term/arm/cros_ec.c; + arm_coreboot = bus/spi/rk3288_spi.c; + arm_coreboot = commands/keylayouts.c; + arm_coreboot = kern/arm/coreboot/dma.c; + + terminfoinkernel = term/terminfo.c; + terminfoinkernel = term/tparm.c; + terminfoinkernel = commands/extcmd.c; + terminfoinkernel = lib/arg.c; + + softdiv = lib/division.c; + + i386 = kern/i386/dl.c; + i386_xen = kern/i386/dl.c; + i386_xen_pvh = kern/i386/dl.c; + + i386_coreboot = kern/i386/coreboot/init.c; + i386_multiboot = kern/i386/coreboot/init.c; + i386_qemu = kern/i386/qemu/init.c; + i386_coreboot_multiboot_qemu = term/i386/pc/vga_text.c; + coreboot = video/coreboot/cbfb.c; + + efi = disk/efi/efidisk.c; + efi = kern/efi/efi.c; + efi = kern/efi/debug.c; + efi = kern/efi/init.c; + efi = kern/efi/mm.c; + efi = term/efi/console.c; + efi = kern/acpi.c; + efi = kern/efi/acpi.c; + efi = kern/efi/sb.c; + efi = kern/lockdown.c; + i386_coreboot = kern/i386/pc/acpi.c; + i386_multiboot = kern/i386/pc/acpi.c; + i386_coreboot = kern/acpi.c; + i386_multiboot = kern/acpi.c; + + x86 = kern/i386/tsc.c; + x86 = kern/i386/tsc_pit.c; + i386_efi = kern/i386/efi/tsc.c; + x86_64_efi = kern/i386/efi/tsc.c; + i386_efi = kern/i386/tsc_pmtimer.c; + i386_coreboot = kern/i386/tsc_pmtimer.c; + x86_64_efi = kern/i386/tsc_pmtimer.c; + + i386_efi = kern/i386/efi/init.c; + i386_efi = bus/pci.c; + + x86_64 = kern/x86_64/dl.c; + x86_64_xen = kern/x86_64/dl.c; + x86_64_efi = kern/i386/efi/init.c; + x86_64_efi = bus/pci.c; + + xen = kern/i386/tsc.c; + xen = kern/i386/xen/tsc.c; + x86_64_xen = kern/x86_64/xen/hypercall.S; + i386_xen = kern/i386/xen/hypercall.S; + xen = kern/xen/init.c; + xen = term/xen/console.c; + xen = disk/xen/xendisk.c; + xen = commands/boot.c; + + i386_xen_pvh = commands/boot.c; + i386_xen_pvh = disk/xen/xendisk.c; + i386_xen_pvh = kern/i386/tsc.c; + i386_xen_pvh = kern/i386/xen/tsc.c; + i386_xen_pvh = kern/i386/xen/pvh.c; + i386_xen_pvh = kern/xen/init.c; + i386_xen_pvh = term/xen/console.c; + + ia64_efi = kern/ia64/efi/startup.S; + ia64_efi = kern/ia64/efi/init.c; + ia64_efi = kern/ia64/dl.c; + ia64_efi = kern/ia64/dl_helper.c; + ia64_efi = kern/ia64/cache.c; + + arm_efi = kern/arm/efi/init.c; + arm_efi = kern/efi/fdt.c; + + arm64_efi = kern/arm64/efi/init.c; + arm64_efi = kern/efi/fdt.c; + + loongarch64_efi = kern/loongarch64/efi/init.c; + loongarch64_efi = kern/efi/fdt.c; + + riscv32_efi = kern/riscv/efi/init.c; + riscv32_efi = kern/efi/fdt.c; + + riscv64_efi = kern/riscv/efi/init.c; + riscv64_efi = kern/efi/fdt.c; + + i386_pc = kern/i386/pc/init.c; + i386_pc = kern/i386/pc/mmap.c; + i386_pc = term/i386/pc/console.c; + + i386_qemu = bus/pci.c; + i386_qemu = kern/vga_init.c; + i386_qemu = kern/i386/qemu/mmap.c; + + coreboot = kern/coreboot/mmap.c; + i386_coreboot = kern/i386/coreboot/cbtable.c; + coreboot = kern/coreboot/cbtable.c; + arm_coreboot = kern/arm/coreboot/cbtable.c; + + i386_multiboot = kern/i386/multiboot_mmap.c; + + mips = kern/mips/cache.S; + mips = kern/mips/dl.c; + mips = kern/mips/init.c; + + mips_qemu_mips = kern/mips/qemu_mips/init.c; + mips_qemu_mips = term/ns8250.c; + mips_qemu_mips = term/serial.c; + mips_qemu_mips = term/at_keyboard.c; + mips_qemu_mips = term/ps2.c; + mips_qemu_mips = commands/boot.c; + mips_qemu_mips = commands/keylayouts.c; + mips_qemu_mips = term/i386/pc/vga_text.c; + mips_qemu_mips = kern/vga_init.c; + + mips_arc = kern/mips/arc/init.c; + mips_arc = term/arc/console.c; + mips_arc = disk/arc/arcdisk.c; + + mips_loongson = term/ns8250.c; + mips_loongson = bus/bonito.c; + mips_loongson = bus/cs5536.c; + mips_loongson = bus/pci.c; + mips_loongson = kern/mips/loongson/init.c; + mips_loongson = term/at_keyboard.c; + mips_loongson = term/ps2.c; + mips_loongson = commands/boot.c; + mips_loongson = term/serial.c; + mips_loongson = video/sm712.c; + mips_loongson = video/sis315pro.c; + mips_loongson = video/radeon_fuloong2e.c; + mips_loongson = video/radeon_yeeloong3a.c; + extra_dist = video/sm712_init.c; + extra_dist = video/sis315_init.c; + mips_loongson = commands/keylayouts.c; + + powerpc_ieee1275 = kern/powerpc/cache.S; + powerpc_ieee1275 = kern/powerpc/dl.c; + powerpc_ieee1275 = kern/powerpc/compiler-rt.S; + + sparc64_ieee1275 = kern/sparc64/cache.S; + sparc64_ieee1275 = kern/sparc64/dl.c; + sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c; + sparc64_ieee1275 = disk/ieee1275/obdisk.c; + + arm = kern/arm/dl.c; + arm = kern/arm/dl_helper.c; + arm = kern/arm/cache_armv6.S; + arm = kern/arm/cache_armv7.S; + extra_dist = kern/arm/cache.S; + arm = kern/arm/cache.c; + arm = kern/arm/compiler-rt.S; + + arm64 = kern/arm64/cache.c; + arm64 = kern/arm64/cache_flush.S; + arm64 = kern/arm64/dl.c; + arm64 = kern/arm64/dl_helper.c; + + loongarch64 = kern/loongarch64/cache.c; + loongarch64 = kern/loongarch64/cache_flush.S; + loongarch64 = kern/loongarch64/dl.c; + loongarch64 = kern/loongarch64/dl_helper.c; + + riscv32 = kern/riscv/cache.c; + riscv32 = kern/riscv/cache_flush.S; + riscv32 = kern/riscv/dl.c; + + riscv64 = kern/riscv/cache.c; + riscv64 = kern/riscv/cache_flush.S; + riscv64 = kern/riscv/dl.c; + + emu = disk/host.c; + emu = kern/emu/cache_s.S; + emu = kern/emu/hostdisk.c; + emu = osdep/unix/hostdisk.c; + emu = osdep/exec.c; + extra_dist = osdep/unix/exec.c; + emu = osdep/devmapper/hostdisk.c; + emu = osdep/hostdisk.c; + emu = kern/emu/hostfs.c; + emu = kern/emu/main.c; + emu = kern/emu/argp_common.c; + emu = kern/emu/misc.c; + emu = kern/emu/mm.c; + emu = kern/emu/time.c; + emu = kern/emu/cache.c; + emu = osdep/emuconsole.c; + extra_dist = osdep/unix/emuconsole.c; + extra_dist = osdep/windows/emuconsole.c; + emu = osdep/dl.c; + extra_dist = osdep/unix/dl.c; + extra_dist = osdep/windows/dl.c; + emu = osdep/sleep.c; + emu = osdep/init.c; + emu = osdep/emunet.c; + extra_dist = osdep/linux/emunet.c; + extra_dist = osdep/basic/emunet.c; + emu = osdep/cputime.c; + extra_dist = osdep/unix/cputime.c; + extra_dist = osdep/windows/cputime.c; + + videoinkernel = term/gfxterm.c; + videoinkernel = font/font.c; + videoinkernel = font/font_cmd.c; + videoinkernel = io/bufio.c; + videoinkernel = video/fb/fbblit.c; + videoinkernel = video/fb/fbfill.c; + videoinkernel = video/fb/fbutil.c; + videoinkernel = video/fb/video_fb.c; + videoinkernel = video/video.c; + + extra_dist = kern/i386/int.S; + extra_dist = kern/i386/realmode.S; + extra_dist = boot/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.exec$(EXEEXT)'; + ldadd = '$(MODULE_FILES)'; + ldadd = 'lib/gnulib/libgnu.a $(LIBINTL) $(LIBUTIL) $(LIBSDL) $(SDL2_LIBS) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + + enable = emu; +}; + +program = { + name = grub-emu-lite; + + emu = kern/emu/lite.c; + emu_nodist = symlist.c; + + ldadd = 'kernel.exec$(EXEEXT)'; + ldadd = 'lib/gnulib/libgnu.a $(LIBINTL) $(LIBUTIL) $(LIBSDL) $(SDL2_LIBS) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; + + 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)'; + + /* The entry point for a.out binaries on sparc64 starts + at 0x4000. Since we are writing the 32 bytes long a.out + header in the assembly code ourselves, we need to tell + the linker to adjust the start of the text segment to + 0x4000 - 0x20 = 0x3fe0. + */ + sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x3fe0'; + sparc64_ieee1275_objcopyflags = '-O binary'; + + objcopyflags = '-O binary'; + enable = i386_pc; + enable = i386_qemu; + enable = sparc64_ieee1275; +}; + +image = { + name = boot_hybrid; + i386_pc = boot/i386/pc/boot.S; + + cppflags = '-DHYBRID_BOOT=1'; + + i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00'; + + objcopyflags = '-O binary'; + enable = i386_pc; +}; + +image = { + name = cdboot; + + i386_pc = boot/i386/pc/cdboot.S; + i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)'; + i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00'; + + sparc64_ieee1275 = boot/sparc64/ieee1275/boot.S; + + /* See comment for sparc64_ieee1275_ldflags above. */ + sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x3fe0'; + sparc64_ieee1275_objcopyflags = '-O binary'; + sparc64_ieee1275_cppflags = '-DCDBOOT=1'; + + objcopyflags = '-O binary'; + + enable = sparc64_ieee1275; + 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 = xz_decompress; + mips_head = boot/mips/startup_raw.S; + common = boot/decompressor/minilib.c; + common = boot/decompressor/xz.c; + common = lib/xzembed/xz_dec_bcj.c; + common = lib/xzembed/xz_dec_lzma2.c; + common = lib/xzembed/xz_dec_stream.c; + common = kern/compiler-rt.c; + + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -DGRUB_EMBED_DECOMPRESSOR=1'; + + objcopyflags = '-O binary'; + mips_ldflags = '-Wl,-Ttext,$(TARGET_DECOMPRESSOR_LINK_ADDR)'; + cflags = '-Wno-unreachable-code'; + enable = mips; +}; + +image = { + name = none_decompress; + mips_head = boot/mips/startup_raw.S; + common = boot/decompressor/none.c; + + cppflags = '-DGRUB_EMBED_DECOMPRESSOR=1'; + + objcopyflags = '-O binary'; + mips_ldflags = '-Wl,-Ttext,$(TARGET_DECOMPRESSOR_LINK_ADDR)'; + enable = mips; +}; + +image = { + name = lzma_decompress; + i386_pc = boot/i386/pc/startup_raw.S; + i386_pc_nodist = rs_decoder.h; + + objcopyflags = '-O binary'; + ldflags = '$(TARGET_IMG_LDFLAGS) $(TARGET_IMG_BASE_LDOPT),0x8200'; + enable = i386_pc; +}; + +image = { + name = fwstart; + mips_loongson = boot/mips/loongson/fwstart.S; + objcopyflags = '-O binary'; + ldflags = '-Wl,-N,-S,-Ttext,0xbfc00000,-Bstatic'; + enable = mips_loongson; +}; + +image = { + name = fwstart_fuloong2f; + mips_loongson = boot/mips/loongson/fuloong2f.S; + objcopyflags = '-O binary'; + ldflags = '-Wl,-N,-S,-Ttext,0xbfc00000,-Bstatic'; + enable = mips_loongson; +}; + +module = { + name = disk; + common = lib/disk.c; + extra_dist = kern/disk_common.c; +}; + +module = { + name = trig; + common_nodist = trigtables.c; + extra_dist = gentrigtables.c; +}; + +module = { + name = cs5536; + x86 = bus/cs5536.c; + enable = x86; +}; + +module = { + name = lsspd; + mips_loongson = commands/mips/loongson/lsspd.c; + enable = mips_loongson; +}; + +module = { + name = usb; + common = bus/usb/usb.c; + common = bus/usb/usbtrans.c; + common = bus/usb/usbhub.c; + enable = usb; +}; + +module = { + name = usbserial_common; + common = bus/usb/serial/common.c; + enable = usb; +}; + +module = { + name = usbserial_pl2303; + common = bus/usb/serial/pl2303.c; + enable = usb; +}; + +module = { + name = usbserial_ftdi; + common = bus/usb/serial/ftdi.c; + enable = usb; +}; + +module = { + name = usbserial_usbdebug; + common = bus/usb/serial/usbdebug_late.c; + enable = usb; +}; + +module = { + name = uhci; + common = bus/usb/uhci.c; + enable = pci; +}; + +module = { + name = ohci; + common = bus/usb/ohci.c; + enable = pci; +}; + +module = { + name = ehci; + common = bus/usb/ehci.c; + arm_coreboot = bus/usb/ehci-fdt.c; + pci = bus/usb/ehci-pci.c; + enable = pci; + enable = arm_coreboot; +}; + +module = { + name = pci; + common = bus/pci.c; + i386_ieee1275 = bus/i386/ieee1275/pci.c; + + enable = i386_pc; + enable = i386_ieee1275; + enable = i386_coreboot; + enable = i386_multiboot; +}; + +module = { + name = nativedisk; + common = commands/nativedisk.c; + + enable = x86; + enable = mips_loongson; + enable = mips_qemu_mips; +}; + +module = { + name = emupci; + common = bus/emu/pci.c; + common = commands/lspci.c; + + enable = emu; + condition = COND_GRUB_EMU_PCI; +}; + +module = { + name = lsdev; + common = commands/arc/lsdev.c; + + enable = mips_arc; +}; + +module = { + name = lsxen; + common = commands/xen/lsxen.c; + + enable = xen; +}; + +module = { + name = cmostest; + common = commands/i386/cmostest.c; + enable = cmos; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = cmosdump; + common = commands/i386/cmosdump.c; + enable = cmos; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = iorw; + common = commands/iorw.c; + enable = x86; +}; + +module = { + name = cbtable; + common = kern/i386/coreboot/cbtable.c; + common = kern/coreboot/cbtable.c; + enable = i386_pc; + enable = i386_efi; + enable = i386_qemu; + enable = i386_multiboot; + enable = i386_ieee1275; + enable = x86_64_efi; +}; + +module = { + name = cbtime; + common = commands/i386/coreboot/cb_timestamps.c; + enable = x86; +}; + +module = { + name = cbls; + common = commands/i386/coreboot/cbls.c; + enable = x86; +}; + +module = { + name = cbmemc; + common = term/i386/coreboot/cbmemc.c; + enable = x86; +}; + +module = { + name = regexp; + common = commands/regexp.c; + common = commands/wildcard.c; + common = lib/gnulib/malloc/dynarray_finalize.c; + common = lib/gnulib/malloc/dynarray_emplace_enlarge.c; + common = lib/gnulib/malloc/dynarray_resize.c; + common = lib/gnulib/regex.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; +}; + +module = { + name = acpi; + + common = commands/acpi.c; + i386_pc = kern/acpi.c; + i386_pc = kern/i386/pc/acpi.c; + + enable = efi; + enable = i386_pc; + enable = i386_coreboot; + enable = i386_multiboot; +}; + +module = { + name = lsacpi; + + common = commands/lsacpi.c; + + enable = efi; + enable = i386_pc; + enable = i386_coreboot; + enable = i386_multiboot; +}; + +module = { + name = lsefisystab; + + common = commands/efi/lsefisystab.c; + + enable = efi; +}; + +module = { + name = lssal; + + common = commands/efi/lssal.c; + + enable = efi; +}; + +module = { + name = lsefimmap; + + common = commands/efi/lsefimmap.c; + + enable = efi; +}; + +module = { + name = lsefi; + common = commands/efi/lsefi.c; + enable = efi; +}; + +module = { + name = efifwsetup; + efi = commands/efi/efifwsetup.c; + enable = efi; +}; + +module = { + name = efitextmode; + efi = commands/efi/efitextmode.c; + enable = efi; +}; + +module = { + name = blocklist; + common = commands/blocklist.c; +}; + +module = { + name = boot; + common = commands/boot.c; + i386_pc = lib/i386/pc/biosnum.c; + enable = x86; + enable = emu; + enable = sparc64_ieee1275; + enable = powerpc_ieee1275; + enable = mips_arc; + enable = ia64_efi; + enable = arm_efi; + enable = arm64_efi; + enable = arm_uboot; + enable = arm_coreboot; + enable = loongarch64_efi; + enable = riscv32_efi; + enable = riscv64_efi; +}; + +module = { + name = cat; + common = commands/cat.c; +}; + +module = { + name = cmp; + common = commands/cmp.c; +}; + +module = { + name = configfile; + common = commands/configfile.c; +}; + +module = { + name = cpuid; + common = commands/i386/cpuid.c; + enable = x86; + enable = i386_xen_pvh; + enable = i386_xen; + enable = x86_64_xen; +}; + +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 = eval; + common = commands/eval.c; +}; + +module = { + name = extcmd; + common = commands/extcmd.c; + common = lib/arg.c; + enable = terminfomodule; +}; + +module = { + name = fixvideo; + common = commands/efi/fixvideo.c; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = gptsync; + common = commands/gptsync.c; +}; + +module = { + name = halt; + nopc = commands/halt.c; + i386_pc = commands/i386/pc/halt.c; + i386_pc = commands/acpihalt.c; + i386_coreboot = commands/acpihalt.c; + i386_multiboot = commands/acpihalt.c; + i386_efi = commands/acpihalt.c; + x86_64_efi = commands/acpihalt.c; + i386_multiboot = lib/i386/halt.c; + i386_coreboot = lib/i386/halt.c; + i386_qemu = lib/i386/halt.c; + xen = lib/xen/halt.c; + i386_xen_pvh = lib/xen/halt.c; + efi = lib/efi/halt.c; + ieee1275 = lib/ieee1275/halt.c; + emu = lib/emu/halt.c; + uboot = lib/dummy/halt.c; + arm_coreboot = lib/dummy/halt.c; +}; + +module = { + name = reboot; + i386 = lib/i386/reboot.c; + i386 = lib/i386/reboot_trampoline.S; + powerpc_ieee1275 = lib/ieee1275/reboot.c; + sparc64_ieee1275 = lib/ieee1275/reboot.c; + mips_arc = lib/mips/arc/reboot.c; + mips_loongson = lib/mips/loongson/reboot.c; + mips_qemu_mips = lib/mips/qemu_mips/reboot.c; + xen = lib/xen/reboot.c; + i386_xen_pvh = lib/xen/reboot.c; + uboot = lib/uboot/reboot.c; + arm_coreboot = lib/dummy/reboot.c; + common = commands/reboot.c; +}; + +module = { + name = hashsum; + common = commands/hashsum.c; +}; + +module = { + name = pgp; + common = commands/pgp.c; + cflags = '$(CFLAGS_POSIX)'; + cppflags = '-I$(srcdir)/lib/posix_wrap'; +}; + +module = { + name = hdparm; + common = commands/hdparm.c; + enable = pci; + enable = mips_qemu_mips; +}; + +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; + common = commands/efi/loadbios.c; + enable = i386_efi; + enable = x86_64_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 = pci; +}; + +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 = spkmodem; + x86 = term/spkmodem.c; + enable = x86; +}; + +module = { + name = morse; + x86 = term/morse.c; + enable = x86; +}; + +module = { + name = probe; + common = commands/probe.c; +}; + +module = { + name = read; + common = commands/read.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 = pci; +}; + +module = { + name = pcidump; + common = commands/pcidump.c; + enable = pci; +}; + +module = { + name = sleep; + common = commands/sleep.c; +}; + +module = { + name = smbios; + + common = commands/smbios.c; + efi = commands/efi/smbios.c; + i386_pc = commands/i386/pc/smbios.c; + i386_coreboot = commands/i386/pc/smbios.c; + i386_multiboot = commands/i386/pc/smbios.c; + + enable = efi; + enable = i386_pc; + enable = i386_coreboot; + enable = i386_multiboot; +}; + +module = { + name = suspend; + ieee1275 = commands/ieee1275/suspend.c; + enable = i386_ieee1275; + enable = powerpc_ieee1275; +}; + +module = { + name = escc; + ieee1275 = term/ieee1275/escc.c; + enable = powerpc_ieee1275; +}; + +module = { + name = tpm; + common = commands/tpm.c; + ieee1275 = commands/ieee1275/ibmvtpm.c; + 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 = usb; +}; + +module = { + name = videoinfo; + common = commands/videoinfo.c; +}; + +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 = cryptodisk; + common = disk/cryptodisk.c; +}; + +module = { + name = plainmount; + common = disk/plainmount.c; +}; + +module = { + name = json; + common = lib/json/json.c; +}; + +module = { + name = afsplitter; + common = disk/AFSplitter.c; +}; + +module = { + name = luks; + common = disk/luks.c; +}; + +module = { + name = luks2; + common = disk/luks2.c; + common = lib/gnulib/base64.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/json'; +}; + +module = { + name = geli; + common = disk/geli.c; +}; + +module = { + name = lvm; + common = disk/lvm.c; +}; + +module = { + name = ldm; + common = disk/ldm.c; +}; + +module = { + name = mdraid09; + common = disk/mdraid_linux.c; +}; + +module = { + name = mdraid09_be; + common = disk/mdraid_linux_be.c; +}; + +module = { + name = mdraid1x; + common = disk/mdraid1x_linux.c; +}; + +module = { + name = diskfilter; + common = disk/diskfilter.c; +}; + +module = { + name = raid5rec; + common = disk/raid5_recover.c; +}; + +module = { + name = raid6rec; + common = disk/raid6_recover.c; +}; + +module = { + name = key_protector; + common = disk/key_protector.c; +}; + +module = { + name = scsi; + common = disk/scsi.c; +}; + +module = { + name = memdisk; + common = disk/memdisk.c; +}; + +module = { + name = ata; + common = disk/ata.c; + enable = pci; + enable = mips_qemu_mips; +}; + +module = { + name = ahci; + common = disk/ahci.c; + enable = pci; +}; + +module = { + name = pata; + common = disk/pata.c; + enable = pci; + enable = mips_qemu_mips; +}; + +module = { + name = biosdisk; + i386_pc = disk/i386/pc/biosdisk.c; + enable = i386_pc; +}; + +module = { + name = usbms; + common = disk/usbms.c; + enable = 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 = videomodules; +}; + +module = { + name = procfs; + common = fs/proc.c; +}; + +module = { + name = affs; + common = fs/affs.c; +}; + +module = { + name = afs; + common = fs/afs.c; +}; + +module = { + name = bfs; + common = fs/bfs.c; +}; + +module = { + name = zstd; + common = lib/zstd/debug.c; + common = lib/zstd/entropy_common.c; + common = lib/zstd/error_private.c; + common = lib/zstd/fse_decompress.c; + common = lib/zstd/huf_decompress.c; + common = lib/zstd/module.c; + common = lib/zstd/xxhash.c; + common = lib/zstd/zstd_common.c; + common = lib/zstd/zstd_decompress.c; + cflags = '$(CFLAGS_POSIX) -Wno-undef'; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd'; +}; + +module = { + name = btrfs; + common = fs/btrfs.c; + common = lib/crc.c; + cflags = '$(CFLAGS_POSIX) -Wno-undef'; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -I$(srcdir)/lib/zstd -DMINILZO_HAVE_CONFIG_H'; +}; + +module = { + name = archelp; + common = fs/archelp.c; +}; + +module = { + name = cbfs; + common = fs/cbfs.c; +}; + +module = { + name = cpio; + common = fs/cpio.c; +}; + +module = { + name = cpio_be; + common = fs/cpio_be.c; +}; + +module = { + name = newc; + common = fs/newc.c; +}; + +module = { + name = odc; + common = fs/odc.c; +}; + +module = { + name = erofs; + common = fs/erofs.c; +}; + +module = { + name = ext2; + common = fs/ext2.c; +}; + +module = { + name = fat; + common = fs/fat.c; +}; + +module = { + name = exfat; + common = fs/exfat.c; +}; + +module = { + name = f2fs; + common = fs/f2fs.c; +}; + +module = { + name = fshelp; + common = fs/fshelp.c; +}; + +module = { + name = hfs; + common = fs/hfs.c; +}; + +module = { + name = hfsplus; + common = fs/hfsplus.c; +}; + +module = { + name = hfspluscomp; + common = fs/hfspluscomp.c; +}; + +module = { + name = iso9660; + common = fs/iso9660.c; +}; + +module = { + name = jfs; + common = fs/jfs.c; +}; + +module = { + name = minix; + common = fs/minix.c; +}; + +module = { + name = minix2; + common = fs/minix2.c; +}; + +module = { + name = minix3; + common = fs/minix3.c; +}; + +module = { + name = minix_be; + common = fs/minix_be.c; +}; + +module = { + name = minix2_be; + common = fs/minix2_be.c; +}; + +module = { + name = minix3_be; + common = fs/minix3_be.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 = romfs; + common = fs/romfs.c; +}; + +module = { + name = sfs; + common = fs/sfs.c; +}; + +module = { + name = squash4; + common = fs/squash4.c; + cflags = '$(CFLAGS_POSIX) -Wno-undef'; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H'; +}; + +module = { + name = tar; + common = fs/tar.c; +}; + +module = { + name = udf; + common = fs/udf.c; +}; + +module = { + name = ufs1; + common = fs/ufs.c; +}; + +module = { + name = ufs1_be; + common = fs/ufs_be.c; +}; + +module = { + name = ufs2; + common = fs/ufs2.c; +}; + +module = { + name = xfs; + common = fs/xfs.c; +}; + +module = { + name = zfs; + common = fs/zfs/zfs.c; + common = fs/zfs/zfs_lzjb.c; + common = fs/zfs/zfs_lz4.c; + common = fs/zfs/zfs_sha256.c; + common = fs/zfs/zfs_fletcher.c; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd'; +}; + +module = { + name = zfscrypt; + common = fs/zfs/zfscrypt.c; +}; + +module = { + name = zfsinfo; + common = fs/zfs/zfsinfo.c; +}; + +module = { + name = macbless; + common = commands/macbless.c; +}; + +module = { + name = pxe; + i386_pc = net/drivers/i386/pc/pxe.c; + enable = i386_pc; +}; + +module = { + name = gettext; + common = gettext/gettext.c; +}; + +module = { + name = gfxmenu; + common = gfxmenu/gfxmenu.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; +}; + +module = { + name = hello; + common = hello/hello.c; +}; + +module = { + name = gzio; + common = io/gzio.c; +}; + +module = { + name = offsetio; + common = io/offset.c; +}; + +module = { + name = bufio; + common = io/bufio.c; + enable = videomodules; +}; + +module = { + name = elf; + common = kern/elf.c; + + extra_dist = kern/elfXX.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_xen_pvh = lib/i386/relocator16.S; + i386_xen_pvh = lib/i386/relocator32.S; + i386_xen_pvh = lib/i386/relocator64.S; + i386 = lib/i386/relocator_asm.S; + i386_xen_pvh = lib/i386/relocator_asm.S; + x86_64 = lib/x86_64/relocator_asm.S; + i386_xen = lib/i386/relocator_asm.S; + x86_64_xen = lib/x86_64/relocator_asm.S; + x86 = lib/i386/relocator.c; + x86 = lib/i386/relocator_common_c.c; + i386_xen_pvh = lib/i386/relocator.c; + i386_xen_pvh = lib/i386/relocator_common_c.c; + ieee1275 = lib/ieee1275/relocator.c; + 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; + xen = lib/xen/relocator.c; + i386_xen = lib/i386/xen/relocator.S; + x86_64_xen = lib/x86_64/xen/relocator.S; + xen = lib/i386/relocator_common_c.c; + x86_64_efi = lib/x86_64/efi/relocator.c; + + extra_dist = lib/i386/relocator_common.S; + extra_dist = kern/powerpc/cache_flush.S; + + enable = mips; + enable = powerpc; + enable = x86; + enable = i386_xen_pvh; + enable = xen; +}; + +module = { + name = datetime; + common = lib/datetime.c; + cmos = lib/cmos_datetime.c; + efi = lib/efi/datetime.c; + uboot = lib/dummy/datetime.c; + arm_coreboot = lib/dummy/datetime.c; + sparc64_ieee1275 = lib/ieee1275/datetime.c; + powerpc_ieee1275 = lib/ieee1275/datetime.c; + sparc64_ieee1275 = lib/ieee1275/cmos.c; + powerpc_ieee1275 = lib/ieee1275/cmos.c; + xen = lib/xen/datetime.c; + i386_xen_pvh = lib/xen/datetime.c; + + mips_arc = lib/arc/datetime.c; +}; + +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; + extra_dist = lib/ia64/setjmp.S; + extra_dist = lib/ia64/longjmp.S; + extra_dist = lib/arm/setjmp.S; + extra_dist = lib/arm64/setjmp.S; + extra_dist = lib/riscv/setjmp.S; + extra_dist = lib/loongarch64/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 = plan9; + i386_pc = loader/i386/pc/plan9.c; + enable = i386_pc; +}; + + +module = { + name = linux16; + common = loader/i386/pc/linux.c; + enable = x86; +}; + +module = { + name = ntldr; + i386_pc = loader/i386/pc/ntldr.c; + enable = i386_pc; +}; + + +module = { + name = truecrypt; + i386_pc = loader/i386/pc/truecrypt.c; + enable = i386_pc; +}; + + +module = { + name = freedos; + i386_pc = loader/i386/pc/freedos.c; + enable = i386_pc; +}; + +module = { + name = pxechain; + i386_pc = loader/i386/pc/pxechainloader.c; + enable = i386_pc; +}; + +module = { + name = multiboot2; + cppflags = "-DGRUB_USE_MULTIBOOT2"; + + common = loader/multiboot.c; + common = loader/multiboot_mbi2.c; + enable = x86; + enable = i386_xen_pvh; + enable = mips; +}; + +module = { + name = multiboot; + common = loader/multiboot.c; + x86 = loader/i386/multiboot_mbi.c; + i386_xen_pvh = loader/i386/multiboot_mbi.c; + extra_dist = loader/multiboot_elfxx.c; + enable = x86; + enable = i386_xen_pvh; +}; + +module = { + name = xen_boot; + arm64 = loader/arm64/xen_boot.c; + enable = arm64; +}; + +module = { + name = linux; + x86 = loader/i386/linux.c; + i386_xen_pvh = loader/i386/linux.c; + xen = loader/i386/xen.c; + i386_pc = lib/i386/pc/vesa_modes_table.c; + i386_xen_pvh = lib/i386/pc/vesa_modes_table.c; + mips = loader/mips/linux.c; + powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c; + sparc64_ieee1275 = loader/sparc64/ieee1275/linux.c; + ia64_efi = loader/ia64/efi/linux.c; + arm_coreboot = loader/arm/linux.c; + arm_efi = loader/efi/linux.c; + arm_uboot = loader/arm/linux.c; + arm64 = loader/efi/linux.c; + loongarch64 = loader/efi/linux.c; + riscv32 = loader/efi/linux.c; + riscv64 = loader/efi/linux.c; + i386_efi = loader/efi/linux.c; + x86_64_efi = loader/efi/linux.c; + emu = loader/emu/linux.c; + common = loader/linux.c; + common = lib/cmdline.c; +}; + +module = { + name = fdt; + efi = loader/efi/fdt.c; + common = lib/fdt.c; + enable = fdt; +}; + +module = { + name = xnu; + x86 = loader/xnu_resume.c; + x86 = loader/i386/xnu.c; + x86 = loader/xnu.c; + + /* Code is pretty generic but relies on RNG which + is available only on few platforms. It's not a + big deal as xnu needs ACPI anyway and we have + RNG on all platforms with ACPI. + */ + enable = i386_multiboot; + enable = i386_coreboot; + enable = i386_pc; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = random; + x86 = lib/i386/random.c; + common = lib/random.c; + + i386_multiboot = kern/i386/tsc_pmtimer.c; + i386_coreboot = kern/i386/tsc_pmtimer.c; + i386_pc = kern/i386/tsc_pmtimer.c; + + enable = i386_multiboot; + enable = i386_coreboot; + enable = i386_pc; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = macho; + + common = loader/macho.c; + common = loader/macho32.c; + common = loader/macho64.c; + common = loader/lzss.c; + extra_dist = loader/machoXX.c; +}; + +module = { + name = appleldr; + common = loader/efi/appleloader.c; + enable = i386_efi; + enable = x86_64_efi; +}; + +module = { + name = chain; + efi = loader/efi/chainloader.c; + i386_pc = loader/i386/pc/chainloader.c; + i386_coreboot = loader/i386/coreboot/chainloader.c; + i386_coreboot = lib/LzmaDec.c; + enable = i386_pc; + enable = i386_coreboot; + enable = efi; +}; + +module = { + name = mmap; + common = mmap/mmap.c; + x86 = mmap/i386/uppermem.c; + x86 = mmap/i386/mmap.c; + i386_xen_pvh = mmap/i386/uppermem.c; + i386_xen_pvh = mmap/i386/mmap.c; + + i386_pc = mmap/i386/pc/mmap.c; + i386_pc = mmap/i386/pc/mmap_helper.S; + + efi = mmap/efi/mmap.c; + + mips = mmap/mips/uppermem.c; + + enable = x86; + enable = i386_xen_pvh; + enable = ia64_efi; + enable = arm_efi; + enable = arm64_efi; + enable = loongarch64_efi; + enable = riscv32_efi; + enable = riscv64_efi; + enable = mips; +}; + +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/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 = lib/getline.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 = commands/menuentry.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-redundant-decls -Wno-unused-but-set-variable'; + 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_plan; + common = partmap/plan.c; +}; + +module = { + name = part_dvh; + common = partmap/dvh.c; +}; + +module = { + name = part_bsd; + common = partmap/bsdlabel.c; +}; + +module = { + name = part_sunpc; + common = partmap/sunpc.c; +}; + +module = { + name = part_dfly; + common = partmap/dfly.c; +}; + +module = { + name = msdospart; + common = parttool/msdospart.c; +}; + +module = { + name = at_keyboard; + common = term/at_keyboard.c; + common = term/ps2.c; + enable = x86; +}; + +module = { + name = gfxterm; + common = term/gfxterm.c; + enable = videomodules; +}; + +module = { + name = gfxterm_background; + common = term/gfxterm_background.c; +}; + +module = { + name = serial; + common = term/serial.c; + x86 = term/ns8250.c; + x86 = term/ns8250-spcr.c; + ieee1275 = term/ieee1275/serial.c; + mips_arc = term/arc/serial.c; + efi = term/efi/serial.c; + x86 = term/pci/serial.c; + + enable = terminfomodule; + enable = ieee1275; + enable = mips_arc; +}; + +module = { + name = sendkey; + i386_pc = commands/i386/pc/sendkey.c; + enable = i386_pc; +}; + +module = { + name = terminfo; + common = term/terminfo.c; + common = term/tparm.c; + enable = terminfomodule; +}; + +module = { + name = usb_keyboard; + common = term/usb_keyboard.c; + enable = usb; +}; + +module = { + name = vga; + common = video/i386/pc/vga.c; + enable = i386_pc; +}; + +module = { + name = vga_text; + common = term/i386/pc/vga_text.c; + enable = i386_pc; +}; + +module = { + name = mda_text; + common = term/i386/pc/mda_text.c; + enable = i386_pc; + enable = i386_coreboot_multiboot_qemu; +}; + +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; + common = tests/checksums.h; + common = tests/video_checksum.c; + common = tests/fake_input.c; + common = video/capture.c; +}; + +module = { + name = exfctest; + common = tests/example_functional_test.c; +}; + +module = { + name = strtoull_test; + common = tests/strtoull_test.c; +}; + +module = { + name = setjmp_test; + common = tests/setjmp_test.c; +}; + +module = { + name = signature_test; + common = tests/signature_test.c; + common = tests/signatures.h; +}; + +module = { + name = sleep_test; + common = tests/sleep_test.c; +}; + +module = { + name = xnu_uuid_test; + common = tests/xnu_uuid_test.c; +}; + +module = { + name = pbkdf2_test; + common = tests/pbkdf2_test.c; +}; + +module = { + name = legacy_password_test; + common = tests/legacy_password_test.c; + enable = i386_pc; + enable = i386_xen_pvh; + enable = i386_efi; + enable = x86_64_efi; + enable = emu; + enable = xen; +}; + +module = { + name = div; + common = lib/division.c; + enable = no_softdiv; +}; + +module = { + name = div_test; + common = tests/div_test.c; +}; + +module = { + name = mul_test; + common = tests/mul_test.c; +}; + +module = { + name = shift_test; + common = tests/shift_test.c; +}; + +module = { + name = cmp_test; + common = tests/cmp_test.c; +}; + +module = { + name = ctz_test; + common = tests/ctz_test.c; +}; + +module = { + name = bswap_test; + common = tests/bswap_test.c; +}; + +module = { + name = videotest_checksum; + common = tests/videotest_checksum.c; +}; + +module = { + name = gfxterm_menu; + common = tests/gfxterm_menu.c; +}; + +module = { + name = cmdline_cat_test; + common = tests/cmdline_cat_test.c; +}; + +module = { + name = bitmap; + common = video/bitmap.c; +}; + +module = { + name = bitmap_scale; + common = video/bitmap_scale.c; +}; + +module = { + name = efi_gop; + efi = video/efi_gop.c; + enable = efi; +}; + +module = { + name = efi_uga; + efi = video/efi_uga.c; + enable = i386_efi; + enable = x86_64_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; + common = 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 = videomodules; +}; + +module = { + name = video; + common = video/video.c; + enable = videomodules; +}; + +module = { + name = video_colors; + common = video/colors.c; +}; + +module = { + name = ieee1275_fb; + ieee1275 = video/ieee1275.c; + enable = powerpc_ieee1275; +}; + +module = { + name = sdl; + emu = video/emu/sdl.c; + enable = emu; + condition = COND_GRUB_EMU_SDL; +}; + +module = { + name = sdl; + emu = video/emu/sdl.c; + enable = emu; + condition = COND_GRUB_EMU_SDL2; + cflags = '$(SDL2_CFLAGS)'; +}; + +module = { + name = datehook; + common = hook/datehook.c; +}; + +module = { + name = net; + common = net/net.c; + common = net/dns.c; + common = net/bootp.c; + common = net/ip.c; + common = net/udp.c; + common = net/tcp.c; + common = net/icmp.c; + common = net/icmp6.c; + common = net/ethernet.c; + common = net/arp.c; + common = net/netbuff.c; +}; + +module = { + name = tftp; + common = net/tftp.c; +}; + +module = { + name = http; + common = net/http.c; +}; + +module = { + name = ofnet; + common = net/drivers/ieee1275/ofnet.c; + enable = ieee1275; +}; + +module = { + name = ubootnet; + common = net/drivers/uboot/ubootnet.c; + enable = uboot; +}; + +module = { + name = efinet; + common = net/drivers/efi/efinet.c; + enable = efi; +}; + +module = { + name = emunet; + emu = net/drivers/emu/emunet.c; + enable = emu; +}; + +module = { + name = legacycfg; + common = commands/legacycfg.c; + common = lib/legacy_parse.c; + emu = lib/i386/pc/vesa_modes_table.c; + i386_efi = lib/i386/pc/vesa_modes_table.c; + x86_64_efi = lib/i386/pc/vesa_modes_table.c; + xen = lib/i386/pc/vesa_modes_table.c; + + enable = i386_pc; + enable = i386_xen_pvh; + enable = i386_efi; + enable = x86_64_efi; + enable = emu; + enable = xen; +}; + +module = { + name = syslinuxcfg; + common = lib/syslinux_parse.c; + common = commands/syslinuxcfg.c; +}; + +module = { + name = test_blockarg; + common = tests/test_blockarg.c; +}; + +module = { + name = xzio; + common = io/xzio.c; + common = lib/xzembed/xz_dec_bcj.c; + common = lib/xzembed/xz_dec_lzma2.c; + common = lib/xzembed/xz_dec_stream.c; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed'; + cflags='-Wno-unreachable-code'; +}; + +module = { + name = lzopio; + common = io/lzopio.c; + common = lib/minilzo/minilzo.c; + cflags = '$(CFLAGS_POSIX) -Wno-undef -Wno-redundant-decls -Wno-error'; + cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H'; +}; + +module = { + name = testload; + common = commands/testload.c; +}; + +module = { + name = backtrace; + x86 = lib/i386/backtrace.c; + i386_xen_pvh = lib/i386/backtrace.c; + i386_xen = lib/i386/backtrace.c; + x86_64_xen = lib/i386/backtrace.c; + common = lib/backtrace.c; + enable = x86; + enable = i386_xen_pvh; + enable = i386_xen; + enable = x86_64_xen; +}; + +module = { + name = lsapm; + common = commands/i386/pc/lsapm.c; + enable = i386_pc; +}; + +module = { + name = keylayouts; + common = commands/keylayouts.c; + enable = x86; +}; + +module = { + name = priority_queue; + common = lib/priority_queue.c; +}; + +module = { + name = time; + common = commands/time.c; +}; + +module = { + name = cacheinfo; + common = commands/cacheinfo.c; + condition = COND_ENABLE_CACHE_STATS; +}; + +module = { + name = boottime; + common = commands/boottime.c; + condition = COND_ENABLE_BOOT_TIME_STATS; +}; + +module = { + name = adler32; + common = lib/adler32.c; +}; + +module = { + name = crc64; + common = lib/crc64.c; +}; + +module = { + name = mpi; + common = lib/libgcrypt-grub/mpi/mpiutil.c; + common = lib/libgcrypt-grub/mpi/mpi-bit.c; + common = lib/libgcrypt-grub/mpi/mpi-add.c; + common = lib/libgcrypt-grub/mpi/mpi-mul.c; + common = lib/libgcrypt-grub/mpi/mpi-mod.c; + common = lib/libgcrypt-grub/mpi/mpi-gcd.c; + common = lib/libgcrypt-grub/mpi/mpi-div.c; + common = lib/libgcrypt-grub/mpi/mpi-cmp.c; + common = lib/libgcrypt-grub/mpi/mpi-inv.c; + common = lib/libgcrypt-grub/mpi/mpi-pow.c; + common = lib/libgcrypt-grub/mpi/mpi-mpow.c; + common = lib/libgcrypt-grub/mpi/mpih-lshift.c; + common = lib/libgcrypt-grub/mpi/mpih-mul.c; + common = lib/libgcrypt-grub/mpi/mpih-mul1.c; + common = lib/libgcrypt-grub/mpi/mpih-mul2.c; + common = lib/libgcrypt-grub/mpi/mpih-mul3.c; + common = lib/libgcrypt-grub/mpi/mpih-add1.c; + common = lib/libgcrypt-grub/mpi/mpih-sub1.c; + common = lib/libgcrypt-grub/mpi/mpih-div.c; + common = lib/libgcrypt-grub/mpi/mpicoder.c; + common = lib/libgcrypt-grub/mpi/mpih-rshift.c; + common = lib/libgcrypt-grub/mpi/mpi-inline.c; + common = lib/libgcrypt_wrap/mem.c; + + cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare'; + cppflags = '$(CPPFLAGS_GCRY)'; +}; + +module = { + name = all_video; + common = lib/fake_module.c; +}; + +module = { + name = gdb; + common = gdb/cstub.c; + common = gdb/gdb.c; + i386 = gdb/i386/idt.c; + i386 = gdb/i386/machdep.S; + i386 = gdb/i386/signal.c; + enable = i386; +}; + +module = { + name = testspeed; + common = commands/testspeed.c; +}; + +module = { + name = tpm; + common = commands/tpm.c; + efi = commands/efi/tpm.c; + enable = efi; +}; + +module = { + name = tss2; + common = lib/tss2/buffer.c; + common = lib/tss2/tss2_mu.c; + common = lib/tss2/tpm2_cmd.c; + common = lib/tss2/tss2.c; + efi = lib/efi/tcg2.c; + emu = lib/tss2/tcg2_emu.c; + powerpc_ieee1275 = lib/ieee1275/tcg2.c; + enable = efi; + enable = emu; + enable = powerpc_ieee1275; + cppflags = '-I$(srcdir)/lib/tss2'; +}; + +module = { + name = tpm2_key_protector; + common = commands/tpm2_key_protector/args.c; + common = commands/tpm2_key_protector/module.c; + common = commands/tpm2_key_protector/tpm2key.c; + common = commands/tpm2_key_protector/tpm2key_asn1_tab.c; + /* The plaform support of tpm2_key_protector depends on the tcg2 implementation in tss2. */ + enable = efi; + enable = emu; + enable = powerpc_ieee1275; + cppflags = '-I$(srcdir)/lib/tss2 -I$(srcdir)/lib/libtasn1-grub'; +}; + +module = { + name = tr; + common = commands/tr.c; +}; + +module = { + name = progress; + common = lib/progress.c; +}; + +module = { + name = file; + common = commands/file.c; + common = commands/file32.c; + common = commands/file64.c; + extra_dist = commands/fileXX.c; + common = loader/i386/xen_file.c; + common = loader/i386/xen_file32.c; + common = loader/i386/xen_file64.c; + extra_dist = loader/i386/xen_fileXX.c; +}; +module = { + name = rdmsr; + common = commands/i386/rdmsr.c; + enable = x86; +}; +module = { + name = wrmsr; + common = commands/i386/wrmsr.c; + enable = x86; +}; + +module = { + name = memtools; + common = commands/memtools.c; + condition = COND_MM_DEBUG; +}; + +module = { + name = bli; + efi = commands/bli.c; + enable = efi; + depends = part_gpt; +}; + +module = { + name = asn1; + common = lib/libtasn1-grub/lib/decoding.c; + common = lib/libtasn1-grub/lib/coding.c; + common = lib/libtasn1-grub/lib/element.c; + common = lib/libtasn1-grub/lib/structure.c; + common = lib/libtasn1-grub/lib/parser_aux.c; + common = lib/libtasn1-grub/lib/gstr.c; + common = lib/libtasn1-grub/lib/errors.c; + common = lib/libtasn1_wrap/wrap.c; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + /* -Wno-type-limits comes from configure.ac of libtasn1 */ + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/lib/libtasn1-grub/lib -Wno-type-limits'; +}; + +module = { + name = asn1_test; + common = tests/asn1/tests/CVE-2018-1000654.c; + common = tests/asn1/tests/object-id-decoding.c; + common = tests/asn1/tests/object-id-encoding.c; + common = tests/asn1/tests/octet-string.c; + common = tests/asn1/tests/reproducers.c; + common = tests/asn1/tests/Test_overflow.c; + common = tests/asn1/tests/Test_simple.c; + common = tests/asn1/tests/Test_strings.c; + common = tests/asn1/asn1_test.c; + cflags = '-Wno-uninitialized'; + cppflags = '-I$(srcdir)/lib/libtasn1-grub -I$(srcdir)/tests/asn1/'; +}; diff --git a/grub-core/boot/decompressor/minilib.c b/grub-core/boot/decompressor/minilib.c new file mode 100644 index 000000000..fc46ee07b --- /dev/null +++ b/grub-core/boot/decompressor/minilib.c @@ -0,0 +1,93 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include + +void * +grub_memset (void *s, int c, grub_size_t len) +{ + grub_uint8_t *ptr; + for (ptr = s; len; ptr++, len--) + *ptr = c; + return s; +} + +void * +grub_memmove (void *dest, const void *src, grub_size_t n) +{ + char *d = (char *) dest; + const char *s = (const char *) src; + + if (d < s) + while (n--) + *d++ = *s++; + else + { + d += n; + s += n; + + while (n--) + *--d = *--s; + } + + return dest; +} + +int +grub_memcmp (const void *s1, const void *s2, grub_size_t n) +{ + const unsigned char *t1 = s1; + const unsigned char *t2 = s2; + + while (n--) + { + if (*t1 != *t2) + return (int) *t1 - (int) *t2; + + t1++; + t2++; + } + + return 0; +} + +void *grub_decompressor_scratch; + +void +find_scratch (void *src, void *dst, unsigned long srcsize, + unsigned long dstsize) +{ +#ifdef _mips + /* Decoding from ROM. */ + if (((grub_addr_t) src & 0x10000000)) + { + grub_decompressor_scratch = (void *) ALIGN_UP((grub_addr_t) dst + dstsize, + 256); + return; + } +#endif + if ((char *) src + srcsize > (char *) dst + dstsize) + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) src + srcsize, + 256); + else + grub_decompressor_scratch = (void *) ALIGN_UP ((grub_addr_t) dst + dstsize, + 256); + return; +} diff --git a/grub-core/boot/decompressor/none.c b/grub-core/boot/decompressor/none.c new file mode 100644 index 000000000..911e861e3 --- /dev/null +++ b/grub-core/boot/decompressor/none.c @@ -0,0 +1,42 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include + +void +grub_decompress_core (void *src, void *dest, unsigned long n, + unsigned long dstsize __attribute__ ((unused))) +{ + char *d = (char *) dest; + const char *s = (const char *) src; + + if (d == s) + return; + + if (d < s) + while (n--) + *d++ = *s++; + else + { + d += n; + s += n; + + while (n--) + *--d = *--s; + } +} diff --git a/grub-core/boot/decompressor/xz.c b/grub-core/boot/decompressor/xz.c new file mode 100644 index 000000000..2279118e1 --- /dev/null +++ b/grub-core/boot/decompressor/xz.c @@ -0,0 +1,60 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include + +#include "xz.h" +#include "xz_stream.h" + +void +grub_decompress_core (void *src, void *dst, unsigned long srcsize, + unsigned long dstsize) +{ + struct xz_dec *dec; + struct xz_buf buf; + + find_scratch (src, dst, srcsize, dstsize); + + dec = xz_dec_init (GRUB_DECOMPRESSOR_DICT_SIZE); + + buf.in = src; + buf.in_pos = 0; + buf.in_size = srcsize; + buf.out = dst; + buf.out_pos = 0; + buf.out_size = dstsize; + + while (buf.in_pos != buf.in_size) + { + enum xz_ret xzret; + xzret = xz_dec_run (dec, &buf); + switch (xzret) + { + case XZ_MEMLIMIT_ERROR: + case XZ_FORMAT_ERROR: + case XZ_OPTIONS_ERROR: + case XZ_DATA_ERROR: + case XZ_BUF_ERROR: + return; + default: + break; + } + } +} diff --git a/boot/i386/pc/boot.S b/grub-core/boot/i386/pc/boot.S similarity index 81% rename from boot/i386/pc/boot.S rename to grub-core/boot/i386/pc/boot.S index 257f9044e..2bd0b2d28 100644 --- a/boot/i386/pc/boot.S +++ b/grub-core/boot/i386/pc/boot.S @@ -18,7 +18,6 @@ */ #include -#include #include /* @@ -27,6 +26,87 @@ /* Print message string */ #define MSG(x) movw $x, %si; call LOCAL(message) +#define ERR(x) movw $x, %si; jmp LOCAL(error_message) + + .macro floppy +part_start: + +LOCAL(probe_values): + .byte 36, 18, 15, 9, 0 + +LOCAL(floppy_probe): + pushw %dx +/* + * Perform floppy probe. + */ +#ifdef __APPLE__ + LOCAL(probe_values_minus_one) = LOCAL(probe_values) - 1 + movw MACRO_DOLLAR(LOCAL(probe_values_minus_one)), %si +#else + movw MACRO_DOLLAR(LOCAL(probe_values)) - 1, %si +#endif + +LOCAL(probe_loop): + /* reset floppy controller INT 13h AH=0 */ + xorw %ax, %ax + int MACRO_DOLLAR(0x13) + + incw %si + movb (%si), %cl + + /* if number of sectors is 0, display error and die */ + testb %cl, %cl + jnz 1f + +/* + * Floppy disk probe failure. + */ + MSG(fd_probe_error_string) + jmp LOCAL(general_error) + +/* "Floppy" */ +fd_probe_error_string: .asciz "Floppy" + +1: + /* perform read */ + movw MACRO_DOLLAR(GRUB_BOOT_MACHINE_BUFFER_SEG), %bx + movw %bx, %es + xorw %bx, %bx + movw MACRO_DOLLAR(0x201), %ax + movb MACRO_DOLLAR(0), %ch + movb MACRO_DOLLAR(0), %dh + int MACRO_DOLLAR(0x13) + + /* if error, jump to "LOCAL(probe_loop)" */ + jc LOCAL(probe_loop) + + /* %cl is already the correct value! */ + movb MACRO_DOLLAR(1), %dh + movb MACRO_DOLLAR(79), %ch + + jmp LOCAL(final_init) + .endm + + .macro scratch + + /* scratch space */ +mode: + .byte 0 +disk_address_packet: +sectors: + .long 0 +heads: + .long 0 +cylinders: + .word 0 +sector_start: + .byte 0 +head_start: + .byte 0 +cylinder_start: + .word 0 + /* more space... */ + .endm .file "boot.S" @@ -51,47 +131,66 @@ start: jmp LOCAL(after_BPB) nop /* do I care about this ??? */ +#ifdef HYBRID_BOOT + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + + nop + nop + nop + nop + nop + nop + nop + nop + + nop + nop + jmp LOCAL(after_BPB) +#else /* * This space is for the BIOS parameter block!!!! Don't change * the first jump, nor start the code anywhere but right after * this area. */ - . = _start + GRUB_BOOT_MACHINE_BPB_START - . = _start + 4 - - /* scratch space */ -mode: - .byte 0 -disk_address_packet: -sectors: - .long 0 -heads: - .long 0 -cylinders: - .word 0 -sector_start: - .byte 0 -head_start: - .byte 0 -cylinder_start: - .word 0 - /* more space... */ - - . = _start + GRUB_BOOT_MACHINE_BPB_END + .org GRUB_BOOT_MACHINE_BPB_START + .org 4 +#endif +#ifdef HYBRID_BOOT + floppy +#else + scratch +#endif + .org GRUB_BOOT_MACHINE_BPB_END /* * End of BIOS parameter block. */ -kernel_address: +LOCAL(kernel_address): .word GRUB_BOOT_MACHINE_KERNEL_ADDR - . = _start + GRUB_BOOT_MACHINE_KERNEL_SECTOR -kernel_sector: - .long 1, 0 +#ifndef HYBRID_BOOT + .org GRUB_BOOT_MACHINE_KERNEL_SECTOR +LOCAL(kernel_sector): + .long 1 +LOCAL(kernel_sector_high): + .long 0 +#endif - . = _start + GRUB_BOOT_MACHINE_BOOT_DRIVE + .org GRUB_BOOT_MACHINE_BOOT_DRIVE boot_drive: .byte 0xff /* the disk to load kernel from */ /* 0xff means use the boot drive */ @@ -109,14 +208,18 @@ LOCAL(after_BPB): * possible boot drive. If GRUB is installed into a floppy, * this does nothing (only jump). */ - . = _start + GRUB_BOOT_MACHINE_DRIVE_CHECK + .org GRUB_BOOT_MACHINE_DRIVE_CHECK boot_drive_check: - jmp 1f /* grub-setup may overwrite this jump */ + jmp 3f /* grub-setup may overwrite this jump */ testb $0x80, %dl - jnz 1f + jz 2f +3: + /* Ignore %dl different from 0-0x0f and 0x80-0x8f. */ + testb $0x70, %dl + jz 1f +2: movb $0x80, %dl 1: - /* * ljmp to the next instruction because some bogus BIOSes * jump to 07C0:0000 instead of 0000:7C00. @@ -152,10 +255,6 @@ real_start: /* set %si to the disk address packet */ movw $disk_address_packet, %si - /* do not probe LBA if the drive is a floppy */ - testb $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl - jz LOCAL(chs_mode) - /* check if LBA is supported */ movb $0x41, %ah movw $0x55aa, %bx @@ -176,7 +275,7 @@ real_start: andw $1, %cx jz LOCAL(chs_mode) -lba_mode: +LOCAL(lba_mode): xorw %ax, %ax movw %ax, 4(%si) @@ -191,9 +290,9 @@ lba_mode: movw $0x0010, (%si) /* the absolute address */ - movl kernel_sector, %ebx + movl LOCAL(kernel_sector), %ebx movl %ebx, 8(%si) - movl kernel_sector + 4, %ebx + movl LOCAL(kernel_sector_high), %ebx movl %ebx, 12(%si) /* the segment of buffer address */ @@ -226,14 +325,15 @@ LOCAL(chs_mode): int $0x13 jnc LOCAL(final_init) + popw %dx /* * The call failed, so maybe use the floppy probe instead. */ - testb $GRUB_BOOT_MACHINE_BIOS_HD_FLAG, %dl - jz LOCAL(floppy_probe) + testb %dl, %dl + jnb LOCAL(floppy_probe) /* Nope, we definitely have a hard disk, and we're screwed. */ - jmp LOCAL(hd_probe_error) + ERR(hd_probe_error_string) LOCAL(final_init): /* set the mode to zero */ @@ -261,13 +361,13 @@ LOCAL(final_init): setup_sectors: /* load logical sector start (top half) */ - movl kernel_sector + 4, %eax + movl LOCAL(kernel_sector_high), %eax orl %eax, %eax jnz LOCAL(geometry_error) /* load logical sector start (bottom half) */ - movl kernel_sector, %eax + movl LOCAL(kernel_sector), %eax /* zero %edx */ xorl %edx, %edx @@ -352,7 +452,7 @@ LOCAL(copy_buffer): popa /* boot kernel */ - jmp *(kernel_address) + jmp *(LOCAL(kernel_address)) /* END OF MAIN LOOP */ @@ -360,22 +460,15 @@ LOCAL(copy_buffer): * BIOS Geometry translation error (past the end of the disk geometry!). */ LOCAL(geometry_error): - MSG(geometry_error_string) - jmp LOCAL(general_error) - -/* - * Disk probe failure. - */ -LOCAL(hd_probe_error): - MSG(hd_probe_error_string) - jmp LOCAL(general_error) + ERR(geometry_error_string) /* * Read error on the disk. */ LOCAL(read_error): - MSG(read_error_string) - + movw $read_error_string, %si +LOCAL(error_message): + call LOCAL(message) LOCAL(general_error): MSG(general_error_string) @@ -417,7 +510,14 @@ LOCAL(message): * number here. */ - . = _start + GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC +#ifdef HYBRID_BOOT + .org 0x1b0 +LOCAL(kernel_sector): + .long 1 +LOCAL(kernel_sector_high): + .long 0 +#endif + .org GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC nt_magic: .long 0 .word 0 @@ -426,60 +526,17 @@ nt_magic: * This is where an MBR would go if on a hard disk. The code * here isn't even referenced unless we're on a floppy. Kinda * sneaky, huh? - */ + */ - . = _start + GRUB_BOOT_MACHINE_PART_START -part_start: + .org GRUB_BOOT_MACHINE_PART_START -probe_values: - .byte 36, 18, 15, 9, 0 - -LOCAL(floppy_probe): -/* - * Perform floppy probe. - */ - - movw $probe_values - 1, %si - -LOCAL(probe_loop): - /* reset floppy controller INT 13h AH=0 */ - xorw %ax, %ax - int $0x13 - - incw %si - movb (%si), %cl - - /* if number of sectors is 0, display error and die */ - cmpb $0, %cl - jne 1f - -/* - * Floppy disk probe failure. - */ - MSG(fd_probe_error_string) - jmp LOCAL(general_error) - -/* "Floppy" */ -fd_probe_error_string: .asciz "Floppy" - -1: - /* perform read */ - movw $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx - movw $0x201, %ax - movb $0, %ch - movb $0, %dh - int $0x13 - - /* if error, jump to "LOCAL(probe_loop)" */ - jc LOCAL(probe_loop) - - /* %cl is already the correct value! */ - movb $1, %dh - movb $79, %ch - - jmp LOCAL(final_init) - - . = _start + GRUB_BOOT_MACHINE_PART_END +#ifndef HYBRID_BOOT + floppy +#else + scratch +#endif + .org GRUB_BOOT_MACHINE_PART_END + /* the last 2 bytes in the sector 0 contain the signature */ .word GRUB_BOOT_MACHINE_SIGNATURE diff --git a/boot/i386/pc/cdboot.S b/grub-core/boot/i386/pc/cdboot.S similarity index 96% rename from boot/i386/pc/cdboot.S rename to grub-core/boot/i386/pc/cdboot.S index 33569ce9d..de4f80929 100644 --- a/boot/i386/pc/cdboot.S +++ b/grub-core/boot/i386/pc/cdboot.S @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -44,7 +43,7 @@ _start: LOCAL(next): jmp 1f - . = start + 8 + .org 8 bi_pvd: .long 0 /* LBA of primary volume descriptor. */ @@ -94,11 +93,12 @@ LOCAL(read_cdrom): pushw $CDBLK_LENG /* Block number. */ + incl %esi pushl %eax pushl %esi /* Buffer address. */ - pushw $((DATA_ADDR - 0x400)>> 4) + pushw $((DATA_ADDR - 0x200)>> 4) pushl %eax pushw $0x10 @@ -168,6 +168,6 @@ err_noboot_msg: err_cdfail_msg: .ascii "cdrom read fails\0" - . = start + 0x1FF + .org 0x7FF .byte 0 diff --git a/boot/i386/pc/diskboot.S b/grub-core/boot/i386/pc/diskboot.S similarity index 97% rename from boot/i386/pc/diskboot.S rename to grub-core/boot/i386/pc/diskboot.S index 92f223f0d..c1addc0df 100644 --- a/boot/i386/pc/diskboot.S +++ b/grub-core/boot/i386/pc/diskboot.S @@ -37,8 +37,8 @@ start: _start: /* - * _start is loaded at 0x2000 and is jumped to with - * CS:IP 0:0x2000 in kernel. + * _start is loaded at 0x8000 and is jumped to with + * CS:IP 0:0x8000 in kernel. */ /* @@ -56,7 +56,7 @@ _start: popw %si /* this sets up for the first run through "bootloop" */ - movw $(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE), %di + movw $LOCAL(firstlist), %di /* save the sector number of the second sector in %ebp */ movl (%di), %ebp @@ -362,8 +362,8 @@ LOCAL(message): .word 0 .word 0 - . = _start + 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE - + .org 0x200 - GRUB_BOOT_MACHINE_LIST_SIZE +LOCAL(firstlist): /* this label has to be before the first list entry!!! */ /* fill the first data listing with the default */ blocklist_default_start: /* this is the sector start parameter, in logical sectors from @@ -376,5 +376,3 @@ blocklist_default_len: blocklist_default_seg: /* this is the segment of the starting address to load the data into */ .word (GRUB_BOOT_MACHINE_KERNEL_SEG + 0x20) - -firstlist: /* this label has to be after the list data!!! */ diff --git a/boot/i386/pc/lnxboot.S b/grub-core/boot/i386/pc/lnxboot.S similarity index 89% rename from boot/i386/pc/lnxboot.S rename to grub-core/boot/i386/pc/lnxboot.S index b4f0030b8..2dda0e06b 100644 --- a/boot/i386/pc/lnxboot.S +++ b/grub-core/boot/i386/pc/lnxboot.S @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -42,7 +41,7 @@ data_start: xorl %ebp, %ebp jmp LOCAL(linux_next) - . = data_start + 0x1F1 + .org 0x1F1 setup_sects: .byte CODE_SECTORS @@ -178,15 +177,21 @@ real_code_2: pushw %es popw %ds - movl $0x200, %ecx - addl %ecx, %esi + movl $0x1000, %ecx + addl $0x200, %esi movl $DATA_ADDR, %edi call LOCAL(move_memory) /* Check for multiboot signature. */ - cmpl $MULTIBOOT_HEADER_MAGIC, %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_DATA_END) + movl $DATA_ADDR, %edi +3: + movl %ss:(%edi), %eax + cmpl $MULTIBOOT_HEADER_MAGIC, %eax jz 1f + addl $4, %edi + cmpl $(DATA_ADDR + 0x1000), %edi + jne 3b movl (ramdisk_image - start), %esi movl (ramdisk_size - start), %ecx @@ -195,16 +200,16 @@ real_code_2: 1: - movl %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_COMPRESSED_SIZE), %ecx - addl $(GRUB_KERNEL_MACHINE_RAW_SIZE - 0x200), %ecx + movl $(DATA_ADDR + 0x1000), %edi + movl %ss:(DATA_ADDR + GRUB_DECOMPRESSOR_MACHINE_COMPRESSED_SIZE), %ecx + addl $GRUB_DECOMPRESSOR_I386_PC_MAX_DECOMPRESSOR_SIZE, %ecx 2: call LOCAL(move_memory) - movsbl %dh, %eax - movl %eax, %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_INSTALL_DOS_PART) - movsbl (reg_edx + 2 - start), %eax - movl %eax, %ss:(DATA_ADDR + GRUB_KERNEL_MACHINE_INSTALL_BSD_PART) + movb %dh, %ss:(DATA_ADDR + GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE + 2) + movb (reg_edx + 2 - start), %dh + movb %dh, %ss:(DATA_ADDR + GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE + 1) movb $0xFF, %dh @@ -287,4 +292,4 @@ LOCAL(fail): err_int15_msg: .ascii "move memory fails\0" - . = _start + CODE_SECTORS * 512 + .org (CODE_SECTORS * 512 + 512) diff --git a/kern/i386/pc/lzma_decode.S b/grub-core/boot/i386/pc/lzma_decode.S similarity index 92% rename from kern/i386/pc/lzma_decode.S rename to grub-core/boot/i386/pc/lzma_decode.S index a5a86848a..88c668d5e 100644 --- a/kern/i386/pc/lzma_decode.S +++ b/grub-core/boot/i386/pc/lzma_decode.S @@ -77,69 +77,6 @@ #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) - -#if 0 - -DbgOut: - pushf - pushl %ebp - pushl %edi - pushl %esi - pushl %edx - pushl %ecx - pushl %ebx - pushl %eax - - call _DebugPrint - - popl %eax - popl %ebx - popl %ecx - popl %edx - popl %esi - popl %edi - popl %ebp - popf - - ret - - -/* - * int LzmaDecodeProperties(CLzmaProperties *propsRes, - * const unsigned char *propsData, - * int size); - */ - -_LzmaDecodePropertiesA: - movb (%edx), %dl - - xorl %ecx, %ecx -1: - cmpb $45, %dl - jb 2f - incl %ecx - subb $45, %dl - jmp 1b -2: - movl %ecx, 8(%eax) /* pb */ - xorl %ecx, %ecx -1: - cmpb $9, %dl - jb 2f - incl %ecx - subb $9, %dl -2: - movl %ecx, 4(%eax) /* lp */ - movb %dl, %cl - movl %ecx, (%eax) /* lc */ - -#endif - -#ifndef ASM_FILE - xorl %eax, %eax -#endif - ret - #define out_size 8(%ebp) #define now_pos -4(%ebp) diff --git a/boot/i386/pc/pxeboot.S b/grub-core/boot/i386/pc/pxeboot.S similarity index 96% rename from boot/i386/pc/pxeboot.S rename to grub-core/boot/i386/pc/pxeboot.S index 446bfc781..b695b24d0 100644 --- a/boot/i386/pc/pxeboot.S +++ b/grub-core/boot/i386/pc/pxeboot.S @@ -38,5 +38,5 @@ start: /* This region is a junk. Do you say that this is wasteful? But I like that the memory layout of the body is consistent among different kernels rather than scamping just for 1.5KB. */ - . = _start + 0x8200 - 0x7C00 - 0x200 - 1 + .org 0x8200 - 0x7C00 - 0x200 - 1 .byte 0 diff --git a/grub-core/boot/i386/pc/startup_raw.S b/grub-core/boot/i386/pc/startup_raw.S new file mode 100644 index 000000000..28974821e --- /dev/null +++ b/grub-core/boot/i386/pc/startup_raw.S @@ -0,0 +1,369 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009,2011 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 . + */ + +#include +#include +#include +#include +#include +#include + +#define ABS(x) ((x) - LOCAL (base) + GRUB_BOOT_MACHINE_KERNEL_ADDR + 0x200) + + .file "startup_raw.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 + + /* + * This is a special data area. + */ + + .org GRUB_DECOMPRESSOR_MACHINE_COMPRESSED_SIZE +LOCAL(compressed_size): + .long 0 + .org GRUB_DECOMPRESSOR_MACHINE_UNCOMPRESSED_SIZE +LOCAL(uncompressed_size): + .long 0 + + .org GRUB_KERNEL_I386_PC_REED_SOLOMON_REDUNDANCY +reed_solomon_redundancy: + .long 0 + .org GRUB_KERNEL_I386_PC_NO_REED_SOLOMON_LENGTH + .short (LOCAL(reed_solomon_part) - _start) + +/* + * This is the area for all of the special variables. + */ + .org GRUB_DECOMPRESSOR_I386_PC_BOOT_DEVICE +LOCAL(boot_dev): + .byte 0xFF, 0xFF, 0xFF +LOCAL(boot_drive): + .byte 0x00 + +/* 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 */ + movb %dl, LOCAL(boot_drive) + + /* reset disk system (%ah = 0) */ + int $0x13 + + /* transition to protected mode */ + calll real_to_prot + + /* The ".code32" directive takes GAS out of 16-bit mode. */ + .code32 + + cld + call grub_gate_a20 + + movl LOCAL(compressed_size), %edx +#ifdef __APPLE__ + addl $decompressor_end, %edx + subl $(LOCAL(reed_solomon_part)), %edx +#else + addl $(LOCAL(decompressor_end) - LOCAL(reed_solomon_part)), %edx +#endif + movl reed_solomon_redundancy, %ecx + leal LOCAL(reed_solomon_part), %eax + cld + call EXT_C (grub_reed_solomon_recover) + jmp post_reed_solomon + +#include "../../../kern/i386/realmode.S" + +/* + * + * This is a workaround for clang adding a section containing only .addrsig + * Since clang itself is unable to assemble this pseudo-opcode, just replace + * it with .text + * + */ +#define addrsig text +#include +#undef addrsig + + .text + +/* + * grub_gate_a20(void) + * + * 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. :-( + */ + +grub_gate_a20: +gate_a20_test_current_state: + /* first of all, test if already in a good state */ + call gate_a20_check_state + testb %al, %al + jnz gate_a20_try_bios + ret + +gate_a20_try_bios: + /* second, try a BIOS call */ + pushl %ebp + call prot_to_real + + .code16 + movw $0x2401, %ax + int $0x15 + + calll real_to_prot + .code32 + + popl %ebp + call gate_a20_check_state + testb %al, %al + 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 + orb $0x02, %al + outb $0x92 + + call gate_a20_check_state + testb %al, %al + jnz gate_a20_try_keyboard_controller + ret + +gate_a20_flush_keyboard_buffer: + inb $0x64 + andb $0x02, %al + jnz gate_a20_flush_keyboard_buffer +2: + inb $0x64 + andb $0x01, %al + jz 3f + inb $0x60 + jmp 2b +3: + ret + +gate_a20_try_keyboard_controller: + /* third, try the keyboard controller */ + call gate_a20_flush_keyboard_buffer + + movb $0xd1, %al + outb $0x64 +4: + inb $0x64 + andb $0x02, %al + jnz 4b + + movb $0xdf, %al + 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 + testb %al, %al + /* 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 + testb %al, %al + 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 0 if A20 is on or 1 if it is off */ + subb %ch, %al + /* restore the original */ + popl %ebx + movb %cl, (%ebx) + popl %ecx + popl %ebx + ret + +LOCAL(reed_solomon_part): + +/* + * 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 */ +#ifdef __APPLE__ + LOCAL(compressed_size_offset) = LOCAL(compressed_size) - LOCAL(base) + movl $0x200, %ecx + addl $decompressor_end, %ecx + subl $LOCAL(base), %ecx + addl LOCAL(compressed_size_offset) + 0x100000 + 0x200, %ecx +#else + movl $(LOCAL(decompressor_end) - _start + 0x200), %ecx + addl LOCAL(compressed_size) - _start + 0x100000 + 0x200, %ecx +#endif + 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, LOCAL(boot_dev) + shrl $24, %edx + /* enter the usual booting */ + call prot_to_real + .code16 + jmp LOCAL (codestart) + .code32 + +post_reed_solomon: + +#ifdef ENABLE_LZMA + movl $GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR, %edi +#ifdef __APPLE__ + movl $decompressor_end, %esi +#else + movl $LOCAL(decompressor_end), %esi +#endif + pushl %edi + movl LOCAL (uncompressed_size), %ecx + leal (%edi, %ecx), %ebx + /* Don't remove this push: it's an argument. */ + push %ecx + call _LzmaDecodeA + pop %ecx + /* _LzmaDecodeA clears DF, so no need to run cld */ + popl %esi +#endif + + movl LOCAL(boot_dev), %edx + movl $prot_to_real, %edi + movl $real_to_prot, %ecx + movl $LOCAL(realidt), %eax + jmp *%esi + +#ifdef ENABLE_LZMA +#include "lzma_decode.S" +#endif + + .p2align 4 + +#ifdef __APPLE__ + .zerofill __DATA, __aa_before_bss, decompressor_end, 10, 0 +#else + .bss +LOCAL(decompressor_end): +#endif diff --git a/boot/i386/qemu/boot.S b/grub-core/boot/i386/qemu/boot.S similarity index 79% rename from boot/i386/qemu/boot.S rename to grub-core/boot/i386/qemu/boot.S index a93fe3943..8c3a1db71 100644 --- a/boot/i386/qemu/boot.S +++ b/grub-core/boot/i386/qemu/boot.S @@ -31,14 +31,11 @@ _start: jmp 1f - . = _start + GRUB_BOOT_MACHINE_CORE_ENTRY_ADDR + .org GRUB_BOOT_I386_QEMU_CORE_ENTRY_ADDR VARIABLE(grub_core_entry_addr) .long 0 1: - /* Process VGA rom. */ - call $0xc000, $0x3 - /* Set up %ds, %ss, and %es. */ xorw %ax, %ax movw %ax, %ds @@ -51,10 +48,17 @@ VARIABLE(grub_core_entry_addr) /* Transition to protected mode. We use pushl to force generation of a flat return address. */ pushl $1f - DATA32 jmp real_to_prot + jmp real_to_prot .code32 1: - movl grub_core_entry_addr, %edx + /* Ensure A20 is enabled. We're in qemu, so control port A works + and there is no need to wait since there is no real logic, it's + all emulated. */ + inb $0x92 + andb $(~0x03), %al + orb $0x02, %al + outb $0x92 + movl EXT_C(grub_core_entry_addr), %edx jmp *%edx #include "../../../kern/i386/realmode.S" @@ -62,6 +66,9 @@ VARIABLE(grub_core_entry_addr) /* Intel, in its infinite wisdom, decided to put the i8086 entry point *right here* and this is why we need this kludge. */ - . = GRUB_BOOT_MACHINE_SIZE - 16 + .org GRUB_BOOT_MACHINE_SIZE - 16 + + .code16 + jmp _start - . = GRUB_BOOT_MACHINE_SIZE + .org GRUB_BOOT_MACHINE_SIZE diff --git a/grub-core/boot/mips/loongson/fuloong2f.S b/grub-core/boot/mips/loongson/fuloong2f.S new file mode 100644 index 000000000..a88c81efe --- /dev/null +++ b/grub-core/boot/mips/loongson/fuloong2f.S @@ -0,0 +1,2 @@ +#define FULOONG2F 1 +#include "fwstart.S" diff --git a/grub-core/boot/mips/loongson/fwstart.S b/grub-core/boot/mips/loongson/fwstart.S new file mode 100644 index 000000000..28c634614 --- /dev/null +++ b/grub-core/boot/mips/loongson/fwstart.S @@ -0,0 +1,756 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef FULOONG2F +#include +#define GRUB_SM712_REG_BASE 0x700000 +#define GRUB_SM712_PCIID 0x0712126f +#endif + +#ifdef FULOONG2F +#define GRUB_MACHINE_SERIAL_PORT GRUB_MACHINE_SERIAL_PORT2 +#define GRUB_MACHINE_SERIAL_DIVISOR_115200 GRUB_MACHINE_SERIAL_PORT2_DIVISOR_115200 +#else +#define GRUB_MACHINE_SERIAL_PORT GRUB_MACHINE_SERIAL_PORT0 +#define GRUB_MACHINE_SERIAL_DIVISOR_115200 GRUB_MACHINE_SERIAL_PORT0_DIVISOR_115200 +#endif + + .set noreorder + .set noat + .set nomacro + .set mips3 + + .global start,_start,__start +start: +_start: +__start: + /* Put serial init as soon as possible. But on Fuloong2f serial is past + Geode, so on Fuloong2f we need Geode first. + */ +#ifndef FULOONG2F + bal serial_hw_init + nop +#endif + + /* Find CS5536 controller. */ + /* $t4 chooses device in priority encoding. */ + /* Resulting value is kept in GRUB_MACHINE_PCI_CONF_CTRL_REG. + This way we don't need to sacrifice a register for it. */ +retry_cs5536: + /* We have only one bus (0). Function is 0. */ + lui $t0, %hi(GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR_2F) + lui $t1, %hi(GRUB_MACHINE_PCI_CONFSPACE_2F) + lui $t3, %hi(GRUB_CS5536_PCIID) + addiu $t3, $t3, %lo(GRUB_CS5536_PCIID) + ori $t4, $zero, 1 +1: + andi $t4, $t4, ((1 << GRUB_PCI_NUM_DEVICES_2F) - 1) + /* In case of failure try again. CS5536 may be slow to come up. */ + beql $t4, $zero, retry_cs5536 + nop + sw $t4, %lo(GRUB_MACHINE_PCI_CONF_CTRL_REG_ADDR_2F) ($t0) + lw $t2, (%lo(GRUB_MACHINE_PCI_CONFSPACE_2F) + GRUB_PCI_REG_PCI_ID) ($t1) + bnel $t2, $t3, 1b + sll $t4, $t4, 1 + +#ifndef FULOONG2F + lui $a0, %hi(cs5536_found) + bal message + addiu $a0, $a0, %lo(cs5536_found) + bal printhex + move $a0, $t4 +#endif + + lui $t0, %hi(GRUB_MACHINE_PCI_CONFSPACE_2F) + li $t1, GRUB_CS5536_MSR_MAILBOX_CONFIG_ENABLED + sw $t1, (%lo(GRUB_MACHINE_PCI_CONFSPACE_2F) + GRUB_CS5536_MSR_MAILBOX_CONFIG) ($t0) + + /* Set GPIO LBAR. */ + lui $a0, %hi(GRUB_CS5536_MSR_GPIO_BAR) + addiu $a0, $a0, %lo(GRUB_CS5536_MSR_GPIO_BAR) + ori $a1, $zero, GRUB_CS5536_LBAR_GPIO + /* Set mask to 0xf and enabled bit to 1. */ + bal wrmsr + ori $a2, $zero, ((GRUB_CS5536_LBAR_MASK_MASK \ + | GRUB_CS5536_LBAR_ENABLE) >> 32) + + bal gpio_init + nop + +#ifdef FULOONG2F + bal serial_hw_init + nop +#endif + + /* Initialise SMBus controller. */ + /* Set SMBUS LBAR. */ + lui $a0, %hi(GRUB_CS5536_MSR_SMB_BAR) + addiu $a0, $a0, %lo(GRUB_CS5536_MSR_SMB_BAR) + ori $a1, $zero, GRUB_CS5536_LBAR_SMBUS + /* Set mask to 0xf and enabled bit to 1. */ + bal wrmsr + ori $a2, $zero, ((GRUB_CS5536_LBAR_MASK_MASK \ + | GRUB_CS5536_LBAR_ENABLE) >> 32) + + lui $a0, %hi(smbus_enabled) + bal message + addiu $a0, $a0, %lo(smbus_enabled) + + lui $t0, %hi(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS) + + /* Disable SMB. */ + sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL2) ($t0) + + /* Disable interrupts. */ + sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1) ($t0) + + /* Set as master. */ + sb $zero, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_ADDR) ($t0) + + /* Launch SMBus controller at slowest speed possible. */ + ori $t1, $zero, 0xff + sb $t1, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL3) ($t0) + sb $t1, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL2) ($t0) + + /* Yeeloong and Fuloong2f have only one memory slot. */ + /* Output first byte on serial for debugging. */ + ori $a1, $zero, GRUB_SMB_RAM_START_ADDR + bal read_spd + move $a0, $zero + bal printhex + move $a0, $v0 + + bal read_spd + ori $a0, $zero, GRUB_SMBUS_SPD_MEMORY_TYPE_ADDR + ori $t0, $zero, GRUB_SMBUS_SPD_MEMORY_TYPE_DDR2 + lui $a0, %hi(unimplemented_memory_type) + bne $t0, $v0, fatal + addiu $a0, $a0, %lo(unimplemented_memory_type) + + /* And here is our goal: DDR2 controller initialisation. */ + lui $t0, %hi(GRUB_CPU_LOONGSON_CORECFG) + ld $t1, %lo(GRUB_CPU_LOONGSON_CORECFG) ($t0) + /* Use addiu for sign-extension. */ + addiu $t2, $zero, ~(GRUB_CPU_LOONGSON_CORECFG_DISABLE_DDR2_SPACE|GRUB_CPU_LOONGSON_CORECFG_BUFFER_CPU) + and $t1, $t1, $t2 + sd $t1, %lo (GRUB_CPU_LOONGSON_CORECFG) ($t0) + + b continue + + .org GRUB_CPU_LOONGSON_FLASH_TLB_REFILL - GRUB_CPU_LOONGSON_FLASH_START +tlb_refill: + mfc0 $s1, GRUB_CPU_LOONGSON_COP0_EPC + mfc0 $s2, GRUB_CPU_LOONGSON_COP0_BADVADDR + move $s3, $ra + lui $a0, %hi(epc) + bal message + addiu $a0, $a0, %lo(epc) + + bal printhex + move $a0, $s1 + + lui $a0, %hi(badvaddr) + bal message + addiu $a0, $a0, %lo(badvaddr) + + bal printhex + move $a0, $s2 + + lui $a0, %hi(return_msg) + bal message + addiu $a0, $a0, %lo(return_msg) + + bal printhex + move $a0, $s3 + + lui $a0, %hi(newline) + bal message + addiu $a0, $a0, %lo(newline) + + lui $a0, %hi(unhandled_tlb_refill) + b fatal + addiu $a0, $a0, %lo(unhandled_tlb_refill) + + .org GRUB_CPU_LOONGSON_FLASH_CACHE_ERROR - GRUB_CPU_LOONGSON_FLASH_START +cache_error: + lui $a0, %hi(unhandled_cache_error) + b fatal + addiu $a0, $a0, %lo(unhandled_cache_error) + + .org GRUB_CPU_LOONGSON_FLASH_OTHER_EXCEPTION - GRUB_CPU_LOONGSON_FLASH_START +other_exception: + mfc0 $s0, GRUB_CPU_LOONGSON_COP0_CAUSE + mfc0 $s1, GRUB_CPU_LOONGSON_COP0_EPC + mfc0 $s2, GRUB_CPU_LOONGSON_COP0_BADVADDR + lui $a0, %hi(cause) + bal message + addiu $a0, $a0, %lo(cause) + + bal printhex + move $a0, $s0 + + lui $a0, %hi(epc) + bal message + addiu $a0, $a0, %lo(epc) + + bal printhex + move $a0, $s1 + + lui $a0, %hi(badvaddr) + bal message + addiu $a0, $a0, %lo(badvaddr) + + bal printhex + move $a0, $s2 + + lui $a0, %hi(newline) + bal message + addiu $a0, $a0, %lo(newline) + + lui $a0, %hi(unhandled_exception) + b fatal + addiu $a0, $a0, %lo(unhandled_exception) + +gpio_init: + lui $t0, %hi(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_GPIO) + addiu $t0, $t0, %lo(GRUB_MACHINE_PCI_IO_BASE_2F + GRUB_CS5536_LBAR_GPIO) + lui $t1, %hi (gpio_dump) + addiu $t1, $t1, %lo (gpio_dump) + +1: + lw $t2, 0($t1) + sw $t2, 0($t0) + addiu $t0, $t0, 4 + addiu $t1, $t1, 4 + lui $t2, %hi (gpio_dump_end) + addiu $t2, $t2, %lo (gpio_dump_end) + bne $t1, $t2, 1b + nop + jr $ra + nop + + /* Same as similarly named C function but in asm since + we need it early. */ + /* In: none. Out: none. Clobbered: $t0, $t1, $t2, $a0, $a1, $a2. */ +serial_hw_init: + move $t2, $ra +#ifdef FULOONG2F + lui $a0, %hi(GRUB_CS5536_MSR_DIVIL_LEG_IO) + addiu $a0, $a0, %lo(GRUB_CS5536_MSR_DIVIL_LEG_IO) + lui $a1, %hi (GRUB_CS5536_MSR_DIVIL_LEG_IO_UART2_COM3 \ + | GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP \ + | GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86 \ + | GRUB_CS5536_MSR_DIVIL_LEG_IO_UART1_COM1) + ori $a1, $a1, (GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0 \ + | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1) + bal wrmsr + move $a2, $zero + + lui $a0, %hi(GRUB_CS5536_MSR_DIVIL_UART1_CONF) + addiu $a0, $a0, %lo(GRUB_CS5536_MSR_DIVIL_UART1_CONF) + li $a1, 2 + bal wrmsr + move $a2, $zero + + lui $a0, %hi(GRUB_CS5536_MSR_DIVIL_UART2_CONF) + addiu $a0, $a0, %lo(GRUB_CS5536_MSR_DIVIL_UART2_CONF) + li $a1, 2 + bal wrmsr + move $a2, $zero +#endif + + lui $t0, %hi (GRUB_MACHINE_SERIAL_PORT) + + /* Turn off the interrupt. */ + sb $zero, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_IER)($t0) + + /* Set DLAB. */ + ori $t1, $zero, UART_DLAB + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_LCR)($t0) + + /* Set the baud rate 115200. */ + ori $t1, $zero, GRUB_MACHINE_SERIAL_DIVISOR_115200 + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_DLL)($t0) + sb $zero, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_DLH)($t0) + + /* Set the line status. */ + ori $t1, $zero, (UART_NO_PARITY | UART_8BITS_WORD | UART_1_STOP_BIT) + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_LCR)($t0) + + /* Enable the FIFO. */ + ori $t1, $zero, UART_ENABLE_FIFO_TRIGGER1 + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_FCR)($t0) + + /* Turn on DTR and RTS. */ + ori $t1, $zero, UART_ENABLE_DTRRTS + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_MCR)($t0) + + /* Let message return to original caller. */ + lui $a0, %hi(notification_string) + addiu $a0, $a0, %lo(notification_string) + move $ra, $t2 + + /* Print message on serial console. */ + /* In: $a0 = asciiz message. Out: none. Clobbered: $t0, $t1, $a0. */ +message: + lui $t0, %hi (GRUB_MACHINE_SERIAL_PORT) +1: + lb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_LSR)($t0) + andi $t1, $t1, UART_EMPTY_TRANSMITTER + beq $t1, $zero, 1b + nop + lb $t1, 0($a0) + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_TX)($t0) + bne $t1, $zero, 1b + addiu $a0, $a0, 1 + jr $ra + nop + + /* Print 32-bit hexadecimal on serial. + In: $a0. Out: None. Clobbered: $a0, $t0, $t1, $t2 + */ +printhex: + lui $t0, %hi (GRUB_MACHINE_SERIAL_PORT) + ori $t2, $zero, 8 +1: + lb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_LSR)($t0) + andi $t1, $t1, UART_EMPTY_TRANSMITTER + beq $t1, $zero, 1b + nop + srl $t1, $a0, 28 + addiu $t1, $t1, -10 + bltz $t1, 2f + sll $a0, $a0, 4 + addiu $t1, $t1, 'A'-10-'0' +2: addiu $t1, $t1, '0'+10 + sb $t1, (%lo (GRUB_MACHINE_SERIAL_PORT) + UART_TX)($t0) + addiu $t2, $t2, -1 + bne $t2, $zero, 1b + nop + jr $ra + nop + +fatal: + bal message + nop +self: + b self + nop + + /* Write CS5536 MSR. + In: $a0 address, $a1 lower word, $a2 upper word. + Out: None + Clobbered: $t0 + */ +wrmsr: + lui $t0, %hi(GRUB_MACHINE_PCI_CONFSPACE_2F) + sw $a0, (%lo(GRUB_MACHINE_PCI_CONFSPACE_2F) + GRUB_CS5536_MSR_MAILBOX_ADDR) ($t0) + sw $a1, (%lo(GRUB_MACHINE_PCI_CONFSPACE_2F) + GRUB_CS5536_MSR_MAILBOX_DATA0) ($t0) + jr $ra + sw $a2, (%lo(GRUB_MACHINE_PCI_CONFSPACE_2F) + GRUB_CS5536_MSR_MAILBOX_DATA1) ($t0) + + /* Wait for SMBus data or empty transmitter. */ + /* In: $a0 = exception handler. Out: none. Clobbered: $t0, $t1 */ +smbus_wait: +1: + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_STATUS + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $t0, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_STATUS + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + andi $t1, $t0, GRUB_CS5536_SMB_REG_STATUS_SDAST + bne $t1, $zero, return + nop + andi $t1, $t0, (GRUB_CS5536_SMB_REG_STATUS_BER | GRUB_CS5536_SMB_REG_STATUS_NACK) + beq $t1, $zero, 1b + nop + jr $a0 + nop +return: + jr $ra + nop + + /* Read SPD byte. In: $a0 byte, $a1 device. Out: $v0 read byte (0x100 on failure). + Clobbered: $t0, $t1, $t2, $t3, $a0. */ +read_spd: + move $t2, $a0 + move $t3, $ra + lui $a0, %hi(read_spd_fail) + addiu $a0, $a0, %lo(read_spd_fail) + + /* Send START. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_START + bal smbus_wait + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send device address. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) + sll $t1, $a1, 1 + bal smbus_wait + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send ACK. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_ACK + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send byte address. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) + bal smbus_wait + sb $t2, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send START. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_START + bal smbus_wait + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send device address. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) + sll $t1, $a1, 1 + ori $t1, $t1, 1 + bal smbus_wait + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + /* Send STOP. */ + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + ori $t1, $t1, GRUB_CS5536_SMB_REG_CTRL1_STOP + bal smbus_wait + sb $t1, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_CTRL1 + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + + lui $t0, %hi(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) + lb $v0, %lo(GRUB_CS5536_LBAR_SMBUS + GRUB_CS5536_SMB_REG_DATA + GRUB_MACHINE_PCI_IO_BASE_2F) ($t0) + jr $t3 + andi $v0, $v0, 0xff +read_spd_fail: + jr $t3 + ori $v0, $v0, 0x100 + +notification_string: .asciz "GRUB " +cs5536_found: .asciz "CS5536 at " +sm_failed: .asciz "SM transaction failed.\n\r" +unhandled_tlb_refill: .asciz "Unhandled TLB refill.\n\r" +unhandled_cache_error: .asciz "Unhandled cache error.\n\r" +unhandled_exception: .asciz "Unhandled exception.\n\r" +smbus_enabled: .asciz "SMBus controller enabled.\n\r" +unimplemented_memory_type: .asciz "non-DDR2 memory isn't supported.\n\r" +no_cas_latency: .asciz "Couldn't determine CAS latency.\n\r" +cause: .asciz "Cause: " +epc: .asciz "\n\rEPC: " +badvaddr: .asciz "\n\rBadVaddr: " +newline: .asciz "\n\r" +return_msg: .asciz "\n\rReturn address: " +caches_enabled: .asciz "Caches enabled\n\r" + + .p2align 3 + +regdump: + .quad 0x0100010000000101 /* 0 */ + .quad 0x0100010100000000 /* 2 */ + .quad 0x0101000001000000 /* 3 */ + .quad 0x0100020200010101 /* 4 */ + .quad 0x0a04030603050203 /* 6 */ + .quad 0x0f0e040000010a0b /* 7 */ +#ifdef FULOONG2F + .quad 0x0000000100000001 /* 8 */ +#else + .quad 0x0000010200000102 /* 8 */ +#endif + .quad 0x0000060c00000000 /* 9 */ + .quad 0x2323233f3f1f0200 /* a */ + .quad 0x5f7f232323232323 /* b */ + .quad 0x002a3c0615000000 /* c */ + .quad 0x002a002a002a002a /* d */ + .quad 0x002a002a002a002a /* e */ +#ifdef FULOONG2F + .quad 0x00b40020005b0004 /* f */ +#else + .quad 0x00b40020006d0004 /* f */ +#endif + .quad 0x070007ff00000087 /* 10 */ + .quad 0x000000000016101f /* 11 */ + .quad 0x001c000000000000 /* 12 */ + .quad 0x28e1000200c8006b /* 13 */ + .quad 0x0000204200c8002f /* 14 */ + .quad 0x0000000000030d40 /* 15 */ + .quad 0 /* 16 */ + .quad 0 /* 17 */ + .quad 0 /* 18 */ + .quad 0 /* 19 */ + .quad 0 /* 1a */ + .quad 0 /* 1b */ + .quad 0 /* 1c */ + +/* Dump of GPIO connections. FIXME: Remove useless and macroify. */ +gpio_dump: +#ifdef FULOONG2F + .long 0xffff0000, 0x2eefd110, 0xffff0000, 0xffff0000 + .long 0x2eefd110, 0xffff0000, 0x1000efff, 0xefff1000 + .long 0x3df3c20c, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0x7df3820c, 0x3df3c20c, 0xffff0000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0x3de3c21c, 0x3d83c27c + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0xffff0000, 0xffff0000, 0x0000ffff, 0xffff0000 + .long 0xefff1000, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0xefff1000, 0xefff1000, 0xffff0000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0xefff1000, 0xefff1000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 +#else + .long 0xffff0000, 0x2ffdd002, 0xffff0000, 0xffff0000 + .long 0x2fffd000, 0xffff0000, 0x1000efff, 0xefff1000 + .long 0x3ffbc004, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0x3ffbc004, 0x3ffbc004, 0xffff0000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0x3ffbc004, 0x3f9bc064 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0xffff0000, 0xffff0000, 0x0000ffff, 0xffff0000 + .long 0xefff1000, 0xffff0000, 0xffff0000, 0xffff0000 + .long 0xefff1000, 0xefff1000, 0xffff0000, 0x00000000 + .long 0xffff0000, 0xffff0000, 0xefff1000, 0xffff0000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x00000000, 0x00000000, 0x00000000 + .long 0x00000000, 0x50000000, 0x00000000, 0x00000000 +#endif +gpio_dump_end: + + .p2align 3 + +write_dumpreg: + ld $t2, 0($t6) + sd $t2, 0($t4) + addiu $t4, $t4, GRUB_CPU_LOONGSON_DDR2_REG_STEP + jr $ra + addiu $t6, $t6, GRUB_CPU_LOONGSON_DDR2_REG_SIZE + +continue: + lui $t4, %hi(GRUB_CPU_LOONGSON_DDR2_BASE) + addiu $t4, $t4, %lo(GRUB_CPU_LOONGSON_DDR2_BASE) + lui $t6, %hi(regdump) + + /* 0 */ + bal write_dumpreg + addiu $t6, $t6, %lo(regdump) + + /* 1 */ + ori $a1, $a1, GRUB_SMB_RAM_START_ADDR + move $t8, $zero + lui $t5, 0x0001 + bal read_spd + ori $a0, $zero, GRUB_SMBUS_SPD_MEMORY_NUM_BANKS_ADDR + ori $t7, $zero, 8 + bne $v0, $t7, 1f + ori $t5, $t5, 0x0001 + ori $t8, $t8, GRUB_CPU_LOONGSON_DDR2_REG1_HI_8BANKS +1: + dsll $t8, $t8, 32 + or $t5, $t5, $t8 + sd $t5, 0 ($t4) + addiu $t4, $t4, GRUB_CPU_LOONGSON_DDR2_REG_STEP + + /* 2 */ + bal write_dumpreg + nop + + /* 3 */ + bal write_dumpreg + nop + + /* 4 */ + bal write_dumpreg + nop + + /* 5 */ + /* FIXME: figure termination resistance. */ + ori $t5, $zero, 0x2 + bal read_spd + ori $a0, $zero, GRUB_SMBUS_SPD_MEMORY_NUM_ROWS_ADDR + /* $v0 = 15 - $v0. */ + xori $v0, $v0, 0xf + andi $v0, $v0, 0x7 + sll $v0, $v0, 8 + or $t5, $t5, $v0 + + /* Find the fastest supported CAS latency. */ + bal read_spd + ori $a0, $zero, GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_ADDR + ori $t0, $zero, GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_MIN_VALUE + ori $t1, $zero, (1 << GRUB_SMBUS_SPD_MEMORY_CAS_LATENCY_MIN_VALUE) +2: + and $t2, $t1, $v0 + bne $t2, $zero, 1f + ori $t3, $zero, 8 + lui $a0, %hi(no_cas_latency) + beq $t0, $t3, fatal + addiu $a0, $a0, %lo(no_cas_latency) + addiu $t0, $t0, 1 + b 2b + sll $t1, $t1, 1 +1: + sll $t0, $t0, 16 + or $t5, $t5, $t0 + + bal read_spd + ori $a0, $zero, GRUB_SMBUS_SPD_MEMORY_NUM_COLUMNS_ADDR + /* $v0 = 15 - ($v0 + 1) = 14 - $v0. */ + addiu $v0, $v0, 1 + xori $v0, $v0, 0xf + andi $v0, $v0, 0x7 + sll $v0, 24 + or $t5, $t5, $v0 + sd $t5, 0 ($t4) + + addiu $t4, $t4, GRUB_CPU_LOONGSON_DDR2_REG_STEP + + ori $t7, $zero, 0x16 + +1: + ld $t2, 0($t6) + sd $t2, 0($t4) + addiu $t4, $t4, GRUB_CPU_LOONGSON_DDR2_REG_STEP + addiu $t7, $t7, -1 + bne $t7, $zero, 1b + addiu $t6, $t6, GRUB_CPU_LOONGSON_DDR2_REG_SIZE + + lui $t4, %hi(GRUB_CPU_LOONGSON_DDR2_BASE) + ld $t5, (%lo(GRUB_CPU_LOONGSON_DDR2_BASE) + 0x30) ($t4) + ori $t0, $zero, 1 + dsll $t0, $t0, 40 + or $t5, $t5, $t0 + sd $t5, (%lo(GRUB_CPU_LOONGSON_DDR2_BASE) + 0x30) ($t4) + + /* Desactivate DDR2 registers. */ + lui $t0, %hi (GRUB_CPU_LOONGSON_CORECFG) + ld $t1, %lo (GRUB_CPU_LOONGSON_CORECFG) ($t0) + ori $t1, $t1, GRUB_CPU_LOONGSON_CORECFG_DISABLE_DDR2_SPACE + sd $t1, %lo (GRUB_CPU_LOONGSON_CORECFG) ($t0) + + /* Enable cache. */ + mfc0 $t0, GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG + addiu $t1, $zero, ~GRUB_CPU_LOONGSON_CACHE_TYPE_MASK + and $t0, $t1, $t1 + /* Set line size to 32 bytes and disabled cache. */ + ori $t0, $t0, (GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG_ILINESIZE \ + | GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG_DLINESIZE \ + | GRUB_CPU_LOONGSON_CACHE_ACCELERATED) + mtc0 $t0, GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG + + /* Invalidate all I-cache entries. */ + srl $t1, $t0, GRUB_CPU_LOONGSON_COP0_CACHE_ISIZE_SHIFT + andi $t1, $t1, GRUB_CPU_LOONGSON_COP0_CACHE_SIZE_MASK + ori $t2, $zero, (1 << (GRUB_CPU_LOONGSON_COP0_CACHE_SIZE_OFFSET \ + - GRUB_CPU_LOONGSON_CACHE_LINE_SIZE_LOG_BIG \ + - GRUB_CPU_LOONGSON_I_CACHE_LOG_WAYS)) + sll $t1, $t2, $t1 + lui $t2, 0x8000 + +1: + cache GRUB_CPU_LOONGSON_COP0_I_INDEX_INVALIDATE, 0($t2) + addiu $t1, $t1, -1 + bne $t1, $zero, 1b + addiu $t2, $t2, (1 << GRUB_CPU_LOONGSON_COP0_I_INDEX_BIT_OFFSET) + + /* Invalidate all D-cache entries. */ + srl $t1, $t0, GRUB_CPU_LOONGSON_COP0_CACHE_DSIZE_SHIFT + andi $t1, $t1, GRUB_CPU_LOONGSON_COP0_CACHE_SIZE_MASK + ori $t2, $zero, (1 << (GRUB_CPU_LOONGSON_COP0_CACHE_SIZE_OFFSET \ + - GRUB_CPU_LOONGSON_CACHE_LINE_SIZE_LOG_BIG \ + - GRUB_CPU_LOONGSON_D_CACHE_LOG_WAYS)) + sll $t1, $t2, $t1 + lui $t2, 0x8000 + mtc0 $zero, GRUB_CPU_LOONGSON_COP0_CACHE_TAGLO + mtc0 $zero, GRUB_CPU_LOONGSON_COP0_CACHE_TAGHI +1: + /* All four ways. */ + cache GRUB_CPU_LOONGSON_COP0_D_INDEX_TAG_STORE, 0($t2) + cache GRUB_CPU_LOONGSON_COP0_D_INDEX_TAG_STORE, 1($t2) + cache GRUB_CPU_LOONGSON_COP0_D_INDEX_TAG_STORE, 2($t2) + cache GRUB_CPU_LOONGSON_COP0_D_INDEX_TAG_STORE, 3($t2) + addiu $t1, $t1, -1 + bne $t1, $zero, 1b + addiu $t2, $t2, (1 << GRUB_CPU_LOONGSON_COP0_D_INDEX_BIT_OFFSET) + + /* Invalidate all S-cache entries. */ + ori $t1, $zero, (1 << (GRUB_CPU_LOONGSON_SECONDARY_CACHE_LOG_SIZE \ + - GRUB_CPU_LOONGSON_CACHE_LINE_SIZE_LOG_BIG \ + - GRUB_CPU_LOONGSON_S_CACHE_LOG_WAYS)) + lui $t2, 0x8000 + mtc0 $zero, GRUB_CPU_LOONGSON_COP0_CACHE_TAGLO + mtc0 $zero, GRUB_CPU_LOONGSON_COP0_CACHE_TAGHI +1: + /* All four ways. */ + cache GRUB_CPU_LOONGSON_COP0_S_INDEX_TAG_STORE, 0($t2) + cache GRUB_CPU_LOONGSON_COP0_S_INDEX_TAG_STORE, 1($t2) + cache GRUB_CPU_LOONGSON_COP0_S_INDEX_TAG_STORE, 2($t2) + cache GRUB_CPU_LOONGSON_COP0_S_INDEX_TAG_STORE, 3($t2) + addiu $t1, $t1, -1 + bne $t1, $zero, 1b + addiu $t2, $t2, (1 << GRUB_CPU_LOONGSON_COP0_D_INDEX_BIT_OFFSET) + + /* Finally enable cache. */ + mfc0 $t0, GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG + addiu $t1, $zero, ~GRUB_CPU_LOONGSON_CACHE_TYPE_MASK + and $t0, $t1, $t1 + ori $t0, $t0, GRUB_CPU_LOONGSON_CACHE_CACHED + mtc0 $t0, GRUB_CPU_LOONGSON_COP0_CACHE_CONFIG + + lui $a0, %hi(caches_enabled) + bal message + addiu $a0, $a0, %lo(caches_enabled) + + /* Set ROM delay cycles to 1. */ + lui $t0, %hi(GRUB_CPU_LOONGSON_LIOCFG) + lw $t1, %lo(GRUB_CPU_LOONGSON_LIOCFG) ($t0) + addiu $t2, $zero, ~(GRUB_CPU_LOONGSON_ROM_DELAY_MASK \ + << GRUB_CPU_LOONGSON_ROM_DELAY_OFFSET) + and $t1, $t1, $t2 + ori $t1, $t1, (1 << GRUB_CPU_LOONGSON_ROM_DELAY_OFFSET) + sw $t1, %lo(GRUB_CPU_LOONGSON_LIOCFG) ($t0) + + addiu $a0, $zero, -1 + addiu $a1, $zero, -1 + + /* Take advantage of cache. */ + lui $t0, %hi(cached_continue - 0x20000000) + addiu $t0, $t0, %lo(cached_continue - 0x20000000) + jr $t0 +#ifdef FULOONG2F + addiu $a2, $zero, -(1 + GRUB_ARCH_MACHINE_FULOONG2F) +#else + addiu $a2, $zero, -(1 + GRUB_ARCH_MACHINE_YEELOONG) +#endif + +cached_continue: diff --git a/grub-core/boot/mips/startup_raw.S b/grub-core/boot/mips/startup_raw.S new file mode 100644 index 000000000..6a81b3733 --- /dev/null +++ b/grub-core/boot/mips/startup_raw.S @@ -0,0 +1,300 @@ +/* startup.S - Startup code for the MIPS. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#define BASE_ADDR 8 + +.extern __bss_start +.extern _end +.extern _edata + + .globl __start, _start, start + .set noreorder + .set nomacro +__start: +_start: +start: + + bal codestart + nop +base: + .org GRUB_DECOMPRESSOR_MACHINE_COMPRESSED_SIZE +compressed_size: + .long 0 + .org GRUB_DECOMPRESSOR_MACHINE_UNCOMPRESSED_SIZE +uncompressed_size: + .long 0 + .org GRUB_DECOMPRESSOR_MACHINE_UNCOMPRESSED_ADDR +uncompressed_addr: + .long 0 +codestart: + /* Save our base. */ + move $s0, $ra + + /* Parse arguments. Has to be done before relocation. + So need to do it in asm. */ +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + lui $t0, %hi (((16 << 20) - 264 + 4) | 0x80000000) + lw $t1, %lo (((16 << 20) - 264 + 4) | 0x80000000) ($t0) + + lui $t2, 0x1234 + ori $t2, 0x5678 + + bne $t1, $t2, 1f + nop + + lui $t0, %hi (((16 << 20) - 264) | 0x80000000) + b 2f + lw $s4, %lo (((16 << 20) - 264) | 0x80000000) ($t0) + +1: + li $s4, 0 +2: +#endif + +#ifdef GRUB_MACHINE_MIPS_LOONGSON + move $s2, $zero + move $s3, $zero + move $s4, $zero + move $s5, $zero + move $s7, $zero + + /* $a2 has the environment. */ + addiu $t0, $zero, -0x10 + and $t1, $a2, $t0 + beq $t0, $t1, argfw + nop + move $t0, $a2 +argcont: + lw $t1, 0($t0) + beq $t1, $zero, argdone + nop +#define DO_PARSE(str, reg) \ + addiu $t2, $s0, (str-base);\ + bal parsestr;\ + nop ;\ + beq $v0, $zero, 1f;\ + nop ;\ + b 2f;\ + move reg, $v0; \ +1: +#define DO_CHECKT1(str, val) \ + move $t6, $t1 ;\ + addiu $t7, $s0, (str - base);\ + bal do_check ;\ + li $t2, val + + DO_PARSE (busclockstr, $s2) + DO_PARSE (cpuclockstr, $s3) + DO_PARSE (memsizestr, $s4) + DO_PARSE (highmemsizestr, $s5) + DO_CHECKT1 (pmon_yeeloong_verstr, GRUB_ARCH_MACHINE_YEELOONG) + DO_CHECKT1 (pmon_fuloong2f_verstr, GRUB_ARCH_MACHINE_FULOONG2F) +2: + b argcont + addiu $t0, $t0, 4 +parsestr: + move $v0, $zero + move $t3, $t1 +3: + lb GRUB_ASM_T4, 0($t2) + lb GRUB_ASM_T5, 0($t3) + addiu $t2, $t2, 1 + addiu $t3, $t3, 1 + beq GRUB_ASM_T5, $zero, 1f + nop + beq GRUB_ASM_T5, GRUB_ASM_T4, 3b + nop + bne GRUB_ASM_T4, $zero, 1f + nop + + addiu $t3, $t3, 0xffff +digcont: + lb GRUB_ASM_T5, 0($t3) + /* Substract '0' from digit. */ + addiu GRUB_ASM_T5, GRUB_ASM_T5, 0xffd0 + bltz GRUB_ASM_T5, 1f + nop + addiu GRUB_ASM_T4, GRUB_ASM_T5, 0xfff7 + bgtz GRUB_ASM_T4, 1f + nop + /* Multiply $v0 by 10 with bitshifts. */ + sll $v0, $v0, 1 + sll GRUB_ASM_T4, $v0, 2 + addu $v0, $v0, GRUB_ASM_T4 + addu $v0, $v0, GRUB_ASM_T5 + addiu $t3, $t3, 1 + b digcont + nop +1: + jr $ra + nop +busclockstr: .asciz "busclock=" +cpuclockstr: .asciz "cpuclock=" +memsizestr: .asciz "memsize=" +highmemsizestr: .asciz "highmemsize=" +machtype_yeeloong_str1: .asciz "machtype=8.9" +machtype_yeeloong_str2: .asciz "machtype=lemote-yeeloong-" +machtype_fuloong2f_str: .asciz "machtype=lemote-fuloong-2f" +machtype_fuloong2e_str: .asciz "machtype=lemote-fuloong-2e" +pmon_yeeloong_str: .asciz "PMON_VER=LM8" +pmon_fuloong2f_str: .asciz "PMON_VER=LM6" +pmon_yeeloong_verstr: .asciz "Version=LM8" +pmon_fuloong2f_verstr: .asciz "Version=LM6" + .p2align 2 + +argdone: + beq $a0, $zero, cmdlinedone + nop +#define DO_CHECKA1(str, val) \ + lw $t6, 0($a1) ;\ + addiu $t7, $s0, (str - base);\ + bal do_check ;\ + li $t2, val + DO_CHECKA1 (machtype_yeeloong_str1, GRUB_ARCH_MACHINE_YEELOONG) + DO_CHECKA1 (machtype_yeeloong_str2, GRUB_ARCH_MACHINE_YEELOONG) + DO_CHECKA1 (pmon_yeeloong_str, GRUB_ARCH_MACHINE_YEELOONG) + DO_CHECKA1 (machtype_fuloong2f_str, GRUB_ARCH_MACHINE_FULOONG2F) + DO_CHECKA1 (machtype_fuloong2e_str, GRUB_ARCH_MACHINE_FULOONG2E) + DO_CHECKA1 (pmon_fuloong2f_str, GRUB_ARCH_MACHINE_FULOONG2F) + addiu $a0, $a0, -1 + b argdone + addiu $a1, $a1, 4 +do_check: + lb GRUB_ASM_T4, 0($t7) + beq GRUB_ASM_T4, $zero, 1f + lb $t3, 0($t6) + bne $t3, GRUB_ASM_T4, 2f + addiu $t6, $t6, 1 + b do_check + addiu $t7, $t7, 1 +1: + move $s7, $t2 +2: + jr $ra + nop +argfw: + not $s7, $a2 +cmdlinedone: +#endif +#ifdef GRUB_MACHINE_ARC + lui $t0, %hi(_start - 256) + addiu $t0, $t0, %lo(_start - 256) + addiu $t3, $t0, 255 + lw $t1, 0($a1) +1: + bne $t0, $t3, 2f + lb $t2, 0($t1) + move $t2, $zero +2: + sb $t2, 0($t0) + addiu $t0, $t0, 1 + bnez $t2, 1b + addiu $t1, $t1, 1 +#endif + /* Copy the decompressor. */ + lui $t1, %hi(base) + addiu $t1, $t1, %lo(base) + lui $t3, %hi(__bss_start) + addiu $t3, $t3, %lo(__bss_start) + move $t2, $s0 + +1: + beq $t1, $t3, 2f + lb GRUB_ASM_T4, 0($t2) + sb GRUB_ASM_T4, 0($t1) + addiu $t1, $t1, 1 + b 1b + addiu $t2, $t2, 1 +2: + /* Clean out its BSS. */ + lui $t1, %hi(__bss_start) + addiu $t1, $t1, %lo(__bss_start) + lui $t2, %hi(_end) + addiu $t2, $t2, %lo(_end) +1: + beq $t1, $t2, 2f + nop + sb $zero, 0($t1) + b 1b + addiu $t1, $t1, 1 +2: + lui $a0, %hi(base) + addiu $a0, $a0, %lo(base) + lui $a1, %hi(_end) + addiu $a1, %lo(_end) + subu $a1,$a1,$a0 + +#include "../../kern/mips/cache_flush.S" + + /* Decompress the payload. */ + lui $a0, %hi(_edata) + addiu $a0, $a0, %lo(_edata) + + lui $t0, %hi(base) + addiu $t0, $t0, %lo(base) + subu $a0, $a0, $t0 + addu $a0, $a0, $s0 + + lw $a1, (GRUB_DECOMPRESSOR_MACHINE_UNCOMPRESSED_ADDR - BASE_ADDR)($s0) + lw $a2, (GRUB_DECOMPRESSOR_MACHINE_COMPRESSED_SIZE - BASE_ADDR)($s0) + lw $a3, (GRUB_DECOMPRESSOR_MACHINE_UNCOMPRESSED_SIZE - BASE_ADDR)($s0) + move $s1, $a1 + + /* $a0 contains source compressed address, $a1 is destination, + $a2 is compressed size, $a3 is uncompressed size. + */ + move $s6, $a3 + + lui $t9, %hi(EXT_C(grub_decompress_core)) + addiu $t9, $t9, %lo(EXT_C(grub_decompress_core)) + +#ifdef GRUB_MACHINE_ARC + lui $sp, %hi(_start - 512) + jalr $t9 + addiu $sp, $sp, %lo(_start - 512) +#else + lui $sp, %hi(_start - 256) + jalr $t9 + addiu $sp, $sp, %lo(_start - 256) +#endif + move $a0, $s1 + move $a1, $s6 + +#include "../../kern/mips/cache_flush.S" + + lui $t1, %hi(GRUB_MACHINE_LINK_ADDR) + addiu $t1, %lo(GRUB_MACHINE_LINK_ADDR) + + jr $t1 + nop + /* Ensure that .data section is created. In code we suppose that _edata + is first location not in decompressor image. Strictly speaking it's + _edata only when .data is present and _etext otherwise. But checking + for .data presence would cost more in code than it is to ensure that + .data is created. + */ + .data + .long 0 diff --git a/grub-core/boot/powerpc/bootinfo.txt.in b/grub-core/boot/powerpc/bootinfo.txt.in new file mode 100644 index 000000000..bc831daa9 --- /dev/null +++ b/grub-core/boot/powerpc/bootinfo.txt.in @@ -0,0 +1,73 @@ + +@PACKAGE@ @VERSION@ +@PACKAGE@ @VERSION@ +boot &device;:\boot\grub\powerpc.elf + + +FF FF FF FF FF FF FF FF FF FF 92 6D 6D 6D 6D 6D 6D 6D 6D 6D DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF B6 92 6D 92 92 92 DB FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF DB 6D 92 DB FF FF FF FF FF DB B6 FF FF 92 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 92 DB FF FF FF FF FF B6 6D 92 DB FF FF FF FF FF FF FF +FF FF FF FF FF FF 49 92 FF FF B6 B6 24 00 24 00 00 00 00 49 6D DB 6D 92 DB B6 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF B6 6D DB 92 6D 24 49 92 6D 6D FF FF FF 92 6D FF FF FF FF FF FF +FF FF FF FF B6 49 DB FF FF 24 00 00 00 92 92 B6 FF DB DB FF DB B6 FF DB 92 49 DB FF FF FF FF FF FF FF FF FF FF FF FF FF DB 49 6D B6 FF 6D B6 6D 6D 92 24 24 00 00 24 6D FF FF 49 DB FF FF FF FF +FF FF FF B6 49 FF DB 49 24 00 49 6D B6 FF B6 92 6D 6D 6D 92 DB DB DB B6 6D 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 6D DB FF FF FF FF DB B6 B6 B6 FF DB 24 00 00 92 B6 FF 49 FF FF FF FF +FF FF DB 49 FF FF 49 00 00 24 FF FF 6D 49 92 DB FF FF FF DB 92 92 92 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 92 92 6D 6D B6 DB DB B6 6D 6D FF FF 24 00 00 DB FF 49 FF FF FF +FF FF 49 FF FF 49 00 00 6D DB DB 49 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 B6 B6 24 00 24 DB DB 6D FF FF +FF B6 92 FF B6 00 00 24 FF DB 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 B6 FF 00 00 49 FF 92 B6 FF +FF 6D FF FF 92 00 00 49 FF 6D FF FF FF FF FF FF FF FF FF FF FF FF FF B6 92 92 6D 6D 6D 6D DB FF FF FF FF FF FF B6 92 92 92 92 92 FF FF FF FF FF FF FF FF FF FF FF FF 6D FF 24 00 24 FF FF 6D FF +DB 92 FF DB 00 00 49 FF 92 DB FF FF FF FF FF FF FF FF FF FF FF DB 6D B6 FF FF FF FF FF FF 92 6D FF FF FF FF 6D B6 FF FF FF FF FF B6 B6 FF FF FF FF FF FF FF FF FF FF FF 92 DB 00 00 92 FF 92 DB +92 FF FF B6 00 00 6D FF 6D FF FF FF FF FF FF FF FF FF FF FF DB 6D FF FF FF 92 49 49 49 92 FF FF 49 DB DB 24 DB FF B6 49 49 92 FF FF DB 92 FF FF FF FF FF FF FF FF FF FF 92 FF 00 00 6D FF DB 92 +6D FF FF FF 00 00 49 92 DB FF FF FF FF FF FF FF FF FF FF DB 6D FF FF 6D 00 00 00 00 00 00 00 B6 FF 49 00 24 24 49 24 00 00 00 00 6D FF DB 92 FF FF FF FF FF FF FF FF FF DB B6 00 00 92 FF FF 6D +6D FF FF 24 00 00 DB 6D FF FF FF FF FF FF FF FF FF FF DB 6D FF DB 00 00 00 00 00 00 00 00 00 00 B6 FF DB B6 49 92 24 24 00 00 00 00 24 FF DB 92 FF FF FF FF FF FF FF FF FF 92 6D 00 00 DB FF 6D +6D FF FF 24 00 00 FF 6D FF FF FF FF FF FF FF FF FF FF 49 FF B6 00 00 00 00 00 00 00 00 00 00 00 00 92 FF FF 92 DB DB 24 24 00 00 00 00 24 FF 92 DB FF FF FF FF FF FF FF FF 92 92 00 00 FF FF 6D +6D FF FF B6 00 00 92 6D FF FF FF FF FF FF FF FF FF 49 FF DB 00 00 00 00 00 00 00 00 00 00 00 00 00 92 FF FF B6 B6 FF 92 24 00 00 00 00 00 49 FF 6D FF FF FF FF FF FF FF FF 92 24 00 49 FF FF 6D +6D FF FF 00 00 00 DB 6D FF FF FF FF FF FF FF FF 6D DB DB 00 00 00 00 00 00 00 00 00 00 00 00 00 00 92 FF FF DB B6 FF B6 49 00 00 00 00 00 00 6D FF 6D FF FF FF FF FF FF FF 92 92 00 00 DB FF 6D +6D FF FF DB 00 00 B6 6D FF FF FF FF FF FF FF 6D B6 FF 24 00 00 00 00 00 00 00 00 00 00 00 24 B6 DB 6D FF FF FF FF FF 6D 49 24 00 00 00 00 00 00 B6 DB B6 FF FF FF FF FF B6 DB 24 00 92 FF FF 6D +6D FF FF 6D 00 00 24 DB 92 FF FF FF FF FF 92 92 FF 49 00 00 00 00 00 49 B6 FF FF DB B6 DB FF FF FF B6 92 FF FF DB 92 FF FF FF 49 6D 92 24 00 00 00 DB B6 DB FF FF FF FF 6D FF 00 00 00 DB FF 6D +6D FF FF 92 24 00 49 FF 6D B6 FF FF FF 6D 92 FF 49 00 00 49 DB FF FF FF FF FF FF B6 FF FF FF FF FF FF B6 6D 92 92 FF FF FF FF 6D FF FF FF DB 24 00 24 FF 92 B6 FF FF 92 B6 FF 00 00 B6 FF FF 6D +92 FF FF FF 00 00 24 92 FF 92 6D 92 49 B6 DB 24 00 24 DB FF FF FF FF FF DB 92 24 00 FF FF FF FF 6D 6D FF FF FF 6D 6D FF FF B6 DB 6D FF FF FF FF 00 00 24 DB B6 6D 6D B6 DB 00 00 00 6D FF FF 6D +DB 92 FF DB 49 00 00 00 B6 FF FF DB FF 6D 00 00 6D FF FF FF FF FF FF FF 24 92 00 49 FF FF FF FF FF 6D B6 FF FF 6D 6D FF 6D 00 DB DB 92 FF FF FF DB 00 00 00 6D FF FF DB 6D 00 00 24 FF FF 92 DB +FF 49 FF FF 6D 00 00 00 24 49 B6 FF 24 00 00 6D FF FF FF FF FF FF FF 49 92 B6 00 DB FF FF DB DB FF FF B6 FF FF FF FF FF 00 49 DB FF 92 FF FF FF FF 92 00 00 00 24 6D 00 00 00 00 24 DB FF 49 FF +FF 92 B6 FF 92 49 00 00 00 00 00 24 00 00 00 FF FF FF FF FF FF FF 92 6D FF B6 DB FF DB B6 DB B6 B6 FF FF B6 FF FF FF DB 00 B6 DB FF 92 FF FF FF FF FF 24 00 00 00 00 00 00 00 00 B6 FF 92 B6 FF +FF FF 49 FF FF 49 24 00 00 00 00 00 00 00 B6 FF FF FF FF FF FF FF B6 FF FF FF FF FF FF FF FF FF 6D FF FF 6D FF FF FF DB 24 FF FF FF 92 FF FF FF FF FF 6D 00 00 00 00 00 00 00 DB FF FF 6D FF FF +FF FF DB 6D FF FF 6D 49 00 00 00 00 00 24 FF FF FF FF FF FF FF FF FF FF FF FF FF DB 6D 49 24 24 24 FF FF DB FF FF FF FF 24 24 00 00 92 FF FF FF FF FF DB 00 00 00 00 00 00 FF DB FF 6D FF FF FF +FF FF FF 92 B6 FF FF DB 49 24 00 00 00 92 FF FF FF FF FF FF FF FF FF DB FF FF FF 49 49 24 00 24 FF FF FF FF FF FF FF FF 49 6D 00 24 49 FF FF FF FF FF FF 49 00 24 6D 6D B6 FF FF 6D B6 FF FF FF +FF FF FF FF 6D B6 FF FF DB 92 B6 49 00 FF FF FF FF FF FF FF FF FF FF B6 FF FF FF 92 DB 92 00 24 FF FF FF FF FF FF FF FF FF 00 00 6D FF FF FF FF FF FF FF DB 00 6D DB FF FF FF 6D B6 FF FF FF FF +FF FF FF FF FF 92 6D FF FF FF FF B6 49 FF FF FF FF FF FF FF FF FF FF 6D FF FF FF FF B6 92 92 B6 B6 DB FF FF FF FF FF FF FF B6 6D 49 6D FF FF FF FF FF FF FF 92 24 FF FF B6 6D DB FF FF FF FF FF +FF FF FF FF FF FF DB 49 6D B6 FF 6D 92 FF FF FF FF FF FF FF FF FF DB 6D FF FF FF FF FF FF FF FF FF 92 FF FF FF FF FF FF FF FF 6D DB 92 FF FF FF FF FF FF FF FF 6D 49 6D DB FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF DB 92 49 00 FF FF FF FF FF FF FF FF FF FF 6D 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 92 6D FF FF FF FF FF FF FF DB 92 FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF 49 FF FF FF FF FF FF FF FF FF DB 00 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 B6 92 6D B6 FF FF FF FF FF FF 49 DB FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF 24 FF FF FF FF FF FF FF FF FF 49 DB 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 6D 6D FF 92 49 92 FF FF FF FF DB 49 DB FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF B6 49 FF FF FF FF FF FF FF FF 6D 92 FF 92 FF FF FF FF FF FF FF FF FF FF B6 6D 49 6D DB FF FF FF FF FF 6D 49 FF FF FF DB 6D 6D 92 92 6D 49 FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF 24 FF FF FF FF FF FF FF FF 6D 92 FF FF FF DB FF FF FF FF FF FF FF FF 6D 6D FF FF FF 92 6D FF FF FF FF FF 49 92 B6 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF DB 6D FF FF FF FF FF FF DB 24 92 FF FF FF FF FF FF FF FF FF FF FF FF FF 49 49 6D DB FF FF DB 6D B6 FF FF FF FF B6 B6 DB 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF 24 B6 FF FF FF FF B6 49 49 24 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF 00 49 FF DB DB FF FF FF B6 92 FF FF FF FF FF FF 92 DB FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF 24 B6 FF FF B6 24 00 6D DB FF 6D 92 FF FF FF FF FF FF FF FF FF FF FF FF FF 6D DB DB 00 00 24 FF FF FF FF B6 FF FF FF FF FF B6 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF B6 B6 DB B6 6D 49 49 92 FF FF FF B6 6D FF FF FF FF FF FF FF 92 92 FF FF FF FF FF FF FF 49 92 DB 49 FF FF FF FF FF FF FF FF FF 92 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF 92 24 49 49 6D FF 6D 92 FF FF FF B6 6D FF FF FF FF FF FF FF FF FF FF DB FF FF FF FF FF FF FF FF B6 FF FF FF FF FF FF FF FF FF 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF 6D 92 FF FF FF B6 6D FF FF FF FF FF FF FF FF FF FF B6 DB DB FF FF FF FF FF FF FF DB FF FF FF FF FF FF FF 6D 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF 6D 92 FF FF FF FF 24 92 FF FF FF FF FF FF FF FF FF FF 6D B6 FF FF FF FF FF FF FF FF FF FF FF FF FF DB 49 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF 6D B6 FF FF FF FF B6 6D FF FF FF FF FF FF FF FF FF FF B6 92 FF FF FF FF FF FF FF FF FF FF FF DB 6D 00 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF 6D 92 FF FF FF FF DB 49 FF FF FF FF FF FF FF FF FF FF FF 49 FF FF FF FF FF FF FF FF FF FF FF FF FF DB 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF 92 6D FF FF FF FF FF 00 24 DB FF FF FF FF FF FF FF FF FF DB 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF B6 49 FF FF FF FF FF FF 92 6D FF FF FF FF FF FF FF FF FF FF 6D B6 FF FF FF FF FF FF FF FF FF FF FF FF B6 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 FF FF FF FF FF FF FF 49 00 DB FF FF FF FF FF FF FF FF FF 6D 6D B6 DB DB DB 92 49 00 00 00 00 00 49 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF 49 DB FF FF FF FF FF FF FF 24 FF FF FF FF FF FF FF FF FF FF FF DB 6D 49 49 6D B6 DB FF FF FF B6 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 92 FF FF FF FF FF FF B6 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 24 FF FF FF FF FF FF DB 00 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 B6 FF FF FF FF FF FF DB 6D 00 49 FF FF FF FF FF FF FF FF FF FF FF FF DB B6 92 6D 6D 6D 49 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 49 FF FF FF FF FF FF FF FF 49 00 92 FF FF FF FF FF FF FF FF 49 00 00 00 00 00 49 B6 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 6D FF FF FF FF FF FF FF FF 6D 6D FF B6 B6 FF FF FF FF FF FF 92 92 FF FF 00 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 B6 FF FF FF FF FF FF DB 00 DB 6D 00 B6 FF FF FF FF FF FF FF FF FF FF 24 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 DB FF FF FF FF FF 92 00 FF 24 00 00 49 FF FF FF FF FF FF FF FF FF B6 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 FF FF FF FF FF FF 49 24 24 00 00 6D FF FF FF FF FF FF FF DB FF DB 6D FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF B6 FF FF FF FF FF FF 6D 00 24 24 24 FF FF FF FF FF FF DB B6 DB 49 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF B6 00 B6 00 49 DB FF FF FF DB 24 6D 24 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 B6 6D 00 00 DB FF 6D 00 00 00 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 00 6D FF FF 00 00 DB 49 00 00 00 00 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 92 DB FF FF 6D 00 00 92 24 00 00 00 00 00 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF B6 FF FF 00 6D 00 00 24 00 00 00 00 00 00 24 92 DB FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 6D DB 00 00 00 00 00 00 00 00 00 00 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 00 24 00 00 6D 00 00 00 B6 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF +FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF DB 49 92 6D 6D DB B6 92 92 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF + + + diff --git a/grub-core/boot/powerpc/grub.chrp.in b/grub-core/boot/powerpc/grub.chrp.in new file mode 100644 index 000000000..9b2218352 --- /dev/null +++ b/grub-core/boot/powerpc/grub.chrp.in @@ -0,0 +1,172 @@ + + +MacRISC MacRISC3 MacRISC4 + + +@PACKAGE@ @VERSION@ + + +3434 +00000000000000F781FB8181818181FBFAF500000000000000000000000000000000000000F6FAFAFAFA81F9F600000000000000 +0000000000F8FBF9F500F95656FCFB5656FBF800000000000000000000000000000000F5FAF9F5F7F600F6F6F9FAF70000000000 +000000F5FBFA0056FDFEFEFDFDFAAC81FB56568181560000000000000000000000F9F9F9F7FCFDFEFEFEFFFC81F656FA00000000 +0000F5AC2BF7FBFEFEFD2BF6568181F9F7F6F6F8FBF50000000000000000000000FAF700F600F5F7F7F6F7FEFFACF82BFB000000 +0000FC2BF5FEFFFFF5F7FC81F70000F7F9FAFAF8000000000000000000000000000056F9F9FAF9F7F7FA812BF7FFFF56F7FA0000 +005656F5FEFFAC2BF9FA000000000000000000000000000000000000000000000000000000000000000000FA56FAFEFEF8F9F700 +00FB00F7FFFF56F9F800000000000000000000F656FAFA56F50000000000F5F8F9F8F5000000000000000000F9F7FCFFFB00FB00 +F8F800ACFFACF6FA000000000000000000F6FA562BF5F5F781FA000000F9FA2B00F556F9F5000000000000000081F8FFFEF6562B +810000FFFFF9FAF500000000000000002B8100F5F9FCACFBF82BFBF6FCFAF6FAFC81F600FA2B000000000000002BF8FEFFF8F5FA +FA00F5FEFFFA8100000000000000002B8100F9FEFFFFFFFFFFFBF6FDFEACFDFEFFFFFFFBF581F600000000000000F9FEFFF700FA +FA00FBFFFEF6F900000000000000F6FB00FCFFFFFFFFFFFFFFFFFCF600FCF7ACFEFFFFFFFDF6810000000000000056F9FFAC00FA +FA00F6FFFFF856000000000000F5FBF5ACFFFFFFFFFFFFFFFFFFFF2B002BF8F5ACFFFFFFFFFDF6FA000000000000F9FCFF560081 +FA0081FFFFF8F9000000000000FBF6FBFFFFFFFFFFFFFFFFFFFFFFF800F55600FCFFFFFFFFFF81F8F80000000000F981FFAC0081 +FA0000FEFEF8FB0000000000812BFAFFFFFFFFFFFFFEFFFFFDF92BFA0000F6F981ACFEFFFFFFFF56F9F600000000F9FDFF2B0081 +FA00FAFFFF81812B000000FAF8F9FFFFFEACFBF80000F500000000F9F900562B0000FCF7F9ACFFFF2BF9F50000F9F6FEFFFB0081 +810000FCFFFBF6FB56F7FBF8F9FFFE5600000000F5FAAC000000F82BF6FAFBF800F556F80000F9FFFE2BFAFAFAF8FAFFFEF60081 +FAF6F5ACFFFFAC00F856F7ACFFFCF500000000FAFCFFFC00000056AC00F581F92BFEF9FAF6000081FFFFFBF6F62BFFFFACF6F6FA +F6FA00FAFFFFFFACFA56FFFFAC0000000000F6FD2BFEF6F5565600F5F800F60081FEF7F656000000FDFFFFFDFDFFFFFFAC0081F5 +0081F52BFDFFFFFFFFFFFFFFF60000000000FBF6F6F5F656F52BF900FA000000FCFAF5F656000000F7FFFFFFFFFFFFFDF7F68100 +00F6FB00F8FDFFFFFFFFFF810000000000F6F5000000F52B56F9FC00F7F70000FCF881FCF500000000FEFFFFFFFFAC5600FBF500 +000056F900F8ACFDFFFFFFF5000000000000002B0000FDFEFFFE560000F60000F9ACFFFE810000000081FFFEFDFAF800FAF70000 +000000FAF9002B56FAFDFC0000000000000000F80000FBF5FEFEF5000000000000ACFFFA2B0000000000FEFB2BF5008156000000 +00000000F9FBF600F6FBF800000000000000F7F8000000F9F9F9F82B0000000000F6ACACF70000000000F7FD2BFA812B00000000 +0000000000F681FCFBFD0000000000000000FBF6000000000000F52B000000000000F92B810000000000008181F6000000000000 +0000000000000000F6FC00000000000000F6FF0000000000000000000000000000000081FBFB2B00000000F7F900000000000000 +000000000000000000FC00000000000000FCFAF600000000000000000000000000000056ACF581FBF700000081FB000000000000 +0000000000000000FAF90000000000008156F5F8000000000000002BFBFCFBF800000000FD2B000056FB8181FBF8000000000000 +0000000000000000AC0000000000F5FBF90000F6000000000000F5AC56F6005681F50000F6ACFCFBF70000000000000000000000 +00000000000000F881000000F5FAFDFD00000000000000000000F7FEFA2B0000F581F70000000000810000000000000000000000 +000000000000F9FCF500FAFDACFAF5FD00000000000000000000F5ACF5FDFEFA0000F82B00000000810000000000000000000000 +000000000000FCF8F9AC81FD000000FD000000000000FAF7000000F50081F9FAF800000000000000FB0000000000000000000000 +000000000000FC81F956F5FD000000FD0000000000000000F800F5000000000056000000000000F7FB0000000000000000000000 +00000000000000000000F5AC000000ACF800000000000000F5FAF80000000000F50000000000F8ACF50000000000000000000000 +00000000000000000000F5AC000000F5AC000000000000000056F9000000000000000000F7ACFCF5000000000000000000000000 +00000000000000000000F5FD00000000AC000000000000000000FB0000000000000000F5F6F5FCF6000000000000000000000000 +0000000000000000000000FD00000000FBFDF600000000000000F8F900000000000000000000F5FB000000000000000000000000 +0000000000000000000000FDF500000000F9ACF800000000000000815600000000F656818181AC56000000000000000000000000 +000000000000000000000081F80000000000F9FC0000000000000000F9ACACACFCFBFAFA81FD2B00000000000000000000000000 +0000000000000000000000F7FB0000000000FBF70000000000000000000000000000000000FB0000000000000000000000000000 +000000000000000000000000ACF500000000F9FD56F5000000000000000000000000000000FB0000000000000000000000000000 +000000000000000000000000F8FA0000000000F6FEFEF5000000000000F55681FCACFDACFC560000000000000000000000000000 +00000000000000000000000000FBF600000000002BFCFA00F700000000F9FDFDFAFEF90000000000000000000000000000000000 +00000000000000000000000000F5FB0000000000F5FEF7ACAC0000000000000000FCF50000000000000000000000000000000000 +0000000000000000000000000000F6FA000000002BFF2BFFFFAC00000000000000F7FA0000000000000000000000000000000000 +000000000000000000000000000000F65600000000FAFEFFFFAC0000000000F800F6810000000000000000000000000000000000 +00000000000000000000000000000000F52B00000000F8FEFBFF5600000000FCFAACF60000000000000000000000000000000000 +0000000000000000000000000000000000000000000000F9FCFCFFFB00F8FEFFFDF5000000000000000000000000000000000000 +00000000000000000000000000000000000000000000F9FDF7F5FFFD56FFFFFFFD00000000000000000000000000000000000000 +00000000000000000000000000000000000000000000810000FBFFFFFBFFFFFFFFACF9F5F5000000000000000000000000000000 +0000000000000000000000000000000000000000000000F600FC81FFFEFFFFFFFFFFFE8100000000000000000000000000000000 +00000000000000000000000000000000000000000000000000F7F6FDFFFFFFFEFFFFACF500000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000F5FC81FC81FAFBF9F500000000000000000000000000000000 + +00000000000000F781FB8181818181FBFAF500000000000000000000000000000000000000F6FAFAFAFA81F9F600000000000000 +0000000000F8FBF9F500F95656FCFB5656FBF800000000000000000000000000000000F5FAF9F5F7F600F6F6F9FAF70000000000 +000000F5FBFA0056FDFEFEFDFDFAAC81FB56568181560000000000000000000000F9F9F9F7FCFDFEFEFEFFFC81F656FA00000000 +0000F5AC2BF7FBFEFEFD2BF6568181F9F7F6F6F8FBF50000000000000000000000FAF700F600F5F7F7F6F7FEFFACF82BFB000000 +0000FC2BF5FEFFFFF5F7FC81F70000F7F9FAFAF8000000000000000000000000000056F9F9FAF9F7F7FA812BF7FFFF56F7FA0000 +005656F5FEFFAC2BF9FA000000000000000000000000000000000000000000000000000000000000000000FA56FAFEFEF8F9F700 +00FB00F7FFFF56F9F800000000000000000000F656FAFA56F50000000000F5F8F9F8F5000000000000000000F9F7FCFFFB00FB00 +F8F800ACFFACF6FA000000000000000000F6FA562BF5F5F781FA000000F9FA2B00F556F9F5000000000000000081F8FFFEF6562B +810000FFFFF9FAF500000000000000002B8100F5F9FCACFBF82BFBF6FCFAF6FAFC81F600FA2B000000000000002BF8FEFFF8F5FA +FA00F5FEFFFA8100000000000000002B8100F9FEFFFFFFFFFFFBF6FDFEACFDFEFFFFFFFBF581F600000000000000F9FEFFF700FA +FA00FBFFFEF6F900000000000000F6FB00FCFFFFFFFFFFFFFFFFFCF600FCF7ACFEFFFFFFFDF6810000000000000056F9FFAC00FA +FA00F6FFFFF856000000000000F5FBF5ACFFFFFFFFFFFFFFFFFFFF2B002BF8F5ACFFFFFFFFFDF6FA000000000000F9FCFF560081 +FA0081FFFFF8F9000000000000FBF6FBFFFFFFFFFFFFFFFFFFFFFFF800F55600FCFFFFFFFFFF81F8F80000000000F981FFAC0081 +FA0000FEFEF8FB0000000000812BFAFFFFFFFFFFFFFEFFFFFDF92BFA0000F6F981ACFEFFFFFFFF56F9F600000000F9FDFF2B0081 +FA00FAFFFF81812B000000FAF8F9FFFFFEACFBF80000F500000000F9F900562B0000FCF7F9ACFFFF2BF9F50000F9F6FEFFFB0081 +810000FCFFFBF6FB56F7FBF8F9FFFE5600000000F5FAAC000000F82BF6FAFBF800F556F80000F9FFFE2BFAFAFAF8FAFFFEF60081 +FAF6F5ACFFFFAC00F856F7ACFFFCF500000000FAFCFFFC00000056AC00F581F92BFEF9FAF6000081FFFFFBF6F62BFFFFACF6F6FA +F6FA00FAFFFFFFACFA56FFFFAC0000000000F6FD2BFEF6F5565600F5F800F60081FEF7F656000000FDFFFFFDFDFFFFFFAC0081F5 +0081F52BFDFFFFFFFFFFFFFFF60000000000FBF6F6F5F656F52BF900FA000000FCFAF5F656000000F7FFFFFFFFFFFFFDF7F68100 +00F6FB00F8FDFFFFFFFFFF810000000000F6F5000000F52B56F9FC00F7F70000FCF881FCF500000000FEFFFFFFFFAC5600FBF500 +000056F900F8ACFDFFFFFFF5000000000000002B0000FDFEFFFE560000F60000F9ACFFFE810000000081FFFEFDFAF800FAF70000 +000000FAF9002B56FAFDFC0000000000000000F80000FBF5FEFEF5000000000000ACFFFA2B0000000000FEFB2BF5008156000000 +00000000F9FBF600F6FBF800000000000000F7F8000000F9F9F9F82B0000000000F6ACACF70000000000F7FD2BFA812B00000000 +0000000000F681FCFBFD0000000000000000FBF6000000000000F52B000000000000F92B810000000000008181F6000000000000 +0000000000000000F6FC00000000000000F6FF0000000000000000000000000000000081FBFB2B00000000F7F900000000000000 +000000000000000000FC00000000000000FCFAF600000000000000000000000000000056ACF581FBF700000081FB000000000000 +0000000000000000FAF90000000000008156F5F8000000000000002BFBFCFBF800000000FD2B000056FB8181FBF8000000000000 +0000000000000000AC0000000000F5FBF90000F6000000000000F5AC56F6005681F50000F6ACFCFBF70000000000000000000000 +00000000000000F881000000F5FAFDFD00000000000000000000F7FEFA2B0000F581F70000000000810000000000000000000000 +000000000000F9FCF500FAFDACFAF5FD00000000000000000000F5ACF5FDFEFA0000F82B00000000810000000000000000000000 +000000000000FCF8F9AC81FD000000FD000000000000FAF7000000F50081F9FAF800000000000000FB0000000000000000000000 +000000000000FC81F956F5FD000000FD0000000000000000F800F5000000000056000000000000F7FB0000000000000000000000 +00000000000000000000F5AC000000ACF800000000000000F5FAF80000000000F50000000000F8ACF50000000000000000000000 +00000000000000000000F5AC000000F5AC000000000000000056F9000000000000000000F7ACFCF5000000000000000000000000 +00000000000000000000F5FD00000000AC000000000000000000FB0000000000000000F5F6F5FCF6000000000000000000000000 +0000000000000000000000FD00000000FBFDF600000000000000F8F900000000000000000000F5FB000000000000000000000000 +0000000000000000000000FDF500000000F9ACF800000000000000815600000000F656818181AC56000000000000000000000000 +000000000000000000000081F80000000000F9FC0000000000000000F9ACACACFCFBFAFA81FD2B00000000000000000000000000 +0000000000000000000000F7FB0000000000FBF70000000000000000000000000000000000FB0000000000000000000000000000 +000000000000000000000000ACF500000000F9FD56F5000000000000000000000000000000FB0000000000000000000000000000 +000000000000000000000000F8FA0000000000F6FEFEF5000000000000F55681FCACFDACFC560000000000000000000000000000 +00000000000000000000000000FBF600000000002BFCFA00F700000000F9FDFDFAFEF90000000000000000000000000000000000 +00000000000000000000000000F5FB0000000000F5FEF7ACAC0000000000000000FCF50000000000000000000000000000000000 +0000000000000000000000000000F6FA000000002BFF2BFFFFAC00000000000000F7FA0000000000000000000000000000000000 +000000000000000000000000000000F65600000000FAFEFFFFAC0000000000F800F6810000000000000000000000000000000000 +00000000000000000000000000000000F52B00000000F8FEFBFF5600000000FCFAACF60000000000000000000000000000000000 +0000000000000000000000000000000000000000000000F9FCFCFFFB00F8FEFFFDF5000000000000000000000000000000000000 +00000000000000000000000000000000000000000000F9FDF7F5FFFD56FFFFFFFD00000000000000000000000000000000000000 +00000000000000000000000000000000000000000000810000FBFFFFFBFFFFFFFFACF9F5F5000000000000000000000000000000 +0000000000000000000000000000000000000000000000F600FC81FFFEFFFFFFFFFFFE8100000000000000000000000000000000 +00000000000000000000000000000000000000000000000000F7F6FDFFFFFFFEFFFFACF500000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000F5FC81FC81FAFBF9F500000000000000000000000000000000 + +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF + + +boot &device;:&partition;,\System\Library\CoreServices\grub.elf + + diff --git a/boot/sparc64/ieee1275/boot.S b/grub-core/boot/sparc64/ieee1275/boot.S similarity index 58% rename from boot/sparc64/ieee1275/boot.S rename to grub-core/boot/sparc64/ieee1275/boot.S index 74f4ee014..ff8a79d3b 100644 --- a/boot/sparc64/ieee1275/boot.S +++ b/grub-core/boot/sparc64/ieee1275/boot.S @@ -17,11 +17,28 @@ * along with GRUB. If not, see . */ -#include #include .text .align 4 + /* + * We're writing the a.out header ourselves as newer + * upstream versions of binutils no longer support + * the a.out format on sparc64. + * + * The boot loader fits into 512 bytes with 32 bytes + * used for the a.out header, hence the text segment + * size is 512 - 32. There is no data segment and no + * code relocation, thus those fields remain zero. + */ + .word 0x1030107 /* Magic number. */ + .word 512 - GRUB_BOOT_AOUT_HEADER_SIZE /* Size of text segment. */ + .word 0 /* Size of initialized data. */ + .word 0 /* Size of uninitialized data. */ + .word 0 /* Size of symbol table || checksum. */ + .word _start /* Entry point. */ + .word 0 /* Size of text relocation. */ + .word 0 /* Size of data relocation. */ .globl _start _start: /* OF CIF entry point arrives in %o4 */ @@ -29,11 +46,9 @@ pic_base: call boot_continue mov %o4, CIF_REG - . = _start + GRUB_BOOT_MACHINE_VER_MAJ -boot_version: .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR - +#ifndef CDBOOT /* The offsets to these locations are defined by the - * GRUB_BOOT_MACHINE_foo macros in include/grub/sparc/ieee1275/boot.h, + * GRUB_BOOT_MACHINE_foo macros in include/grub/sparc64/ieee1275/boot.h, * and grub-setup uses this to patch these next three values as needed. * * The boot_path will be the OF device path of the partition where the @@ -43,11 +58,27 @@ boot_version: .byte GRUB_BOOT_VERSION_MAJOR, GRUB_BOOT_VERSION_MINOR * * After loading in that block we will execute it by jumping to the * load address plus the size of the prepended A.OUT header (32 bytes). + * + * Since this assembly code includes the 32 bytes long a.out header, + * we need to move the actual code entry point forward by the size + * of the a.out header, i.e. += GRUB_BOOT_AOUT_HEADER_SIZE. */ + .org GRUB_BOOT_MACHINE_BOOT_DEVPATH + GRUB_BOOT_AOUT_HEADER_SIZE boot_path: - . = _start + GRUB_BOOT_MACHINE_KERNEL_SECTOR -kernel_sector: .xword 2 + .org GRUB_BOOT_MACHINE_KERNEL_BYTE + GRUB_BOOT_AOUT_HEADER_SIZE +boot_path_end: +kernel_byte: .xword (2 << 9) kernel_address: .word GRUB_BOOT_MACHINE_KERNEL_ADDR +#else +#define boot_path (_start + 512 + SCRATCH_PAD_BOOT_SIZE) +#define boot_path_end (_start + 1024) +#include + + .org 8 + GRUB_BOOT_AOUT_HEADER_SIZE +kernel_byte: .xword (2 << 9) +kernel_size: .word 512 +kernel_address: .word GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS +#endif prom_finddev_name: .asciz "finddevice" prom_chosen_path: .asciz "/chosen" @@ -60,6 +91,10 @@ prom_seek_name: .asciz "seek" prom_read_name: .asciz "read" prom_exit_name: .asciz "exit" grub_name: .asciz "GRUB " +#ifdef CDBOOT +prom_close_name: .asciz "close" +#endif + #define GRUB_NAME_LEN 5 .align 4 @@ -77,11 +112,23 @@ prom_error: /* %o0: OF call name * %o1: input arg 1 */ -prom_call_1_1: - mov 1, %g1 - ba prom_call - mov 1, %o5 +prom_call_1_1_o2: + clr %o2 + ba prom_call_x_1 + mov 1, %g1 +prom_call_getprop: + mov 4, %g1 + stx %g1, [%l1 + 256] + mov CHOSEN_NODE_REG, %o1 + ba prom_call_x_1 + GET_ABS(prom_getprop_name, %o0) + +prom_call_3_1_o1: + ba prom_call_3_1 + mov BOOTDEV_REG, %o1 + + /* %o2: message string * %o3: message length */ @@ -95,8 +142,9 @@ console_write: * %o2: input arg 2 * %o3: input arg 3 */ -prom_call_3_1: +prom_call_3_1: mov 3, %g1 +prom_call_x_1: mov 1, %o5 /* fallthru */ @@ -118,7 +166,11 @@ prom_call: boot_continue: mov %o7, PIC_REG /* PIC base */ - sethi %hi(SCRATCH_PAD), %l1 /* OF argument slots */ +#ifndef CDBOOT + sethi %hi(SCRATCH_PAD_BOOT), %l1 /* OF argument slots */ +#else + GET_ABS(_start + 512, %l1) /* OF argument slots */ +#endif /* Find the /chosen node so we can fetch the stdout handle, * and thus perform console output. @@ -126,23 +178,17 @@ boot_continue: * chosen_node = prom_finddevice("/chosen") */ GET_ABS(prom_finddev_name, %o0) - GET_ABS(prom_chosen_path, %o1) - call prom_call_1_1 - clr %o2 + call prom_call_1_1_o2 + GET_ABS(prom_chosen_path, %o1) ldx [%l1 + 0x20], CHOSEN_NODE_REG brz CHOSEN_NODE_REG, prom_error /* getprop(chosen_node, "stdout", &buffer, buffer_size) */ - GET_ABS(prom_getprop_name, %o0) - mov 4, %g1 - mov 1, %o5 - mov CHOSEN_NODE_REG, %o1 - GET_ABS(prom_stdout_name, %o2) + GET_ABS(prom_stdout_name, %o2) add %l1, 256, %o3 - mov 1024, %o4 - call prom_call - stx %g1, [%l1 + 256] + call prom_call_getprop + mov 1024, %o4 lduw [%l1 + 256], STDOUT_NODE_REG brz,pn STDOUT_NODE_REG, prom_error @@ -152,15 +198,27 @@ boot_continue: call console_write mov GRUB_NAME_LEN, %o3 + GET_ABS(boot_path, %o3) +#ifndef CDBOOT + ldub [%o3], %o1 + brnz,pn %o1, bootpath_known +#endif + + /* getprop(chosen_node, "bootpath", &buffer, buffer_size) */ + GET_ABS(prom_bootpath_name, %o2) + call prom_call_getprop + mov (boot_path_end - boot_path), %o4 + +bootpath_known: + /* Open up the boot_path, and use that handle to read the * first block of the GRUB kernel image. * * bootdev_handle = open(boot_path) */ GET_ABS(prom_open_name, %o0) - GET_ABS(boot_path, %o1) - call prom_call_1_1 - clr %o2 + call prom_call_1_1_o2 + GET_ABS(boot_path, %o1) ldx [%l1 + 0x20], BOOTDEV_REG brz,pn BOOTDEV_REG, prom_open_error @@ -168,29 +226,37 @@ boot_continue: /* Since we have 64-bit cells, the high cell of the seek offset * is zero and the low cell is the entire value. * - * seek(bootdev, 0, *kernel_sector << 9) + * seek(bootdev, 0, *kernel_byte) */ GET_ABS(prom_seek_name, %o0) - mov BOOTDEV_REG, %o1 clr %o2 - LDX_ABS(kernel_sector, 0x00, %o3) - call prom_call_3_1 - sllx %o3, 9, %o3 + call prom_call_3_1_o1 + LDX_ABS(kernel_byte, 0x00, %o3) /* read(bootdev, *kernel_address, 512) */ GET_ABS(prom_read_name, %o0) - mov BOOTDEV_REG, %o1 LDUW_ABS(kernel_address, 0x00, %o2) - call prom_call_3_1 + call prom_call_3_1_o1 +#ifdef CDBOOT + LDUW_ABS(kernel_size, 0x00, %o3) + + GET_ABS(prom_close_name, %o0) + mov 1, %g1 + mov 0, %o5 + call prom_call + mov BOOTDEV_REG, %o1 +#else mov 512, %o3 +#endif LDUW_ABS(kernel_address, 0x00, %o2) jmpl %o2, %o7 +#ifdef CDBOOT + mov CIF_REG, %o4 +#else nop - -1: ba,a 1b - - . = _start + GRUB_BOOT_MACHINE_CODE_END +#endif + .org GRUB_BOOT_MACHINE_CODE_END /* the last 4 bytes in the sector 0 contain the signature */ .word GRUB_BOOT_MACHINE_SIGNATURE diff --git a/boot/sparc64/ieee1275/diskboot.S b/grub-core/boot/sparc64/ieee1275/diskboot.S similarity index 86% rename from boot/sparc64/ieee1275/diskboot.S rename to grub-core/boot/sparc64/ieee1275/diskboot.S index 68ed0eee0..35e02c1b6 100644 --- a/boot/sparc64/ieee1275/diskboot.S +++ b/grub-core/boot/sparc64/ieee1275/diskboot.S @@ -17,8 +17,8 @@ * along with GRUB. If not, see . */ -#include #include +#include .text .align 4 @@ -81,14 +81,14 @@ prom_call: after_info_block: - sethi %hi(SCRATCH_PAD), %l1 /* OF argument slots */ + sethi %hi(SCRATCH_PAD_DISKBOOT), %l1 /* OF argument slots */ GET_ABS(notification_string, %o2) call console_write mov NOTIFICATION_STRING_LEN, %o3 - GET_ABS(firstlist - GRUB_BOOT_MACHINE_LIST_SIZE, %l2) - set GRUB_BOOT_MACHINE_IMAGE_ADDRESS, %l3 + GET_ABS(firstlist - GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE, %l2) + set GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS, %l3 bootloop: lduw [%l2 + 0x08], %o0 brz %o0, bootit @@ -115,7 +115,7 @@ bootloop: mov NOTIFICATION_STEP_LEN, %o3 ba bootloop - sub %l2, GRUB_BOOT_MACHINE_LIST_SIZE, %l2 + sub %l2, GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE, %l2 bootit: GET_ABS(prom_close_name, %o0) @@ -127,16 +127,16 @@ bootit: GET_ABS(notification_done, %o2) call console_write mov NOTIFICATION_DONE_LEN, %o3 - sethi %hi(GRUB_BOOT_MACHINE_IMAGE_ADDRESS), %o2 - jmpl %o2 + %lo(GRUB_BOOT_MACHINE_IMAGE_ADDRESS), %o7 - mov CIF_REG, %o0 + sethi %hi(GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS), %o2 + jmpl %o2 + %lo(GRUB_BOOT_SPARC64_IEEE1275_IMAGE_ADDRESS), %o7 + mov CIF_REG, %o4 1: ba,a 1b lastlist: .word 0 .word 0 - . = _start + (0x200 - GRUB_BOOT_MACHINE_LIST_SIZE) + .org (0x200 - GRUB_BOOT_SPARC64_IEEE1275_LIST_SIZE) blocklist_default_start: .word 0 .word 2 diff --git a/grub-core/bus/bonito.c b/grub-core/bus/bonito.c new file mode 100644 index 000000000..23b9a9915 --- /dev/null +++ b/grub-core/bus/bonito.c @@ -0,0 +1,176 @@ +/* bonito.c - PCI bonito interface. */ +/* + * 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 . + */ + +#include +#include + +static grub_uint32_t base_win[GRUB_MACHINE_PCI_NUM_WIN]; +static const grub_size_t sizes_win[GRUB_MACHINE_PCI_NUM_WIN] = + {GRUB_MACHINE_PCI_WIN1_SIZE, GRUB_MACHINE_PCI_WIN_SIZE, + GRUB_MACHINE_PCI_WIN_SIZE}; +/* Usage counters. */ +static int usage_win[GRUB_MACHINE_PCI_NUM_WIN]; +static grub_addr_t addr_win[GRUB_MACHINE_PCI_NUM_WIN] = + {GRUB_MACHINE_PCI_WIN1_ADDR, GRUB_MACHINE_PCI_WIN2_ADDR, + GRUB_MACHINE_PCI_WIN3_ADDR}; + +grub_bonito_type_t grub_bonito_type; + +static volatile void * +config_addr (grub_pci_address_t addr) +{ + if (grub_bonito_type == GRUB_BONITO_2F) + { + GRUB_MACHINE_PCI_CONF_CTRL_REG_2F = 1 << ((addr >> 11) & 0xf); + return (volatile void *) (GRUB_MACHINE_PCI_CONFSPACE_2F + | (addr & 0x07ff)); + } + else + { + + if (addr >> 16) + return (volatile void *) (GRUB_MACHINE_PCI_CONFSPACE_3A_EXT | addr); + else + return (volatile void *) (GRUB_MACHINE_PCI_CONFSPACE_3A | addr); + } +} + +grub_uint32_t +grub_pci_read (grub_pci_address_t addr) +{ + return *(volatile grub_uint32_t *) config_addr (addr); +} + +grub_uint16_t +grub_pci_read_word (grub_pci_address_t addr) +{ + return *(volatile grub_uint16_t *) config_addr (addr); +} + +grub_uint8_t +grub_pci_read_byte (grub_pci_address_t addr) +{ + return *(volatile grub_uint8_t *) config_addr (addr); +} + +void +grub_pci_write (grub_pci_address_t addr, grub_uint32_t data) +{ + *(volatile grub_uint32_t *) config_addr (addr) = data; +} + +void +grub_pci_write_word (grub_pci_address_t addr, grub_uint16_t data) +{ + *(volatile grub_uint16_t *) config_addr (addr) = data; +} + +void +grub_pci_write_byte (grub_pci_address_t addr, grub_uint8_t data) +{ + *(volatile grub_uint8_t *) config_addr (addr) = data; +} + + +static inline void +write_bases_2f (void) +{ + int i; + grub_uint32_t reg = 0; + for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) + reg |= (((base_win[i] >> GRUB_MACHINE_PCI_WIN_SHIFT) + & GRUB_MACHINE_PCI_WIN_MASK) + << (i * GRUB_MACHINE_PCI_WIN_MASK_SIZE)); + GRUB_MACHINE_PCI_IO_CTRL_REG_2F = reg; +} + +volatile void * +grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), + grub_addr_t base, grub_size_t size) +{ + if (grub_bonito_type == GRUB_BONITO_2F) + { + int i; + grub_addr_t newbase; + + /* First try already used registers. */ + for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) + if (usage_win[i] && base_win[i] <= base + && base_win[i] + sizes_win[i] > base + size) + { + usage_win[i]++; + return (void *) + (addr_win[i] | (base & GRUB_MACHINE_PCI_WIN_OFFSET_MASK)); + } + /* Map new register. */ + newbase = base & ~GRUB_MACHINE_PCI_WIN_OFFSET_MASK; + for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) + if (!usage_win[i] && newbase <= base + && newbase + sizes_win[i] > base + size) + { + usage_win[i]++; + base_win[i] = newbase; + write_bases_2f (); + return (void *) + (addr_win[i] | (base & GRUB_MACHINE_PCI_WIN_OFFSET_MASK)); + } + grub_fatal ("Out of PCI windows."); + } + else + { + int region = 0; + if (base >= 0x10000000 + && base + size <= 0x18000000) + region = 1; + if (base >= 0x1c000000 + && base + size <= 0x1f000000) + region = 2; + if (region == 0) + grub_fatal ("Attempt to map out of regions"); + return (void *) (0xa0000000 | base); + } +} + +void * +grub_pci_device_map_range_cached (grub_pci_device_t dev, + grub_addr_t base, grub_size_t size) +{ + return (void *) (((grub_addr_t) grub_pci_device_map_range (dev, base, size)) + & ~0x20000000); +} + +void +grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), + volatile void *mem, + grub_size_t size __attribute__ ((unused))) +{ + if (grub_bonito_type == GRUB_BONITO_2F) + { + int i; + for (i = 0; i < GRUB_MACHINE_PCI_NUM_WIN; i++) + if (usage_win[i] && addr_win[i] + == (((grub_addr_t) mem | 0x20000000) + & ~GRUB_MACHINE_PCI_WIN_OFFSET_MASK)) + { + usage_win[i]--; + return; + } + grub_fatal ("Tried to unmap not mapped region"); + } +} diff --git a/grub-core/bus/cs5536.c b/grub-core/bus/cs5536.c new file mode 100644 index 000000000..cd0a45e58 --- /dev/null +++ b/grub-core/bus/cs5536.c @@ -0,0 +1,382 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#ifdef GRUB_MACHINE_MIPS_LOONGSON +#include +#endif + +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Context for grub_cs5536_find. */ +struct grub_cs5536_find_ctx +{ + grub_pci_device_t *devp; + int found; +}; + +/* Helper for grub_cs5536_find. */ +static int +grub_cs5536_find_iter (grub_pci_device_t dev, grub_pci_id_t pciid, void *data) +{ + struct grub_cs5536_find_ctx *ctx = data; + + if (pciid == GRUB_CS5536_PCIID) + { + *ctx->devp = dev; + ctx->found = 1; + return 1; + } + return 0; +} + +int +grub_cs5536_find (grub_pci_device_t *devp) +{ + struct grub_cs5536_find_ctx ctx = { + .devp = devp, + .found = 0 + }; + + grub_pci_iterate (grub_cs5536_find_iter, &ctx); + + return ctx.found; +} + +grub_uint64_t +grub_cs5536_read_msr (grub_pci_device_t dev, grub_uint32_t addr) +{ + grub_uint64_t ret = 0; + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + ret = (grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0)); + ret |= (((grub_uint64_t) + grub_pci_read (grub_pci_make_address (dev, + GRUB_CS5536_MSR_MAILBOX_DATA1))) + << 32); + return ret; +} + +void +grub_cs5536_write_msr (grub_pci_device_t dev, grub_uint32_t addr, + grub_uint64_t val) +{ + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_ADDR), + addr); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA0), + val & 0xffffffff); + grub_pci_write (grub_pci_make_address (dev, GRUB_CS5536_MSR_MAILBOX_DATA1), + val >> 32); +} + +grub_err_t +grub_cs5536_smbus_wait (grub_port_t smbbase) +{ + grub_uint64_t start = grub_get_time_ms (); + while (1) + { + grub_uint8_t status; + status = grub_inb (smbbase + GRUB_CS5536_SMB_REG_STATUS); + if (status & GRUB_CS5536_SMB_REG_STATUS_SDAST) + return GRUB_ERR_NONE; + if (status & GRUB_CS5536_SMB_REG_STATUS_BER) + return grub_error (GRUB_ERR_IO, "SM bus error"); + if (status & GRUB_CS5536_SMB_REG_STATUS_NACK) + return grub_error (GRUB_ERR_IO, "NACK received"); + if (grub_get_time_ms () > start + 40) + return grub_error (GRUB_ERR_IO, "SM stalled"); + } +} + +grub_err_t +grub_cs5536_read_spd_byte (grub_port_t smbbase, grub_uint8_t dev, + grub_uint8_t addr, grub_uint8_t *res) +{ + grub_err_t err; + + /* Send START. */ + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (dev << 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send ACK. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_ACK, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send byte address. */ + grub_outb (addr, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send START. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_START, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Send device address. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb ((dev << 1) | 1, smbbase + GRUB_CS5536_SMB_REG_DATA); + + /* Send STOP. */ + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + grub_outb (grub_inb (smbbase + GRUB_CS5536_SMB_REG_CTRL1) + | GRUB_CS5536_SMB_REG_CTRL1_STOP, + smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + err = grub_cs5536_smbus_wait (smbbase); + if (err) + return err; + *res = grub_inb (smbbase + GRUB_CS5536_SMB_REG_DATA); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_init_smbus (grub_pci_device_t dev, grub_uint16_t divisor, + grub_port_t *smbbase) +{ + grub_uint64_t smbbar; + + smbbar = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_SMB_BAR); + + /* FIXME */ + if (!(smbbar & GRUB_CS5536_LBAR_ENABLE)) + return grub_error(GRUB_ERR_IO, "SMB controller not enabled\n"); + *smbbase = (smbbar & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE; + + if (divisor < 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid divisor"); + + /* Disable SMB. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + /* Disable interrupts. */ + grub_outb (0, *smbbase + GRUB_CS5536_SMB_REG_CTRL1); + + /* Set as master. */ + grub_outb (GRUB_CS5536_SMB_REG_ADDR_MASTER, + *smbbase + GRUB_CS5536_SMB_REG_ADDR); + + /* Launch. */ + grub_outb (((divisor >> 7) & 0xff), *smbbase + GRUB_CS5536_SMB_REG_CTRL3); + grub_outb (((divisor << 1) & 0xfe) | GRUB_CS5536_SMB_REG_CTRL2_ENABLE, + *smbbase + GRUB_CS5536_SMB_REG_CTRL2); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_cs5536_read_spd (grub_port_t smbbase, grub_uint8_t dev, + struct grub_smbus_spd *res) +{ + grub_err_t err; + grub_size_t size; + grub_uint8_t b; + grub_size_t ptr; + + err = grub_cs5536_read_spd_byte (smbbase, dev, 0, &b); + if (err) + return err; + if (b == 0) + return grub_error (GRUB_ERR_IO, "no SPD found"); + size = b; + + ((grub_uint8_t *) res)[0] = b; + for (ptr = 1; ptr < size; ptr++) + { + err = grub_cs5536_read_spd_byte (smbbase, dev, ptr, + &((grub_uint8_t *) res)[ptr]); + if (err) + return err; + } + return GRUB_ERR_NONE; +} + +static inline void +set_io_space (grub_pci_device_t dev, int num, grub_uint16_t start, + grub_uint16_t len) +{ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_REGIONS_START + num, + ((((grub_uint64_t) start + len - 4) + << GRUB_CS5536_MSR_GL_REGION_IO_TOP_SHIFT) + & GRUB_CS5536_MSR_GL_REGION_TOP_MASK) + | (((grub_uint64_t) start + << GRUB_CS5536_MSR_GL_REGION_IO_BASE_SHIFT) + & GRUB_CS5536_MSR_GL_REGION_BASE_MASK) + | GRUB_CS5536_MSR_GL_REGION_IO + | GRUB_CS5536_MSR_GL_REGION_ENABLE); +} + +static inline void +set_iod (grub_pci_device_t dev, int num, int dest, int start, int mask) +{ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_IOD_START + num, + ((grub_uint64_t) dest << GRUB_CS5536_IOD_DEST_SHIFT) + | (((grub_uint64_t) start & GRUB_CS5536_IOD_ADDR_MASK) + << GRUB_CS5536_IOD_BASE_SHIFT) + | ((mask & GRUB_CS5536_IOD_ADDR_MASK) + << GRUB_CS5536_IOD_MASK_SHIFT)); +} + +static inline void +set_p2d (grub_pci_device_t dev, int num, int dest, grub_uint32_t start) +{ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_P2D_START + num, + (((grub_uint64_t) dest) << GRUB_CS5536_P2D_DEST_SHIFT) + | ((grub_uint64_t) (start >> GRUB_CS5536_P2D_LOG_ALIGN) + << GRUB_CS5536_P2D_BASE_SHIFT) + | (((1 << (32 - GRUB_CS5536_P2D_LOG_ALIGN)) - 1) + << GRUB_CS5536_P2D_MASK_SHIFT)); +} + +void +grub_cs5536_init_geode (grub_pci_device_t dev) +{ + /* Enable more BARs. */ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IRQ_MAP_BAR, + GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_IRQ_MAP); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_MFGPT_BAR, + GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_MFGPT); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_ACPI_BAR, + GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_ACPI); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_PM_BAR, + GRUB_CS5536_LBAR_TURN_ON | GRUB_CS5536_LBAR_PM); + + /* Setup DIVIL. */ +#ifdef GRUB_MACHINE_MIPS_LOONGSON + switch (grub_arch_machine) + { + case GRUB_ARCH_MACHINE_YEELOONG: + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LEG_IO, + GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP + | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1); + break; + case GRUB_ARCH_MACHINE_FULOONG2F: + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LEG_IO, + GRUB_CS5536_MSR_DIVIL_LEG_IO_UART2_COM3 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_UART1_COM1 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_MODE_X86 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_F_REMAP + | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE0 + | GRUB_CS5536_MSR_DIVIL_LEG_IO_RTC_ENABLE1); + break; + } +#endif + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_PRIMARY_MASK, + (~GRUB_CS5536_DIVIL_LPC_INTERRUPTS) & 0xffff); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_IRQ_MAPPER_LPC_MASK, + GRUB_CS5536_DIVIL_LPC_INTERRUPTS); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL, + GRUB_CS5536_MSR_DIVIL_LPC_SERIAL_IRQ_CONTROL_ENABLE); + + /* Initialise USB controller. */ + /* FIXME: assign adresses dynamically. */ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE, + GRUB_CS5536_MSR_USB_BASE_BUS_MASTER + | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE + | 0x05024000); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE, + GRUB_CS5536_MSR_USB_BASE_BUS_MASTER + | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE + | (0x20ULL << GRUB_CS5536_MSR_USB_EHCI_BASE_FLDJ_SHIFT) + | 0x05023000); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_CONTROLLER_BASE, + GRUB_CS5536_MSR_USB_BASE_BUS_MASTER + | GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE | 0x05020000); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OPTION_CONTROLLER_BASE, + GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE | 0x05022000); + set_p2d (dev, 0, GRUB_CS5536_DESTINATION_USB, 0x05020000); + set_p2d (dev, 1, GRUB_CS5536_DESTINATION_USB, 0x05022000); + set_p2d (dev, 5, GRUB_CS5536_DESTINATION_USB, 0x05024000); + set_p2d (dev, 6, GRUB_CS5536_DESTINATION_USB, 0x05023000); + + { + volatile grub_uint32_t *oc; + + oc = grub_absolute_pointer (grub_pci_device_map_range (dev, 0x05022000, + GRUB_CS5536_USB_OPTION_REGS_SIZE)); + + oc[GRUB_CS5536_USB_OPTION_REG_UOCMUX] = + (oc[GRUB_CS5536_USB_OPTION_REG_UOCMUX] + & ~GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_MASK) + | GRUB_CS5536_USB_OPTION_REG_UOCMUX_PMUX_HC; + grub_pci_device_unmap_range (dev, oc, GRUB_CS5536_USB_OPTION_REGS_SIZE); + } + + /* Setup IDE controller. */ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_IO_BAR, + GRUB_CS5536_LBAR_IDE + | GRUB_CS5536_MSR_IDE_IO_BAR_UNITS); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_CFG, + GRUB_CS5536_MSR_IDE_CFG_CHANNEL_ENABLE); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_TIMING, + (GRUB_CS5536_MSR_IDE_TIMING_PIO0 + << GRUB_CS5536_MSR_IDE_TIMING_DRIVE0_SHIFT) + | (GRUB_CS5536_MSR_IDE_TIMING_PIO0 + << GRUB_CS5536_MSR_IDE_TIMING_DRIVE1_SHIFT)); + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_IDE_CAS_TIMING, + (GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_PIO0 + << GRUB_CS5536_MSR_IDE_CAS_TIMING_CMD_SHIFT) + | (GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0 + << GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE0_SHIFT) + | (GRUB_CS5536_MSR_IDE_CAS_TIMING_PIO0 + << GRUB_CS5536_MSR_IDE_CAS_TIMING_DRIVE1_SHIFT)); + + /* Setup Geodelink PCI. */ + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_GL_PCI_CTRL, + (4ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_OUT_THR_SHIFT) + | (4ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_IN_THR_SHIFT) + | (8ULL << GRUB_CS5536_MSR_GL_PCI_CTRL_LATENCY_SHIFT) + | GRUB_CS5536_MSR_GL_PCI_CTRL_IO_ENABLE + | GRUB_CS5536_MSR_GL_PCI_CTRL_MEMORY_ENABLE); + + /* Setup windows. */ + set_io_space (dev, 0, GRUB_CS5536_LBAR_SMBUS, GRUB_CS5536_SMBUS_REGS_SIZE); + set_io_space (dev, 1, GRUB_CS5536_LBAR_GPIO, GRUB_CS5536_GPIO_REGS_SIZE); + set_io_space (dev, 2, GRUB_CS5536_LBAR_MFGPT, GRUB_CS5536_MFGPT_REGS_SIZE); + set_io_space (dev, 3, GRUB_CS5536_LBAR_IRQ_MAP, GRUB_CS5536_IRQ_MAP_REGS_SIZE); + set_io_space (dev, 4, GRUB_CS5536_LBAR_PM, GRUB_CS5536_PM_REGS_SIZE); + set_io_space (dev, 5, GRUB_CS5536_LBAR_ACPI, GRUB_CS5536_ACPI_REGS_SIZE); + set_iod (dev, 0, GRUB_CS5536_DESTINATION_IDE, GRUB_ATA_CH0_PORT1, 0xffff8); + set_iod (dev, 1, GRUB_CS5536_DESTINATION_ACC, GRUB_CS5536_LBAR_ACC, 0xfff80); + set_iod (dev, 2, GRUB_CS5536_DESTINATION_IDE, GRUB_CS5536_LBAR_IDE, 0xffff0); +} diff --git a/util/pci.c b/grub-core/bus/emu/pci.c similarity index 85% rename from util/pci.c rename to grub-core/bus/emu/pci.c index 420ae320b..267f2622d 100644 --- a/util/pci.c +++ b/grub-core/bus/emu/pci.c @@ -19,6 +19,7 @@ #include #include +#include #include grub_pci_address_t @@ -31,7 +32,7 @@ grub_pci_make_address (grub_pci_device_t dev, int reg) } void -grub_pci_iterate (grub_pci_iteratefunc_t hook) +grub_pci_iterate (grub_pci_iteratefunc_t hook, void *hook_data) { struct pci_device_iterator *iter; struct pci_slot_match slot; @@ -42,7 +43,7 @@ grub_pci_iterate (grub_pci_iteratefunc_t hook) slot.func = PCI_MATCH_ANY; iter = pci_slot_match_iterator_create (&slot); while ((dev = pci_device_next (iter))) - hook (dev, dev->vendor_id | (dev->device_id << 16)); + hook (dev, dev->vendor_id | (dev->device_id << 16), hook_data); pci_iterator_destroy (iter); } @@ -54,7 +55,8 @@ grub_pci_device_map_range (grub_pci_device_t dev, grub_addr_t base, int err; err = pci_device_map_range (dev, base, size, PCI_DEV_MAP_FLAG_WRITABLE, &addr); if (err) - grub_util_error ("mapping 0x%x failed (error %d)\n", base, err); + grub_util_error ("mapping 0x%llx failed (error %d)", + (unsigned long long) base, err); return addr; } @@ -65,12 +67,12 @@ grub_pci_device_unmap_range (grub_pci_device_t dev, void *mem, pci_device_unmap_range (dev, mem, size); } -GRUB_MOD_INIT (pci) +GRUB_MOD_INIT (emupci) { pci_system_init (); } -GRUB_MOD_FINI (pci) +GRUB_MOD_FINI (emupci) { pci_system_cleanup (); } diff --git a/grub-core/bus/fdt.c b/grub-core/bus/fdt.c new file mode 100644 index 000000000..135da497b --- /dev/null +++ b/grub-core/bus/fdt.c @@ -0,0 +1,256 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include +#include +#include + +static const void *dtb; +static grub_size_t root_address_cells, root_size_cells; +/* Pointer to this symbol signals invalid mapping. */ +char grub_fdtbus_invalid_mapping[1]; + +struct grub_fdtbus_dev *devs; +struct grub_fdtbus_driver *drivers; + +int +grub_fdtbus_is_compatible (const char *compat_string, + const struct grub_fdtbus_dev *dev) +{ + grub_size_t compatible_size; + const char *compatible = grub_fdt_get_prop (dtb, dev->node, "compatible", + &compatible_size); + if (!compatible) + return 0; + const char *compatible_end = compatible + compatible_size; + while (compatible < compatible_end) + { + if (grub_strcmp (compat_string, compatible) == 0) + return 1; + compatible += grub_strlen (compatible) + 1; + } + return 0; +} + +static void +fdtbus_scan (struct grub_fdtbus_dev *parent) +{ + int node; + for (node = grub_fdt_first_node (dtb, parent ? parent->node : 0); node >= 0; + node = grub_fdt_next_node (dtb, node)) + { + struct grub_fdtbus_dev *dev; + struct grub_fdtbus_driver *driver; + dev = grub_zalloc (sizeof (*dev)); + if (!dev) + { + grub_print_error (); + return; + } + dev->node = node; + dev->next = devs; + dev->parent = parent; + devs = dev; + FOR_LIST_ELEMENTS(driver, drivers) + if (!dev->driver && grub_fdtbus_is_compatible (driver->compatible, dev)) + { + grub_dprintf ("fdtbus", "Attaching %s\n", driver->compatible); + if (driver->attach (dev) == GRUB_ERR_NONE) + { + grub_dprintf ("fdtbus", "Attached %s\n", driver->compatible); + dev->driver = driver; + break; + } + grub_print_error (); + } + fdtbus_scan (dev); + } +} + +void +grub_fdtbus_register (struct grub_fdtbus_driver *driver) +{ + struct grub_fdtbus_dev *dev; + grub_dprintf ("fdtbus", "Registering %s\n", driver->compatible); + grub_list_push (GRUB_AS_LIST_P (&drivers), + GRUB_AS_LIST (driver)); + for (dev = devs; dev; dev = dev->next) + if (!dev->driver && grub_fdtbus_is_compatible (driver->compatible, dev)) + { + grub_dprintf ("fdtbus", "Attaching %s (%p)\n", driver->compatible, dev); + if (driver->attach (dev) == GRUB_ERR_NONE) + { + grub_dprintf ("fdtbus", "Attached %s\n", driver->compatible); + dev->driver = driver; + } + grub_print_error (); + } +} + +void +grub_fdtbus_unregister (struct grub_fdtbus_driver *driver) +{ + grub_list_remove (GRUB_AS_LIST (driver)); + struct grub_fdtbus_dev *dev; + for (dev = devs; dev; dev = dev->next) + if (dev->driver == driver) + { + if (driver->detach) + driver->detach(dev); + dev->driver = 0; + } +} + +void +grub_fdtbus_init (const void *dtb_in, grub_size_t size) +{ + if (!dtb_in || grub_fdt_check_header (dtb_in, size) < 0) + grub_fatal ("invalid FDT"); + dtb = dtb_in; + const grub_uint32_t *prop = grub_fdt_get_prop (dtb, 0, "#address-cells", 0); + if (prop) + root_address_cells = grub_be_to_cpu32 (*prop); + else + root_address_cells = 1; + + prop = grub_fdt_get_prop (dtb, 0, "#size-cells", 0); + if (prop) + root_size_cells = grub_be_to_cpu32 (*prop); + else + root_size_cells = 1; + + fdtbus_scan (0); +} + +static int +get_address_cells (const struct grub_fdtbus_dev *dev) +{ + const grub_uint32_t *prop; + if (!dev) + return root_address_cells; + prop = grub_fdt_get_prop (dtb, dev->node, "#address-cells", 0); + if (prop) + return grub_be_to_cpu32 (*prop); + return 1; +} + +static int +get_size_cells (const struct grub_fdtbus_dev *dev) +{ + const grub_uint32_t *prop; + if (!dev) + return root_size_cells; + prop = grub_fdt_get_prop (dtb, dev->node, "#size-cells", 0); + if (prop) + return grub_be_to_cpu32 (*prop); + return 1; +} + +static grub_uint64_t +get64 (const grub_uint32_t *reg, grub_size_t cells) +{ + grub_uint64_t val = 0; + if (cells >= 1) + val = grub_be_to_cpu32 (reg[cells - 1]); + if (cells >= 2) + val |= ((grub_uint64_t) grub_be_to_cpu32 (reg[cells - 2])) << 32; + return val; +} + +static volatile void * +translate (const struct grub_fdtbus_dev *dev, const grub_uint32_t *reg) +{ + volatile void *ret; + const grub_uint32_t *ranges; + grub_size_t ranges_size, cells_per_mapping; + grub_size_t parent_address_cells, child_address_cells, child_size_cells; + grub_size_t nmappings, i; + if (dev == 0) + { + grub_uint64_t val; + val = get64 (reg, root_address_cells); + if (sizeof (void *) == 4 && (val >> 32)) + return grub_fdtbus_invalid_mapping; + return (void *) (grub_addr_t) val; + } + ranges = grub_fdt_get_prop (dtb, dev->node, "ranges", &ranges_size); + if (!ranges) + return grub_fdtbus_invalid_mapping; + if (ranges_size == 0) + return translate (dev->parent, reg); + parent_address_cells = get_address_cells (dev->parent); + child_address_cells = get_address_cells (dev); + child_size_cells = get_size_cells (dev); + cells_per_mapping = parent_address_cells + child_address_cells + child_size_cells; + nmappings = ranges_size / 4 / cells_per_mapping; + for (i = 0; i < nmappings; i++) + { + const grub_uint32_t *child_addr = &ranges[i * cells_per_mapping]; + const grub_uint32_t *parent_addr = child_addr + child_address_cells; + grub_uint64_t child_size = get64 (parent_addr + parent_address_cells, child_size_cells); + + if (child_address_cells > 2 && grub_memcmp (reg, child_addr, (child_address_cells - 2) * 4) != 0) + continue; + if (get64 (reg, child_address_cells) < get64 (child_addr, child_address_cells)) + continue; + + grub_uint64_t offset = get64 (reg, child_address_cells) - get64 (child_addr, child_address_cells); + if (offset >= child_size) + continue; + + ret = translate (dev->parent, parent_addr); + if (grub_fdtbus_is_mapping_valid (ret)) + ret = (volatile char *) ret + offset; + return ret; + } + return grub_fdtbus_invalid_mapping; +} + +volatile void * +grub_fdtbus_map_reg (const struct grub_fdtbus_dev *dev, int regno, grub_size_t *size) +{ + grub_size_t address_cells, size_cells; + address_cells = get_address_cells (dev->parent); + size_cells = get_size_cells (dev->parent); + const grub_uint32_t *reg = grub_fdt_get_prop (dtb, dev->node, "reg", 0); + if (size && size_cells) + *size = reg[(address_cells + size_cells) * regno + address_cells]; + if (size && !size_cells) + *size = 0; + return translate (dev->parent, reg + (address_cells + size_cells) * regno); +} + +const char * +grub_fdtbus_get_name (const struct grub_fdtbus_dev *dev) +{ + return grub_fdt_get_nodename (dtb, dev->node); +} + +const void * +grub_fdtbus_get_prop (const struct grub_fdtbus_dev *dev, + const char *name, + grub_uint32_t *len) +{ + return grub_fdt_get_prop (dtb, dev->node, name, len); +} + +const void * +grub_fdtbus_get_fdt (void) +{ + return dtb; +} diff --git a/grub-core/bus/i386/ieee1275/pci.c b/grub-core/bus/i386/ieee1275/pci.c new file mode 100644 index 000000000..1fd3b5610 --- /dev/null +++ b/grub-core/bus/i386/ieee1275/pci.c @@ -0,0 +1,42 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include + +volatile void * +grub_pci_device_map_range (grub_pci_device_t dev __attribute__ ((unused)), + grub_addr_t base, + grub_size_t size) +{ + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)) + return (volatile void *) base; + if (grub_ieee1275_map (base, base, size, 7)) + grub_fatal ("couldn't map 0x%lx", base); + return (volatile void *) base; +} + +void +grub_pci_device_unmap_range (grub_pci_device_t dev __attribute__ ((unused)), + volatile void *mem __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused))) +{ +} diff --git a/grub-core/bus/pci.c b/grub-core/bus/pci.c new file mode 100644 index 000000000..c1ee9dc58 --- /dev/null +++ b/grub-core/bus/pci.c @@ -0,0 +1,173 @@ +/* pci.c - Generic PCI interfaces. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* FIXME: correctly support 64-bit architectures. */ +/* #if GRUB_TARGET_SIZEOF_VOID_P == 4 */ +struct grub_pci_dma_chunk * +grub_memalign_dma32 (grub_size_t align, grub_size_t size) +{ + void *ret; + if (align < 64) + align = 64; + size = ALIGN_UP (size, align); + ret = grub_memalign (align, size); +#if GRUB_CPU_SIZEOF_VOID_P == 8 + if ((grub_addr_t) ret >> 32) + { + /* Shouldn't happend since the only platform in this case is + x86_64-efi and it skips any regions > 4GiB because + of EFI bugs anyway. */ + grub_error (GRUB_ERR_BUG, "allocation outside 32-bit range"); + return 0; + } +#endif + if (!ret) + return 0; + grub_arch_sync_dma_caches (ret, size); + return ret; +} + +/* FIXME: evil. */ +void +grub_dma_free (struct grub_pci_dma_chunk *ch) +{ + grub_size_t size = (((struct grub_mm_header *) ch) - 1)->size * GRUB_MM_ALIGN; + grub_arch_sync_dma_caches (ch, size); + grub_free (ch); +} +/* #endif */ + +#ifdef GRUB_MACHINE_MIPS_LOONGSON +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ((((grub_uint32_t) ch) & 0x1fffffff) | 0xa0000000); +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (((grub_uint32_t) ch) & 0x1fffffff) | 0x80000000; +} +#else + +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ch; +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (grub_uint32_t) (grub_addr_t) ch; +} + +#endif + +grub_pci_address_t +grub_pci_make_address (grub_pci_device_t dev, int reg) +{ + return (1 << 31) | (dev.bus << 16) | (dev.device << 11) + | (dev.function << 8) | reg; +} + +void +grub_pci_iterate (grub_pci_iteratefunc_t hook, void *hook_data) +{ + grub_pci_device_t dev; + grub_pci_address_t addr; + grub_pci_id_t id; + grub_uint32_t hdr; + + for (dev.bus = 0; dev.bus < GRUB_PCI_NUM_BUS; dev.bus++) + { + for (dev.device = 0; dev.device < GRUB_PCI_NUM_DEVICES; dev.device++) + { + for (dev.function = 0; dev.function < 8; dev.function++) + { + addr = grub_pci_make_address (dev, GRUB_PCI_REG_PCI_ID); + id = grub_pci_read (addr); + + /* Check if there is a device present. */ + if (id >> 16 == 0xFFFF) + { + if (dev.function == 0) + /* Devices are required to implement function 0, so if + it's missing then there is no device here. */ + break; + else + continue; + } + + if (hook (dev, id, hook_data)) + return; + + /* Probe only func = 0 if the device if not multifunction */ + if (dev.function == 0) + { + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CACHELINE); + hdr = grub_pci_read (addr); + if (!(hdr & 0x800000)) + break; + } + } + } + } +} + +grub_uint8_t +grub_pci_find_capability (grub_pci_device_t dev, grub_uint8_t cap) +{ + grub_uint8_t pos = 0x34; + int ttl = 48; + + while (ttl--) + { + grub_uint8_t id; + grub_pci_address_t addr; + + addr = grub_pci_make_address (dev, pos); + pos = grub_pci_read_byte (addr); + if (pos < 0x40) + break; + + pos &= ~3; + + addr = grub_pci_make_address (dev, pos); + id = grub_pci_read_byte (addr); + + if (id == 0xff) + break; + + if (id == cap) + return pos; + pos++; + } + return 0; +} diff --git a/grub-core/bus/spi/rk3288_spi.c b/grub-core/bus/spi/rk3288_spi.c new file mode 100644 index 000000000..aacb79ffe --- /dev/null +++ b/grub-core/bus/spi/rk3288_spi.c @@ -0,0 +1,103 @@ +/* + * GRUB -- GRand Unified Bootloader + * + * Copyright (C) 2012 Google Inc. + * Copyright (C) 2016 Free Software Foundation, Inc. + * + * This is based on depthcharge code. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +static grub_err_t +spi_send (const struct grub_fdtbus_dev *dev, const void *data, grub_size_t sz) +{ + const grub_uint8_t *ptr = data, *end = ptr + sz; + volatile grub_uint32_t *spi = grub_fdtbus_map_reg (dev, 0, 0); + spi[2] = 0; + spi[1] = sz - 1; + spi[0] = ((1 << 18) | spi[0]) & ~(1 << 19); + spi[2] = 1; + while (ptr < end) + { + while (spi[9] & 2); + spi[256] = *ptr++; + } + while (spi[9] & 1); + return GRUB_ERR_NONE; +} + +static grub_err_t +spi_receive (const struct grub_fdtbus_dev *dev, void *data, grub_size_t sz) +{ + grub_uint8_t *ptr = data, *end = ptr + sz; + volatile grub_uint32_t *spi = grub_fdtbus_map_reg (dev, 0, 0); + spi[2] = 0; + spi[1] = sz - 1; + spi[0] = ((1 << 19) | spi[0]) & ~(1 << 18); + spi[2] = 1; + while (ptr < end) + { + while (spi[9] & 8); + *ptr++ = spi[512]; + } + while (spi[9] & 1); + return GRUB_ERR_NONE; +} + +static grub_err_t +spi_start (const struct grub_fdtbus_dev *dev) +{ + volatile grub_uint32_t *spi = grub_fdtbus_map_reg (dev, 0, 0); + spi[3] = 1; + return GRUB_ERR_NONE; +} + +static void +spi_stop (const struct grub_fdtbus_dev *dev) +{ + volatile grub_uint32_t *spi = grub_fdtbus_map_reg (dev, 0, 0); + spi[3] = 0; +} + +static grub_err_t +spi_attach(const struct grub_fdtbus_dev *dev) +{ + if (!grub_fdtbus_is_mapping_valid (grub_fdtbus_map_reg (dev, 0, 0))) + return GRUB_ERR_IO; + + return GRUB_ERR_NONE; +} + +static struct grub_fdtbus_driver spi = +{ + .compatible = "rockchip,rk3288-spi", + .attach = spi_attach, + .send = spi_send, + .receive = spi_receive, + .start = spi_start, + .stop = spi_stop, +}; + +void +grub_rk3288_spi_init (void) +{ + grub_fdtbus_register (&spi); +} diff --git a/grub-core/bus/usb/ehci-fdt.c b/grub-core/bus/usb/ehci-fdt.c new file mode 100644 index 000000000..29b50bdd5 --- /dev/null +++ b/grub-core/bus/usb/ehci-fdt.c @@ -0,0 +1,45 @@ +/* ehci.c - EHCI Support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include + +static grub_err_t +ehci_attach(const struct grub_fdtbus_dev *dev) +{ + grub_dprintf ("ehci", "Found generic-ehci\n"); + + grub_ehci_init_device (grub_fdtbus_map_reg (dev, 0, 0)); + return 0; +} + +struct grub_fdtbus_driver ehci = +{ + .compatible = "generic-ehci", + .attach = ehci_attach +}; + +void +grub_ehci_pci_scan (void) +{ + grub_fdtbus_register (&ehci); +} diff --git a/grub-core/bus/usb/ehci-pci.c b/grub-core/bus/usb/ehci-pci.c new file mode 100644 index 000000000..aa04988fd --- /dev/null +++ b/grub-core/bus/usb/ehci-pci.c @@ -0,0 +1,208 @@ +/* ehci.c - EHCI Support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_EHCI_PCI_SBRN_REG 0x60 +#define GRUB_EHCI_ADDR_MEM_MASK (~0xff) + +/* USBLEGSUP bits and related OS OWNED byte offset */ +enum +{ + GRUB_EHCI_BIOS_OWNED = (1 << 16), + GRUB_EHCI_OS_OWNED = (1 << 24) +}; + +/* PCI iteration function... */ +static int +grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + volatile grub_uint32_t *regs; + grub_uint32_t base, base_h; + grub_uint32_t eecp_offset; + grub_uint32_t usblegsup = 0; + grub_uint64_t maxtime; + grub_uint32_t interf; + grub_uint32_t subclass; + grub_uint32_t class; + grub_uint8_t release; + grub_uint32_t class_code; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n"); + + if (pciid == GRUB_CS5536_PCIID) + { + grub_uint64_t basereg; + + basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE); + if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE)) + { + /* Shouldn't happen. */ + grub_dprintf ("ehci", "No EHCI 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; + basereg &= ~GRUB_CS5536_MSR_USB_BASE_SMI_ENABLE; + grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE, basereg); + } + else + { + grub_pci_address_t addr; + 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 EHCI controller, just return. */ + if (class != 0x0c || subclass != 0x03 || interf != 0x20) + return 0; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: class OK\n"); + + /* Check Serial Bus Release Number */ + addr = grub_pci_make_address (dev, GRUB_EHCI_PCI_SBRN_REG); + release = grub_pci_read_byte (addr); + if (release != 0x20) + { + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n", + release); + return 0; + } + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: bus rev. num. OK\n"); + + /* Determine EHCI EHCC registers base address. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0); + base = grub_pci_read (addr); + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1); + base_h = grub_pci_read (addr); + /* Stop if registers are mapped above 4G - GRUB does not currently + * work with registers mapped above 4G */ + if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32) + && (base_h != 0)) + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n"); + return 0; + } + base &= GRUB_PCI_ADDR_MEM_MASK; + if (!base) + { + grub_dprintf ("ehci", + "EHCI: EHCI is not mapped\n"); + return 0; + } + + /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word(addr, + GRUB_PCI_COMMAND_MEM_ENABLED + | GRUB_PCI_COMMAND_BUS_MASTER + | grub_pci_read_word(addr)); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n"); + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n", + (base & GRUB_EHCI_ADDR_MEM_MASK)); + + regs = grub_pci_device_map_range (dev, + (base & GRUB_EHCI_ADDR_MEM_MASK), + 0x100); + + /* Is there EECP ? */ + eecp_offset = (grub_le_to_cpu32 (regs[2]) >> 8) & 0xff; + + /* Determine and change ownership. */ + /* EECP offset valid in HCCPARAMS */ + /* Ownership can be changed via EECP only */ + if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40) + { + grub_pci_address_t pciaddr_eecp; + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset); + + usblegsup = grub_pci_read (pciaddr_eecp); + if (usblegsup & GRUB_EHCI_BIOS_OWNED) + { + grub_boot_time ("Taking ownership of EHCI controller"); + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n"); + /* Ownership change - set OS_OWNED bit */ + grub_pci_write (pciaddr_eecp, usblegsup | GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + + /* Wait for finish of ownership change, EHCI specification + * doesn't say how long it can take... */ + maxtime = grub_get_time_ms () + 1000; + while ((grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) + && (grub_get_time_ms () < maxtime)); + if (grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED) + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI change ownership timeout"); + /* Change ownership in "hard way" - reset BIOS ownership */ + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + } + else if (usblegsup & GRUB_EHCI_OS_OWNED) + /* XXX: What to do in this case - nothing ? Can it happen ? */ + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: OS\n"); + else + { + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: EHCI owned by: NONE\n"); + /* XXX: What to do in this case ? Can it happen ? + * Is code below correct ? */ + /* Ownership change - set OS_OWNED bit */ + grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + + /* Disable SMI, just to be sure. */ + pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4); + grub_pci_write (pciaddr_eecp, 0); + /* Ensure PCI register is written */ + grub_pci_read (pciaddr_eecp); + } + + grub_dprintf ("ehci", "inithw: EHCI grub_ehci_pci_iter: ownership OK\n"); + + grub_ehci_init_device (regs); + return 0; +} + +void +grub_ehci_pci_scan (void) +{ + grub_pci_iterate (grub_ehci_pci_iter, NULL); +} diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c new file mode 100644 index 000000000..2db07c7c0 --- /dev/null +++ b/grub-core/bus/usb/ehci.c @@ -0,0 +1,1839 @@ +/* ehci.c - EHCI Support. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* This simple GRUB implementation of EHCI driver: + * - assumes no IRQ + * - is not supporting isochronous transfers (iTD, siTD) + * - is not supporting interrupt transfers + */ + +/* Capability registers offsets */ +enum +{ + GRUB_EHCI_EHCC_CAPLEN = 0x00, /* byte */ + GRUB_EHCI_EHCC_VERSION = 0x02, /* word */ + GRUB_EHCI_EHCC_SPARAMS = 0x04, /* dword */ + GRUB_EHCI_EHCC_CPARAMS = 0x08, /* dword */ + GRUB_EHCI_EHCC_PROUTE = 0x0c, /* 60 bits */ +}; + +#define GRUB_EHCI_EECP_MASK (0xff << 8) +#define GRUB_EHCI_EECP_SHIFT 8 + +#define GRUB_EHCI_POINTER_MASK (~0x1f) + +/* Capability register SPARAMS bits */ +enum +{ + GRUB_EHCI_SPARAMS_N_PORTS = (0xf << 0), + GRUB_EHCI_SPARAMS_PPC = (1 << 4), /* Power port control */ + GRUB_EHCI_SPARAMS_PRR = (1 << 7), /* Port routing rules */ + GRUB_EHCI_SPARAMS_N_PCC = (0xf << 8), /* No of ports per comp. */ + GRUB_EHCI_SPARAMS_NCC = (0xf << 12), /* No of com. controllers */ + GRUB_EHCI_SPARAMS_P_IND = (1 << 16), /* Port indicators present */ + GRUB_EHCI_SPARAMS_DEBUG_P = (0xf << 20) /* Debug port */ +}; + +#define GRUB_EHCI_MAX_N_PORTS 15 /* Max. number of ports */ + +/* Capability register CPARAMS bits */ +enum +{ + GRUB_EHCI_CPARAMS_64BIT = (1 << 0), + GRUB_EHCI_CPARAMS_PROG_FRAMELIST = (1 << 1), + GRUB_EHCI_CPARAMS_PARK_CAP = (1 << 2) +}; + +#define GRUB_EHCI_N_FRAMELIST 1024 +#define GRUB_EHCI_N_QH 256 +#define GRUB_EHCI_N_TD 640 + +#define GRUB_EHCI_QH_EMPTY 1 + +/* Operational registers offsets */ +enum +{ + GRUB_EHCI_COMMAND = 0x00, + GRUB_EHCI_STATUS = 0x04, + GRUB_EHCI_INTERRUPT = 0x08, + GRUB_EHCI_FRAME_INDEX = 0x0c, + GRUB_EHCI_64BIT_SEL = 0x10, + GRUB_EHCI_FL_BASE = 0x14, + GRUB_EHCI_CUR_AL_ADDR = 0x18, + GRUB_EHCI_CONFIG_FLAG = 0x40, + GRUB_EHCI_PORT_STAT_CMD = 0x44 +}; + +/* Operational register COMMAND bits */ +enum +{ + GRUB_EHCI_CMD_RUNSTOP = (1 << 0), + GRUB_EHCI_CMD_HC_RESET = (1 << 1), + GRUB_EHCI_CMD_FL_SIZE = (3 << 2), + GRUB_EHCI_CMD_PS_ENABL = (1 << 4), + GRUB_EHCI_CMD_AS_ENABL = (1 << 5), + GRUB_EHCI_CMD_AS_ADV_D = (1 << 6), + GRUB_EHCI_CMD_L_HC_RES = (1 << 7), + GRUB_EHCI_CMD_AS_PARKM = (3 << 8), + GRUB_EHCI_CMD_AS_PARKE = (1 << 11), + GRUB_EHCI_CMD_INT_THRS = (0xff << 16) +}; + +/* Operational register STATUS bits */ +enum +{ + GRUB_EHCI_ST_INTERRUPT = (1 << 0), + GRUB_EHCI_ST_ERROR_INT = (1 << 1), + GRUB_EHCI_ST_PORT_CHG = (1 << 2), + GRUB_EHCI_ST_FL_ROLLOVR = (1 << 3), + GRUB_EHCI_ST_HS_ERROR = (1 << 4), + GRUB_EHCI_ST_AS_ADVANCE = (1 << 5), + GRUB_EHCI_ST_HC_HALTED = (1 << 12), + GRUB_EHCI_ST_RECLAM = (1 << 13), + GRUB_EHCI_ST_PS_STATUS = (1 << 14), + GRUB_EHCI_ST_AS_STATUS = (1 << 15) +}; + +/* Operational register PORT_STAT_CMD bits */ +enum +{ + GRUB_EHCI_PORT_CONNECT = (1 << 0), + GRUB_EHCI_PORT_CONNECT_CH = (1 << 1), + GRUB_EHCI_PORT_ENABLED = (1 << 2), + GRUB_EHCI_PORT_ENABLED_CH = (1 << 3), + GRUB_EHCI_PORT_OVERCUR = (1 << 4), + GRUB_EHCI_PORT_OVERCUR_CH = (1 << 5), + GRUB_EHCI_PORT_RESUME = (1 << 6), + GRUB_EHCI_PORT_SUSPEND = (1 << 7), + GRUB_EHCI_PORT_RESET = (1 << 8), + GRUB_EHCI_PORT_LINE_STAT = (3 << 10), + GRUB_EHCI_PORT_POWER = (1 << 12), + GRUB_EHCI_PORT_OWNER = (1 << 13), + GRUB_EHCI_PORT_INDICATOR = (3 << 14), + GRUB_EHCI_PORT_TEST = (0xf << 16), + GRUB_EHCI_PORT_WON_CONN_E = (1 << 20), + GRUB_EHCI_PORT_WON_DISC_E = (1 << 21), + GRUB_EHCI_PORT_WON_OVER_E = (1 << 22), + + GRUB_EHCI_PORT_LINE_SE0 = (0 << 10), + GRUB_EHCI_PORT_LINE_K = (1 << 10), + GRUB_EHCI_PORT_LINE_J = (2 << 10), + GRUB_EHCI_PORT_LINE_UNDEF = (3 << 10), + GRUB_EHCI_PORT_LINE_LOWSP = GRUB_EHCI_PORT_LINE_K, /* K state means low speed */ + GRUB_EHCI_PORT_WMASK = ~(GRUB_EHCI_PORT_CONNECT_CH + | GRUB_EHCI_PORT_ENABLED_CH + | GRUB_EHCI_PORT_OVERCUR_CH) +}; + +/* Operational register CONFIGFLAGS bits */ +enum +{ + GRUB_EHCI_CF_EHCI_OWNER = (1 << 0) +}; + +/* Queue Head & Transfer Descriptor constants */ +#define GRUB_EHCI_HPTR_OFF 5 /* Horiz. pointer bit offset */ +enum +{ + GRUB_EHCI_HPTR_TYPE_MASK = (3 << 1), + GRUB_EHCI_HPTR_TYPE_ITD = (0 << 1), + GRUB_EHCI_HPTR_TYPE_QH = (1 << 1), + GRUB_EHCI_HPTR_TYPE_SITD = (2 << 1), + GRUB_EHCI_HPTR_TYPE_FSTN = (3 << 1) +}; + +enum +{ + GRUB_EHCI_C = (1 << 27), + GRUB_EHCI_MAXPLEN_MASK = (0x7ff << 16), + GRUB_EHCI_H = (1 << 15), + GRUB_EHCI_DTC = (1 << 14), + GRUB_EHCI_SPEED_MASK = (3 << 12), + GRUB_EHCI_SPEED_FULL = (0 << 12), + GRUB_EHCI_SPEED_LOW = (1 << 12), + GRUB_EHCI_SPEED_HIGH = (2 << 12), + GRUB_EHCI_SPEED_RESERVED = (3 << 12), + GRUB_EHCI_EP_NUM_MASK = (0xf << 8), + GRUB_EHCI_DEVADDR_MASK = 0x7f, + GRUB_EHCI_TARGET_MASK = (GRUB_EHCI_EP_NUM_MASK | GRUB_EHCI_DEVADDR_MASK) +}; + +enum +{ + GRUB_EHCI_MAXPLEN_OFF = 16, + GRUB_EHCI_SPEED_OFF = 12, + GRUB_EHCI_EP_NUM_OFF = 8 +}; + +enum +{ + GRUB_EHCI_MULT_MASK = (3 << 30), + GRUB_EHCI_MULT_RESERVED = (0 << 30), + GRUB_EHCI_MULT_ONE = (1 << 30), + GRUB_EHCI_MULT_TWO = (2 << 30), + GRUB_EHCI_MULT_THREE = (3 << 30), + GRUB_EHCI_DEVPORT_MASK = (0x7f << 23), + GRUB_EHCI_HUBADDR_MASK = (0x7f << 16), + GRUB_EHCI_CMASK_MASK = (0xff << 8), + GRUB_EHCI_SMASK_MASK = (0xff << 0), +}; + +enum +{ + GRUB_EHCI_MULT_OFF = 30, + GRUB_EHCI_DEVPORT_OFF = 23, + GRUB_EHCI_HUBADDR_OFF = 16, + GRUB_EHCI_CMASK_OFF = 8, + GRUB_EHCI_SMASK_OFF = 0, +}; + +#define GRUB_EHCI_TERMINATE (1<<0) + +#define GRUB_EHCI_TOGGLE ((grub_uint32_t) 1<<31) + +enum +{ + GRUB_EHCI_TOTAL_MASK = (0x7fff << 16), + GRUB_EHCI_CERR_MASK = (3 << 10), + GRUB_EHCI_CERR_0 = (0 << 10), + GRUB_EHCI_CERR_1 = (1 << 10), + GRUB_EHCI_CERR_2 = (2 << 10), + GRUB_EHCI_CERR_3 = (3 << 10), + GRUB_EHCI_PIDCODE_OUT = (0 << 8), + GRUB_EHCI_PIDCODE_IN = (1 << 8), + GRUB_EHCI_PIDCODE_SETUP = (2 << 8), + GRUB_EHCI_STATUS_MASK = 0xff, + GRUB_EHCI_STATUS_ACTIVE = (1 << 7), + GRUB_EHCI_STATUS_HALTED = (1 << 6), + GRUB_EHCI_STATUS_BUFERR = (1 << 5), + GRUB_EHCI_STATUS_BABBLE = (1 << 4), + GRUB_EHCI_STATUS_TRANERR = (1 << 3), + GRUB_EHCI_STATUS_MISSDMF = (1 << 2), + GRUB_EHCI_STATUS_SPLITST = (1 << 1), + GRUB_EHCI_STATUS_PINGERR = (1 << 0) +}; + +enum +{ + GRUB_EHCI_TOTAL_OFF = 16, + GRUB_EHCI_CERR_OFF = 10 +}; + +#define GRUB_EHCI_BUFPTR_MASK (0xfffff<<12) +#define GRUB_EHCI_QHTDPTR_MASK 0xffffffe0 + +#define GRUB_EHCI_TD_BUF_PAGES 5 + +#define GRUB_EHCI_BUFPAGELEN 0x1000 +#define GRUB_EHCI_MAXBUFLEN 0x5000 + +struct grub_ehci_td; +struct grub_ehci_qh; +typedef volatile struct grub_ehci_td *grub_ehci_td_t; +typedef volatile struct grub_ehci_qh *grub_ehci_qh_t; + +/* EHCI Isochronous Transfer Descriptor */ +/* Currently not supported */ + +/* EHCI Split Transaction Isochronous Transfer Descriptor */ +/* Currently not supported */ + +/* EHCI Queue Element Transfer Descriptor (qTD) */ +/* Align to 32-byte boundaries */ +struct grub_ehci_td +{ + /* EHCI HW part */ + grub_uint32_t next_td; /* Pointer to next qTD */ + grub_uint32_t alt_next_td; /* Pointer to alternate next qTD */ + grub_uint32_t token; /* Toggle, Len, Interrupt, Page, Error, PID, Status */ + grub_uint32_t buffer_page[GRUB_EHCI_TD_BUF_PAGES]; /* Buffer pointer (+ cur. offset in page 0 */ + /* 64-bits part */ + grub_uint32_t buffer_page_high[GRUB_EHCI_TD_BUF_PAGES]; + /* EHCI driver part */ + grub_uint32_t link_td; /* pointer to next free/chained TD */ + grub_uint32_t size; + grub_uint32_t pad[1]; /* padding to some multiple of 32 bytes */ +}; + +/* EHCI Queue Head */ +/* Align to 32-byte boundaries */ +/* QH allocation is made in the similar/same way as in OHCI driver, + * because unlninking QH from the Asynchronous list is not so + * trivial as on UHCI (at least it is time consuming) */ +struct grub_ehci_qh +{ + /* EHCI HW part */ + grub_uint32_t qh_hptr; /* Horiz. pointer & Terminate */ + grub_uint32_t ep_char; /* EP characteristics */ + grub_uint32_t ep_cap; /* EP capabilities */ + grub_uint32_t td_current; /* current TD link pointer */ + struct grub_ehci_td td_overlay; /* TD overlay area = 64 bytes */ + /* EHCI driver part */ + grub_uint32_t pad[4]; /* padding to some multiple of 32 bytes */ +}; + +/* EHCI Periodic Frame Span Traversal Node */ +/* Currently not supported */ + +struct grub_ehci +{ + volatile grub_uint32_t *iobase_ehcc; /* Capability registers */ + volatile grub_uint32_t *iobase; /* Operational registers */ + struct grub_pci_dma_chunk *framelist_chunk; /* Currently not used */ + volatile grub_uint32_t *framelist_virt; + grub_uint32_t framelist_phys; + struct grub_pci_dma_chunk *qh_chunk; /* GRUB_EHCI_N_QH Queue Heads */ + grub_ehci_qh_t qh_virt; + grub_uint32_t qh_phys; + struct grub_pci_dma_chunk *td_chunk; /* GRUB_EHCI_N_TD Transfer Descriptors */ + grub_ehci_td_t td_virt; + grub_uint32_t td_phys; + grub_ehci_td_t tdfree_virt; /* Free Transfer Descriptors */ + int flag64; + grub_uint32_t reset; /* bits 1-15 are flags if port was reset from connected time or not */ + struct grub_ehci *next; +}; + +static struct grub_ehci *ehci; + +static void +sync_all_caches (struct grub_ehci *e) +{ + if (!e) + return; + if (e->td_virt) + grub_arch_sync_dma_caches (e->td_virt, sizeof (struct grub_ehci_td) * + GRUB_EHCI_N_TD); + if (e->qh_virt) + grub_arch_sync_dma_caches (e->qh_virt, sizeof (struct grub_ehci_qh) * + GRUB_EHCI_N_QH); + if (e->framelist_virt) + grub_arch_sync_dma_caches (e->framelist_virt, 4096); +} + +/* EHCC registers access functions */ +static inline grub_uint32_t +grub_ehci_ehcc_read32 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu32 (*((volatile grub_uint32_t *) e->iobase_ehcc + + (addr / sizeof (grub_uint32_t)))); +} + +static inline grub_uint16_t +grub_ehci_ehcc_read16 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu16 (*((volatile grub_uint16_t *) e->iobase_ehcc + + (addr / sizeof (grub_uint16_t)))); +} + +static inline grub_uint8_t +grub_ehci_ehcc_read8 (struct grub_ehci *e, grub_uint32_t addr) +{ + return *((volatile grub_uint8_t *) e->iobase_ehcc + addr); +} + +/* Operational registers access functions */ +static inline grub_uint32_t +grub_ehci_oper_read32 (struct grub_ehci *e, grub_uint32_t addr) +{ + return + grub_le_to_cpu32 (* + ((volatile grub_uint32_t *) e->iobase + + (addr / sizeof (grub_uint32_t)))); +} + +static inline void +grub_ehci_oper_write32 (struct grub_ehci *e, grub_uint32_t addr, + grub_uint32_t value) +{ + *((volatile grub_uint32_t *) e->iobase + (addr / sizeof (grub_uint32_t))) = + grub_cpu_to_le32 (value); +} + +static inline grub_uint32_t +grub_ehci_port_read (struct grub_ehci *e, grub_uint32_t port) +{ + return grub_ehci_oper_read32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4); +} + +static inline void +grub_ehci_port_resbits (struct grub_ehci *e, grub_uint32_t port, + grub_uint32_t bits) +{ + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4, + grub_ehci_port_read (e, + port) & GRUB_EHCI_PORT_WMASK & + ~(bits)); + grub_ehci_port_read (e, port); +} + +static inline void +grub_ehci_port_setbits (struct grub_ehci *e, grub_uint32_t port, + grub_uint32_t bits) +{ + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + port * 4, + (grub_ehci_port_read (e, port) & + GRUB_EHCI_PORT_WMASK) | bits); + grub_ehci_port_read (e, port); +} + +/* Halt if EHCI HC not halted */ +static grub_usb_err_t +grub_ehci_halt (struct grub_ehci *e) +{ + grub_uint64_t maxtime; + + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) & GRUB_EHCI_ST_HC_HALTED) == 0) /* EHCI is not halted */ + { + /* Halt EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + ~GRUB_EHCI_CMD_RUNSTOP + & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + maxtime = grub_get_time_ms () + 1000; /* Fix: Should be 2ms ! */ + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) == 0) + && (grub_get_time_ms () < maxtime)); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) == 0) + return GRUB_USB_ERR_TIMEOUT; + } + + return GRUB_USB_ERR_NONE; +} + +/* EHCI HC reset */ +static grub_usb_err_t +grub_ehci_reset (struct grub_ehci *e) +{ + grub_uint64_t maxtime; + + sync_all_caches (e); + + grub_dprintf ("ehci", "reset\n"); + + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_HC_RESET); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + /* XXX: How long time could take reset of HC ? */ + maxtime = grub_get_time_ms () + 1000; + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) + & GRUB_EHCI_CMD_HC_RESET) != 0) + && (grub_get_time_ms () < maxtime)); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND) + & GRUB_EHCI_CMD_HC_RESET) != 0) + return GRUB_USB_ERR_TIMEOUT; + + return GRUB_USB_ERR_NONE; +} + +/* PCI iteration function... */ +void +grub_ehci_init_device (volatile void *regs) +{ + struct grub_ehci *e; + grub_uint32_t fp; + int i; + grub_uint32_t n_ports; + grub_uint8_t caplen; + + /* Allocate memory for the controller and fill basic values. */ + e = grub_zalloc (sizeof (*e)); + if (!e) + return; + e->framelist_chunk = NULL; + e->td_chunk = NULL; + e->qh_chunk = NULL; + e->iobase_ehcc = regs; + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CAPLEN: %02x\n", + grub_ehci_ehcc_read8 (e, GRUB_EHCI_EHCC_CAPLEN)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: VERSION: %04x\n", + grub_ehci_ehcc_read16 (e, GRUB_EHCI_EHCC_VERSION)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: SPARAMS: %08x\n", + grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CPARAMS: %08x\n", + grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS)); + + /* Determine base address of EHCI operational registers */ + caplen = grub_ehci_ehcc_read8 (e, GRUB_EHCI_EHCC_CAPLEN); +#ifndef GRUB_HAVE_UNALIGNED_ACCESS + if (caplen & (sizeof (grub_uint32_t) - 1)) + { + grub_dprintf ("ehci", "Unaligned caplen\n"); + return; + } + e->iobase = ((volatile grub_uint32_t *) e->iobase_ehcc + + (caplen / sizeof (grub_uint32_t))); +#else + e->iobase = (volatile grub_uint32_t *) + ((grub_uint8_t *) e->iobase_ehcc + caplen); +#endif + + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08llxx\n", + (unsigned long long) (grub_addr_t) e->iobase_ehcc + caplen); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: COMMAND: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: STATUS: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: INTERRUPT: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_INTERRUPT)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FRAME_INDEX: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FRAME_INDEX)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FL_BASE: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FL_BASE)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CUR_AL_ADDR: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CUR_AL_ADDR)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); + + /* Check format of data structures requested by EHCI */ + /* XXX: In fact it is not used at any place, it is prepared for future + * This implementation uses 32-bits pointers only */ + e->flag64 = ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_CPARAMS) + & GRUB_EHCI_CPARAMS_64BIT) != 0); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: flag64=%d\n", e->flag64); + + /* Reserve a page for the frame list - it is accurate for max. + * possible size of framelist. But currently it is not used. */ + e->framelist_chunk = grub_memalign_dma32 (4096, 4096); + if (!e->framelist_chunk) + goto fail; + e->framelist_virt = grub_dma_get_virt (e->framelist_chunk); + e->framelist_phys = grub_dma_get_phys (e->framelist_chunk); + grub_memset ((void *) e->framelist_virt, 0, 4096); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: framelist mem=%p. OK\n", + e->framelist_virt); + + /* Allocate memory for the QHs and register it in "e". */ + e->qh_chunk = grub_memalign_dma32 (4096, + sizeof (struct grub_ehci_qh) * + GRUB_EHCI_N_QH); + if (!e->qh_chunk) + goto fail; + e->qh_virt = (grub_ehci_qh_t) grub_dma_get_virt (e->qh_chunk); + e->qh_phys = grub_dma_get_phys (e->qh_chunk); + grub_memset ((void *) e->qh_virt, 0, + sizeof (struct grub_ehci_qh) * GRUB_EHCI_N_QH); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH mem=%p. OK\n", + e->qh_virt); + + /* Allocate memory for the TDs and register it in "e". */ + e->td_chunk = grub_memalign_dma32 (4096, + sizeof (struct grub_ehci_td) * + GRUB_EHCI_N_TD); + if (!e->td_chunk) + goto fail; + e->td_virt = (grub_ehci_td_t) grub_dma_get_virt (e->td_chunk); + e->td_phys = grub_dma_get_phys (e->td_chunk); + grub_memset ((void *) e->td_virt, 0, + sizeof (struct grub_ehci_td) * GRUB_EHCI_N_TD); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: TD mem=%p. OK\n", + e->td_virt); + + /* Setup all frame list pointers. Since no isochronous transfers + are supported, they all point to the (same!) queue + head with index 0. */ + fp = grub_cpu_to_le32 ((e->qh_phys & GRUB_EHCI_POINTER_MASK) + | GRUB_EHCI_HPTR_TYPE_QH); + for (i = 0; i < GRUB_EHCI_N_FRAMELIST; i++) + e->framelist_virt[i] = fp; + /* Prepare chain of all TDs and set Terminate in all TDs */ + for (i = 0; i < (GRUB_EHCI_N_TD - 1); i++) + { + e->td_virt[i].link_td = e->td_phys + (i + 1) * sizeof (struct grub_ehci_td); + e->td_virt[i].next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + e->td_virt[i].alt_next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + } + e->td_virt[GRUB_EHCI_N_TD - 1].next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + e->td_virt[GRUB_EHCI_N_TD - 1].alt_next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + e->tdfree_virt = e->td_virt; + /* Set Terminate in first QH, which is used in framelist */ + e->qh_virt[0].qh_hptr = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE | GRUB_EHCI_HPTR_TYPE_QH); + e->qh_virt[0].td_overlay.next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + e->qh_virt[0].td_overlay.alt_next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + /* Also set Halted bit in token */ + e->qh_virt[0].td_overlay.token = grub_cpu_to_le32_compile_time (GRUB_EHCI_STATUS_HALTED); + /* Set the H bit in first QH used for AL */ + e->qh_virt[1].ep_char = grub_cpu_to_le32_compile_time (GRUB_EHCI_H); + /* Set Terminate into TD in rest of QHs and set horizontal link + * pointer to itself - these QHs will be used for asynchronous + * schedule and they should have valid value in horiz. link */ + for (i = 1; i < GRUB_EHCI_N_QH; i++) + { + e->qh_virt[i].qh_hptr = + grub_cpu_to_le32 ((grub_dma_virt2phys (&e->qh_virt[i], + e->qh_chunk) & + GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); + e->qh_virt[i].td_overlay.next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + e->qh_virt[i].td_overlay.alt_next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + /* Also set Halted bit in token */ + e->qh_virt[i].td_overlay.token = + grub_cpu_to_le32_compile_time (GRUB_EHCI_STATUS_HALTED); + } + + /* Note: QH 0 and QH 1 are reserved and must not be used anywhere. + * QH 0 is used as empty QH for framelist + * QH 1 is used as starting empty QH for asynchronous schedule + * QH 1 must exist at any time because at least one QH linked to + * itself must exist in asynchronous schedule + * QH 1 has the H flag set to one */ + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: QH/TD init. OK\n"); + + /* Now we can setup EHCI (maybe...) */ + + /* Check if EHCI is halted and halt it if not */ + if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE) + { + grub_error (GRUB_ERR_TIMEOUT, + "EHCI grub_ehci_pci_iter: EHCI halt timeout"); + goto fail; + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: halted OK\n"); + + /* Reset EHCI */ + if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE) + { + grub_error (GRUB_ERR_TIMEOUT, + "EHCI grub_ehci_pci_iter: EHCI reset timeout"); + goto fail; + } + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: reset OK\n"); + + /* Setup list address registers */ + grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); + grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, + grub_dma_virt2phys (&e->qh_virt[1], + e->qh_chunk)); + + /* Set ownership of root hub ports to EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_CONFIG_FLAG, GRUB_EHCI_CF_EHCI_OWNER); + + /* Enable both lists */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ENABL + | GRUB_EHCI_CMD_PS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Now should be possible to power-up and enumerate ports etc. */ + if ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_PPC) != 0) + { /* EHCI has port powering control */ + /* Power on all ports */ + n_ports = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + for (i = 0; i < (int) n_ports; i++) + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + i * 4, + GRUB_EHCI_PORT_POWER + | grub_ehci_oper_read32 (e, + GRUB_EHCI_PORT_STAT_CMD + + i * 4)); + } + + /* Ensure all commands are written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + /* Enable EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + /* Link to ehci now that initialisation is successful. */ + e->next = ehci; + ehci = e; + + sync_all_caches (e); + + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: OK at all\n"); + + grub_dprintf ("ehci", + "EHCI grub_ehci_pci_iter: iobase of oper. regs: %08llx\n", + (unsigned long long) (grub_addr_t) regs); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: COMMAND: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: STATUS: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: INTERRUPT: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_INTERRUPT)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FRAME_INDEX: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FRAME_INDEX)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: FL_BASE: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_FL_BASE)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CUR_AL_ADDR: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CUR_AL_ADDR)); + grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: CONFIG_FLAG: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_CONFIG_FLAG)); + + return; + +fail: + if (e) + { + if (e->td_chunk) + grub_dma_free ((void *) e->td_chunk); + if (e->qh_chunk) + grub_dma_free ((void *) e->qh_chunk); + if (e->framelist_chunk) + grub_dma_free (e->framelist_chunk); + } + grub_free (e); + + return; +} + +static int +grub_ehci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data) +{ + struct grub_ehci *e; + struct grub_usb_controller dev; + + for (e = ehci; e; e = e->next) + { + dev.data = e; + if (hook (&dev, hook_data)) + return 1; + } + + return 0; +} + +static void +grub_ehci_setup_qh (grub_ehci_qh_t qh, grub_usb_transfer_t transfer) +{ + grub_uint32_t ep_char = 0; + grub_uint32_t ep_cap = 0; + + /* Note: Another part of code is responsible to this QH is + * Halted ! But it can be linked in AL, so we cannot erase or + * change qh_hptr ! */ + /* We will not change any TD field because they should/must be + * in safe state from previous use. */ + + /* EP characteristic setup */ + /* Currently not used NAK counter (RL=0), + * C bit set if EP is not HIGH speed and is control, + * Max Packet Length is taken from transfer structure, + * H bit = 0 (because QH[1] has this bit set), + * DTC bit set to 1 because we are using our own toggle bit control, + * SPEED is selected according to value from transfer structure, + * EP number is taken from transfer structure + * "I" bit must not be set, + * Device Address is taken from transfer structure + * */ + if ((transfer->dev->speed != GRUB_USB_SPEED_HIGH) + && (transfer->type == GRUB_USB_TRANSACTION_TYPE_CONTROL)) + ep_char |= GRUB_EHCI_C; + ep_char |= (transfer->max << GRUB_EHCI_MAXPLEN_OFF) + & GRUB_EHCI_MAXPLEN_MASK; + ep_char |= GRUB_EHCI_DTC; + switch (transfer->dev->speed) + { + case GRUB_USB_SPEED_LOW: + ep_char |= GRUB_EHCI_SPEED_LOW; + break; + case GRUB_USB_SPEED_FULL: + ep_char |= GRUB_EHCI_SPEED_FULL; + break; + case GRUB_USB_SPEED_HIGH: + default: + ep_char |= GRUB_EHCI_SPEED_HIGH; + /* XXX: How we will handle unknown value of speed? */ + } + ep_char |= (transfer->endpoint << GRUB_EHCI_EP_NUM_OFF) + & GRUB_EHCI_EP_NUM_MASK; + ep_char |= transfer->devaddr & GRUB_EHCI_DEVADDR_MASK; + qh->ep_char = grub_cpu_to_le32 (ep_char); + /* EP capabilities setup */ + /* MULT field - we try to use max. number + * PortNumber - included now in device structure referenced + * inside transfer structure + * HubAddress - included now in device structure referenced + * inside transfer structure + * SplitCompletionMask - AFAIK it is ignored in asynchronous list, + * InterruptScheduleMask - AFAIK it should be zero in async. list */ + ep_cap |= GRUB_EHCI_MULT_THREE; + ep_cap |= (transfer->dev->split_hubport << GRUB_EHCI_DEVPORT_OFF) + & GRUB_EHCI_DEVPORT_MASK; + ep_cap |= (transfer->dev->split_hubaddr << GRUB_EHCI_HUBADDR_OFF) + & GRUB_EHCI_HUBADDR_MASK; + if (transfer->dev->speed == GRUB_USB_SPEED_LOW + && transfer->type != GRUB_USB_TRANSACTION_TYPE_CONTROL) + { + ep_cap |= (1<<0) << GRUB_EHCI_SMASK_OFF; + ep_cap |= (7<<2) << GRUB_EHCI_CMASK_OFF; + } + qh->ep_cap = grub_cpu_to_le32 (ep_cap); + + grub_dprintf ("ehci", "setup_qh: qh=%p, not changed: qh_hptr=%08x\n", + qh, grub_le_to_cpu32 (qh->qh_hptr)); + grub_dprintf ("ehci", "setup_qh: ep_char=%08x, ep_cap=%08x\n", + ep_char, ep_cap); + grub_dprintf ("ehci", "setup_qh: end\n"); + grub_dprintf ("ehci", "setup_qh: not changed: td_current=%08x\n", + grub_le_to_cpu32 (qh->td_current)); + grub_dprintf ("ehci", "setup_qh: not changed: next_td=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.next_td)); + grub_dprintf ("ehci", "setup_qh: not changed: alt_next_td=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.alt_next_td)); + grub_dprintf ("ehci", "setup_qh: not changed: token=%08x\n", + grub_le_to_cpu32 (qh->td_overlay.token)); +} + +static grub_ehci_qh_t +grub_ehci_find_qh (struct grub_ehci *e, grub_usb_transfer_t transfer) +{ + grub_uint32_t target, mask; + int i; + grub_ehci_qh_t qh = e->qh_virt; + grub_ehci_qh_t head; + grub_uint32_t qh_phys; + grub_uint32_t qh_terminate = + GRUB_EHCI_TERMINATE | GRUB_EHCI_HPTR_TYPE_QH; + grub_ehci_qh_t qh_iter; + + /* Prepare part of EP Characteristic to find existing QH */ + target = ((transfer->endpoint << GRUB_EHCI_EP_NUM_OFF) | + transfer->devaddr) & GRUB_EHCI_TARGET_MASK; + target = grub_cpu_to_le32 (target); + mask = grub_cpu_to_le32_compile_time (GRUB_EHCI_TARGET_MASK); + + /* low speed interrupt transfers are linked to the periodic */ + /* schedule, everything else to the asynchronous schedule */ + if (transfer->dev->speed == GRUB_USB_SPEED_LOW + && transfer->type != GRUB_USB_TRANSACTION_TYPE_CONTROL) + head = &qh[0]; + else + head = &qh[1]; + + /* First try to find existing QH with proper target in proper list */ + qh_phys = grub_le_to_cpu32( head->qh_hptr ); + if (qh_phys != qh_terminate) + qh_iter = grub_dma_phys2virt ( qh_phys & GRUB_EHCI_QHTDPTR_MASK, + e->qh_chunk ); + else + qh_iter = NULL; + + for ( + i = 0; + (qh_phys != qh_terminate) && (qh_iter != NULL) && + (qh_iter != head) && (i < GRUB_EHCI_N_QH); + i++ ) + { + if (target == (qh_iter->ep_char & mask)) + { + /* Found proper existing (and linked) QH, do setup of QH */ + grub_dprintf ("ehci", "find_qh: found, QH=%p\n", qh_iter); + grub_ehci_setup_qh (qh_iter, transfer); + sync_all_caches (e); + return qh_iter; + } + + qh_phys = grub_le_to_cpu32( qh_iter->qh_hptr ); + if (qh_phys != qh_terminate) + qh_iter = grub_dma_phys2virt ( qh_phys & GRUB_EHCI_QHTDPTR_MASK, + e->qh_chunk ); + else + qh_iter = NULL; + } + + /* variable "i" should be never equal to GRUB_EHCI_N_QH here */ + if (i >= GRUB_EHCI_N_QH) + { /* Something very bad happened in QH list(s) ! */ + grub_dprintf ("ehci", "find_qh: Mismatch in QH list! head=%p\n", + head); + } + + /* QH with target_addr does not exist, we have to find and add it */ + for (i = 2; i < GRUB_EHCI_N_QH; i++) /* We ignore zero and first QH */ + { + if (!qh[i].ep_char) + break; /* Found first not-allocated QH, finish */ + } + + /* Have we any free QH in array ? */ + if (i >= GRUB_EHCI_N_QH) /* No. */ + { + grub_dprintf ("ehci", "find_qh: end - no free QH\n"); + return NULL; + } + grub_dprintf ("ehci", "find_qh: new, i=%d, QH=%p\n", + i, &qh[i]); + /* Currently we simply take next (current) QH in array, no allocation + * function is used. It should be no problem until we will need to + * de-allocate QHs of unplugged devices. */ + /* We should preset new QH and link it into AL */ + grub_ehci_setup_qh (&qh[i], transfer); + + /* Linking - this new (last) QH will copy the QH from the head QH */ + qh[i].qh_hptr = head->qh_hptr; + /* Linking - the head QH will point to this new QH */ + head->qh_hptr = grub_cpu_to_le32 (GRUB_EHCI_HPTR_TYPE_QH + | grub_dma_virt2phys (&qh[i], + e->qh_chunk)); + + return &qh[i]; +} + +static grub_ehci_td_t +grub_ehci_alloc_td (struct grub_ehci *e) +{ + grub_ehci_td_t ret; + + /* Check if there is a Transfer Descriptor available. */ + if (!e->tdfree_virt) + { + grub_dprintf ("ehci", "alloc_td: end - no free TD\n"); + return NULL; + } + + ret = e->tdfree_virt; /* Take current free TD */ + /* Advance to next free TD in chain */ + if (ret->link_td) + e->tdfree_virt = grub_dma_phys2virt (ret->link_td, e->td_chunk); + else + e->tdfree_virt = NULL; + ret->link_td = 0; /* Reset link_td in allocated TD */ + return ret; +} + +static void +grub_ehci_free_td (struct grub_ehci *e, grub_ehci_td_t td) +{ + /* Chain new free TD & rest */ + if (e->tdfree_virt) + td->link_td = grub_dma_virt2phys (e->tdfree_virt, e->td_chunk); + else + td->link_td = 0; + e->tdfree_virt = td; /* Change address of first free TD */ +} + +static void +grub_ehci_free_tds (struct grub_ehci *e, grub_ehci_td_t td, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + int i; /* Index of TD in transfer */ + grub_uint32_t token, to_transfer; + + /* Note: Another part of code is responsible to this QH is + * INACTIVE ! */ + *actual = 0; + + /* Free the TDs in this queue and set last_trans. */ + for (i = 0; td; i++) + { + grub_ehci_td_t tdprev; + + token = grub_le_to_cpu32 (td->token); + to_transfer = (token & GRUB_EHCI_TOTAL_MASK) >> GRUB_EHCI_TOTAL_OFF; + + /* Check state of TD - if it did not transfer + * whole data then set last_trans - it should be last executed TD + * in case when something went wrong. */ + if (transfer && (td->size != to_transfer)) + transfer->last_trans = i; + + *actual += td->size - to_transfer; + + /* Unlink the TD */ + tdprev = td; + if (td->link_td) + td = grub_dma_phys2virt (td->link_td, e->td_chunk); + else + td = NULL; + + /* Free the TD. */ + grub_ehci_free_td (e, tdprev); + } + + /* Check if last_trans was set. If not and something was + * transferred (it should be all data in this case), set it + * to index of last TD, i.e. i-1 */ + if (transfer && (transfer->last_trans < 0) && (*actual != 0)) + transfer->last_trans = i - 1; + + /* XXX: Fix it: last_trans may be set to bad index. + * Probably we should test more error flags to distinguish + * if TD was at least partialy executed or not at all. + * Generaly, we still could have problem with toggling because + * EHCI can probably split transactions into smaller parts then + * we defined in transaction even if we did not exceed MaxFrame + * length - it probably could happen at the end of microframe (?) + * and if the buffer is crossing page boundary (?). */ +} + +static grub_ehci_td_t +grub_ehci_transaction (struct grub_ehci *e, + grub_transfer_type_t type, + unsigned int toggle, grub_size_t size, + grub_uint32_t data, grub_ehci_td_t td_alt) +{ + grub_ehci_td_t td; + grub_uint32_t token; + grub_uint32_t bufadr; + int i; + + /* Test of transfer size, it can be: + * <= GRUB_EHCI_MAXBUFLEN if data aligned to page boundary + * <= GRUB_EHCI_MAXBUFLEN - GRUB_EHCI_BUFPAGELEN if not aligned + * (worst case) + */ + if ((((data % GRUB_EHCI_BUFPAGELEN) == 0) + && (size > GRUB_EHCI_MAXBUFLEN)) + || + (((data % GRUB_EHCI_BUFPAGELEN) != 0) + && (size > (GRUB_EHCI_MAXBUFLEN - GRUB_EHCI_BUFPAGELEN)))) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "too long data buffer for EHCI transaction"); + return 0; + } + + /* Grab a free Transfer Descriptor and initialize it. */ + td = grub_ehci_alloc_td (e); + if (!td) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no transfer descriptors available for EHCI transfer"); + return 0; + } + + grub_dprintf ("ehci", + "transaction: type=%d, toggle=%d, size=%lu data=0x%x td=%p\n", + type, toggle, (unsigned long) size, data, td); + + /* Fill whole TD by zeros */ + grub_memset ((void *) td, 0, sizeof (struct grub_ehci_td)); + + /* Don't point to any TD yet, just terminate. */ + td->next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + /* Set alternate pointer. When short packet occurs, alternate TD + * will not be really fetched because it is not active. But don't + * forget, EHCI will try to fetch alternate TD every scan of AL + * until QH is halted. */ + td->alt_next_td = grub_cpu_to_le32 (grub_dma_virt2phys (td_alt, + e->td_chunk)); + /* token: + * TOGGLE - according to toggle + * TOTAL SIZE = size + * Interrupt On Complete = FALSE, we don't need IRQ + * Current Page = 0 + * Error Counter = max. value = 3 + * PID Code - according to type + * STATUS: + * ACTIVE bit should be set to one + * SPLIT TRANS. STATE bit should be zero. It is ignored + * in HIGH speed transaction, and should be zero for LOW/FULL + * speed to indicate state Do Split Transaction */ + token = toggle ? GRUB_EHCI_TOGGLE : 0; + token |= (size << GRUB_EHCI_TOTAL_OFF) & GRUB_EHCI_TOTAL_MASK; + token |= GRUB_EHCI_CERR_3; + switch (type) + { + case GRUB_USB_TRANSFER_TYPE_IN: + token |= GRUB_EHCI_PIDCODE_IN; + break; + case GRUB_USB_TRANSFER_TYPE_OUT: + token |= GRUB_EHCI_PIDCODE_OUT; + break; + case GRUB_USB_TRANSFER_TYPE_SETUP: + token |= GRUB_EHCI_PIDCODE_SETUP; + break; + default: /* XXX: Should not happen, but what to do if it does ? */ + break; + } + token |= GRUB_EHCI_STATUS_ACTIVE; + td->token = grub_cpu_to_le32 (token); + + /* Fill buffer pointers according to size */ + bufadr = data; + td->buffer_page[0] = grub_cpu_to_le32 (bufadr); + bufadr = ((bufadr / GRUB_EHCI_BUFPAGELEN) + 1) * GRUB_EHCI_BUFPAGELEN; + for (i = 1; ((bufadr - data) < size) && (i < GRUB_EHCI_TD_BUF_PAGES); i++) + { + td->buffer_page[i] = grub_cpu_to_le32 (bufadr & GRUB_EHCI_BUFPTR_MASK); + bufadr = ((bufadr / GRUB_EHCI_BUFPAGELEN) + 1) * GRUB_EHCI_BUFPAGELEN; + } + + /* Remember data size for future use... */ + td->size = (grub_uint32_t) size; + + grub_dprintf ("ehci", "td=%p\n", td); + grub_dprintf ("ehci", "HW: next_td=%08x, alt_next_td=%08x\n", + grub_le_to_cpu32 (td->next_td), + grub_le_to_cpu32 (td->alt_next_td)); + grub_dprintf ("ehci", "HW: token=%08x, buffer[0]=%08x\n", + grub_le_to_cpu32 (td->token), + grub_le_to_cpu32 (td->buffer_page[0])); + grub_dprintf ("ehci", "HW: buffer[1]=%08x, buffer[2]=%08x\n", + grub_le_to_cpu32 (td->buffer_page[1]), + grub_le_to_cpu32 (td->buffer_page[2])); + grub_dprintf ("ehci", "HW: buffer[3]=%08x, buffer[4]=%08x\n", + grub_le_to_cpu32 (td->buffer_page[3]), + grub_le_to_cpu32 (td->buffer_page[4])); + grub_dprintf ("ehci", "link_td=%08x, size=%08x\n", + td->link_td, td->size); + + return td; +} + +struct grub_ehci_transfer_controller_data +{ + grub_ehci_qh_t qh_virt; + grub_ehci_td_t td_first_virt; + grub_ehci_td_t td_alt_virt; + grub_ehci_td_t td_last_virt; + grub_uint32_t td_last_phys; +}; + +static grub_usb_err_t +grub_ehci_setup_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_ehci_td_t td = NULL; + grub_ehci_td_t td_prev = NULL; + int i; + struct grub_ehci_transfer_controller_data *cdata; + grub_uint32_t status; + + sync_all_caches (e); + + /* Check if EHCI is running and AL is enabled */ + status = grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS); + if ((status & GRUB_EHCI_ST_HC_HALTED) != 0) + /* XXX: Fix it: Currently we don't do anything to restart EHCI */ + { + grub_dprintf ("ehci", "setup_transfer: halted, status = 0x%x\n", + status); + return GRUB_USB_ERR_INTERNAL; + } + status = grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS); + if ((status + & (GRUB_EHCI_ST_AS_STATUS | GRUB_EHCI_ST_PS_STATUS)) == 0) + /* XXX: Fix it: Currently we don't do anything to restart EHCI */ + { + grub_dprintf ("ehci", "setup_transfer: no AS/PS, status = 0x%x\n", + status); + return GRUB_USB_ERR_INTERNAL; + } + + /* Allocate memory for controller transfer data. */ + cdata = grub_malloc (sizeof (*cdata)); + if (!cdata) + return GRUB_USB_ERR_INTERNAL; + cdata->td_first_virt = NULL; + + /* Allocate a queue head for the transfer queue. */ + cdata->qh_virt = grub_ehci_find_qh (e, transfer); + if (!cdata->qh_virt) + { + grub_dprintf ("ehci", "setup_transfer: no QH\n"); + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + + /* To detect short packet we need some additional "alternate" TD, + * allocate it first. */ + cdata->td_alt_virt = grub_ehci_alloc_td (e); + if (!cdata->td_alt_virt) + { + grub_dprintf ("ehci", "setup_transfer: no TDs\n"); + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + /* Fill whole alternate TD by zeros (= inactive) and set + * Terminate bits and Halt bit */ + grub_memset ((void *) cdata->td_alt_virt, 0, sizeof (struct grub_ehci_td)); + cdata->td_alt_virt->next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + cdata->td_alt_virt->alt_next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + cdata->td_alt_virt->token = grub_cpu_to_le32_compile_time (GRUB_EHCI_STATUS_HALTED); + + /* Allocate appropriate number of TDs and set */ + for (i = 0; i < transfer->transcnt; i++) + { + grub_usb_transaction_t tr = &transfer->transactions[i]; + + td = grub_ehci_transaction (e, tr->pid, tr->toggle, tr->size, + tr->data, cdata->td_alt_virt); + + if (!td) /* de-allocate and free all */ + { + grub_size_t actual = 0; + + if (cdata->td_first_virt) + grub_ehci_free_tds (e, cdata->td_first_virt, NULL, &actual); + + grub_free (cdata); + grub_dprintf ("ehci", "setup_transfer: no TD\n"); + return GRUB_USB_ERR_INTERNAL; + } + + /* Register new TD in cdata or previous TD */ + if (!cdata->td_first_virt) + cdata->td_first_virt = td; + else + { + td_prev->link_td = grub_dma_virt2phys (td, e->td_chunk); + td_prev->next_td = + grub_cpu_to_le32 (grub_dma_virt2phys (td, e->td_chunk)); + } + td_prev = td; + } + + /* Remember last TD */ + cdata->td_last_virt = td; + cdata->td_last_phys = grub_dma_virt2phys (td, e->td_chunk); + /* Last TD should not have set alternate TD */ + cdata->td_last_virt->alt_next_td = grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + + grub_dprintf ("ehci", "setup_transfer: cdata=%p, qh=%p\n", + cdata,cdata->qh_virt); + grub_dprintf ("ehci", "setup_transfer: td_first=%p, td_alt=%p\n", + cdata->td_first_virt, + cdata->td_alt_virt); + grub_dprintf ("ehci", "setup_transfer: td_last=%p\n", + cdata->td_last_virt); + + /* Start transfer: */ + /* Unlink possible alternate pointer in QH */ + cdata->qh_virt->td_overlay.alt_next_td = + grub_cpu_to_le32_compile_time (GRUB_EHCI_TERMINATE); + /* Link new TDs with QH via next_td */ + cdata->qh_virt->td_overlay.next_td = + grub_cpu_to_le32 (grub_dma_virt2phys + (cdata->td_first_virt, e->td_chunk)); + /* Reset Active and Halted bits in QH to activate Advance Queue, + * i.e. reset token */ + cdata->qh_virt->td_overlay.token = grub_cpu_to_le32_compile_time (0); + + sync_all_caches (e); + + /* Finito */ + transfer->controller_data = cdata; + + return GRUB_USB_ERR_NONE; +} + +/* This function expects QH is not active. + * Function set Halt bit in QH TD overlay and possibly prints + * necessary debug information. */ +static void +grub_ehci_pre_finish_transfer (grub_usb_transfer_t transfer) +{ + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + /* Collect debug data here if necessary */ + + /* Set Halt bit in not active QH. AL will not attempt to do + * Advance Queue on QH with Halt bit set, i.e., we can then + * safely manipulate with QH TD part. */ + cdata->qh_virt->td_overlay.token = (cdata->qh_virt->td_overlay.token + | + grub_cpu_to_le32_compile_time + (GRUB_EHCI_STATUS_HALTED)) & + grub_cpu_to_le32_compile_time (~GRUB_EHCI_STATUS_ACTIVE); + + /* Print debug data here if necessary */ + +} + +static grub_usb_err_t +grub_ehci_parse_notrun (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + grub_dprintf ("ehci", "parse_notrun: info\n"); + + /* QH can be in any state in this case. */ + /* But EHCI or AL is not running, so QH is surely not active + * even if it has Active bit set... */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + sync_all_caches (e); + + /* Additionally, do something with EHCI to make it running (what?) */ + /* Try enable EHCI and AL */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP | GRUB_EHCI_CMD_AS_ENABL + | GRUB_EHCI_CMD_PS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + + return GRUB_USB_ERR_UNRECOVERABLE; +} + +static grub_usb_err_t +grub_ehci_parse_halt (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_uint32_t token; + grub_usb_err_t err = GRUB_USB_ERR_NAK; + + /* QH should be halted and not active in this case. */ + + grub_dprintf ("ehci", "parse_halt: info\n"); + + /* Remember token before call pre-finish function */ + token = grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token); + + /* Do things like in normal finish */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + sync_all_caches (e); + + /* Evaluation of error code - currently we don't have GRUB USB error + * codes for some EHCI states, GRUB_USB_ERR_DATA is used for them. + * Order of evaluation is critical, specially bubble/stall. */ + if ((token & GRUB_EHCI_STATUS_BABBLE) != 0) + err = GRUB_USB_ERR_BABBLE; + else if ((token & GRUB_EHCI_CERR_MASK) != 0) + err = GRUB_USB_ERR_STALL; + else if ((token & GRUB_EHCI_STATUS_TRANERR) != 0) + err = GRUB_USB_ERR_DATA; + else if ((token & GRUB_EHCI_STATUS_BUFERR) != 0) + err = GRUB_USB_ERR_DATA; + else if ((token & GRUB_EHCI_STATUS_MISSDMF) != 0) + err = GRUB_USB_ERR_DATA; + + return err; +} + +static grub_usb_err_t +grub_ehci_parse_success (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + + grub_dprintf ("ehci", "parse_success: info\n"); + + /* QH should be not active in this case, but it is not halted. */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + + sync_all_caches (e); + + return GRUB_USB_ERR_NONE; +} + + +static grub_usb_err_t +grub_ehci_check_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer, grub_size_t * actual) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_uint32_t token, token_ftd; + + sync_all_caches (e); + + grub_dprintf ("ehci", + "check_transfer: EHCI STATUS=%08x, cdata=%p, qh=%p\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS), + cdata, cdata->qh_virt); + grub_dprintf ("ehci", "check_transfer: qh_hptr=%08x, ep_char=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->qh_hptr), + grub_le_to_cpu32 (cdata->qh_virt->ep_char)); + grub_dprintf ("ehci", "check_transfer: ep_cap=%08x, td_current=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->ep_cap), + grub_le_to_cpu32 (cdata->qh_virt->td_current)); + grub_dprintf ("ehci", "check_transfer: next_td=%08x, alt_next_td=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td), + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.alt_next_td)); + grub_dprintf ("ehci", "check_transfer: token=%08x, buffer[0]=%08x\n", + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token), + grub_le_to_cpu32 (cdata->qh_virt->td_overlay.buffer_page[0])); + + /* Check if EHCI is running and AL is enabled */ + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_HC_HALTED) != 0) + return grub_ehci_parse_notrun (dev, transfer, actual); + if ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & (GRUB_EHCI_ST_AS_STATUS | GRUB_EHCI_ST_PS_STATUS)) == 0) + return grub_ehci_parse_notrun (dev, transfer, actual); + + token = grub_le_to_cpu32 (cdata->qh_virt->td_overlay.token); + /* If the transfer consist from only one TD, we should check */ + /* if the TD was really executed and deactivated - to prevent */ + /* false detection of transfer finish. */ + token_ftd = grub_le_to_cpu32 (cdata->td_first_virt->token); + + /* Detect QH halted */ + if ((token & GRUB_EHCI_STATUS_HALTED) != 0) + return grub_ehci_parse_halt (dev, transfer, actual); + + /* Detect QH not active - QH is not active and no next TD */ + if (token && ((token & GRUB_EHCI_STATUS_ACTIVE) == 0) + && ((token_ftd & GRUB_EHCI_STATUS_ACTIVE) == 0)) + { + /* It could be finish at all or short packet condition */ + if ((grub_le_to_cpu32 (cdata->qh_virt->td_overlay.next_td) + & GRUB_EHCI_TERMINATE) && + ((grub_le_to_cpu32 (cdata->qh_virt->td_current) + & GRUB_EHCI_QHTDPTR_MASK) == cdata->td_last_phys)) + /* Normal finish */ + return grub_ehci_parse_success (dev, transfer, actual); + else if ((token & GRUB_EHCI_TOTAL_MASK) != 0) + /* Short packet condition */ + /* But currently we don't handle it - higher level will do it */ + return grub_ehci_parse_success (dev, transfer, actual); + } + + return GRUB_USB_ERR_WAIT; +} + +static grub_usb_err_t +grub_ehci_cancel_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer) +{ + struct grub_ehci *e = dev->data; + struct grub_ehci_transfer_controller_data *cdata = + transfer->controller_data; + grub_size_t actual; + int i; + grub_uint64_t maxtime; + grub_uint32_t qh_phys; + + sync_all_caches (e); + + grub_uint32_t interrupt = + cdata->qh_virt->ep_cap & GRUB_EHCI_SMASK_MASK; + + /* QH can be active and should be de-activated and halted */ + + grub_dprintf ("ehci", "cancel_transfer: begin\n"); + + /* First check if EHCI is running - if not, there is no problem */ + /* to cancel any transfer. Or, if transfer is asynchronous, check */ + /* if AL is enabled - if not, transfer can be canceled also. */ + if (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) & + GRUB_EHCI_ST_HC_HALTED) != 0) || + (!interrupt && ((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) & + (GRUB_EHCI_ST_AS_STATUS | GRUB_EHCI_ST_PS_STATUS)) == 0))) + { + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + grub_free (cdata); + sync_all_caches (e); + grub_dprintf ("ehci", "cancel_transfer: end - EHCI not running\n"); + return GRUB_USB_ERR_NONE; + } + + /* EHCI and (AL or SL) are running. What to do? */ + /* Try to Halt QH via de-scheduling QH. */ + /* Find index of previous QH */ + qh_phys = grub_dma_virt2phys(cdata->qh_virt, e->qh_chunk); + for (i = 0; i < GRUB_EHCI_N_QH; i++) + { + if ((grub_le_to_cpu32(e->qh_virt[i].qh_hptr) + & GRUB_EHCI_QHTDPTR_MASK) == qh_phys) + break; + } + if (i == GRUB_EHCI_N_QH) + { + grub_printf ("%s: prev not found, queues are corrupt\n", __func__); + return GRUB_USB_ERR_UNRECOVERABLE; + } + /* Unlink QH from AL */ + e->qh_virt[i].qh_hptr = cdata->qh_virt->qh_hptr; + + sync_all_caches (e); + + /* If this is an interrupt transfer, we just wait for the periodic + * schedule to advance a few times and then assume that the EHCI + * controller has read the updated QH. */ + if (cdata->qh_virt->ep_cap & GRUB_EHCI_SMASK_MASK) + { + grub_millisleep(20); + } + else + { + /* For the asynchronous schedule we use the advance doorbell to find + * out when the EHCI controller has read the updated QH. */ + + /* Ring the doorbell */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ADV_D + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND); + /* Wait answer with timeout */ + maxtime = grub_get_time_ms () + 2; + while (((grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS) + & GRUB_EHCI_ST_AS_ADVANCE) == 0) + && (grub_get_time_ms () < maxtime)); + + /* We do not detect the timeout because if timeout occurs, it most + * probably means something wrong with EHCI - maybe stopped etc. */ + + /* Shut up the doorbell */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + ~GRUB_EHCI_CMD_AS_ADV_D + & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + grub_ehci_oper_write32 (e, GRUB_EHCI_STATUS, + GRUB_EHCI_ST_AS_ADVANCE + | grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + /* Ensure command is written */ + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS); + } + + /* Now is QH out of AL and we can do anything with it... */ + grub_ehci_pre_finish_transfer (transfer); + grub_ehci_free_tds (e, cdata->td_first_virt, transfer, &actual); + grub_ehci_free_td (e, cdata->td_alt_virt); + + /* "Free" the QH - link it to itself */ + cdata->qh_virt->ep_char = 0; + cdata->qh_virt->qh_hptr = + grub_cpu_to_le32 ((grub_dma_virt2phys (cdata->qh_virt, + e->qh_chunk) + & GRUB_EHCI_POINTER_MASK) | GRUB_EHCI_HPTR_TYPE_QH); + + grub_free (cdata); + + grub_dprintf ("ehci", "cancel_transfer: end\n"); + + sync_all_caches (e); + + return GRUB_USB_ERR_NONE; +} + +static int +grub_ehci_hubports (grub_usb_controller_t dev) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint32_t portinfo; + + portinfo = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + grub_dprintf ("ehci", "root hub ports=%d\n", portinfo); + return portinfo; +} + +static grub_usb_err_t +grub_ehci_portstatus (grub_usb_controller_t dev, + unsigned int port, unsigned int enable) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint64_t endtime; + + grub_dprintf ("ehci", "portstatus: EHCI STATUS: %08x\n", + grub_ehci_oper_read32 (e, GRUB_EHCI_STATUS)); + grub_dprintf ("ehci", + "portstatus: begin, iobase=%p, port=%d, status=0x%02x\n", + e->iobase, port, grub_ehci_port_read (e, port)); + + /* In any case we need to disable port: + * - if enable==false - we should disable port + * - if enable==true we will do the reset and the specification says + * PortEnable should be FALSE in such case */ + /* Disable the port and wait for it. */ + grub_ehci_port_resbits (e, port, GRUB_EHCI_PORT_ENABLED); + endtime = grub_get_time_ms () + 1000; + while (grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_ENABLED) + if (grub_get_time_ms () > endtime) + return GRUB_USB_ERR_TIMEOUT; + + if (!enable) /* We don't need reset port */ + { + grub_dprintf ("ehci", "portstatus: Disabled.\n"); + grub_dprintf ("ehci", "portstatus: end, status=0x%02x\n", + grub_ehci_port_read (e, port)); + return GRUB_USB_ERR_NONE; + } + + grub_dprintf ("ehci", "portstatus: enable\n"); + + grub_boot_time ("Resetting port %d", port); + + /* Now we will do reset - if HIGH speed device connected, it will + * result in Enabled state, otherwise port remains disabled. */ + /* Set RESET bit for 50ms */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_RESET); + grub_millisleep (50); + + /* Reset RESET bit and wait for the end of reset */ + grub_ehci_port_resbits (e, port, GRUB_EHCI_PORT_RESET); + endtime = grub_get_time_ms () + 1000; + while (grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_RESET) + if (grub_get_time_ms () > endtime) + return GRUB_USB_ERR_TIMEOUT; + grub_boot_time ("Port %d reset", port); + /* Remember "we did the reset" - needed by detect_dev */ + e->reset |= (1 << port); + /* Test if port enabled, i.e. HIGH speed device connected */ + if ((grub_ehci_port_read (e, port) & GRUB_EHCI_PORT_ENABLED) != 0) /* yes! */ + { + grub_dprintf ("ehci", "portstatus: Enabled!\n"); + /* "Reset recovery time" (USB spec.) */ + grub_millisleep (10); + } + else /* no... */ + { + /* FULL speed device connected - change port ownership. + * It results in disconnected state of this EHCI port. */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_OWNER); + return GRUB_USB_ERR_BADDEVICE; + } + + /* XXX: Fix it! There is possible problem - we can say to calling + * function that we lost device if it is FULL speed onlu via + * return value <> GRUB_ERR_NONE. It (maybe) displays also error + * message on screen - but this situation is not error, it is normal + * state! */ + + grub_dprintf ("ehci", "portstatus: end, status=0x%02x\n", + grub_ehci_port_read (e, port)); + + return GRUB_USB_ERR_NONE; +} + +static grub_usb_speed_t +grub_ehci_detect_dev (grub_usb_controller_t dev, int port, int *changed) +{ + struct grub_ehci *e = (struct grub_ehci *) dev->data; + grub_uint32_t status, line_state; + + status = grub_ehci_port_read (e, port); + + /* Connect Status Change bit - it detects change of connection */ + if (status & GRUB_EHCI_PORT_CONNECT_CH) + { + *changed = 1; + /* Reset bit Connect Status Change */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_CONNECT_CH); + } + else + *changed = 0; + + if (!(status & GRUB_EHCI_PORT_CONNECT)) + { /* We should reset related "reset" flag in not connected state */ + e->reset &= ~(1 << port); + return GRUB_USB_SPEED_NONE; + } + /* Detected connected state, so we should return speed. + * But we can detect only LOW speed device and only at connection + * time when PortEnabled=FALSE. FULL / HIGH speed detection is made + * later by EHCI-specific reset procedure. + * Another thing - if detected speed is LOW at connection time, + * we should change port ownership to companion controller. + * So: + * 1. If we detect connected and enabled and EHCI-owned port, + * we can say it is HIGH speed. + * 2. If we detect connected and not EHCI-owned port, we can say + * NONE speed, because such devices are not handled by EHCI. + * 3. If we detect connected, not enabled but reset port, we can say + * NONE speed, because it means FULL device connected to port and + * such devices are not handled by EHCI. + * 4. If we detect connected, not enabled and not reset port, which + * has line state != "K", we will say HIGH - it could be FULL or HIGH + * device, we will see it later after end of EHCI-specific reset + * procedure. + * 5. If we detect connected, not enabled and not reset port, which + * has line state == "K", we can say NONE speed, because LOW speed + * device is connected and we should change port ownership. */ + if ((status & GRUB_EHCI_PORT_ENABLED) != 0) /* Port already enabled, return high speed. */ + return GRUB_USB_SPEED_HIGH; + if ((status & GRUB_EHCI_PORT_OWNER) != 0) /* EHCI is not port owner */ + return GRUB_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ + if ((e->reset & (1 << port)) != 0) /* Port reset was done = FULL speed */ + return GRUB_USB_SPEED_NONE; /* EHCI driver is ignoring this port. */ + else /* Port connected but not enabled - test port speed. */ + { + line_state = status & GRUB_EHCI_PORT_LINE_STAT; + if (line_state != GRUB_EHCI_PORT_LINE_LOWSP) + return GRUB_USB_SPEED_HIGH; + /* Detected LOW speed device, we should change + * port ownership. + * XXX: Fix it!: There should be test if related companion + * controler is available ! And what to do if it does not exist ? */ + grub_ehci_port_setbits (e, port, GRUB_EHCI_PORT_OWNER); + return GRUB_USB_SPEED_NONE; /* Ignore this port */ + /* Note: Reset of PORT_OWNER bit is done by EHCI HW when + * device is really disconnected from port. + * Don't do PORT_OWNER bit reset by SW when not connected signal + * is detected in port register ! */ + } +} + +static grub_err_t +grub_ehci_restore_hw (void) +{ + struct grub_ehci *e; + grub_uint32_t n_ports; + int i; + + /* We should re-enable all EHCI HW similarly as on inithw */ + for (e = ehci; e; e = e->next) + { + /* Check if EHCI is halted and halt it if not */ + if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI halt timeout"); + + /* Reset EHCI */ + if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE) + grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI reset timeout"); + + /* Setup some EHCI registers and enable EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_FL_BASE, e->framelist_phys); + grub_ehci_oper_write32 (e, GRUB_EHCI_CUR_AL_ADDR, + grub_dma_virt2phys (&e->qh_virt[1], + e->qh_chunk)); + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_RUNSTOP | + grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Set ownership of root hub ports to EHCI */ + grub_ehci_oper_write32 (e, GRUB_EHCI_CONFIG_FLAG, + GRUB_EHCI_CF_EHCI_OWNER); + + /* Enable asynchronous list */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + GRUB_EHCI_CMD_AS_ENABL + | GRUB_EHCI_CMD_PS_ENABL + | grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Now should be possible to power-up and enumerate ports etc. */ + if ((grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_PPC) != 0) + { /* EHCI has port powering control */ + /* Power on all ports */ + n_ports = grub_ehci_ehcc_read32 (e, GRUB_EHCI_EHCC_SPARAMS) + & GRUB_EHCI_SPARAMS_N_PORTS; + for (i = 0; i < (int) n_ports; i++) + grub_ehci_oper_write32 (e, GRUB_EHCI_PORT_STAT_CMD + i * 4, + GRUB_EHCI_PORT_POWER + | grub_ehci_oper_read32 (e, + GRUB_EHCI_PORT_STAT_CMD + + i * 4)); + } + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ehci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ehci *e; + + /* We should disable all EHCI HW to prevent any DMA access etc. */ + for (e = ehci; e; e = e->next) + { + /* Disable both lists */ + grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND, + ~(GRUB_EHCI_CMD_AS_ENABL | GRUB_EHCI_CMD_PS_ENABL) + & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND)); + + /* Check if EHCI is halted and halt it if not */ + grub_ehci_halt (e); + + /* Reset EHCI */ + grub_ehci_reset (e); + } + + return GRUB_ERR_NONE; +} + +static struct grub_usb_controller_dev usb_controller = { + .name = "ehci", + .iterate = grub_ehci_iterate, + .setup_transfer = grub_ehci_setup_transfer, + .check_transfer = grub_ehci_check_transfer, + .cancel_transfer = grub_ehci_cancel_transfer, + .hubports = grub_ehci_hubports, + .portstatus = grub_ehci_portstatus, + .detect_dev = grub_ehci_detect_dev, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = GRUB_EHCI_N_TD * 3 / 4 +}; + +GRUB_MOD_INIT (ehci) +{ + COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_td) == 64); + COMPILE_TIME_ASSERT (sizeof (struct grub_ehci_qh) == 96); + + grub_stop_disk_firmware (); + + grub_boot_time ("Initing EHCI hardware"); + grub_ehci_pci_scan (); + grub_boot_time ("Registering EHCI driver"); + grub_usb_controller_dev_register (&usb_controller); + grub_boot_time ("EHCI driver registered"); + grub_loader_register_preboot_hook (grub_ehci_fini_hw, grub_ehci_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); +} + +GRUB_MOD_FINI (ehci) +{ + grub_ehci_fini_hw (0); + grub_usb_controller_dev_unregister (&usb_controller); +} diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c new file mode 100644 index 000000000..5363a61f6 --- /dev/null +++ b/grub-core/bus/usb/ohci.c @@ -0,0 +1,1468 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +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]; +} GRUB_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 */ + volatile struct grub_ohci_td *link_td; /* pointer to next free/chained TD + * pointer as uint32 */ + grub_uint32_t prev_td_phys; /* we need it to find previous TD + * physical address in CPU endian */ + grub_uint32_t tr_index; /* index of TD in transfer */ + grub_uint8_t pad[8 - sizeof (volatile struct grub_ohci_td *)]; /* padding to 32 bytes */ +} GRUB_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; +} GRUB_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 */ +}; + +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 256 +#define GRUB_OHCI_BULK_EDS 510 +#define GRUB_OHCI_TDS 640 + +#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); +} + + + +/* Iterate over all PCI devices. Determine if a device is an OHCI + controller. If this is the case, initialize it. */ +static int +grub_ohci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_uint32_t interf; + grub_uint32_t base; + grub_pci_address_t addr; + struct grub_ohci *o; + grub_uint32_t revision; + int j; + + /* Determine IO base address. */ + grub_dprintf ("ohci", "pciid = %x\n", pciid); + + if (pciid == GRUB_CS5536_PCIID) + { + grub_uint64_t basereg; + + 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); + + base &= GRUB_PCI_ADDR_MEM_MASK; + if (!base) + { + grub_dprintf ("ehci", + "EHCI: EHCI is not mapper\n"); + return 0; + } + + /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word(addr, + GRUB_PCI_COMMAND_MEM_ENABLED + | GRUB_PCI_COMMAND_BUS_MASTER + | grub_pci_read_word(addr)); + + 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_compile_time (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_compile_time (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 = &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); +#if 0 /* We don't need it at all, handled via hotplugging */ + /* Now we have hot-plugging, we need to wait for stable power only */ + grub_millisleep (100); +#endif + + /* 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, NULL); +} + + + +static int +grub_ohci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data) +{ + struct grub_ohci *o; + struct grub_usb_controller dev; + + for (o = ohci; o; o = o->next) + { + dev.data = o; + if (hook (&dev, hook_data)) + 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 = 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=%lu\n", + td, type, toggle, (unsigned long) 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 */ + token |= ( 7 << 21); /* Never generate interrupt */ + 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; +}; + +static grub_usb_err_t +grub_ohci_setup_transfer (grub_usb_controller_t dev, + grub_usb_transfer_t transfer) +{ + struct grub_ohci *o = (struct grub_ohci *) dev->data; + int bulk = 0; + grub_ohci_td_t td_next_virt; + grub_uint32_t target; + grub_uint32_t td_head_phys; + grub_uint32_t td_tail_phys; + int i; + struct grub_ohci_transfer_controller_data *cdata; + + 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; + + default: + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + + /* Find proper ED or add new ED */ + 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_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) + { + cdata->td_head_virt = grub_ohci_alloc_td (o); + if (!cdata->td_head_virt) + { + grub_free (cdata); + 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. */ + 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 + cdata->td_head_virt = grub_ohci_td_phys2virt ( o, td_head_phys ); + + /* Set TDs */ + 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]; + + grub_ohci_transaction (cdata->td_current_virt, tr->pid, tr->toggle, + tr->size, tr->data); + + /* Set index of TD in transfer */ + cdata->td_current_virt->tr_index = (grub_uint32_t) i; + + /* Remember last used (processed) TD phys. addr. */ + 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_ohci_free_tds (o, grub_ohci_td_phys2virt(o, + grub_le_to_cpu32 (cdata->td_head_virt->next_td))); + /* Reset head TD */ + 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 */ + + cdata->td_current_virt->link_td = 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", + 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. */ + cdata->ed_virt->target = grub_cpu_to_le32 (target | (1 << 14)); + /* Set td_tail */ + cdata->ed_virt->td_tail + = grub_cpu_to_le32 (grub_ohci_td_virt2phys (o, cdata->td_current_virt)); + /* Now reset skip bit */ + 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; + } + } + + transfer->controller_data = cdata; + + return GRUB_USB_ERR_NONE; +} + +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 */ + 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, td_last_phys=0x%02x\n", + intstatus, cdata->td_last_phys); + grub_dprintf ("ohci", "TARGET=0x%02x, HEAD=0x%02x, TAIL=0x%02x\n", + target, + 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 */ + + /* Un-chainig of last TD */ + if (cdata->td_current_virt->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; + + cdata->td_current_virt->prev_td_phys = 0; + } + + grub_dprintf ("ohci", "OHCI finished, freeing\n"); + grub_ohci_free_tds (o, cdata->td_head_virt); + grub_free (cdata); +} + +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; + + *actual = 0; + + pre_finish_transfer (dev, transfer); + + /* First we must get proper tderr_phys value */ + /* 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; + } + 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) + { + 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; + 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; + } + + finish_transfer (dev, transfer); + + return err; +} + +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); + + /* I hope we can do it as transfer (most probably) finished OK */ + cdata->tderr_phys = cdata->td_last_phys; + + /* 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; + 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 ); + 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 ((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)) + return parse_halt (dev, transfer, actual); + + /* Finished ED detection */ + if ( (grub_le_to_cpu32 (cdata->ed_virt->td_head) & ~0xfU) == + (grub_le_to_cpu32 (cdata->ed_virt->td_tail) & ~0xfU) ) /* Empty ED */ + { + /* Check the HALT bit */ + /* It looks like nonsense - it was tested previously... + * but it can change because OHCI is working + * simultaneously via DMA... */ + 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; +} + +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); + + /* Possible 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); + + grub_dprintf ("ohci", "Cancel: tderr_phys=0x%x, tderr_virt=%p\n", + cdata->tderr_phys, tderr_virt); + + 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_usb_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; + int i; + + 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_USB_ERR_TIMEOUT; + + grub_dprintf ("ohci", "end of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); + return GRUB_USB_ERR_NONE; + } + + /* OHCI does one reset signal 10ms long but USB spec. + * requests 50ms for root hub (no need to be continuous). + * So, we do reset 5 times... */ + for (i = 0; i < 5; i++) + { + /* Reset the port - timing of reset is done by OHCI */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET); + + /* Wait for reset completion */ + endtime = grub_get_time_ms () + 1000; + while (! (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port) + & GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE)) + if (grub_get_time_ms () > endtime) + return GRUB_USB_ERR_TIMEOUT; + + /* End the reset signaling - reset the reset status change */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE); + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); + } + + /* Enable port */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_SET_PORT_ENABLE); + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port); + + /* Wait for signal enabled */ + 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_USB_ERR_TIMEOUT; + + /* Reset bit Connect Status Change */ + grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, + GRUB_OHCI_RESET_CONNECT_CHANGE); + + /* "Reset recovery time" (USB spec.) */ + grub_millisleep (10); + + grub_dprintf ("ohci", "end of portstatus=0x%02x\n", + grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port)); + + return GRUB_USB_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); + + /* 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_compile_time (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_compile_time (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; +} + + +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, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = GRUB_OHCI_TDS * 3 / 4 +}; + +static struct grub_preboot *fini_hnd; + +GRUB_MOD_INIT(ohci) +{ + COMPILE_TIME_ASSERT (sizeof (struct grub_ohci_td) == 32); + COMPILE_TIME_ASSERT (sizeof (struct grub_ohci_ed) == 16); + + grub_stop_disk_firmware (); + + grub_ohci_inithw (); + grub_usb_controller_dev_register (&usb_controller); + fini_hnd = 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_loader_unregister_preboot_hook (fini_hnd); + grub_usb_controller_dev_unregister (&usb_controller); +} diff --git a/grub-core/bus/usb/serial/common.c b/grub-core/bus/usb/serial/common.c new file mode 100644 index 000000000..e9c995a0a --- /dev/null +++ b/grub-core/bus/usb/serial/common.c @@ -0,0 +1,139 @@ +/* + * 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 . + */ + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +void +grub_usbserial_fini (struct grub_serial_port *port) +{ + port->usbdev->config[port->configno].interf[port->interfno].detach_hook = 0; + port->usbdev->config[port->configno].interf[port->interfno].attached = 0; +} + +void +grub_usbserial_detach (grub_usb_device_t usbdev, int configno, int interfno) +{ + static struct grub_serial_port *port; + port = usbdev->config[configno].interf[interfno].detach_data; + + grub_serial_unregister (port); +} + +static int usbnum = 0; + +int +grub_usbserial_attach (grub_usb_device_t usbdev, int configno, int interfno, + struct grub_serial_driver *driver, int in_endp, + int out_endp) +{ + struct grub_serial_port *port; + int j; + struct grub_usb_desc_if *interf; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + + interf = usbdev->config[configno].interf[interfno].descif; + + port = grub_zalloc (sizeof (*port)); + if (!port) + { + grub_print_error (); + return 0; + } + + port->name = grub_xasprintf ("usb%d", usbnum++); + if (!port->name) + { + grub_free (port); + grub_print_error (); + return 0; + } + + port->usbdev = usbdev; + port->driver = driver; + for (j = 0; j < interf->endpointcnt; j++) + { + struct grub_usb_desc_endp *endp; + endp = &usbdev->config[0].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2 + && (in_endp == GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING + || in_endp == endp->endp_addr)) + { + /* Bulk IN endpoint. */ + port->in_endp = endp; + } + else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2 + && (out_endp == GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING + || out_endp == endp->endp_addr)) + { + /* Bulk OUT endpoint. */ + port->out_endp = endp; + } + } + + /* Configure device */ + if (port->out_endp && port->in_endp) + err = grub_usb_set_configuration (usbdev, configno + 1); + + if (!port->out_endp || !port->in_endp || err) + { + grub_free (port->name); + grub_free (port); + return 0; + } + + port->configno = configno; + port->interfno = interfno; + + grub_serial_config_defaults (port); + grub_serial_register (port); + + port->usbdev->config[port->configno].interf[port->interfno].detach_hook + = grub_usbserial_detach; + port->usbdev->config[port->configno].interf[port->interfno].detach_data + = port; + + return 1; +} + +int +grub_usbserial_fetch (struct grub_serial_port *port, grub_size_t header_size) +{ + grub_usb_err_t err; + grub_size_t actual; + + if (port->bufstart < port->bufend) + return port->buf[port->bufstart++]; + + err = grub_usb_bulk_read_extended (port->usbdev, port->in_endp, + sizeof (port->buf), port->buf, 10, + &actual); + if (err != GRUB_USB_ERR_NONE) + return -1; + + port->bufstart = header_size; + port->bufend = actual; + if (port->bufstart >= port->bufend) + return -1; + + return port->buf[port->bufstart++]; +} diff --git a/grub-core/bus/usb/serial/ftdi.c b/grub-core/bus/usb/serial/ftdi.c new file mode 100644 index 000000000..3c1388ed2 --- /dev/null +++ b/grub-core/bus/usb/serial/ftdi.c @@ -0,0 +1,218 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + GRUB_FTDI_MODEM_CTRL = 0x01, + GRUB_FTDI_FLOW_CTRL = 0x02, + GRUB_FTDI_SPEED_CTRL = 0x03, + GRUB_FTDI_DATA_CTRL = 0x04 + }; + +#define GRUB_FTDI_MODEM_CTRL_DTRRTS 3 +#define GRUB_FTDI_FLOW_CTRL_DTRRTS 3 + +/* Convert speed to divisor. */ +static grub_uint32_t +get_divisor (unsigned int speed) +{ + unsigned int i; + + /* The structure for speed vs. divisor. */ + struct divisor + { + unsigned int speed; + grub_uint32_t div; + }; + + /* The table which lists common configurations. */ + /* Computed with a division formula with 3MHz as base frequency. */ + static struct divisor divisor_tab[] = + { + { 2400, 0x04e2 }, + { 4800, 0x0271 }, + { 9600, 0x4138 }, + { 19200, 0x809c }, + { 38400, 0xc04e }, + { 57600, 0xc034 }, + { 115200, 0x001a } + }; + + /* Set the baud rate. */ + for (i = 0; i < ARRAY_SIZE (divisor_tab); i++) + if (divisor_tab[i].speed == speed) + return divisor_tab[i].div; + return 0; +} + +static void +real_config (struct grub_serial_port *port) +{ + grub_uint32_t divisor; + const grub_uint16_t parities[] = { + [GRUB_SERIAL_PARITY_NONE] = 0x0000, + [GRUB_SERIAL_PARITY_ODD] = 0x0100, + [GRUB_SERIAL_PARITY_EVEN] = 0x0200 + }; + const grub_uint16_t stop_bits[] = { + [GRUB_SERIAL_STOP_BITS_1] = 0x0000, + [GRUB_SERIAL_STOP_BITS_1_5] = 0x0800, + [GRUB_SERIAL_STOP_BITS_2] = 0x1000, + }; + + if (port->configured) + return; + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_MODEM_CTRL, + port->config.rtscts ? GRUB_FTDI_MODEM_CTRL_DTRRTS : 0, + 0, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_FLOW_CTRL, + port->config.rtscts ? GRUB_FTDI_FLOW_CTRL_DTRRTS : 0, + 0, 0, 0); + + divisor = get_divisor (port->config.speed); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_SPEED_CTRL, + divisor & 0xffff, divisor >> 16, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + GRUB_FTDI_DATA_CTRL, + parities[port->config.parity] + | stop_bits[port->config.stop_bits] + | port->config.word_len, 0, 0, 0); + + port->configured = 1; +} + +/* Fetch a key. */ +static int +ftdi_hw_fetch (struct grub_serial_port *port) +{ + real_config (port); + + return grub_usbserial_fetch (port, 2); +} + +/* Put a character. */ +static void +ftdi_hw_put (struct grub_serial_port *port, const int c) +{ + char cc = c; + + real_config (port); + + grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc); +} + +static grub_err_t +ftdi_hw_configure (struct grub_serial_port *port, + struct grub_serial_config *config) +{ + grub_uint16_t divisor; + + divisor = get_divisor (config->speed); + if (divisor == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port speed")); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port word length")); + + port->config = *config; + port->configured = 0; + + return GRUB_ERR_NONE; +} + +static struct grub_serial_driver grub_ftdi_driver = + { + .configure = ftdi_hw_configure, + .fetch = ftdi_hw_fetch, + .put = ftdi_hw_put, + .fini = grub_usbserial_fini + }; + +static const struct +{ + grub_uint16_t vendor, product; +} products[] = + { + {0x0403, 0x6001} /* QEMU virtual USBserial. */ + }; + +static int +grub_ftdi_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned j; + + for (j = 0; j < ARRAY_SIZE (products); j++) + if (usbdev->descdev.vendorid == products[j].vendor + && usbdev->descdev.prodid == products[j].product) + break; + if (j == ARRAY_SIZE (products)) + return 0; + + return grub_usbserial_attach (usbdev, configno, interfno, + &grub_ftdi_driver, + GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING, + GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING); +} + +static struct grub_usb_attach_desc attach_hook = +{ + .class = 0xff, + .hook = grub_ftdi_attach +}; + +GRUB_MOD_INIT(usbserial_ftdi) +{ + grub_usb_register_attach_hook_class (&attach_hook); +} + +GRUB_MOD_FINI(usbserial_ftdi) +{ + grub_serial_unregister_driver (&grub_ftdi_driver); + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/grub-core/bus/usb/serial/pl2303.c b/grub-core/bus/usb/serial/pl2303.c new file mode 100644 index 000000000..8576c4611 --- /dev/null +++ b/grub-core/bus/usb/serial/pl2303.c @@ -0,0 +1,231 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Convert speed to divisor. */ +static grub_uint32_t +is_speed_supported (unsigned int speed) +{ + unsigned int i; + unsigned int supported[] = { 2400, 4800, 9600, 19200, 38400, 57600, 115200}; + + for (i = 0; i < ARRAY_SIZE (supported); i++) + if (supported[i] == speed) + return 1; + return 0; +} + +#define GRUB_PL2303_REQUEST_SET_CONFIG 0x20 +#define GRUB_PL2303_STOP_BITS_1 0x0 +#define GRUB_PL2303_STOP_BITS_1_5 0x1 +#define GRUB_PL2303_STOP_BITS_2 0x2 + +#define GRUB_PL2303_PARITY_NONE 0 +#define GRUB_PL2303_PARITY_ODD 1 +#define GRUB_PL2303_PARITY_EVEN 2 + +struct grub_pl2303_config +{ + grub_uint32_t speed; + grub_uint8_t stop_bits; + grub_uint8_t parity; + grub_uint8_t word_len; +} GRUB_PACKED; + +static void +real_config (struct grub_serial_port *port) +{ + struct grub_pl2303_config config_pl2303; + char xx; + + if (port->configured) + return; + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0x0404, 0, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8383, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0x0404, 1, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8484, 0, 1, &xx); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_IN, + 1, 0x8383, 0, 1, &xx); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0, 1, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 1, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 2, 0x44, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 8, 0, 0, 0); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 9, 0, 0, 0); + + if (port->config.stop_bits == GRUB_SERIAL_STOP_BITS_2) + config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_2; + else if (port->config.stop_bits == GRUB_SERIAL_STOP_BITS_1_5) + config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_1_5; + else + config_pl2303.stop_bits = GRUB_PL2303_STOP_BITS_1; + + switch (port->config.parity) + { + case GRUB_SERIAL_PARITY_NONE: + config_pl2303.parity = GRUB_PL2303_PARITY_NONE; + break; + case GRUB_SERIAL_PARITY_ODD: + config_pl2303.parity = GRUB_PL2303_PARITY_ODD; + break; + case GRUB_SERIAL_PARITY_EVEN: + config_pl2303.parity = GRUB_PL2303_PARITY_EVEN; + break; + } + + config_pl2303.word_len = port->config.word_len; + config_pl2303.speed = port->config.speed; + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + GRUB_PL2303_REQUEST_SET_CONFIG, 0, 0, + sizeof (config_pl2303), (char *) &config_pl2303); + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + 0x22, 3, 0, 0, 0); + + grub_usb_control_msg (port->usbdev, GRUB_USB_REQTYPE_VENDOR_OUT, + 1, 0, port->config.rtscts ? 0x61 : 0, 0, 0); + port->configured = 1; +} + +/* Fetch a key. */ +static int +pl2303_hw_fetch (struct grub_serial_port *port) +{ + real_config (port); + + return grub_usbserial_fetch (port, 0); +} + +/* Put a character. */ +static void +pl2303_hw_put (struct grub_serial_port *port, const int c) +{ + char cc = c; + + real_config (port); + + grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc); +} + +static grub_err_t +pl2303_hw_configure (struct grub_serial_port *port, + struct grub_serial_config *config) +{ + if (!is_speed_supported (config->speed)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port speed")); + + if (config->parity != GRUB_SERIAL_PARITY_NONE + && config->parity != GRUB_SERIAL_PARITY_ODD + && config->parity != GRUB_SERIAL_PARITY_EVEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port parity")); + + if (config->stop_bits != GRUB_SERIAL_STOP_BITS_1 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_1_5 + && config->stop_bits != GRUB_SERIAL_STOP_BITS_2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port stop bits number")); + + if (config->word_len < 5 || config->word_len > 8) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unsupported serial port word length")); + + port->config = *config; + port->configured = 0; + + return GRUB_ERR_NONE; +} + +static struct grub_serial_driver grub_pl2303_driver = + { + .configure = pl2303_hw_configure, + .fetch = pl2303_hw_fetch, + .put = pl2303_hw_put, + .fini = grub_usbserial_fini + }; + +static const struct +{ + grub_uint16_t vendor, product; +} products[] = + { + {0x067b, 0x2303} + }; + +static int +grub_pl2303_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + unsigned j; + + for (j = 0; j < ARRAY_SIZE (products); j++) + if (usbdev->descdev.vendorid == products[j].vendor + && usbdev->descdev.prodid == products[j].product) + break; + if (j == ARRAY_SIZE (products)) + return 0; + + return grub_usbserial_attach (usbdev, configno, interfno, + &grub_pl2303_driver, + GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING, + GRUB_USB_SERIAL_ENDPOINT_LAST_MATCHING); +} + +static struct grub_usb_attach_desc attach_hook = +{ + .class = 0xff, + .hook = grub_pl2303_attach +}; + +GRUB_MOD_INIT(usbserial_pl2303) +{ + grub_usb_register_attach_hook_class (&attach_hook); +} + +GRUB_MOD_FINI(usbserial_pl2303) +{ + grub_serial_unregister_driver (&grub_pl2303_driver); + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/grub-core/bus/usb/serial/usbdebug_late.c b/grub-core/bus/usb/serial/usbdebug_late.c new file mode 100644 index 000000000..e88ba130e --- /dev/null +++ b/grub-core/bus/usb/serial/usbdebug_late.c @@ -0,0 +1,93 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009,2010,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +/* Fetch a key. */ +static int +usbdebug_late_hw_fetch (struct grub_serial_port *port) +{ + return grub_usbserial_fetch (port, 0); +} + +/* Put a character. */ +static void +usbdebug_late_hw_put (struct grub_serial_port *port, const int c) +{ + char cc = c; + + grub_usb_bulk_write (port->usbdev, port->out_endp, 1, &cc); +} + +static grub_err_t +usbdebug_late_hw_configure (struct grub_serial_port *port __attribute__ ((unused)), + struct grub_serial_config *config __attribute__ ((unused))) +{ + return GRUB_ERR_NONE; +} + +static struct grub_serial_driver grub_usbdebug_late_driver = + { + .configure = usbdebug_late_hw_configure, + .fetch = usbdebug_late_hw_fetch, + .put = usbdebug_late_hw_put, + .fini = grub_usbserial_fini + }; + +static int +grub_usbdebug_late_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + grub_usb_err_t err; + struct grub_usb_desc_debug debugdesc; + + err = grub_usb_get_descriptor (usbdev, GRUB_USB_DESCRIPTOR_DEBUG, configno, + sizeof (debugdesc), (char *) &debugdesc); + if (err) + return 0; + + return grub_usbserial_attach (usbdev, configno, interfno, + &grub_usbdebug_late_driver, + debugdesc.in_endp, debugdesc.out_endp); +} + +static struct grub_usb_attach_desc attach_hook = +{ + .class = 0xff, + .hook = grub_usbdebug_late_attach +}; + +GRUB_MOD_INIT(usbserial_usbdebug_late) +{ + grub_usb_register_attach_hook_class (&attach_hook); +} + +GRUB_MOD_FINI(usbserial_usbdebug_late) +{ + grub_serial_unregister_driver (&grub_usbdebug_late_driver); + grub_usb_unregister_attach_hook_class (&attach_hook); +} diff --git a/grub-core/bus/usb/uhci.c b/grub-core/bus/usb/uhci.c new file mode 100644 index 000000000..0fdea4c1e --- /dev/null +++ b/grub-core/bus/usb/uhci.c @@ -0,0 +1,871 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_UHCI_IOMASK (0x7FF << 5) + +#define N_QH 256 +#define N_TD 640 + +typedef enum + { + GRUB_UHCI_REG_USBCMD = 0x00, + GRUB_UHCI_REG_USBINTR = 0x04, + GRUB_UHCI_REG_FLBASEADD = 0x08, + GRUB_UHCI_REG_PORTSC1 = 0x10, + GRUB_UHCI_REG_PORTSC2 = 0x12, + GRUB_UHCI_REG_USBLEGSUP = 0xc0 + } grub_uhci_reg_t; + +enum + { + GRUB_UHCI_DETECT_CHANGED = (1 << 1), + GRUB_UHCI_DETECT_HAVE_DEVICE = 1, + GRUB_UHCI_DETECT_LOW_SPEED = (1 << 8) + }; + +/* R/WC legacy support bits */ +enum + { + GRUB_UHCI_LEGSUP_END_A20GATE = (1 << 15), + GRUB_UHCI_TRAP_BY_64H_WSTAT = (1 << 11), + GRUB_UHCI_TRAP_BY_64H_RSTAT = (1 << 10), + GRUB_UHCI_TRAP_BY_60H_WSTAT = (1 << 9), + GRUB_UHCI_TRAP_BY_60H_RSTAT = (1 << 8) + }; + +/* Reset all legacy support - clear all R/WC bits and all R/W bits */ +#define GRUB_UHCI_RESET_LEGSUP_SMI ( GRUB_UHCI_LEGSUP_END_A20GATE \ + | GRUB_UHCI_TRAP_BY_64H_WSTAT \ + | GRUB_UHCI_TRAP_BY_64H_RSTAT \ + | GRUB_UHCI_TRAP_BY_60H_WSTAT \ + | GRUB_UHCI_TRAP_BY_60H_RSTAT ) + +/* Some UHCI commands */ +#define GRUB_UHCI_CMD_RUN_STOP (1 << 0) +#define GRUB_UHCI_CMD_HCRESET (1 << 1) +#define GRUB_UHCI_CMD_MAXP (1 << 7) + +/* Important bits in structures */ +#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, + /* These bits should not be written as 1 unless we really need it */ + GRUB_UHCI_PORTSC_RWC = ((1 << 1) | (1 << 3) | (1 << 11) | (3 << 13)) + }; + +/* 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]; +} GRUB_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]; +} GRUB_PACKED; + +typedef volatile struct grub_uhci_td *grub_uhci_td_t; +typedef volatile struct grub_uhci_qh *grub_uhci_qh_t; + +struct grub_uhci +{ + grub_port_t iobase; + volatile grub_uint32_t *framelist_virt; + grub_uint32_t framelist_phys; + struct grub_pci_dma_chunk *framelist_chunk; + + /* N_QH Queue Heads. */ + struct grub_pci_dma_chunk *qh_chunk; + volatile grub_uhci_qh_t qh_virt; + grub_uint32_t qh_phys; + + /* N_TD Transfer Descriptors. */ + struct grub_pci_dma_chunk *td_chunk; + volatile grub_uhci_td_t td_virt; + grub_uint32_t td_phys; + + /* 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); +} + +/* Iterate over all PCI devices. Determine if a device is an UHCI + controller. If this is the case, initialize it. */ +static int +grub_uhci_pci_iter (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__((unused)), + void *data __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 & GRUB_PCI_ADDR_SPACE_MASK) != GRUB_PCI_ADDR_SPACE_IO) + return 0; + + if ((base & GRUB_UHCI_IOMASK) == 0) + return 0; + + /* Set bus master - needed for coreboot or broken BIOSes */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word(addr, GRUB_PCI_COMMAND_IO_ENABLED + | GRUB_PCI_COMMAND_BUS_MASTER + | GRUB_PCI_COMMAND_MEM_ENABLED + | grub_pci_read_word (addr)); + + grub_dprintf ("uhci", "base = %x\n", base); + + /* Allocate memory for the controller and register it. */ + u = grub_zalloc (sizeof (*u)); + if (! u) + return 1; + + u->iobase = (base & GRUB_UHCI_IOMASK) + GRUB_MACHINE_PCI_IO_BASE; + + /* Reset PIRQ and SMI */ + addr = grub_pci_make_address (dev, GRUB_UHCI_REG_USBLEGSUP); + grub_pci_write_word(addr, GRUB_UHCI_RESET_LEGSUP_SMI); + /* Reset the HC */ + grub_uhci_writereg16(u, GRUB_UHCI_REG_USBCMD, GRUB_UHCI_CMD_HCRESET); + grub_millisleep(5); + /* Disable interrupts and commands (just to be safe) */ + grub_uhci_writereg16(u, GRUB_UHCI_REG_USBINTR, 0); + /* Finish HC reset, HC remains disabled */ + grub_uhci_writereg16(u, GRUB_UHCI_REG_USBCMD, 0); + /* Read back to be sure PCI write is done */ + grub_uhci_readreg16(u, GRUB_UHCI_REG_USBCMD); + + /* Reserve a page for the frame list. */ + u->framelist_chunk = grub_memalign_dma32 (4096, 4096); + if (! u->framelist_chunk) + goto fail; + u->framelist_virt = grub_dma_get_virt (u->framelist_chunk); + u->framelist_phys = grub_dma_get_phys (u->framelist_chunk); + + grub_dprintf ("uhci", + "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n", + class, subclass, interf, u->iobase, u->framelist_virt); + + /* The QH pointer of UHCI is only 32 bits, make sure this + code works on on 64 bits architectures. */ + u->qh_chunk = grub_memalign_dma32 (4096, sizeof(struct grub_uhci_qh) * N_QH); + if (! u->qh_chunk) + goto fail; + u->qh_virt = grub_dma_get_virt (u->qh_chunk); + u->qh_phys = grub_dma_get_phys (u->qh_chunk); + + /* The TD pointer of UHCI is only 32 bits, make sure this + code works on on 64 bits architectures. */ + u->td_chunk = grub_memalign_dma32 (4096, sizeof(struct grub_uhci_td) * N_TD); + if (! u->td_chunk) + goto fail; + u->td_virt = grub_dma_get_virt (u->td_chunk); + u->td_phys = grub_dma_get_phys (u->td_chunk); + + grub_dprintf ("uhci", "QH=%p, TD=%p\n", + u->qh_virt, u->td_virt); + + /* Link all Transfer Descriptors in a list of available Transfer + Descriptors. */ + for (i = 0; i < N_TD; i++) + u->td_virt[i].linkptr = u->td_phys + (i + 1) * sizeof(struct grub_uhci_td); + u->td_virt[N_TD - 2].linkptr = 0; + u->tdfree = u->td_virt; + + /* Setup the frame list pointers. Since no isochronous transfers + are and will be supported, they all point to the (same!) queue + head. */ + fp = u->qh_phys & (~15); + /* Mark this as a queue head. */ + fp |= 2; + for (i = 0; i < 1024; i++) + u->framelist_virt[i] = fp; + /* Program the framelist address into the UHCI controller. */ + grub_uhci_writereg32 (u, GRUB_UHCI_REG_FLBASEADD, u->framelist_phys); + + /* Make the Queue Heads point to each other. */ + for (i = 0; i < N_QH; i++) + { + /* Point to the next QH. */ + u->qh_virt[i].linkptr = ((u->qh_phys + + (i + 1) * sizeof(struct grub_uhci_qh)) + & (~15)); + + /* This is a QH. */ + u->qh_virt[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_virt[i].elinkptr = 1; + } + + /* The last Queue Head should terminate. */ + u->qh_virt[N_QH - 1].linkptr = 1; + + /* Enable UHCI again. */ + grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, + GRUB_UHCI_CMD_RUN_STOP | GRUB_UHCI_CMD_MAXP); + + /* 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_dma_free (u->qh_chunk); + grub_dma_free (u->framelist_chunk); + } + grub_free (u); + + return 1; +} + +static void +grub_uhci_inithw (void) +{ + grub_pci_iterate (grub_uhci_pci_iter, NULL); +} + +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_dma_phys2virt (u->tdfree->linkptr, u->td_chunk); + + return ret; +} + +static void +grub_free_td (struct grub_uhci *u, grub_uhci_td_t td) +{ + td->linkptr = grub_dma_virt2phys (u->tdfree, u->td_chunk); + u->tdfree = td; +} + +static void +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_virt] = 0; + + *actual = 0; + + /* Free the TDs in this queue and set last_trans. */ + for (i=0; td; i++) + { + grub_uhci_td_t tdprev; + + grub_dprintf ("uhci", "Freeing %p\n", td); + /* 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; + if (!td->linkptr2) + td = 0; + else + td = grub_dma_phys2virt (td->linkptr2, u->td_chunk); + + /* 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; + + for (; i < N_QH; i++) + { + if (!u->qh_busy[i]) + break; + } + qh = &u->qh_virt[i]; + if (i == N_QH) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no free queue heads available"); + return NULL; + } + + u->qh_busy[qh - u->qh_virt] = 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_usb_speed_t speed) +{ + 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=%lu data=0x%x td=%p\n", + endp, type, addr, toggle, (unsigned long) 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) | + ((speed == GRUB_USB_SPEED_LOW) ? (1 << 26) : 0); + + /* 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_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; + grub_uhci_td_t td_prev = NULL; + int i; + struct grub_uhci_transfer_controller_data *cdata; + + cdata = grub_malloc (sizeof (*cdata)); + if (!cdata) + return GRUB_USB_ERR_INTERNAL; + + cdata->td_first = NULL; + + /* Allocate a queue head for the transfer queue. */ + 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]; + + td = grub_uhci_transaction (u, transfer->endpoint & 15, tr->pid, + transfer->devaddr, tr->toggle, + tr->size, tr->data, + transfer->dev->speed); + if (! td) + { + grub_size_t actual = 0; + /* Terminate and free. */ + if (td_prev) + { + td_prev->linkptr2 = 0; + td_prev->linkptr = 1; + } + + if (cdata->td_first) + grub_free_queue (u, cdata->qh, cdata->td_first, NULL, &actual); + + grub_free (cdata); + return GRUB_USB_ERR_INTERNAL; + } + + if (! cdata->td_first) + cdata->td_first = td; + else + { + td_prev->linkptr2 = grub_dma_virt2phys (td, u->td_chunk); + td_prev->linkptr = grub_dma_virt2phys (td, u->td_chunk); + 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. */ + cdata->qh->elinkptr = grub_dma_virt2phys (cdata->td_first, u->td_chunk); + + grub_dprintf ("uhci", "initiate transaction\n"); + + transfer->controller_data = cdata; + + return GRUB_USB_ERR_NONE; +} + +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; + + *actual = 0; + + if (cdata->qh->elinkptr & ~0x0f) + errtd = grub_dma_phys2virt (cdata->qh->elinkptr & ~0x0f, u->qh_chunk); + else + errtd = 0; + + if (errtd) + { + grub_dprintf ("uhci", ">t status=0x%02x data=0x%02x td=%p, %x\n", + errtd->ctrl_status, errtd->buffer & (~15), errtd, + cdata->qh->elinkptr); + } + + /* 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; + } + + if (errtd && !(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. */ + else if (errtd->ctrl_status & (1 << 21)) + err = GRUB_USB_ERR_DATA; + + /* Check if a babble error occurred. */ + else if (errtd->ctrl_status & (1 << 20)) + err = GRUB_USB_ERR_BABBLE; + + /* Check if a NAK occurred. */ + else if (errtd->ctrl_status & (1 << 19)) + err = GRUB_USB_ERR_NAK; + + /* Check if a timeout occurred. */ + else if (errtd->ctrl_status & (1 << 18)) + err = GRUB_USB_ERR_TIMEOUT; + + /* Check if a bitstuff error occurred. */ + else if (errtd->ctrl_status & (1 << 17)) + err = GRUB_USB_ERR_BITSTUFF; + + if (err) + { + grub_dprintf ("uhci", "transaction failed\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 err; + } + } + + /* Fall through, no errors occurred, so the QH might be + updated. */ + grub_dprintf ("uhci", "transaction fallthrough\n"); + + return GRUB_USB_ERR_WAIT; +} + +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. */ + cdata->qh->elinkptr = 1; + grub_free_queue (u, cdata->qh, cdata->td_first, transfer, &actual); + grub_free (cdata); + + return GRUB_USB_ERR_NONE; +} + +static int +grub_uhci_iterate (grub_usb_controller_iterate_hook_t hook, void *hook_data) +{ + struct grub_uhci *u; + struct grub_usb_controller dev; + + for (u = uhci; u; u = u->next) + { + dev.data = u; + if (hook (&dev, hook_data)) + return 1; + } + + return 0; +} + +static grub_usb_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_USB_ERR_INTERNAL; + + 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_USB_ERR_TIMEOUT; + + status = grub_uhci_readreg16 (u, reg); + grub_dprintf ("uhci", ">3detect=0x%02x\n", status); + return GRUB_USB_ERR_NONE; + } + + /* Reset the port. */ + status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC; + grub_uhci_writereg16 (u, reg, status | (1 << 9)); + grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */ + + /* 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_PORTSC_RWC; + grub_uhci_writereg16 (u, reg, status & ~(1 << 9)); + grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */ + + /* Note: some debug prints were removed because they affected reset/enable timing. */ + + grub_millisleep (1); /* Probably not needed at all or only few microsecs. */ + + /* Reset bits Connect & Enable Status Change */ + status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC; + grub_uhci_writereg16 (u, reg, status | (1 << 3) | GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED); + grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */ + + /* Enable the port. */ + status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC; + grub_uhci_writereg16 (u, reg, status | (1 << 2)); + grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */ + + endtime = grub_get_time_ms () + 1000; + while (! ((status = grub_uhci_readreg16 (u, reg)) & (1 << 2))) + if (grub_get_time_ms () > endtime) + return GRUB_USB_ERR_TIMEOUT; + + /* Reset recovery time */ + grub_millisleep (10); + + /* Read final port status */ + status = grub_uhci_readreg16 (u, reg); + grub_dprintf ("uhci", ">3detect=0x%02x\n", status); + + + return GRUB_USB_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_USB_SPEED_NONE; + + 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 */ + if (status & GRUB_UHCI_DETECT_CHANGED) + { + *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 & GRUB_UHCI_DETECT_HAVE_DEVICE)) + return GRUB_USB_SPEED_NONE; + else if (status & GRUB_UHCI_DETECT_LOW_SPEED) + 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; +} + + +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, + /* estimated max. count of TDs for one bulk transfer */ + .max_bulk_tds = N_TD * 3 / 4 +}; + +GRUB_MOD_INIT(uhci) +{ + grub_stop_disk_firmware (); + + 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); +} diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c new file mode 100644 index 000000000..7bd49d201 --- /dev/null +++ b/grub-core/bus/usb/usb.c @@ -0,0 +1,346 @@ +/* usb.c - Generic USB interfaces. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static struct grub_usb_attach_desc *attach_hooks; + +#if 0 +/* Context for grub_usb_controller_iterate. */ +struct grub_usb_controller_iterate_ctx +{ + grub_usb_controller_iterate_hook_t hook; + void *hook_data; + grub_usb_controller_dev_t p; +}; + +/* Helper for grub_usb_controller_iterate. */ +static int +grub_usb_controller_iterate_iter (grub_usb_controller_t dev, void *data) +{ + struct grub_usb_controller_iterate_ctx *ctx = data; + + dev->dev = ctx->p; + if (ctx->hook (dev, ctx->hook_data)) + return 1; + return 0; +} + +int +grub_usb_controller_iterate (grub_usb_controller_iterate_hook_t hook, + void *hook_data) +{ + struct grub_usb_controller_iterate_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + + /* Iterate over all controller drivers. */ + for (ctx.p = grub_usb_list; ctx.p; ctx.p = ctx.p->next) + { + /* Iterate over the busses of the controllers. XXX: Actually, a + hub driver should do this. */ + if (ctx.p->iterate (grub_usb_controller_iterate_iter, &ctx)) + return 1; + } + + return 0; +} +#endif + + +grub_usb_err_t +grub_usb_clear_halt (grub_usb_device_t dev, int endpoint) +{ + if (endpoint >= GRUB_USB_MAX_TOGGLE) + return GRUB_USB_ERR_BADDEVICE; + + dev->toggle[endpoint] = 0; + return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_STANDARD + | GRUB_USB_REQTYPE_TARGET_ENDP), + GRUB_USB_REQ_CLEAR_FEATURE, + GRUB_USB_FEATURE_ENDP_HALT, + endpoint, 0, 0); +} + +grub_usb_err_t +grub_usb_set_configuration (grub_usb_device_t dev, int configuration) +{ + grub_memset (dev->toggle, 0, sizeof (dev->toggle)); + + return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT + | GRUB_USB_REQTYPE_STANDARD + | GRUB_USB_REQTYPE_TARGET_DEV), + GRUB_USB_REQ_SET_CONFIGURATION, configuration, + 0, 0, NULL); +} + +grub_usb_err_t +grub_usb_get_descriptor (grub_usb_device_t dev, + grub_uint8_t type, grub_uint8_t index, + grub_size_t size, char *data) +{ + return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN + | GRUB_USB_REQTYPE_STANDARD + | GRUB_USB_REQTYPE_TARGET_DEV), + GRUB_USB_REQ_GET_DESCRIPTOR, + (type << 8) | index, + 0, size, data); +} + +grub_usb_err_t +grub_usb_device_initialize (grub_usb_device_t dev) +{ + struct grub_usb_desc_device *descdev; + struct grub_usb_desc_config config; + grub_usb_err_t err; + int i; + + /* First we have to read first 8 bytes only and determine + * max. size of packet */ + dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */ + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, + 0, 8, (char *) &dev->descdev); + if (err) + return err; + + /* Now we have valid value in dev->descdev.maxsize0, + * so we can read whole device descriptor */ + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, + 0, sizeof (struct grub_usb_desc_device), + (char *) &dev->descdev); + if (err) + return err; + descdev = &dev->descdev; + + for (i = 0; i < GRUB_USB_MAX_CONF; i++) + dev->config[i].descconf = NULL; + + if (descdev->configcnt == 0 || descdev->configcnt > GRUB_USB_MAX_CONF) + { + err = GRUB_USB_ERR_BADDEVICE; + goto fail; + } + + for (i = 0; i < descdev->configcnt; i++) + { + int pos; + int currif; + char *data; + struct grub_usb_desc *desc; + + /* First just read the first 4 bytes of the configuration + descriptor, after that it is known how many bytes really have + to be read. */ + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, 4, + (char *) &config); + + data = grub_malloc (config.totallen); + if (! data) + { + err = GRUB_USB_ERR_INTERNAL; + goto fail; + } + + dev->config[i].descconf = (struct grub_usb_desc_config *) data; + err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, + config.totallen, data); + if (err) + goto fail; + + /* Skip the configuration descriptor. */ + pos = dev->config[i].descconf->length; + + if (dev->config[i].descconf->numif > GRUB_USB_MAX_IF) + { + err = GRUB_USB_ERR_BADDEVICE; + goto fail; + } + + /* Read all interfaces. */ + for (currif = 0; currif < dev->config[i].descconf->numif; currif++) + { + while (pos < config.totallen) + { + desc = (struct grub_usb_desc *)&data[pos]; + if (desc->type == GRUB_USB_DESCRIPTOR_INTERFACE) + break; + if (!desc->length) + { + err = GRUB_USB_ERR_BADDEVICE; + goto fail; + } + pos += desc->length; + } + + dev->config[i].interf[currif].descif + = (struct grub_usb_desc_if *) &data[pos]; + pos += dev->config[i].interf[currif].descif->length; + + while (pos < config.totallen) + { + desc = (struct grub_usb_desc *)&data[pos]; + if (desc->type == GRUB_USB_DESCRIPTOR_ENDPOINT) + break; + if (!desc->length) + { + err = GRUB_USB_ERR_BADDEVICE; + goto fail; + } + pos += desc->length; + } + + /* Point to the first endpoint. */ + dev->config[i].interf[currif].descendp + = (struct grub_usb_desc_endp *) &data[pos]; + pos += (sizeof (struct grub_usb_desc_endp) + * dev->config[i].interf[currif].descif->endpointcnt); + } + } + + return GRUB_USB_ERR_NONE; + + fail: + + for (i = 0; i < GRUB_USB_MAX_CONF; i++) + grub_free (dev->config[i].descconf); + + return err; +} + +void grub_usb_device_attach (grub_usb_device_t dev) +{ + int i; + + /* XXX: Just check configuration 0 for now. */ + for (i = 0; i < dev->config[0].descconf->numif; i++) + { + struct grub_usb_desc_if *interf; + struct grub_usb_attach_desc *desc; + + interf = dev->config[0].interf[i].descif; + + grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", + i, interf->class, interf->subclass, interf->protocol); + + if (dev->config[0].interf[i].attached) + continue; + + for (desc = attach_hooks; desc; desc = desc->next) + if (interf->class == desc->class) + { + grub_boot_time ("Probing USB device driver class %x", desc->class); + if (desc->hook (dev, 0, i)) + dev->config[0].interf[i].attached = 1; + grub_boot_time ("Probed USB device driver class %x", desc->class); + } + + if (dev->config[0].interf[i].attached) + continue; + + switch (interf->class) + { + case GRUB_USB_CLASS_MASS_STORAGE: + grub_dl_load ("usbms"); + grub_print_error (); + break; + case GRUB_USB_CLASS_HID: + grub_dl_load ("usb_keyboard"); + grub_print_error (); + break; + case 0xff: + /* FIXME: don't load useless modules. */ + grub_dl_load ("usbserial_ftdi"); + grub_print_error (); + grub_dl_load ("usbserial_pl2303"); + grub_print_error (); + grub_dl_load ("usbserial_usbdebug"); + grub_print_error (); + break; + } + } +} + +/* Helper for grub_usb_register_attach_hook_class. */ +static int +grub_usb_register_attach_hook_class_iter (grub_usb_device_t usbdev, void *data) +{ + struct grub_usb_attach_desc *desc = data; + struct grub_usb_desc_device *descdev = &usbdev->descdev; + int i; + + if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 + || descdev->configcnt == 0) + return 0; + + /* XXX: Just check configuration 0 for now. */ + for (i = 0; i < usbdev->config[0].descconf->numif; i++) + { + struct grub_usb_desc_if *interf; + + interf = usbdev->config[0].interf[i].descif; + + grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", + i, interf->class, interf->subclass, interf->protocol); + + if (usbdev->config[0].interf[i].attached) + continue; + + if (interf->class != desc->class) + continue; + if (desc->hook (usbdev, 0, i)) + usbdev->config[0].interf[i].attached = 1; + } + + return 0; +} + +void +grub_usb_register_attach_hook_class (struct grub_usb_attach_desc *desc) +{ + desc->next = attach_hooks; + attach_hooks = desc; + + grub_usb_iterate (grub_usb_register_attach_hook_class_iter, desc); +} + +void +grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc) +{ + grub_list_remove (GRUB_AS_LIST (desc)); +} + + +GRUB_MOD_INIT(usb) +{ + grub_term_poll_usb = grub_usb_poll_devices; +} + +GRUB_MOD_FINI(usb) +{ + grub_term_poll_usb = NULL; +} diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c new file mode 100644 index 000000000..f5608e330 --- /dev/null +++ b/grub-core/bus/usb/usbhub.c @@ -0,0 +1,756 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include + +#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; +static int npending = 0; + +struct grub_usb_hub +{ + struct grub_usb_hub *next; + grub_usb_controller_t controller; + int nports; + struct grub_usb_device **devices; + struct grub_usb_hub_port *ports; + grub_usb_device_t dev; +}; + +static struct grub_usb_hub *hubs; +static grub_usb_controller_dev_t grub_usb_list; + +/* 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, + int split_hubport, int split_hubaddr) +{ + grub_usb_device_t dev; + int i; + grub_usb_err_t err; + + grub_boot_time ("Attaching USB device"); + + dev = grub_zalloc (sizeof (struct grub_usb_device)); + if (! dev) + return NULL; + + dev->controller = *controller; + dev->speed = speed; + dev->split_hubport = split_hubport; + dev->split_hubaddr = split_hubaddr; + + 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 < GRUB_USB_MAX_CONF; 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 < GRUB_USB_MAX_CONF; i++) + grub_free (dev->config[i].descconf); + grub_free (dev); + return NULL; + } + + dev->addr = i; + dev->initialized = 1; + grub_usb_devs[i] = dev; + + grub_dprintf ("usb", "Added new usb device: %p, addr=%d\n", + dev, i); + grub_dprintf ("usb", "speed=%d, split_hubport=%d, split_hubaddr=%d\n", + speed, split_hubport, split_hubaddr); + + /* Wait "recovery interval", spec. says 2ms */ + grub_millisleep (2); + + grub_boot_time ("Probing USB device driver"); + + grub_usb_device_attach (dev); + + grub_boot_time ("Attached USB device"); + + return dev; +} + + +static grub_usb_err_t +grub_usb_add_hub (grub_usb_device_t dev) +{ + struct grub_usb_usb_hubdesc hubdesc; + grub_usb_err_t err; + int i; + + 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->nports = hubdesc.portcnt; + dev->children = grub_calloc (hubdesc.portcnt, sizeof (dev->children[0])); + dev->ports = grub_calloc (dev->nports, sizeof (dev->ports[0])); + if (!dev->children || !dev->ports) + { + grub_free (dev->children); + grub_free (dev->ports); + return GRUB_USB_ERR_INTERNAL; + } + + /* 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 */ + 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); + } + + /* 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]; + + if ((endp->endp_addr & 128) && grub_usb_get_ep_type(endp) + == GRUB_USB_EP_INTERRUPT) + { + grub_size_t len; + dev->hub_endpoint = endp; + len = endp->maxpacket; + if (len > sizeof (dev->statuschange)) + len = sizeof (dev->statuschange); + dev->hub_transfer + = grub_usb_bulk_read_background (dev, endp, len, + (char *) &dev->statuschange); + break; + } + } + + rescan = 1; + + return GRUB_USB_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_usb_err_t err; + + grub_boot_time ("After detect_dev"); + + /* Enable the port. */ + err = hub->controller->dev->portstatus (hub->controller, portno, 1); + if (err) + return; + hub->controller->dev->pending_reset = grub_get_time_ms () + 5000; + npending++; + + grub_millisleep (10); + + grub_boot_time ("Port enabled"); + + /* Enable the port and create a device. */ + /* High speed device needs not transaction translation + and full/low speed device cannot be connected to EHCI root hub + and full/low speed device connected to OHCI/UHCI needs not + transaction translation - e.g. hubport and hubaddr should be + always none (zero) for any device connected to any root hub. */ + dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0); + hub->controller->dev->pending_reset = 0; + npending--; + 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_boot_time ("Attached root port"); +} + +/* Iterate over all controllers found by the driver. */ +static int +grub_usb_controller_dev_register_iter (grub_usb_controller_t controller, void *data) +{ + grub_usb_controller_dev_t usb = data; + struct grub_usb_hub *hub; + + controller->dev = usb; + + grub_boot_time ("Registering USB root hub"); + + 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_calloc (hub->nports, sizeof (hub->devices[0])); + hub->ports = grub_calloc (hub->nports, sizeof (hub->ports[0])); + if (!hub->devices || !hub->ports) + { + grub_free (hub->devices); + grub_free (hub->ports); + grub_free (hub->controller); + grub_free (hub); + grub_print_error (); + return 0; + } + + return 0; +} + +void +grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb) +{ + grub_usb_controller_dev_t *p, q; + + for (p = &grub_usb_list, q = *p; q; p = &(q->next), q = q->next) + if (q == usb) + { + *p = q->next; + break; + } +} + +void +grub_usb_controller_dev_register (grub_usb_controller_dev_t usb) +{ + int portno; + int continue_waiting = 0; + struct grub_usb_hub *hub; + + usb->next = grub_usb_list; + grub_usb_list = usb; + + if (usb->iterate) + usb->iterate (grub_usb_controller_dev_register_iter, usb); + + grub_boot_time ("waiting for stable power on USB root\n"); + + while (1) + { + for (hub = hubs; hub; hub = hub->next) + if (hub->controller->dev == usb) + { + /* Wait for completion of insertion and stable power (USB spec.) + * Should be at least 100ms, some devices requires more... + * There is also another thing - some devices have worse contacts + * and connected signal is unstable for some time - we should handle + * it - but prevent deadlock in case when device is too faulty... */ + for (portno = 0; portno < hub->nports; portno++) + { + grub_usb_speed_t speed; + int changed = 0; + + speed = hub->controller->dev->detect_dev (hub->controller, portno, + &changed); + + if (hub->ports[portno].state == PORT_STATE_NORMAL + && speed != GRUB_USB_SPEED_NONE) + { + hub->ports[portno].soft_limit_time = grub_get_time_ms () + 250; + hub->ports[portno].hard_limit_time = hub->ports[portno].soft_limit_time + 1750; + hub->ports[portno].state = PORT_STATE_WAITING_FOR_STABLE_POWER; + grub_boot_time ("Scheduling stable power wait for port %p:%d", + usb, portno); + continue_waiting++; + continue; + } + + if (hub->ports[portno].state == PORT_STATE_WAITING_FOR_STABLE_POWER + && speed == GRUB_USB_SPEED_NONE) + { + hub->ports[portno].soft_limit_time = grub_get_time_ms () + 250; + continue; + } + if (hub->ports[portno].state == PORT_STATE_WAITING_FOR_STABLE_POWER + && grub_get_time_ms () > hub->ports[portno].soft_limit_time) + { + hub->ports[portno].state = PORT_STATE_STABLE_POWER; + grub_boot_time ("Got stable power wait for port %p:%d", + usb, portno); + continue_waiting--; + continue; + } + if (hub->ports[portno].state == PORT_STATE_WAITING_FOR_STABLE_POWER + && grub_get_time_ms () > hub->ports[portno].hard_limit_time) + { + hub->ports[portno].state = PORT_STATE_FAILED_DEVICE; + continue_waiting--; + continue; + } + } + } + if (!continue_waiting) + break; + grub_millisleep (1); + } + + grub_boot_time ("After the stable power wait on USB root"); + + for (hub = hubs; hub; hub = hub->next) + if (hub->controller->dev == usb) + for (portno = 0; portno < hub->nports; portno++) + if (hub->ports[portno].state == PORT_STATE_STABLE_POWER) + { + grub_usb_speed_t speed; + int changed = 0; + hub->ports[portno].state = PORT_STATE_NORMAL; + speed = hub->controller->dev->detect_dev (hub->controller, portno, &changed); + attach_root_port (hub, portno, speed); + } + + grub_boot_time ("USB root hub registered"); +} + +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 int +wait_power_nonroot_hub (grub_usb_device_t dev) +{ + grub_usb_err_t err; + int continue_waiting = 0; + unsigned i; + + for (i = 1; i <= dev->nports; i++) + if (dev->ports[i - 1].state == PORT_STATE_WAITING_FOR_STABLE_POWER) + { + grub_uint64_t tm; + grub_uint32_t current_status = 0; + + /* 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 (current_status), + (char *) ¤t_status); + if (err) + { + dev->ports[i - 1].state = PORT_STATE_FAILED_DEVICE; + continue; + } + tm = grub_get_time_ms (); + if (!(current_status & GRUB_USB_HUB_STATUS_PORT_CONNECTED)) + dev->ports[i - 1].soft_limit_time = tm + 250; + if (tm >= dev->ports[i - 1].soft_limit_time) + { + if (dev->controller.dev->pending_reset) + continue; + /* 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); + dev->ports[i - 1].state = PORT_STATE_NORMAL; + grub_boot_time ("Resetting port %p:%d", dev, i - 1); + + rescan = 1; + /* We cannot reset more than one device at the same time ! + * Resetting more devices together results in very bad + * situation: more than one device has default address 0 + * at the same time !!! + * Additionaly, we cannot perform another reset + * anywhere on the same OHCI controller until + * we will finish addressing of reseted device ! */ + dev->controller.dev->pending_reset = grub_get_time_ms () + 5000; + npending++; + continue; + } + if (tm >= dev->ports[i - 1].hard_limit_time) + { + dev->ports[i - 1].state = PORT_STATE_FAILED_DEVICE; + continue; + } + continue_waiting = 1; + } + return continue_waiting && dev->controller.dev->pending_reset == 0; +} + +static void +poll_nonroot_hub (grub_usb_device_t dev) +{ + grub_usb_err_t err; + unsigned i; + grub_uint32_t changed; + grub_size_t actual, len; + + if (!dev->hub_transfer) + return; + + err = grub_usb_check_transfer (dev->hub_transfer, &actual); + + if (err == GRUB_USB_ERR_WAIT) + return; + + changed = dev->statuschange; + + len = dev->hub_endpoint->maxpacket; + if (len > sizeof (dev->statuschange)) + len = sizeof (dev->statuschange); + dev->hub_transfer + = grub_usb_bulk_read_background (dev, dev->hub_endpoint, len, + (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)) + || dev->ports[i - 1].state == PORT_STATE_WAITING_FOR_STABLE_POWER) + 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); + + grub_dprintf ("usb", "dev = %p, i = %d, status = %08x\n", + dev, i, status); + + if (err) + continue; + + /* 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 (!dev->controller.dev->pending_reset && + (status & GRUB_USB_HUB_STATUS_C_PORT_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; + + /* Connected and status of connection changed ? */ + if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED) + { + grub_boot_time ("Before the stable power wait portno=%d", i); + /* A device is actually connected to this port. */ + /* Wait for completion of insertion and stable power (USB spec.) + * Should be at least 100ms, some devices requires more... + * There is also another thing - some devices have worse contacts + * and connected signal is unstable for some time - we should handle + * it - but prevent deadlock in case when device is too faulty... */ + dev->ports[i - 1].soft_limit_time = grub_get_time_ms () + 250; + dev->ports[i - 1].hard_limit_time = dev->ports[i - 1].soft_limit_time + 1750; + dev->ports[i - 1].state = PORT_STATE_WAITING_FOR_STABLE_POWER; + grub_boot_time ("Scheduling stable power wait for port %p:%d", + dev, i - 1); + continue; + } + } + + if (status & GRUB_USB_HUB_STATUS_C_PORT_RESET) + { + 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); + + grub_boot_time ("Port %d reset", i); + + if (status & GRUB_USB_HUB_STATUS_PORT_CONNECTED) + { + grub_usb_speed_t speed; + grub_usb_device_t next_dev; + int split_hubport = 0; + int split_hubaddr = 0; + + /* Determine the device speed. */ + if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED) + speed = GRUB_USB_SPEED_LOW; + else + { + 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); + + /* Find correct values for SPLIT hubport and hubaddr */ + if (speed == GRUB_USB_SPEED_HIGH) + { + /* HIGH speed device needs not transaction translation */ + split_hubport = 0; + split_hubaddr = 0; + } + else + /* FULL/LOW device needs hub port and hub address + for transaction translation (if connected to EHCI) */ + if (dev->speed == GRUB_USB_SPEED_HIGH) + { + /* This port is the first FULL/LOW speed port + in the chain from root hub. Attached device + should use its port number and hub address */ + split_hubport = i; + split_hubaddr = dev->addr; + } + else + { + /* This port is NOT the first FULL/LOW speed port + in the chain from root hub. Attached device + should use values inherited from some parent + HIGH speed hub - if any. */ + split_hubport = dev->split_hubport; + split_hubaddr = dev->split_hubaddr; + } + + /* Add the device and assign a device address to it. */ + next_dev = grub_usb_hub_add_dev (&dev->controller, speed, + split_hubport, split_hubaddr); + if (dev->controller.dev->pending_reset) + { + dev->controller.dev->pending_reset = 0; + npending--; + } + 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); + } + } + } +} + +void +grub_usb_poll_devices (int wait_for_completion) +{ + 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 = GRUB_USB_SPEED_NONE; + int changed = 0; + + if (hub->controller->dev->pending_reset) + { + /* Check for possible timeout */ + if (grub_get_time_ms () > hub->controller->dev->pending_reset) + { + /* Something went wrong, reset device was not + * addressed properly, timeout happened */ + hub->controller->dev->pending_reset = 0; + npending--; + } + } + if (!hub->controller->dev->pending_reset) + 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); + } + } + } + + 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); + } + + while (1) + { + int continue_waiting = 0; + for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++) + { + grub_usb_device_t dev = grub_usb_devs[i]; + + if (dev && dev->descdev.class == 0x09) + continue_waiting = continue_waiting || wait_power_nonroot_hub (dev); + } + if (!continue_waiting) + break; + grub_millisleep (1); + } + + if (!(rescan || (npending && wait_for_completion))) + break; + grub_millisleep (25); + } +} + +int +grub_usb_iterate (grub_usb_iterate_hook_t hook, void *hook_data) +{ + int i; + + for (i = 0; i < GRUB_USBHUB_MAX_DEVICES; i++) + { + if (grub_usb_devs[i]) + { + if (hook (grub_usb_devs[i], hook_data)) + return 1; + } + } + + return 0; +} diff --git a/grub-core/bus/usb/usbtrans.c b/grub-core/bus/usb/usbtrans.c new file mode 100644 index 000000000..c5680b33a --- /dev/null +++ b/grub-core/bus/usb/usbtrans.c @@ -0,0 +1,462 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static inline unsigned int +grub_usb_bulk_maxpacket (grub_usb_device_t dev, + struct grub_usb_desc_endp *endpoint) +{ + /* Use the maximum packet size given in the endpoint descriptor. */ + if (dev->initialized && endpoint && (unsigned int) endpoint->maxpacket) + return endpoint->maxpacket; + + return 64; +} + + +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; + + err = dev->controller.dev->setup_transfer (&dev->controller, transfer); + if (err) + return err; + /* endtime moved behind setup transfer to prevent false timeouts + * while debugging... */ + endtime = grub_get_time_ms () + timeout; + 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_arch_sync_dma_caches (data, size); + + grub_dprintf ("usb", + "control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x size=%lu\n", + reqtype, request, value, index, (unsigned long)size); + + /* Create a transfer. */ + transfer = grub_malloc (sizeof (*transfer)); + if (! transfer) + { + grub_dma_free (data_chunk); + return GRUB_USB_ERR_INTERNAL; + } + + setupdata_chunk = grub_memalign_dma32 (32, sizeof (*setupdata)); + if (! setupdata_chunk) + { + grub_free (transfer); + grub_dma_free (data_chunk); + return GRUB_USB_ERR_INTERNAL; + } + + 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_USB_ERR_INTERNAL; + } + + /* Build a Setup packet. XXX: Endianness. */ + setupdata->reqtype = reqtype; + setupdata->request = request; + setupdata->value = value; + setupdata->index = index; + setupdata->length = size; + grub_arch_sync_dma_caches (setupdata, sizeof (*setupdata)); + + 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; + + 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 (setupdata_chunk); + + grub_arch_sync_dma_caches (data, size0); + grub_memcpy (data_in, (char *) data, size0); + + grub_dma_free (data_chunk); + + return err; +} + +static grub_usb_transfer_t +grub_usb_bulk_setup_readwrite (grub_usb_device_t dev, + struct grub_usb_desc_endp *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; + 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->endp_addr]; + + grub_dprintf ("usb", "bulk: size=0x%02lx type=%d\n", (unsigned long) size, + type); + + /* FIXME: avoid allocation any kind of buffer in a first place. */ + data_chunk = grub_memalign_dma32 (128, size); + if (!data_chunk) + 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); + grub_arch_sync_dma_caches (data, size); + } + + /* Create a transfer. */ + transfer = grub_malloc (sizeof (struct grub_usb_transfer)); + if (! transfer) + { + grub_dma_free (data_chunk); + return NULL; + } + + max = grub_usb_bulk_maxpacket (dev, endpoint); + + datablocks = ((size + max - 1) / max); + transfer->transcnt = datablocks; + transfer->size = size - 1; + transfer->endpoint = endpoint->endp_addr; + 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); + 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]; + + /* 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 + 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_arch_sync_dma_caches (grub_dma_get_virt (transfer->data_chunk), + transfer->size + 1); + grub_memcpy (transfer->data, (void *) + grub_dma_get_virt (transfer->data_chunk), + transfer->size + 1); + } + + grub_free (transfer->transactions); + grub_dma_free (transfer->data_chunk); + grub_free (transfer); +} + +static grub_usb_err_t +grub_usb_bulk_readwrite (grub_usb_device_t dev, + struct grub_usb_desc_endp *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; +} + +static grub_usb_err_t +grub_usb_bulk_readwrite_packetize (grub_usb_device_t dev, + struct grub_usb_desc_endp *endpoint, + grub_transfer_type_t type, + grub_size_t size, char *data) +{ + grub_size_t actual, transferred; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + grub_size_t current_size, position; + grub_size_t max_bulk_transfer_len = MAX_USB_TRANSFER_LEN; + grub_size_t max; + + if (dev->controller.dev->max_bulk_tds) + { + max = grub_usb_bulk_maxpacket (dev, endpoint); + + /* Calculate max. possible length of bulk transfer */ + max_bulk_transfer_len = dev->controller.dev->max_bulk_tds * max; + } + + for (position = 0, transferred = 0; + position < size; position += max_bulk_transfer_len) + { + current_size = size - position; + if (current_size >= max_bulk_transfer_len) + current_size = max_bulk_transfer_len; + err = grub_usb_bulk_readwrite (dev, endpoint, current_size, + &data[position], type, 1000, &actual); + transferred += actual; + if (err || (current_size != actual)) break; + } + + if (!err && transferred != size) + err = GRUB_USB_ERR_DATA; + return err; +} + +grub_usb_err_t +grub_usb_bulk_write (grub_usb_device_t dev, + struct grub_usb_desc_endp *endpoint, + grub_size_t size, char *data) +{ + return grub_usb_bulk_readwrite_packetize (dev, endpoint, + GRUB_USB_TRANSFER_TYPE_OUT, + size, data); +} + +grub_usb_err_t +grub_usb_bulk_read (grub_usb_device_t dev, + struct grub_usb_desc_endp *endpoint, + grub_size_t size, char *data) +{ + return grub_usb_bulk_readwrite_packetize (dev, endpoint, + GRUB_USB_TRANSFER_TYPE_IN, + size, data); +} + +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, + struct grub_usb_desc_endp *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, + struct grub_usb_desc_endp *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); +} diff --git a/commands/acpi.c b/grub-core/commands/acpi.c similarity index 67% rename from commands/acpi.c rename to grub-core/commands/acpi.c index 5bbfd008b..f72a19c03 100644 --- a/commands/acpi.c +++ b/grub-core/commands/acpi.c @@ -23,26 +23,43 @@ #include #include #include -#include #include #include -#include #include #include +#include #ifdef GRUB_MACHINE_EFI #include #include #endif +#pragma GCC diagnostic ignored "-Wcast-align" + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + OPTION_EXCLUDE = 0, + OPTION_LOAD_ONLY, + OPTION_V1, + OPTION_V2, + OPTION_OEMID, + OPTION_OEMTABLE, + OPTION_OEMTABLEREV, + OPTION_OEMTABLECREATOR, + OPTION_OEMTABLECREATORREV, + OPTION_NO_EBDA + }; + static const struct grub_arg_option options[] = { {"exclude", 'x', 0, N_("Don't load host tables specified by comma-separated list."), 0, ARG_TYPE_STRING}, {"load-only", 'n', 0, N_("Load only tables specified by comma-separated list."), 0, ARG_TYPE_STRING}, - {"v1", '1', 0, N_("Expose v1 tables."), 0, ARG_TYPE_NONE}, - {"v2", '2', 0, N_("Expose v2 and v3 tables."), 0, ARG_TYPE_NONE}, + {"v1", '1', 0, N_("Export version 1 tables to the OS."), 0, ARG_TYPE_NONE}, + {"v2", '2', 0, N_("Export version 2 and version 3 tables to the OS."), 0, ARG_TYPE_NONE}, {"oemid", 'o', 0, N_("Set OEMID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING}, {"oemtable", 't', 0, N_("Set OEMTABLE ID of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING}, @@ -52,24 +69,13 @@ static const struct grub_arg_option options[] = { N_("Set creator field of RSDP, XSDT and RSDT."), 0, ARG_TYPE_STRING}, {"oemtablecreatorrev", 'd', 0, N_("Set creator revision of RSDP, XSDT and RSDT."), 0, ARG_TYPE_INT}, - {"no-ebda", 'e', 0, N_("Don't update EBDA. May fix failures or hangs on some." - " BIOSes but makes it ineffective with OS not receiving RSDP from GRUB."), + /* TRANSLATORS: "hangs" here is a noun, not a verb. */ + {"no-ebda", 'e', 0, N_("Don't update EBDA. May fix failures or hangs on some " + "BIOSes but makes it ineffective with OS not receiving RSDP from GRUB."), 0, ARG_TYPE_NONE}, {0, 0, 0, 0, 0, 0} }; -/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */ -grub_uint8_t -grub_byte_checksum (void *base, grub_size_t size) -{ - grub_uint8_t *ptr; - grub_uint8_t ret = 0; - for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size; - ptr++) - ret += *ptr; - return ret; -} - /* rev1 is 1 if ACPIv1 is to be generated, 0 otherwise. rev2 contains the revision of ACPIv2+ to generate or 0 if none. */ static int rev1, rev2; @@ -128,6 +134,8 @@ grub_acpi_get_rsdpv1 (void) return grub_machine_acpi_get_rsdpv1 (); } +#if defined (__i386__) || defined (__x86_64__) + static inline int iszero (grub_uint8_t *reg, int size) { @@ -138,50 +146,62 @@ iszero (grub_uint8_t *reg, int size) return 1; } +/* Context for grub_acpi_create_ebda. */ +struct grub_acpi_create_ebda_ctx { + int ebda_len; + grub_uint64_t highestlow; +}; + +/* Helper for grub_acpi_create_ebda. */ +static int +find_hook (grub_uint64_t start, grub_uint64_t size, grub_memory_type_t type, + void *data) +{ + struct grub_acpi_create_ebda_ctx *ctx = data; + grub_uint64_t end = start + size; + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + if (end > 0x100000) + end = 0x100000; + if (end > start + ctx->ebda_len + && ctx->highestlow < ((end - ctx->ebda_len) & (~0xf)) ) + ctx->highestlow = (end - ctx->ebda_len) & (~0xf); + return 0; +} + grub_err_t grub_acpi_create_ebda (void) { - int ebda_kb_len; - int ebda_len; + struct grub_acpi_create_ebda_ctx ctx = { + .highestlow = 0 + }; + int ebda_kb_len = 0; int mmapregion = 0; grub_uint8_t *ebda, *v1inebda = 0, *v2inebda = 0; - grub_uint64_t highestlow = 0; grub_uint8_t *targetebda, *target; struct grub_acpi_rsdp_v10 *v1; struct grub_acpi_rsdp_v20 *v2; - auto int NESTED_FUNC_ATTR find_hook (grub_uint64_t, grub_uint64_t, - grub_uint32_t); - int NESTED_FUNC_ATTR find_hook (grub_uint64_t start, grub_uint64_t size, - grub_uint32_t type) - { - grub_uint64_t end = start + size; - if (type != GRUB_MACHINE_MEMORY_AVAILABLE) - return 0; - if (end > 0x100000) - end = 0x100000; - if (end > start + ebda_len - && highestlow < ((end - ebda_len) & (~0xf)) ) - highestlow = (end - ebda_len) & (~0xf); - return 0; - } - ebda = (grub_uint8_t *) UINT_TO_PTR ((*((grub_uint16_t *)0x40e)) << 4); - ebda_kb_len = *(grub_uint16_t *) ebda; - if (! ebda || ebda_kb_len > 16) + ebda = (grub_uint8_t *) (grub_addr_t) ((*((grub_uint16_t *) grub_absolute_pointer (0x40e))) << 4); + grub_dprintf ("acpi", "EBDA @%p\n", ebda); + if (ebda) + ebda_kb_len = *(grub_uint16_t *) ebda; + grub_dprintf ("acpi", "EBDA length 0x%x\n", ebda_kb_len); + if (ebda_kb_len > 16) ebda_kb_len = 0; - ebda_len = (ebda_kb_len + 1) << 10; + ctx.ebda_len = (ebda_kb_len + 1) << 10; /* FIXME: use low-memory mm allocation once it's available. */ - grub_mmap_iterate (find_hook); - targetebda = (grub_uint8_t *) UINT_TO_PTR (highestlow); + grub_mmap_iterate (find_hook, &ctx); + targetebda = (grub_uint8_t *) (grub_addr_t) ctx.highestlow; grub_dprintf ("acpi", "creating ebda @%llx\n", - (unsigned long long) highestlow); - if (! highestlow) + (unsigned long long) ctx.highestlow); + if (! ctx.highestlow) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for the new EBDA"); - mmapregion = grub_mmap_register (PTR_TO_UINT64 (targetebda), ebda_len, - GRUB_MACHINE_MEMORY_RESERVED); + mmapregion = grub_mmap_register ((grub_addr_t) targetebda, ctx.ebda_len, + GRUB_MEMORY_RESERVED); if (! mmapregion) return grub_errno; @@ -203,7 +223,7 @@ grub_acpi_create_ebda (void) { grub_dprintf ("acpi", "Scanning EBDA for old rsdpv2\n"); for (; target < targetebda + 0x400 - v2->length; target += 0x10) - if (grub_memcmp (target, "RSD PTR ", 8) == 0 + if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) target)->revision != 0 @@ -213,7 +233,7 @@ grub_acpi_create_ebda (void) grub_dprintf ("acpi", "Copying rsdpv2 to %p\n", target); v2inebda = target; target += v2->length; - target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); + target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16); v2 = 0; break; } @@ -224,7 +244,7 @@ grub_acpi_create_ebda (void) grub_dprintf ("acpi", "Scanning EBDA for old rsdpv1\n"); for (; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10); target += 0x10) - if (grub_memcmp (target, "RSD PTR ", 8) == 0 + if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0) { @@ -232,7 +252,7 @@ grub_acpi_create_ebda (void) grub_dprintf ("acpi", "Copying rsdpv1 to %p\n", target); v1inebda = target; target += sizeof (struct grub_acpi_rsdp_v10); - target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); + target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16); v1 = 0; break; } @@ -251,7 +271,7 @@ grub_acpi_create_ebda (void) grub_memcpy (target, v2, v2->length); v2inebda = target; target += v2->length; - target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); + target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16); v2 = 0; break; } @@ -268,7 +288,7 @@ grub_acpi_create_ebda (void) grub_memcpy (target, v1, sizeof (struct grub_acpi_rsdp_v10)); v1inebda = target; target += sizeof (struct grub_acpi_rsdp_v10); - target = (grub_uint8_t *) ((((long) target - 1) | 0xf) + 1); + target = (grub_uint8_t *) ALIGN_UP((grub_addr_t) target, 16); v1 = 0; break; } @@ -285,18 +305,19 @@ grub_acpi_create_ebda (void) for (target = targetebda; target < targetebda + 0x400 - sizeof (struct grub_acpi_rsdp_v10); target += 0x10) - if (grub_memcmp (target, "RSD PTR ", 8) == 0 + if (grub_memcmp (target, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (target, sizeof (struct grub_acpi_rsdp_v10)) == 0 && target != v1inebda && target != v2inebda) *target = 0; grub_dprintf ("acpi", "Switching EBDA\n"); - (*((grub_uint16_t *) 0x40e)) = ((long)targetebda) >> 4; + (*((grub_uint16_t *) grub_absolute_pointer (0x40e))) = ((grub_addr_t) targetebda) >> 4; grub_dprintf ("acpi", "EBDA switched\n"); return GRUB_ERR_NONE; } +#endif /* Create tables common to ACPIv1 and ACPIv2+ */ static void @@ -325,15 +346,16 @@ setup_common_tables (void) /* If it's FADT correct DSDT and FACS addresses. */ fadt = (struct grub_acpi_fadt *) cur->addr; - if (grub_memcmp (fadt->hdr.signature, "FACP", 4) == 0) + if (grub_memcmp (fadt->hdr.signature, GRUB_ACPI_FADT_SIGNATURE, + sizeof (fadt->hdr.signature)) == 0) { - fadt->dsdt_addr = PTR_TO_UINT32 (table_dsdt); + fadt->dsdt_addr = (grub_addr_t) table_dsdt; fadt->facs_addr = facs_addr; /* Does a revision 2 exist at all? */ if (fadt->hdr.revision >= 3) { - fadt->dsdt_xaddr = PTR_TO_UINT64 (table_dsdt); + fadt->dsdt_xaddr = (grub_addr_t) table_dsdt; fadt->facs_xaddr = facs_addr; } @@ -349,22 +371,22 @@ setup_common_tables (void) numoftables++; rsdt_addr = rsdt = (struct grub_acpi_table_header *) playground_ptr; - playground_ptr += sizeof (struct grub_acpi_table_header) + 4 * numoftables; + playground_ptr += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables; - rsdt_entry = (grub_uint32_t *)(rsdt + 1); + rsdt_entry = (grub_uint32_t *) (rsdt + 1); /* Fill RSDT header. */ grub_memcpy (&(rsdt->signature), "RSDT", 4); - rsdt->length = sizeof (struct grub_acpi_table_header) + 4 * numoftables; + rsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables; rsdt->revision = 1; - grub_memcpy (&(rsdt->oemid), root_oemid, 6); - grub_memcpy (&(rsdt->oemtable), root_oemtable, 4); + grub_memcpy (&(rsdt->oemid), root_oemid, sizeof (rsdt->oemid)); + grub_memcpy (&(rsdt->oemtable), root_oemtable, sizeof (rsdt->oemtable)); rsdt->oemrev = root_oemrev; - grub_memcpy (&(rsdt->creator_id), root_creator_id, 6); + grub_memcpy (&(rsdt->creator_id), root_creator_id, sizeof (rsdt->creator_id)); rsdt->creator_rev = root_creator_rev; for (cur = acpi_tables; cur; cur = cur->next) - *(rsdt_entry++) = PTR_TO_UINT32 (cur->addr); + *(rsdt_entry++) = (grub_addr_t) cur->addr; /* Recompute checksum. */ rsdt->checksum = 0; @@ -378,10 +400,11 @@ setv1table (void) /* Create RSDP. */ rsdpv1_new = (struct grub_acpi_rsdp_v10 *) playground_ptr; playground_ptr += sizeof (struct grub_acpi_rsdp_v10); - grub_memcpy (&(rsdpv1_new->signature), "RSD PTR ", 8); + grub_memcpy (&(rsdpv1_new->signature), GRUB_RSDP_SIGNATURE, + sizeof (rsdpv1_new->signature)); grub_memcpy (&(rsdpv1_new->oemid), root_oemid, sizeof (rsdpv1_new->oemid)); rsdpv1_new->revision = 0; - rsdpv1_new->rsdt_addr = PTR_TO_UINT32 (rsdt_addr); + rsdpv1_new->rsdt_addr = (grub_addr_t) rsdt_addr; rsdpv1_new->checksum = 0; rsdpv1_new->checksum = 1 + ~grub_byte_checksum (rsdpv1_new, sizeof (*rsdpv1_new)); @@ -402,13 +425,13 @@ setv2table (void) /* Create XSDT. */ xsdt = (struct grub_acpi_table_header *) playground_ptr; - playground_ptr += sizeof (struct grub_acpi_table_header) + 8 * numoftables; + playground_ptr += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables; xsdt_entry = (grub_uint64_t *)(xsdt + 1); for (cur = acpi_tables; cur; cur = cur->next) - *(xsdt_entry++) = PTR_TO_UINT64 (cur->addr); + *(xsdt_entry++) = (grub_addr_t) cur->addr; grub_memcpy (&(xsdt->signature), "XSDT", 4); - xsdt->length = sizeof (struct grub_acpi_table_header) + 8 * numoftables; + xsdt->length = sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables; xsdt->revision = 1; grub_memcpy (&(xsdt->oemid), root_oemid, sizeof (xsdt->oemid)); grub_memcpy (&(xsdt->oemtable), root_oemtable, sizeof (xsdt->oemtable)); @@ -421,17 +444,17 @@ setv2table (void) /* Create RSDPv2. */ rsdpv2_new = (struct grub_acpi_rsdp_v20 *) playground_ptr; playground_ptr += sizeof (struct grub_acpi_rsdp_v20); - grub_memcpy (&(rsdpv2_new->rsdpv1.signature), "RSD PTR ", + grub_memcpy (&(rsdpv2_new->rsdpv1.signature), GRUB_RSDP_SIGNATURE, sizeof (rsdpv2_new->rsdpv1.signature)); grub_memcpy (&(rsdpv2_new->rsdpv1.oemid), root_oemid, sizeof (rsdpv2_new->rsdpv1.oemid)); rsdpv2_new->rsdpv1.revision = rev2; - rsdpv2_new->rsdpv1.rsdt_addr = PTR_TO_UINT32 (rsdt_addr); + rsdpv2_new->rsdpv1.rsdt_addr = (grub_addr_t) rsdt_addr; rsdpv2_new->rsdpv1.checksum = 0; rsdpv2_new->rsdpv1.checksum = 1 + ~grub_byte_checksum (&(rsdpv2_new->rsdpv1), sizeof (rsdpv2_new->rsdpv1)); rsdpv2_new->length = sizeof (*rsdpv2_new); - rsdpv2_new->xsdt_addr = PTR_TO_UINT64 (xsdt); + rsdpv2_new->xsdt_addr = (grub_addr_t) xsdt; rsdpv2_new->checksum = 0; rsdpv2_new->checksum = 1 + ~grub_byte_checksum (rsdpv2_new, rsdpv2_new->length); @@ -456,13 +479,11 @@ free_tables (void) } static grub_err_t -grub_cmd_acpi (struct grub_extcmd *cmd, - int argc, char **args) +grub_cmd_acpi (struct grub_extcmd_context *ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; struct grub_acpi_rsdp_v10 *rsdp; struct efiemu_acpi_table *cur, *t; - grub_err_t err; int i, mmapregion; int numoftables; @@ -479,23 +500,25 @@ grub_cmd_acpi (struct grub_extcmd *cmd, if (! rsdp) rsdp = grub_machine_acpi_get_rsdpv1 (); + grub_dprintf ("acpi", "RSDP @%p\n", rsdp); + if (rsdp) { - grub_uint32_t *entry_ptr; + grub_uint8_t *entry_ptr; char *exclude = 0; char *load_only = 0; char *ptr; - /* RSDT consists of header and an array of 32-bit pointers. */ - struct grub_acpi_table_header *rsdt; + grub_size_t tbl_addr_size; + struct grub_acpi_table_header *table_head; - exclude = state[0].set ? grub_strdup (state[0].arg) : 0; + exclude = state[OPTION_EXCLUDE].set ? grub_strdup (state[OPTION_EXCLUDE].arg) : 0; if (exclude) { for (ptr = exclude; *ptr; ptr++) *ptr = grub_tolower (*ptr); } - load_only = state[1].set ? grub_strdup (state[1].arg) : 0; + load_only = state[OPTION_LOAD_ONLY].set ? grub_strdup (state[OPTION_LOAD_ONLY].arg) : 0; if (load_only) { for (ptr = load_only; *ptr; ptr++) @@ -505,17 +528,32 @@ grub_cmd_acpi (struct grub_extcmd *cmd, /* Set revision variables to replicate the same version as host. */ rev1 = ! rsdp->revision; rev2 = rsdp->revision; - rsdt = (struct grub_acpi_table_header *) UINT_TO_PTR (rsdp->rsdt_addr); + if (rev2 && ((struct grub_acpi_table_header *) (grub_addr_t) ((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr) != NULL) + { + /* XSDT consists of header and an array of 64-bit pointers. */ + table_head = (struct grub_acpi_table_header *) (grub_addr_t) ((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr; + tbl_addr_size = sizeof (((struct grub_acpi_rsdp_v20 *) rsdp)->xsdt_addr); + } + else + { + /* RSDT consists of header and an array of 32-bit pointers. */ + table_head = (struct grub_acpi_table_header *) (grub_addr_t) rsdp->rsdt_addr; + tbl_addr_size = sizeof (rsdp->rsdt_addr); + } + /* Load host tables. */ - for (entry_ptr = (grub_uint32_t *) (rsdt + 1); - entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt) - + rsdt->length); - entry_ptr++) + for (entry_ptr = (grub_uint8_t *) (table_head + 1); + entry_ptr < (grub_uint8_t *) (((grub_uint8_t *) table_head) + table_head->length); + entry_ptr += tbl_addr_size) { char signature[5]; struct efiemu_acpi_table *table; - struct grub_acpi_table_header *curtable - = (struct grub_acpi_table_header *) UINT_TO_PTR (*entry_ptr); + struct grub_acpi_table_header *curtable; + if (tbl_addr_size == sizeof (rsdp->rsdt_addr)) + curtable = (struct grub_acpi_table_header *) (grub_addr_t) *((grub_uint32_t *) entry_ptr); + else + curtable = (struct grub_acpi_table_header *) (grub_addr_t) *((grub_uint64_t *) entry_ptr); + signature[4] = 0; for (i = 0; i < 4;i++) signature[i] = grub_tolower (curtable->signature[i]); @@ -527,7 +565,7 @@ grub_cmd_acpi (struct grub_extcmd *cmd, struct grub_acpi_fadt *fadt = (struct grub_acpi_fadt *) curtable; /* Set root header variables to the same values - as FACP by default. */ + as FADT by default. */ grub_memcpy (&root_oemid, &(fadt->hdr.oemid), sizeof (root_oemid)); grub_memcpy (&root_oemtable, &(fadt->hdr.oemtable), @@ -539,7 +577,7 @@ grub_cmd_acpi (struct grub_extcmd *cmd, /* Load DSDT if not excluded. */ dsdt = (struct grub_acpi_table_header *) - UINT_TO_PTR (fadt->dsdt_addr); + (grub_addr_t) fadt->dsdt_addr; if (dsdt && (! exclude || ! grub_strword (exclude, "dsdt")) && (! load_only || grub_strword (load_only, "dsdt")) && dsdt->length >= sizeof (*dsdt)) @@ -551,8 +589,7 @@ grub_cmd_acpi (struct grub_extcmd *cmd, free_tables (); grub_free (exclude); grub_free (load_only); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate table"); + return grub_errno; } grub_memcpy (table_dsdt, dsdt, dsdt->length); } @@ -578,8 +615,7 @@ grub_cmd_acpi (struct grub_extcmd *cmd, free_tables (); grub_free (exclude); grub_free (load_only); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate table structure"); + return grub_errno; } table->size = curtable->length; table->addr = grub_malloc (table->size); @@ -587,8 +623,10 @@ grub_cmd_acpi (struct grub_extcmd *cmd, if (! table->addr) { free_tables (); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate table"); + grub_free (exclude); + grub_free (load_only); + grub_free (table); + return grub_errno; } table->next = acpi_tables; acpi_tables = table; @@ -599,26 +637,26 @@ grub_cmd_acpi (struct grub_extcmd *cmd, } /* Does user specify versions to generate? */ - if (state[2].set || state[3].set) + if (state[OPTION_V1].set || state[OPTION_V2].set) { - rev1 = state[2].set; - if (state[3].set) + rev1 = state[OPTION_V1].set; + if (state[OPTION_V2].set) rev2 = rev2 ? : 2; else rev2 = 0; } /* Does user override root header information? */ - if (state[4].set) - grub_strncpy (root_oemid, state[4].arg, sizeof (root_oemid)); - if (state[5].set) - grub_strncpy (root_oemtable, state[5].arg, sizeof (root_oemtable)); - if (state[6].set) - root_oemrev = grub_strtoul (state[6].arg, 0, 0); - if (state[7].set) - grub_strncpy (root_creator_id, state[7].arg, sizeof (root_creator_id)); - if (state[8].set) - root_creator_rev = grub_strtoul (state[8].arg, 0, 0); + if (state[OPTION_OEMID].set) + grub_strncpy (root_oemid, state[OPTION_OEMID].arg, sizeof (root_oemid)); + if (state[OPTION_OEMTABLE].set) + grub_strncpy (root_oemtable, state[OPTION_OEMTABLE].arg, sizeof (root_oemtable)); + if (state[OPTION_OEMTABLEREV].set) + root_oemrev = grub_strtoul (state[OPTION_OEMTABLEREV].arg, 0, 0); + if (state[OPTION_OEMTABLECREATOR].set) + grub_strncpy (root_creator_id, state[OPTION_OEMTABLECREATOR].arg, sizeof (root_creator_id)); + if (state[OPTION_OEMTABLECREATORREV].set) + root_creator_rev = grub_strtoul (state[OPTION_OEMTABLECREATORREV].arg, 0, 0); /* Load user tables */ for (i = 0; i < argc; i++) @@ -627,11 +665,11 @@ grub_cmd_acpi (struct grub_extcmd *cmd, grub_size_t size; char *buf; - file = grub_gzfile_open (args[i], 1); + file = grub_file_open (args[i], GRUB_FILE_TYPE_ACPI_TABLE); if (! file) { free_tables (); - return grub_error (GRUB_ERR_BAD_OS, "couldn't open file %s", args[i]); + return grub_errno; } size = grub_file_size (file); @@ -639,7 +677,8 @@ grub_cmd_acpi (struct grub_extcmd *cmd, { grub_file_close (file); free_tables (); - return grub_error (GRUB_ERR_BAD_OS, "file %s is too small", args[i]); + return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + args[i]); } buf = (char *) grub_malloc (size); @@ -647,15 +686,17 @@ grub_cmd_acpi (struct grub_extcmd *cmd, { grub_file_close (file); free_tables (); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't read file %s", args[i]); + return grub_errno; } if (grub_file_read (file, buf, size) != (int) size) { grub_file_close (file); free_tables (); - return grub_error (GRUB_ERR_BAD_OS, "couldn't read file %s", args[i]); + if (!grub_errno) + grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + args[i]); + return grub_errno; } grub_file_close (file); @@ -674,8 +715,7 @@ grub_cmd_acpi (struct grub_extcmd *cmd, if (! table) { free_tables (); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate table structure"); + return grub_errno; } table->size = size; @@ -694,17 +734,17 @@ grub_cmd_acpi (struct grub_extcmd *cmd, /* DSDT. */ playground_size += dsdt_size; /* RSDT. */ - playground_size += sizeof (struct grub_acpi_table_header) + 4 * numoftables; + playground_size += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint32_t) * numoftables; /* RSDPv1. */ playground_size += sizeof (struct grub_acpi_rsdp_v10); /* XSDT. */ - playground_size += sizeof (struct grub_acpi_table_header) + 8 * numoftables; + playground_size += sizeof (struct grub_acpi_table_header) + sizeof (grub_uint64_t) * numoftables; /* RSDPv2. */ playground_size += sizeof (struct grub_acpi_rsdp_v20); playground = playground_ptr = grub_mmap_malign_and_register (1, playground_size, &mmapregion, - GRUB_MACHINE_MEMORY_ACPI, 0); + GRUB_MEMORY_ACPI, 0); if (! playground) { @@ -731,23 +771,30 @@ grub_cmd_acpi (struct grub_extcmd *cmd, } acpi_tables = 0; - if (! state[9].set && (err = grub_acpi_create_ebda ())) +#if defined (__i386__) || defined (__x86_64__) + if (! state[OPTION_NO_EBDA].set) { - rsdpv1_new = 0; - rsdpv2_new = 0; - grub_mmap_free_and_unregister (mmapregion); - return err; + grub_err_t err; + err = grub_acpi_create_ebda (); + if (err) + { + rsdpv1_new = 0; + rsdpv2_new = 0; + grub_mmap_free_and_unregister (mmapregion); + return err; + } } +#endif #ifdef GRUB_MACHINE_EFI { - struct grub_efi_guid acpi = GRUB_EFI_ACPI_TABLE_GUID; - struct grub_efi_guid acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID; + static grub_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID; + static grub_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID; - grub_efi_system_table->boot_services->install_configuration_table - (&acpi20, grub_acpi_get_rsdpv2 ()); - grub_efi_system_table->boot_services->install_configuration_table - (&acpi, grub_acpi_get_rsdpv1 ()); + grub_efi_system_table->boot_services->install_configuration_table (&acpi20, + grub_acpi_get_rsdpv2 ()); + grub_efi_system_table->boot_services->install_configuration_table (&acpi, + grub_acpi_get_rsdpv1 ()); } #endif @@ -758,14 +805,13 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(acpi) { - cmd = grub_register_extcmd ("acpi", grub_cmd_acpi, - GRUB_COMMAND_FLAG_BOTH, - N_("[-1|-2] [--exclude=TABLE1,TABLE2|" - "--load-only=table1,table2] FILE1" - " [FILE2] [...]"), - N_("Load host ACPI tables and tables " - "specified by arguments."), - options); + cmd = grub_register_extcmd_lockdown ("acpi", grub_cmd_acpi, 0, + N_("[-1|-2] [--exclude=TABLE1,TABLE2|" + "--load-only=TABLE1,TABLE2] FILE1" + " [FILE2] [...]"), + N_("Load host ACPI tables and tables " + "specified by arguments."), + options); } GRUB_MOD_FINI(acpi) diff --git a/grub-core/commands/acpihalt.c b/grub-core/commands/acpihalt.c new file mode 100644 index 000000000..9a5e22155 --- /dev/null +++ b/grub-core/commands/acpihalt.c @@ -0,0 +1,454 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#ifdef GRUB_DSDT_TEST +#include +#include +#include +#include +#include +#include + +#define grub_dprintf(cond, args...) printf ( args ) +#define grub_printf printf +#define grub_util_fopen fopen +#define grub_memcmp memcmp +typedef uint64_t grub_uint64_t; +typedef uint32_t grub_uint32_t; +typedef uint16_t grub_uint16_t; +typedef uint8_t grub_uint8_t; + +#endif + +#include +#ifndef GRUB_DSDT_TEST +#include +#else +#define _(x) x +#define N_(x) x +#endif + +#ifndef GRUB_DSDT_TEST +#include +#include +#include +#include +#endif + +static inline grub_uint32_t +decode_length (const grub_uint8_t *ptr, int *numlen) +{ + int num_bytes, i; + grub_uint32_t ret; + if (*ptr < 64) + { + if (numlen) + *numlen = 1; + return *ptr; + } + num_bytes = *ptr >> 6; + if (numlen) + *numlen = num_bytes + 1; + ret = *ptr & 0xf; + ptr++; + for (i = 0; i < num_bytes; i++) + { + ret |= *ptr << (8 * i + 4); + ptr++; + } + return ret; +} + +static inline grub_uint32_t +skip_name_string (const grub_uint8_t *ptr, const grub_uint8_t *end) +{ + const grub_uint8_t *ptr0 = ptr; + + while (ptr < end && (*ptr == '^' || *ptr == '\\')) + ptr++; + switch (*ptr) + { + case '.': + ptr++; + ptr += 8; + break; + case '/': + ptr++; + ptr += 1 + (*ptr) * 4; + break; + case 0: + ptr++; + break; + default: + ptr += 4; + break; + } + return ptr - ptr0; +} + +static inline grub_uint32_t +skip_data_ref_object (const grub_uint8_t *ptr, const grub_uint8_t *end) +{ + grub_dprintf ("acpi", "data type = 0x%x\n", *ptr); + switch (*ptr) + { + case GRUB_ACPI_OPCODE_PACKAGE: + case GRUB_ACPI_OPCODE_BUFFER: + return 1 + decode_length (ptr + 1, 0); + case GRUB_ACPI_OPCODE_ZERO: + case GRUB_ACPI_OPCODE_ONES: + case GRUB_ACPI_OPCODE_ONE: + return 1; + case GRUB_ACPI_OPCODE_BYTE_CONST: + return 2; + case GRUB_ACPI_OPCODE_WORD_CONST: + return 3; + case GRUB_ACPI_OPCODE_DWORD_CONST: + return 5; + case GRUB_ACPI_OPCODE_STRING_CONST: + { + const grub_uint8_t *ptr0 = ptr; + for (ptr++; ptr < end && *ptr; ptr++); + if (ptr == end) + return 0; + return ptr - ptr0 + 1; + } + default: + if (*ptr == '^' || *ptr == '\\' || *ptr == '_' + || (*ptr >= 'A' && *ptr <= 'Z')) + return skip_name_string (ptr, end); + grub_printf ("Unknown opcode 0x%x\n", *ptr); + return 0; + } +} + +static inline grub_uint32_t +skip_term (const grub_uint8_t *ptr, const grub_uint8_t *end) +{ + grub_uint32_t add; + const grub_uint8_t *ptr0 = ptr; + + switch(*ptr) + { + case GRUB_ACPI_OPCODE_ADD: + case GRUB_ACPI_OPCODE_AND: + case GRUB_ACPI_OPCODE_CONCAT: + case GRUB_ACPI_OPCODE_CONCATRES: + case GRUB_ACPI_OPCODE_DIVIDE: + case GRUB_ACPI_OPCODE_INDEX: + case GRUB_ACPI_OPCODE_LSHIFT: + case GRUB_ACPI_OPCODE_MOD: + case GRUB_ACPI_OPCODE_MULTIPLY: + case GRUB_ACPI_OPCODE_NAND: + case GRUB_ACPI_OPCODE_NOR: + case GRUB_ACPI_OPCODE_OR: + case GRUB_ACPI_OPCODE_RSHIFT: + case GRUB_ACPI_OPCODE_SUBTRACT: + case GRUB_ACPI_OPCODE_TOSTRING: + case GRUB_ACPI_OPCODE_XOR: + /* + * Parameters for these opcodes: TermArg, TermArg Target, see ACPI + * spec r5.0, page 828f. + */ + ptr++; + ptr += add = skip_term (ptr, end); + if (!add) + return 0; + ptr += add = skip_term (ptr, end); + if (!add) + return 0; + ptr += skip_name_string (ptr, end); + break; + default: + return skip_data_ref_object (ptr, end); + } + return ptr - ptr0; +} + +static inline grub_uint32_t +skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end) +{ + const grub_uint8_t *ptr0 = ptr; + int add; + grub_dprintf ("acpi", "Extended opcode: 0x%x\n", *ptr); + switch (*ptr) + { + case GRUB_ACPI_EXTOPCODE_MUTEX: + ptr++; + ptr += skip_name_string (ptr, end); + ptr++; + break; + case GRUB_ACPI_EXTOPCODE_EVENT_OP: + ptr++; + ptr += skip_name_string (ptr, end); + break; + case GRUB_ACPI_EXTOPCODE_OPERATION_REGION: + ptr++; + ptr += skip_name_string (ptr, end); + ptr++; + ptr += add = skip_term (ptr, end); + if (!add) + return 0; + ptr += add = skip_term (ptr, end); + if (!add) + return 0; + break; + case GRUB_ACPI_EXTOPCODE_FIELD_OP: + case GRUB_ACPI_EXTOPCODE_DEVICE_OP: + case GRUB_ACPI_EXTOPCODE_PROCESSOR_OP: + case GRUB_ACPI_EXTOPCODE_POWER_RES_OP: + case GRUB_ACPI_EXTOPCODE_THERMAL_ZONE_OP: + case GRUB_ACPI_EXTOPCODE_INDEX_FIELD_OP: + case GRUB_ACPI_EXTOPCODE_BANK_FIELD_OP: + ptr++; + ptr += decode_length (ptr, 0); + break; + default: + grub_printf ("Unexpected extended opcode: 0x%x\n", *ptr); + return 0; + } + return ptr - ptr0; +} + + +static int +get_sleep_type (grub_uint8_t *table, grub_uint8_t *ptr, grub_uint8_t *end, + grub_uint8_t *scope, int scope_len) +{ + grub_uint8_t *prev = table; + + if (!ptr) + ptr = table + sizeof (struct grub_acpi_table_header); + while (ptr < end && prev < ptr) + { + int add; + prev = ptr; + grub_dprintf ("acpi", "Opcode 0x%x\n", *ptr); + grub_dprintf ("acpi", "Tell %x\n", (unsigned) (ptr - table)); + switch (*ptr) + { + case GRUB_ACPI_OPCODE_EXTOP: + ptr++; + ptr += add = skip_ext_op (ptr, end); + if (!add) + return -1; + break; + case GRUB_ACPI_OPCODE_CREATE_DWORD_FIELD: + case GRUB_ACPI_OPCODE_CREATE_WORD_FIELD: + case GRUB_ACPI_OPCODE_CREATE_BYTE_FIELD: + { + ptr += 5; + ptr += add = skip_data_ref_object (ptr, end); + if (!add) + return -1; + ptr += 4; + break; + } + case GRUB_ACPI_OPCODE_NAME: + ptr++; + if ((!scope || grub_memcmp (scope, "\\", scope_len) == 0) && + (grub_memcmp (ptr, "_S5_", 4) == 0 || grub_memcmp (ptr, "\\_S5_", 4) == 0)) + { + int ll; + grub_uint8_t *ptr2 = ptr; + grub_dprintf ("acpi", "S5 found\n"); + ptr2 += skip_name_string (ptr, end); + if (*ptr2 != 0x12) + { + grub_printf ("Unknown opcode in _S5: 0x%x\n", *ptr2); + return -1; + } + ptr2++; + decode_length (ptr2, &ll); + ptr2 += ll; + ptr2++; + switch (*ptr2) + { + case GRUB_ACPI_OPCODE_ZERO: + return 0; + case GRUB_ACPI_OPCODE_ONE: + return 1; + case GRUB_ACPI_OPCODE_BYTE_CONST: + return ptr2[1]; + default: + grub_printf ("Unknown data type in _S5: 0x%x\n", *ptr2); + return -1; + } + } + ptr += add = skip_name_string (ptr, end); + if (!add) + return -1; + ptr += add = skip_data_ref_object (ptr, end); + if (!add) + return -1; + break; + case GRUB_ACPI_OPCODE_ALIAS: + ptr++; + /* We need to skip two name strings */ + ptr += add = skip_name_string (ptr, end); + if (!add) + return -1; + ptr += add = skip_name_string (ptr, end); + if (!add) + return -1; + break; + + case GRUB_ACPI_OPCODE_SCOPE: + { + int scope_sleep_type; + int ll; + grub_uint8_t *name; + int name_len; + + ptr++; + add = decode_length (ptr, &ll); + name = ptr + ll; + name_len = skip_name_string (name, ptr + add); + if (!name_len) + return -1; + scope_sleep_type = get_sleep_type (table, name + name_len, + ptr + add, name, name_len); + if (scope_sleep_type != -2) + return scope_sleep_type; + ptr += add; + break; + } + case GRUB_ACPI_OPCODE_IF: + case GRUB_ACPI_OPCODE_METHOD: + { + ptr++; + ptr += decode_length (ptr, 0); + break; + } + default: + grub_printf ("Unknown opcode 0x%x\n", *ptr); + return -1; + } + } + + return -2; +} + +#ifdef GRUB_DSDT_TEST +int +main (int argc, char **argv) +{ + FILE *f; + size_t len; + unsigned char *buf; + if (argc < 2) + printf ("Usage: %s FILE\n", argv[0]); + f = grub_util_fopen (argv[1], "rb"); + if (!f) + { + printf ("Couldn't open file\n"); + return 1; + } + fseek (f, 0, SEEK_END); + len = ftell (f); + fseek (f, 0, SEEK_SET); + buf = malloc (len); + if (!buf) + { + printf (_("error: %s.\n"), _("out of memory")); + fclose (f); + return 2; + } + if (fread (buf, 1, len, f) != len) + { + printf (_("cannot read `%s': %s"), argv[1], strerror (errno)); + free (buf); + fclose (f); + return 2; + } + + printf ("Sleep type = %d\n", get_sleep_type (buf, NULL, buf + len, NULL, 0)); + free (buf); + fclose (f); + return 0; +} + +#else + +void +grub_acpi_halt (void) +{ + struct grub_acpi_rsdp_v20 *rsdp2; + struct grub_acpi_rsdp_v10 *rsdp1; + struct grub_acpi_table_header *rsdt; + grub_uint32_t *entry_ptr; + grub_uint32_t port = 0; + int sleep_type = -1; + + rsdp2 = grub_acpi_get_rsdpv2 (); + if (rsdp2) + rsdp1 = &(rsdp2->rsdpv1); + else + rsdp1 = grub_acpi_get_rsdpv1 (); + grub_dprintf ("acpi", "rsdp1=%p\n", rsdp1); + if (!rsdp1) + return; + + rsdt = (struct grub_acpi_table_header *) (grub_addr_t) rsdp1->rsdt_addr; + for (entry_ptr = (grub_uint32_t *) (rsdt + 1); + entry_ptr < (grub_uint32_t *) (((grub_uint8_t *) rsdt) + + rsdt->length); + entry_ptr++) + { + if (grub_memcmp ((void *) (grub_addr_t) *entry_ptr, "FACP", 4) == 0) + { + struct grub_acpi_fadt *fadt + = ((struct grub_acpi_fadt *) (grub_addr_t) *entry_ptr); + struct grub_acpi_table_header *dsdt + = (struct grub_acpi_table_header *) (grub_addr_t) fadt->dsdt_addr; + grub_uint8_t *buf = (grub_uint8_t *) dsdt; + + port = fadt->pm1a; + + grub_dprintf ("acpi", "PM1a port=%x\n", port); + + if (grub_memcmp (dsdt->signature, "DSDT", + sizeof (dsdt->signature)) == 0 + && sleep_type < 0) + sleep_type = get_sleep_type (buf, NULL, buf + dsdt->length, + NULL, 0); + } + else if (grub_memcmp ((void *) (grub_addr_t) *entry_ptr, "SSDT", 4) == 0 + && sleep_type < 0) + { + struct grub_acpi_table_header *ssdt + = (struct grub_acpi_table_header *) (grub_addr_t) *entry_ptr; + grub_uint8_t *buf = (grub_uint8_t *) ssdt; + + grub_dprintf ("acpi", "SSDT = %p\n", ssdt); + + sleep_type = get_sleep_type (buf, NULL, buf + ssdt->length, NULL, 0); + } + } + + grub_dprintf ("acpi", "SLP_TYP = %d, port = 0x%x\n", sleep_type, port); + if (port && sleep_type >= 0 && sleep_type < 8) + grub_outw (GRUB_ACPI_SLP_EN | (sleep_type << GRUB_ACPI_SLP_TYP_OFFSET), + port & 0xffff); + + grub_millisleep (1500); + + /* TRANSLATORS: It's computer shutdown using ACPI, not disabling ACPI. */ + grub_puts_ (N_("ACPI shutdown failed")); +} +#endif diff --git a/commands/i386/pc/pxecmd.c b/grub-core/commands/arc/lsdev.c similarity index 56% rename from commands/i386/pc/pxecmd.c rename to grub-core/commands/arc/lsdev.c index b576a8ea4..27ed0a248 100644 --- a/commands/i386/pc/pxecmd.c +++ b/grub-core/commands/arc/lsdev.c @@ -1,7 +1,6 @@ -/* pxe.c - command to control the pxe driver */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2011 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 @@ -18,35 +17,41 @@ */ #include -#include #include -#include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Helper for grub_cmd_lsdev. */ +static int +grub_cmd_lsdev_iter (const char *name, + const struct grub_arc_component *comp __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + grub_printf ("%s\n", name); + return 0; +} static grub_err_t -grub_cmd_pxe_unload (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) +grub_cmd_lsdev (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) { - if (! grub_pxe_pxenv) - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "no pxe environment"); - - grub_pxe_unload (); - + grub_arc_iterate_devs (grub_cmd_lsdev_iter, 0, 0); return 0; } static grub_command_t cmd; -GRUB_MOD_INIT(pxecmd) +GRUB_MOD_INIT(lsdev) { - cmd = grub_register_command ("pxe_unload", grub_cmd_pxe_unload, - 0, - N_("Unload PXE environment.")); + cmd = grub_register_command ("lsdev", grub_cmd_lsdev, "", + N_("List devices.")); } -GRUB_MOD_FINI(pxecmd) +GRUB_MOD_FINI(lsdev) { grub_unregister_command (cmd); } diff --git a/grub-core/commands/bli.c b/grub-core/commands/bli.c new file mode 100644 index 000000000..298c5f70a --- /dev/null +++ b/grub-core/commands/bli.c @@ -0,0 +1,138 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 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 . + * + * Implementation of the Boot Loader Interface. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define MODNAME "bli" + +static const grub_guid_t bli_vendor_guid = GRUB_EFI_VENDOR_BOOT_LOADER_INTERFACE_GUID; + +static grub_err_t +get_part_uuid (const char *device_name, char **part_uuid) +{ + grub_device_t device; + grub_err_t status = GRUB_ERR_NONE; + grub_disk_t disk = NULL; + struct grub_gpt_partentry entry; + + device = grub_device_open (device_name); + if (device == NULL) + return grub_error (grub_errno, N_("cannot open device: %s"), device_name); + + if (device->disk == NULL) + { + grub_dprintf ("bli", "%s is not a disk device, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + + if (device->disk->partition == NULL) + { + grub_dprintf ("bli", "%s has no partition, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + + disk = grub_disk_open (device->disk->name); + if (disk == NULL) + { + status = grub_error (grub_errno, N_("cannot open disk: %s"), device_name); + grub_device_close (device); + return status; + } + + if (grub_strcmp (device->disk->partition->partmap->name, "gpt") != 0) + { + status = grub_error (GRUB_ERR_BAD_PART_TABLE, + N_("this is not a GPT partition table: %s"), device_name); + goto fail; + } + + if (grub_disk_read (disk, device->disk->partition->offset, + device->disk->partition->index, sizeof (entry), &entry) != GRUB_ERR_NONE) + { + status = grub_error (grub_errno, N_("read error: %s"), device_name); + goto fail; + } + + *part_uuid = grub_xasprintf ("%pG", &entry.guid); + if (*part_uuid == NULL) + status = grub_errno; + + fail: + grub_disk_close (disk); + grub_device_close (device); + + return status; +} + +static grub_err_t +set_loader_device_part_uuid (void) +{ + grub_efi_loaded_image_t *image; + char *device_name; + grub_err_t status = GRUB_ERR_NONE; + char *part_uuid = NULL; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (image == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device")); + + device_name = grub_efidisk_get_device_name (image->device_handle); + if (device_name == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device")); + + status = get_part_uuid (device_name, &part_uuid); + + if (status == GRUB_ERR_NONE && part_uuid) + status = grub_efi_set_variable_to_string ("LoaderDevicePartUUID", &bli_vendor_guid, part_uuid, + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + else + grub_error (status, N_("unable to determine partition UUID of boot device")); + + grub_free (part_uuid); + grub_free (device_name); + return status; +} + +GRUB_MOD_INIT (bli) +{ + grub_efi_set_variable_to_string ("LoaderInfo", &bli_vendor_guid, PACKAGE_STRING, + GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS); + set_loader_device_part_uuid (); + /* No error here is critical, other than being logged */ + grub_print_error (); +} diff --git a/grub-core/commands/blocklist.c b/grub-core/commands/blocklist.c new file mode 100644 index 000000000..48fd85a33 --- /dev/null +++ b/grub-core/commands/blocklist.c @@ -0,0 +1,162 @@ +/* blocklist.c - print the block list of a file */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Context for grub_cmd_blocklist. */ +struct blocklist_ctx +{ + unsigned long start_sector; + unsigned num_sectors; + int num_entries; + grub_disk_addr_t part_start; +}; + +/* Helper for grub_cmd_blocklist. */ +static void +print_blocklist (grub_disk_addr_t sector, unsigned num, + unsigned offset, unsigned length, struct blocklist_ctx *ctx) +{ + if (ctx->num_entries++) + grub_printf (","); + + grub_printf ("%llu", (unsigned long long) (sector - ctx->part_start)); + if (num > 0) + grub_printf ("+%u", num); + if (offset != 0 || length != 0) + grub_printf ("[%u-%u]", offset, offset + length); +} + +/* Helper for grub_cmd_blocklist. */ +static grub_err_t +read_blocklist (grub_disk_addr_t sector, unsigned offset, unsigned length, + char *buf __attribute__ ((unused)), void *data) +{ + struct blocklist_ctx *ctx = data; + + if (ctx->num_sectors > 0) + { + if (ctx->start_sector + ctx->num_sectors == sector + && offset == 0 && length >= GRUB_DISK_SECTOR_SIZE) + { + ctx->num_sectors += length >> GRUB_DISK_SECTOR_BITS; + sector += length >> GRUB_DISK_SECTOR_BITS; + length &= (GRUB_DISK_SECTOR_SIZE - 1); + } + + if (!length) + return GRUB_ERR_NONE; + print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx); + ctx->num_sectors = 0; + } + + if (offset) + { + unsigned l = length + offset; + l &= (GRUB_DISK_SECTOR_SIZE - 1); + l -= offset; + print_blocklist (sector, 0, offset, l, ctx); + length -= l; + sector++; + offset = 0; + } + + if (!length) + return GRUB_ERR_NONE; + + if (length & (GRUB_DISK_SECTOR_SIZE - 1)) + { + if (length >> GRUB_DISK_SECTOR_BITS) + { + print_blocklist (sector, length >> GRUB_DISK_SECTOR_BITS, 0, 0, ctx); + sector += length >> GRUB_DISK_SECTOR_BITS; + } + print_blocklist (sector, 0, 0, length & (GRUB_DISK_SECTOR_SIZE - 1), ctx); + } + else + { + ctx->start_sector = sector; + ctx->num_sectors = length >> GRUB_DISK_SECTOR_BITS; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_file_t file; + char buf[GRUB_DISK_SECTOR_SIZE]; + struct blocklist_ctx ctx = { + .start_sector = 0, + .num_sectors = 0, + .num_entries = 0, + .part_start = 0 + }; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (args[0], GRUB_FILE_TYPE_PRINT_BLOCKLIST + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (! file) + return grub_errno; + + if (! file->device->disk) + return grub_error (GRUB_ERR_BAD_DEVICE, + "this command is available only for disk devices"); + + ctx.part_start = grub_partition_get_start (file->device->disk->partition); + + file->read_hook = read_blocklist; + file->read_hook_data = &ctx; + + while (grub_file_read (file, buf, sizeof (buf)) > 0) + ; + + if (ctx.num_sectors > 0) + print_blocklist (ctx.start_sector, ctx.num_sectors, 0, 0, &ctx); + + grub_file_close (file); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(blocklist) +{ + cmd = grub_register_command ("blocklist", grub_cmd_blocklist, + N_("FILE"), N_("Print a block list.")); +} + +GRUB_MOD_FINI(blocklist) +{ + grub_unregister_command (cmd); +} diff --git a/commands/boot.c b/grub-core/commands/boot.c similarity index 62% rename from commands/boot.c rename to grub-core/commands/boot.c index 1ec1e6f77..61514788e 100644 --- a/commands/boot.c +++ b/grub-core/commands/boot.c @@ -25,23 +25,58 @@ #include #include -static grub_err_t (*grub_loader_boot_func) (void); -static grub_err_t (*grub_loader_unload_func) (void); -static int grub_loader_noreturn; +GRUB_MOD_LICENSE ("GPLv3+"); -struct grub_preboot_t +static grub_err_t (*grub_loader_boot_func) (void *context); +static grub_err_t (*grub_loader_unload_func) (void *context); +static void *grub_loader_context; +static int grub_loader_flags; + +struct grub_simple_loader_hooks +{ + grub_err_t (*boot) (void); + grub_err_t (*unload) (void); +}; + +/* Don't heap allocate this to avoid making grub_loader_set() fallible. */ +static struct grub_simple_loader_hooks simple_loader_hooks; + +struct grub_preboot { grub_err_t (*preboot_func) (int); grub_err_t (*preboot_rest_func) (void); grub_loader_preboot_hook_prio_t prio; - struct grub_preboot_t *next; - struct grub_preboot_t *prev; + struct grub_preboot *next; + struct grub_preboot *prev; }; static int grub_loader_loaded; -static struct grub_preboot_t *preboots_head = 0, +static struct grub_preboot *preboots_head = 0, *preboots_tail = 0; +static grub_err_t +grub_simple_boot_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + + hooks = (struct grub_simple_loader_hooks *) context; + return hooks->boot (); +} + +static grub_err_t +grub_simple_unload_hook (void *context) +{ + struct grub_simple_loader_hooks *hooks; + grub_err_t ret; + + hooks = (struct grub_simple_loader_hooks *) context; + + ret = hooks->unload (); + grub_memset (hooks, 0, sizeof (*hooks)); + + return ret; +} + int grub_loader_is_loaded (void) { @@ -49,23 +84,20 @@ grub_loader_is_loaded (void) } /* Register a preboot hook. */ -void * -grub_loader_register_preboot_hook (grub_err_t (*preboot_func) (int noreturn), +struct grub_preboot * +grub_loader_register_preboot_hook (grub_err_t (*preboot_func) (int flags), grub_err_t (*preboot_rest_func) (void), grub_loader_preboot_hook_prio_t prio) { - struct grub_preboot_t *cur, *new_preboot; + struct grub_preboot *cur, *new_preboot; if (! preboot_func && ! preboot_rest_func) return 0; - new_preboot = (struct grub_preboot_t *) - grub_malloc (sizeof (struct grub_preboot_t)); + new_preboot = (struct grub_preboot *) + grub_malloc (sizeof (struct grub_preboot)); if (! new_preboot) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, "hook not added"); - return 0; - } + return 0; new_preboot->preboot_func = preboot_func; new_preboot->preboot_rest_func = preboot_rest_func; @@ -94,9 +126,9 @@ grub_loader_register_preboot_hook (grub_err_t (*preboot_func) (int noreturn), } void -grub_loader_unregister_preboot_hook (void *hnd) +grub_loader_unregister_preboot_hook (struct grub_preboot *hnd) { - struct grub_preboot_t *preb = hnd; + struct grub_preboot *preb = hnd; if (preb->next) preb->next->prev = preb->prev; @@ -111,28 +143,45 @@ grub_loader_unregister_preboot_hook (void *hnd) } void -grub_loader_set (grub_err_t (*boot) (void), - grub_err_t (*unload) (void), - int noreturn) +grub_loader_set_ex (grub_err_t (*boot) (void *context), + grub_err_t (*unload) (void *context), + void *context, + int flags) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = boot; grub_loader_unload_func = unload; - grub_loader_noreturn = noreturn; + grub_loader_context = context; + grub_loader_flags = flags; grub_loader_loaded = 1; } +void +grub_loader_set (grub_err_t (*boot) (void), + grub_err_t (*unload) (void), + int flags) +{ + grub_loader_set_ex (grub_simple_boot_hook, + grub_simple_unload_hook, + &simple_loader_hooks, + flags); + + simple_loader_hooks.boot = boot; + simple_loader_hooks.unload = unload; +} + void grub_loader_unset(void) { if (grub_loader_loaded && grub_loader_unload_func) - grub_loader_unload_func (); + grub_loader_unload_func (grub_loader_context); grub_loader_boot_func = 0; grub_loader_unload_func = 0; + grub_loader_context = 0; grub_loader_loaded = 0; } @@ -141,17 +190,17 @@ grub_err_t grub_loader_boot (void) { grub_err_t err = GRUB_ERR_NONE; - struct grub_preboot_t *cur; + struct grub_preboot *cur; if (! grub_loader_loaded) - return grub_error (GRUB_ERR_NO_KERNEL, "no loaded kernel"); + return grub_error (GRUB_ERR_NO_KERNEL, + N_("you need to load the kernel first")); - if (grub_loader_noreturn) - grub_machine_fini (); + grub_machine_fini (grub_loader_flags); for (cur = preboots_head; cur; cur = cur->next) { - err = cur->preboot_func (grub_loader_noreturn); + err = cur->preboot_func (grub_loader_flags); if (err) { for (cur = cur->prev; cur; cur = cur->prev) @@ -159,7 +208,7 @@ grub_loader_boot (void) return err; } } - err = (grub_loader_boot_func) (); + err = (grub_loader_boot_func) (grub_loader_context); for (cur = preboots_tail; cur; cur = cur->prev) if (! err) diff --git a/grub-core/commands/boottime.c b/grub-core/commands/boottime.c new file mode 100644 index 000000000..424412851 --- /dev/null +++ b/grub-core/commands/boottime.c @@ -0,0 +1,65 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +static grub_err_t +grub_cmd_boottime (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + struct grub_boot_time *cur; + grub_uint64_t last_time = 0, start_time = 0; + if (!grub_boot_time_head) + { + grub_puts_ (N_("No boot time statistics is available\n")); + return 0; + } + start_time = last_time = grub_boot_time_head->tp; + for (cur = grub_boot_time_head; cur; cur = cur->next) + { + grub_uint32_t tmabs = cur->tp - start_time; + grub_uint32_t tmrel = cur->tp - last_time; + last_time = cur->tp; + + grub_printf ("%3d.%03ds %2d.%03ds %s:%d %s\n", + tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000, cur->file, cur->line, + cur->msg); + } + return 0; +} + +static grub_command_t cmd_boottime; + +GRUB_MOD_INIT(boottime) +{ + cmd_boottime = + grub_register_command ("boottime", grub_cmd_boottime, + 0, N_("Show boot time statistics.")); +} + +GRUB_MOD_FINI(boottime) +{ + grub_unregister_command (cmd_boottime); +} diff --git a/grub-core/commands/cacheinfo.c b/grub-core/commands/cacheinfo.c new file mode 100644 index 000000000..e8ee05b85 --- /dev/null +++ b/grub-core/commands/cacheinfo.c @@ -0,0 +1,62 @@ +/* cacheinfo.c - disk cache statistics */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_rescue_cmd_info (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + unsigned long hits, misses; + + grub_disk_cache_get_performance (&hits, &misses); + if (hits + misses) + { + unsigned long ratio = hits * 10000 / (hits + misses); + grub_printf ("(%lu.%lu%%)\n", ratio / 100, ratio % 100); + grub_printf_ (N_("Disk cache statistics: hits = %lu (%lu.%02lu%%)," + " misses = %lu\n"), ratio / 100, ratio % 100, + hits, misses); + } + else + grub_printf ("%s\n", _("No disk cache statistics available\n")); + + return 0; +} + +static grub_command_t cmd_cacheinfo; + +GRUB_MOD_INIT(cacheinfo) +{ + cmd_cacheinfo = + grub_register_command ("cacheinfo", grub_rescue_cmd_info, + 0, N_("Get disk cache info.")); +} + +GRUB_MOD_FINI(cacheinfo) +{ + grub_unregister_command (cmd_cacheinfo); +} diff --git a/grub-core/commands/cat.c b/grub-core/commands/cat.c new file mode 100644 index 000000000..2b67c1c7f --- /dev/null +++ b/grub-core/commands/cat.c @@ -0,0 +1,170 @@ +/* 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +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_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + int dos = 0; + grub_file_t file; + unsigned char buf[GRUB_DISK_SECTOR_SIZE]; + grub_ssize_t size; + int key = GRUB_TERM_NO_KEY; + grub_uint32_t code = 0; + int count = 0; + unsigned char utbuf[GRUB_MAX_UTF8_PER_CODEPOINT + 1]; + int utcount = 0; + int is_0d = 0; + int j; + + if (state[0].set) + dos = 1; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (args[0], GRUB_FILE_TYPE_CAT); + 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++) + { + utbuf[utcount++] = buf[i]; + + if (is_0d && buf[i] != '\n') + { + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + grub_printf ("<%x>", (int) '\r'); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + } + + is_0d = 0; + + if (!grub_utf8_process (buf[i], &code, &count)) + { + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + for (j = 0; j < utcount - 1; j++) + grub_printf ("<%x>", (unsigned int) utbuf[j]); + code = 0; + count = 0; + if (utcount == 1 || !grub_utf8_process (buf[i], &code, &count)) + { + grub_printf ("<%x>", (unsigned int) buf[i]); + code = 0; + count = 0; + utcount = 0; + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + continue; + } + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + utcount = 1; + } + if (count) + continue; + + if ((code >= 0xa1 || grub_isprint (code) + || grub_isspace (code)) && code != '\r') + { + grub_printf ("%C", code); + count = 0; + code = 0; + utcount = 0; + continue; + } + + if (dos && code == '\r') + { + is_0d = 1; + count = 0; + code = 0; + utcount = 0; + continue; + } + + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + for (j = 0; j < utcount; j++) + grub_printf ("<%x>", (unsigned int) utbuf[j]); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + count = 0; + code = 0; + utcount = 0; + } + + do + key = grub_getkey_noblock (); + while (key != GRUB_TERM_ESC && key != GRUB_TERM_NO_KEY); + } + + if (is_0d) + { + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + grub_printf ("<%x>", (unsigned int) '\r'); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + } + + if (utcount) + { + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + for (j = 0; j < utcount; j++) + grub_printf ("<%x>", (unsigned int) utbuf[j]); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + } + + grub_xputs ("\n"); + grub_refresh (); + grub_file_close (file); + + return 0; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(cat) +{ + cmd = grub_register_extcmd ("cat", grub_cmd_cat, 0, + N_("FILE"), N_("Show the contents of a file."), + options); +} + +GRUB_MOD_FINI(cat) +{ + grub_unregister_extcmd (cmd); +} diff --git a/commands/cmp.c b/grub-core/commands/cmp.c similarity index 56% rename from commands/cmp.c rename to grub-core/commands/cmp.c index 626e1d022..46185fbbc 100644 --- a/commands/cmp.c +++ b/grub-core/commands/cmp.c @@ -21,14 +21,22 @@ #include #include #include -#include #include +#include #include +GRUB_MOD_LICENSE ("GPLv3+"); + #define BUFFER_SIZE 512 +static const struct grub_arg_option options[] = + { + {0, 'v', 0, N_("Enable verbose output"), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + static grub_err_t -grub_cmd_cmp (grub_command_t cmd __attribute__ ((unused)), +grub_cmd_cmp (grub_extcmd_context_t ctxt, int argc, char **args) { grub_ssize_t rd1, rd2; @@ -37,22 +45,23 @@ grub_cmd_cmp (grub_command_t cmd __attribute__ ((unused)), grub_file_t file2 = 0; char *buf1 = 0; char *buf2 = 0; + grub_err_t err = GRUB_ERR_TEST_FAILURE; if (argc != 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); - grub_printf ("Compare file `%s' with `%s':\n", args[0], - args[1]); + if (ctxt->state[0].set) + grub_printf_ (N_("Compare file `%s' with `%s':\n"), args[0], args[1]); - file1 = grub_gzfile_open (args[0], 1); - file2 = grub_gzfile_open (args[1], 1); + file1 = grub_file_open (args[0], GRUB_FILE_TYPE_CMP); + file2 = grub_file_open (args[1], GRUB_FILE_TYPE_CMP); if (! file1 || ! file2) goto cleanup; - if (grub_file_size (file1) != grub_file_size (file2)) - grub_printf ("Files differ in size: %llu [%s], %llu [%s]\n", - (unsigned long long) grub_file_size (file1), args[0], - (unsigned long long) grub_file_size (file2), args[1]); + if (ctxt->state[0].set && (grub_file_size (file1) != grub_file_size (file2))) + grub_printf_ (N_("Files differ in size: %llu [%s], %llu [%s]\n"), + (unsigned long long) grub_file_size (file1), args[0], + (unsigned long long) grub_file_size (file2), args[1]); else { pos = 0; @@ -77,9 +86,10 @@ grub_cmd_cmp (grub_command_t cmd __attribute__ ((unused)), { if (buf1[i] != buf2[i]) { - grub_printf ("Files differ at the offset %llu: 0x%x [%s], 0x%x [%s]\n", - (unsigned long long) (i + pos), buf1[i], args[0], - buf2[i], args[1]); + if (ctxt->state[0].set) + grub_printf_ (N_("Files differ at the offset %llu: 0x%x [%s], 0x%x [%s]\n"), + (unsigned long long) (i + pos), buf1[i], + args[0], buf2[i], args[1]); goto cleanup; } } @@ -88,32 +98,34 @@ grub_cmd_cmp (grub_command_t cmd __attribute__ ((unused)), } while (rd2); - grub_printf ("The files are identical.\n"); + /* TRANSLATORS: it's always exactly 2 files. */ + if (ctxt->state[0].set) + grub_printf_ (N_("The files are identical.\n")); + err = GRUB_ERR_NONE; } cleanup: - if (buf1) - grub_free (buf1); - if (buf2) - grub_free (buf2); + grub_free (buf1); + grub_free (buf2); if (file1) grub_file_close (file1); if (file2) grub_file_close (file2); - return grub_errno; + return err; } -static grub_command_t cmd; +static grub_extcmd_t cmd; GRUB_MOD_INIT(cmp) { - cmd = grub_register_command ("cmp", grub_cmd_cmp, - N_("FILE1 FILE2"), N_("Compare two files.")); + cmd = grub_register_extcmd ("cmp", grub_cmd_cmp, 0, + N_("FILE1 FILE2"), N_("Compare two files."), + options); } GRUB_MOD_FINI(cmp) { - grub_unregister_command (cmd); + grub_unregister_extcmd (cmd); } diff --git a/commands/configfile.c b/grub-core/commands/configfile.c similarity index 63% rename from commands/configfile.c rename to grub-core/commands/configfile.c index 469447711..f2d2abb5f 100644 --- a/commands/configfile.c +++ b/grub-core/commands/configfile.c @@ -24,31 +24,39 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_err_t grub_cmd_source (grub_command_t cmd, int argc, char **args) { - int new_env; + int new_env, extractor; if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); - new_env = (cmd->name[0] == 'c'); + extractor = (cmd->name[0] == 'e'); + new_env = (cmd->name[extractor ? sizeof ("extract_entries_") - 1 : 0] == 'c'); if (new_env) - { - grub_cls (); - grub_env_context_open (1); - } + grub_cls (); + + if (new_env && !extractor) + grub_env_context_open (); + if (extractor) + grub_env_extractor_open (!new_env); grub_normal_execute (args[0], 1, ! new_env); - if (new_env) + if (new_env && !extractor) grub_env_context_close (); + if (extractor) + grub_env_extractor_close (!new_env); return 0; } static grub_command_t cmd_configfile, cmd_source, cmd_dot; +static grub_command_t cmd_extractor_source, cmd_extractor_configfile; GRUB_MOD_INIT(configfile) { @@ -60,6 +68,19 @@ GRUB_MOD_INIT(configfile) N_("FILE"), N_("Load another config file without changing context.") ); + + cmd_extractor_source = + grub_register_command ("extract_entries_source", grub_cmd_source, + N_("FILE"), + N_("Load another config file without changing context but take only menu entries.") + ); + + cmd_extractor_configfile = + grub_register_command ("extract_entries_configfile", grub_cmd_source, + N_("FILE"), + N_("Load another config file but take only menu entries.") + ); + cmd_dot = grub_register_command (".", grub_cmd_source, N_("FILE"), @@ -71,5 +92,7 @@ GRUB_MOD_FINI(configfile) { grub_unregister_command (cmd_configfile); grub_unregister_command (cmd_source); + grub_unregister_command (cmd_extractor_configfile); + grub_unregister_command (cmd_extractor_source); grub_unregister_command (cmd_dot); } diff --git a/commands/date.c b/grub-core/commands/date.c similarity index 97% rename from commands/date.c rename to grub-core/commands/date.c index 623db4943..5cb4fafd4 100644 --- a/commands/date.c +++ b/grub-core/commands/date.c @@ -24,6 +24,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + #define GRUB_DATETIME_SET_YEAR 1 #define GRUB_DATETIME_SET_MONTH 2 #define GRUB_DATETIME_SET_DAY 4 @@ -57,7 +59,8 @@ grub_cmd_date (grub_command_t cmd __attribute__ ((unused)), for (; argc; argc--, args++) { - char *p, c; + const char *p; + char c; int m1, ofs, n, cur_mask; p = args[0]; @@ -137,7 +140,7 @@ GRUB_MOD_INIT(date) cmd = grub_register_command ("date", grub_cmd_date, N_("[[year-]month-day] [hour:minute[:second]]"), - N_("Command to display/set current datetime.")); + N_("Display/set current datetime.")); } GRUB_MOD_FINI(date) diff --git a/commands/echo.c b/grub-core/commands/echo.c similarity index 72% rename from commands/echo.c rename to grub-core/commands/echo.c index 4fea091cb..f95877285 100644 --- a/commands/echo.c +++ b/grub-core/commands/echo.c @@ -1,7 +1,7 @@ /* echo.c - Command to display a line of text */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,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 @@ -21,6 +21,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = { @@ -30,9 +33,9 @@ static const struct grub_arg_option options[] = }; static grub_err_t -grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) +grub_cmd_echo (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; int newline = 1; int i; @@ -43,8 +46,14 @@ grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) for (i = 0; i < argc; i++) { char *arg = *args; + /* Unescaping results in a string no longer than the original. */ + char *unescaped = grub_malloc (grub_strlen (arg) + 1); + char *p = unescaped; args++; + if (!unescaped) + return grub_errno; + while (*arg) { /* In case `-e' is used, parse backslashes. */ @@ -57,11 +66,11 @@ grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) switch (*arg) { case '\\': - grub_printf ("\\"); + *p++ = '\\'; break; case 'a': - grub_printf ("\a"); + *p++ = '\a'; break; case 'c': @@ -69,23 +78,23 @@ grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) break; case 'f': - grub_printf ("\f"); + *p++ = '\f'; break; case 'n': - grub_printf ("\n"); + *p++ = '\n'; break; case 'r': - grub_printf ("\r"); + *p++ = '\r'; break; case 't': - grub_printf ("\t"); + *p++ = '\t'; break; case 'v': - grub_printf ("\v"); + *p++ = '\v'; break; } arg++; @@ -94,10 +103,14 @@ grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) /* This was not an escaped character, or escaping is not enabled. */ - grub_printf ("%c", *arg); + *p++ = *arg; arg++; } + *p = '\0'; + grub_xputs (unescaped); + grub_free (unescaped); + /* If another argument follows, insert a space. */ if (i != argc - 1) grub_printf (" " ); @@ -106,6 +119,8 @@ grub_cmd_echo (grub_extcmd_t cmd, int argc, char **args) if (newline) grub_printf ("\n"); + grub_refresh (); + return 0; } @@ -113,7 +128,9 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(echo) { - cmd = grub_register_extcmd ("echo", grub_cmd_echo, GRUB_COMMAND_FLAG_BOTH, + cmd = grub_register_extcmd ("echo", grub_cmd_echo, + GRUB_COMMAND_ACCEPT_DASH + | GRUB_COMMAND_OPTIONS_AT_START, N_("[-e|-n] STRING"), N_("Display a line of text."), options); } diff --git a/grub-core/commands/efi/efifwsetup.c b/grub-core/commands/efi/efifwsetup.c new file mode 100644 index 000000000..704f9d352 --- /dev/null +++ b/grub-core/commands/efi/efifwsetup.c @@ -0,0 +1,102 @@ +/* fwsetup.c - Reboot into firmware setup menu. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_efi_boolean_t efifwsetup_is_supported (void); + +static grub_err_t +grub_cmd_fwsetup (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_efi_uint64_t *old_os_indications; + grub_efi_uint64_t os_indications = GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + grub_err_t status; + grub_size_t oi_size; + static grub_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + + if (argc >= 1 && grub_strcmp(args[0], "--is-supported") == 0) + return !efifwsetup_is_supported (); + + if (!efifwsetup_is_supported ()) + return grub_error (GRUB_ERR_INVALID_COMMAND, + N_("reboot to firmware setup is not supported by the current firmware")); + + grub_efi_get_variable ("OsIndications", &global, &oi_size, + (void **) &old_os_indications); + + if (old_os_indications != NULL && oi_size == sizeof (os_indications)) + os_indications |= *old_os_indications; + + grub_free (old_os_indications); + + status = grub_efi_set_variable ("OsIndications", &global, &os_indications, + sizeof (os_indications)); + if (status != GRUB_ERR_NONE) + return status; + + grub_reboot (); + + return GRUB_ERR_BUG; +} + +static grub_command_t cmd = NULL; + +static grub_efi_boolean_t +efifwsetup_is_supported (void) +{ + grub_efi_uint64_t *os_indications_supported = NULL; + grub_size_t oi_size = 0; + static grub_guid_t global = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_efi_boolean_t ret = 0; + + grub_efi_get_variable ("OsIndicationsSupported", &global, &oi_size, + (void **) &os_indications_supported); + + if (!os_indications_supported) + goto done; + + if (*os_indications_supported & GRUB_EFI_OS_INDICATIONS_BOOT_TO_FW_UI) + ret = 1; + + done: + grub_free (os_indications_supported); + return ret; +} + +GRUB_MOD_INIT (efifwsetup) +{ + cmd = grub_register_command ("fwsetup", grub_cmd_fwsetup, NULL, + N_("Reboot into firmware setup menu.")); +} + +GRUB_MOD_FINI (efifwsetup) +{ + if (cmd) + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/efitextmode.c b/grub-core/commands/efi/efitextmode.c new file mode 100644 index 000000000..198bc694d --- /dev/null +++ b/grub-core/commands/efi/efitextmode.c @@ -0,0 +1,153 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 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 . + * + * Set/Get UEFI text output mode resolution. + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_efi_set_mode (grub_efi_simple_text_output_interface_t *o, + grub_efi_int32_t mode) +{ + grub_efi_status_t status; + + if (mode != o->mode->mode) + { + status = o->set_mode (o, mode); + if (status == GRUB_EFI_SUCCESS) + ; + else if (status == GRUB_EFI_DEVICE_ERROR) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("device error: could not set requested mode")); + else if (status == GRUB_EFI_UNSUPPORTED) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("invalid mode: number not valid")); + else + return grub_error (GRUB_ERR_BAD_FIRMWARE, + N_("unexpected EFI error number: `%u'"), + (unsigned) status); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_efitextmode (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_efi_simple_text_output_interface_t *o = grub_efi_system_table->con_out; + unsigned long mode; + const char *p = NULL; + grub_err_t err; + grub_efi_uintn_t columns, rows; + grub_efi_int32_t i; + + if (o == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("no UEFI output console interface")); + + if (o->mode == NULL) + return grub_error (GRUB_ERR_BUG, N_("no mode struct for UEFI output console")); + + if (argc > 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("at most two arguments expected")); + + if (argc == 0) + { + grub_printf_ (N_("Available modes for console output device.\n")); + + for (i = 0; i < o->mode->max_mode; i++) + if (GRUB_EFI_SUCCESS == o->query_mode (o, i, &columns, &rows)) + grub_printf_ (N_(" [%" PRIuGRUB_EFI_UINT32_T "] Col %5" + PRIuGRUB_EFI_UINTN_T " Row %5" PRIuGRUB_EFI_UINTN_T + " %c\n"), + i, columns, rows, (i == o->mode->mode) ? '*' : ' '); + } + else if (argc == 1) + { + if (grub_strcmp (args[0], "min") == 0) + mode = 0; + else if (grub_strcmp (args[0], "max") == 0) + mode = o->mode->max_mode - 1; + else + { + mode = grub_strtoul (args[0], &p, 0); + + if (*args[0] == '\0' || *p != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("non-numeric or invalid mode `%s'"), args[0]); + } + + if (mode < (unsigned long) o->mode->max_mode) + { + err = grub_efi_set_mode (o, (grub_efi_int32_t) mode); + if (err != GRUB_ERR_NONE) + return err; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid mode: `%lu' is greater than maximum mode `%lu'"), + mode, (unsigned long) o->mode->max_mode); + } + else if (argc == 2) + { + grub_efi_uintn_t u_columns, u_rows; + + u_columns = (grub_efi_uintn_t) grub_strtoul (args[0], &p, 0); + + if (*args[0] == '\0' || *p != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("non-numeric or invalid columns number `%s'"), args[0]); + + u_rows = (grub_efi_uintn_t) grub_strtoul (args[1], &p, 0); + + if (*args[1] == '\0' || *p != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("non-numeric or invalid rows number `%s'"), args[1]); + + for (i = 0; i < o->mode->max_mode; i++) + if (GRUB_EFI_SUCCESS == o->query_mode (o, i, &columns, &rows)) + if (u_columns == columns && u_rows == rows) + return grub_efi_set_mode (o, (grub_efi_int32_t) i); + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("no mode found with requested columns and rows")); + } + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; +GRUB_MOD_INIT (efitextmode) +{ + cmd = grub_register_command ("efitextmode", grub_cmd_efitextmode, + N_("[min | max | | ]"), + N_("Get or set EFI text mode.")); +} + +GRUB_MOD_FINI (efitextmode) +{ + grub_unregister_command (cmd); +} diff --git a/commands/efi/fixvideo.c b/grub-core/commands/efi/fixvideo.c similarity index 81% rename from commands/efi/fixvideo.c rename to grub-core/commands/efi/fixvideo.c index 6430be5e3..d9d54a2e8 100644 --- a/commands/efi/fixvideo.c +++ b/grub-core/commands/efi/fixvideo.c @@ -23,6 +23,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static struct grub_video_patch { @@ -38,8 +41,9 @@ static struct grub_video_patch {0, 0, 0, 0, 0} }; -static int NESTED_FUNC_ATTR -scan_card (grub_pci_device_t dev, grub_pci_id_t pciid) +static int +scan_card (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) { grub_pci_address_t addr; @@ -52,26 +56,26 @@ scan_card (grub_pci_device_t dev, grub_pci_id_t pciid) { if (p->pci_id == pciid) { - grub_target_addr_t base; + grub_addr_t base; - grub_printf ("Found graphic card: %s\n", p->name); + grub_dprintf ("fixvideo", "Found graphic card: %s\n", p->name); addr += 8 + p->mmio_bar * 4; base = grub_pci_read (addr); if ((! base) || (base & GRUB_PCI_ADDR_SPACE_IO) || (base & GRUB_PCI_ADDR_MEM_PREFETCH)) - grub_printf ("Invalid MMIO bar %d\n", p->mmio_bar); + grub_dprintf ("fixvideo", "Invalid MMIO bar %d\n", p->mmio_bar); else { base &= GRUB_PCI_ADDR_MEM_MASK; base += p->mmio_reg; if (*((volatile grub_uint32_t *) base) != p->mmio_old) - grub_printf ("Old value don't match\n"); + grub_dprintf ("fixvideo", "Old value doesn't match\n"); else { *((volatile grub_uint32_t *) base) = 0; if (*((volatile grub_uint32_t *) base)) - grub_printf ("Set MMIO fails\n"); + grub_dprintf ("fixvideo", "Setting MMIO fails\n"); } } @@ -80,7 +84,7 @@ scan_card (grub_pci_device_t dev, grub_pci_id_t pciid) p++; } - grub_printf ("Unknown graphic card: %x\n", pciid); + grub_dprintf ("fixvideo", "Unknown graphic card: %x\n", pciid); } return 0; @@ -91,7 +95,7 @@ grub_cmd_fixvideo (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { - grub_pci_iterate (scan_card); + grub_pci_iterate (scan_card, NULL); return 0; } diff --git a/commands/efi/loadbios.c b/grub-core/commands/efi/loadbios.c similarity index 69% rename from commands/efi/loadbios.c rename to grub-core/commands/efi/loadbios.c index 8c7c25abd..8e042095a 100644 --- a/commands/efi/loadbios.c +++ b/grub-core/commands/efi/loadbios.c @@ -25,9 +25,11 @@ #include #include -static grub_efi_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; -static grub_efi_guid_t acpi2_guid = GRUB_EFI_ACPI_20_TABLE_GUID; -static grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; +static grub_guid_t acpi2_guid = GRUB_EFI_ACPI_20_TABLE_GUID; +static grub_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; #define EBDA_SEG_ADDR 0x40e #define LOW_MEM_ADDR 0x413 @@ -44,10 +46,10 @@ enable_rom_area (void) grub_uint32_t *rom_ptr; grub_pci_device_t dev = { .bus = 0, .device = 0, .function = 0}; - rom_ptr = (grub_uint32_t *) VBIOS_ADDR; + rom_ptr = grub_absolute_pointer (VBIOS_ADDR); if (*rom_ptr != BLANK_MEM) { - grub_printf ("ROM image is present.\n"); + grub_puts_ (N_("ROM image is present.")); return 0; } @@ -65,7 +67,7 @@ enable_rom_area (void) *rom_ptr = 0; if (*rom_ptr != 0) { - grub_printf ("Can\'t enable ROM area.\n"); + grub_puts_ (N_("Can\'t enable ROM area.")); return 0; } @@ -90,47 +92,29 @@ lock_rom_area (void) static void fake_bios_data (int use_rom) { - unsigned i; void *acpi, *smbios; grub_uint16_t *ebda_seg_ptr, *low_mem_ptr; - ebda_seg_ptr = (grub_uint16_t *) EBDA_SEG_ADDR; - low_mem_ptr = (grub_uint16_t *) LOW_MEM_ADDR; + ebda_seg_ptr = grub_absolute_pointer (EBDA_SEG_ADDR); + low_mem_ptr = grub_absolute_pointer (LOW_MEM_ADDR); if ((*ebda_seg_ptr) || (*low_mem_ptr)) return; - acpi = 0; - smbios = 0; - for (i = 0; i < grub_efi_system_table->num_table_entries; i++) - { - grub_efi_guid_t *guid = - &grub_efi_system_table->configuration_table[i].vendor_guid; + acpi = grub_efi_find_configuration_table (&acpi2_guid); + grub_dprintf ("efi", "ACPI2: %p\n", acpi); + if (!acpi) { + acpi = grub_efi_find_configuration_table (&acpi_guid); + grub_dprintf ("efi", "ACPI: %p\n", acpi); + } - if (! grub_memcmp (guid, &acpi2_guid, sizeof (grub_efi_guid_t))) - { - acpi = grub_efi_system_table->configuration_table[i].vendor_table; - grub_dprintf ("efi", "ACPI2: %p\n", acpi); - } - else if (! grub_memcmp (guid, &acpi_guid, sizeof (grub_efi_guid_t))) - { - void *t; - - t = grub_efi_system_table->configuration_table[i].vendor_table; - if (! acpi) - acpi = t; - grub_dprintf ("efi", "ACPI: %p\n", t); - } - else if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_efi_guid_t))) - { - smbios = grub_efi_system_table->configuration_table[i].vendor_table; - grub_dprintf ("efi", "SMBIOS: %p\n", smbios); - } - } + smbios = grub_efi_find_configuration_table (&smbios_guid); + grub_dprintf ("efi", "SMBIOS: %p\n", smbios); *ebda_seg_ptr = FAKE_EBDA_SEG; *low_mem_ptr = (FAKE_EBDA_SEG >> 6); - *((grub_uint16_t *) (FAKE_EBDA_SEG << 4)) = 640 - *low_mem_ptr; + /* *((grub_uint16_t *) (FAKE_EBDA_SEG << 4)) = 640 - *low_mem_ptr; */ + *((grub_uint16_t *) (grub_absolute_pointer (FAKE_EBDA_SEG << 4))) = 640 - *low_mem_ptr; if (acpi) grub_memcpy ((char *) ((FAKE_EBDA_SEG << 4) + 16), acpi, 1024 - 16); @@ -163,11 +147,11 @@ grub_cmd_loadbios (grub_command_t cmd __attribute__ ((unused)), int size; if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no ROM image specified"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); if (argc > 1) { - file = grub_file_open (argv[1]); + file = grub_file_open (argv[1], GRUB_FILE_TYPE_VBE_DUMP); if (! file) return grub_errno; @@ -181,7 +165,7 @@ grub_cmd_loadbios (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } - file = grub_file_open (argv[0]); + file = grub_file_open (argv[0], GRUB_FILE_TYPE_VBE_DUMP); if (! file) return grub_errno; @@ -203,12 +187,14 @@ static grub_command_t cmd_fakebios, cmd_loadbios; GRUB_MOD_INIT(loadbios) { - cmd_fakebios = grub_register_command ("fakebios", grub_cmd_fakebios, - 0, N_("Fake BIOS.")); + cmd_fakebios = grub_register_command_lockdown ("fakebios", grub_cmd_fakebios, + 0, N_("Create BIOS-like structures for" + " backward compatibility with" + " existing OS.")); - cmd_loadbios = grub_register_command ("loadbios", grub_cmd_loadbios, - "BIOS_DUMP [INT10_DUMP]", - N_("Load BIOS dump.")); + cmd_loadbios = grub_register_command_lockdown ("loadbios", grub_cmd_loadbios, + N_("BIOS_DUMP [INT10_DUMP]"), + N_("Load BIOS dump.")); } GRUB_MOD_FINI(loadbios) diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c new file mode 100644 index 000000000..7b8316d41 --- /dev/null +++ b/grub-core/commands/efi/lsefi.c @@ -0,0 +1,146 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static struct known_protocol +{ + grub_guid_t guid; + const char *name; +} known_protocols[] = + { + { GRUB_EFI_DISK_IO_GUID, "disk" }, + { GRUB_EFI_BLOCK_IO_GUID, "block" }, + { GRUB_EFI_SERIAL_IO_GUID, "serial" }, + { GRUB_EFI_SIMPLE_NETWORK_GUID, "network" }, + { GRUB_EFI_PXE_GUID, "pxe" }, + { GRUB_EFI_DEVICE_PATH_GUID, "device path" }, + { GRUB_EFI_PCI_IO_GUID, "PCI" }, + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root" }, + { GRUB_EFI_EDID_ACTIVE_GUID, "active EDID" }, + { GRUB_EFI_EDID_DISCOVERED_GUID, "discovered EDID" }, + { GRUB_EFI_EDID_OVERRIDE_GUID, "override EDID" }, + { GRUB_EFI_GOP_GUID, "GOP" }, + { GRUB_EFI_UGA_DRAW_GUID, "UGA draw" }, + { GRUB_EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID, "simple text output" }, + { GRUB_EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID, "simple text input" }, + { GRUB_EFI_SIMPLE_POINTER_PROTOCOL_GUID, "simple pointer" }, + { GRUB_EFI_CONSOLE_CONTROL_GUID, "console control" }, + { GRUB_EFI_ABSOLUTE_POINTER_PROTOCOL_GUID, "absolute pointer" }, + { GRUB_EFI_DRIVER_BINDING_PROTOCOL_GUID, "EFI driver binding" }, + { GRUB_EFI_LOAD_FILE_PROTOCOL_GUID, "load file" }, + { GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID, "load file2" }, + { GRUB_EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, "simple FS" }, + { GRUB_EFI_TAPE_IO_PROTOCOL_GUID, "tape I/O" }, + { GRUB_EFI_UNICODE_COLLATION_PROTOCOL_GUID, "unicode collation" }, + { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O" }, + { GRUB_EFI_USB2_HC_PROTOCOL_GUID, "USB host" }, + { GRUB_EFI_DEBUG_SUPPORT_PROTOCOL_GUID, "debug support" }, + { GRUB_EFI_DEBUGPORT_PROTOCOL_GUID, "debug port" }, + { GRUB_EFI_DECOMPRESS_PROTOCOL_GUID, "decompress" }, + { GRUB_EFI_LOADED_IMAGE_PROTOCOL_GUID, "loaded image" }, + { GRUB_EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID, "device path to text" }, + { GRUB_EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID, "device path utilities" }, + { GRUB_EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID, "device path from text" }, + { GRUB_EFI_HII_CONFIG_ROUTING_PROTOCOL_GUID, "HII config routing" }, + { GRUB_EFI_HII_DATABASE_PROTOCOL_GUID, "HII database" }, + { GRUB_EFI_HII_STRING_PROTOCOL_GUID, "HII string" }, + { GRUB_EFI_HII_IMAGE_PROTOCOL_GUID, "HII image" }, + { GRUB_EFI_HII_FONT_PROTOCOL_GUID, "HII font" }, + { GRUB_EFI_COMPONENT_NAME2_PROTOCOL_GUID, "component name 2" }, + { GRUB_EFI_HII_CONFIGURATION_ACCESS_PROTOCOL_GUID, + "HII configuration access" }, + { GRUB_EFI_USB_IO_PROTOCOL_GUID, "USB I/O" }, + }; + +static grub_err_t +grub_cmd_lsefi (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + unsigned i, j, k; + + handles = grub_efi_locate_handle (GRUB_EFI_ALL_HANDLES, + NULL, NULL, &num_handles); + + for (i = 0; i < num_handles; i++) + { + grub_efi_handle_t handle = handles[i]; + grub_efi_status_t status; + grub_efi_uintn_t num_protocols; + grub_packed_guid_t **protocols; + grub_efi_device_path_t *dp; + + grub_printf ("Handle %p\n", handle); + + dp = grub_efi_get_device_path (handle); + if (dp) + { + grub_printf (" "); + grub_efi_print_device_path (dp); + } + + status = grub_efi_system_table->boot_services->protocols_per_handle (handle, + &protocols, + &num_protocols); + if (status != GRUB_EFI_SUCCESS) { + grub_printf ("Unable to retrieve protocols\n"); + continue; + } + for (j = 0; j < num_protocols; j++) + { + for (k = 0; k < ARRAY_SIZE (known_protocols); k++) + if (grub_memcmp (protocols[j], &known_protocols[k].guid, + sizeof (known_protocols[k].guid)) == 0) + break; + if (k < ARRAY_SIZE (known_protocols)) + grub_printf (" %s\n", known_protocols[k].name); + else + grub_printf (" %pG\n", protocols[j]); + } + + } + + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsefi) +{ + cmd = grub_register_command ("lsefi", grub_cmd_lsefi, + NULL, "Display EFI handles."); +} + +GRUB_MOD_FINI(lsefi) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/lsefimmap.c b/grub-core/commands/efi/lsefimmap.c new file mode 100644 index 000000000..d0885d72f --- /dev/null +++ b/grub-core/commands/efi/lsefimmap.c @@ -0,0 +1,160 @@ +/* lsefimemmap.c - Display memory map. */ +/* + * 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 . + */ +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define ADD_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) + +static grub_err_t +grub_cmd_lsefimmap (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_efi_uintn_t map_size; + grub_efi_memory_descriptor_t *memory_map; + grub_efi_memory_descriptor_t *memory_map_end; + grub_efi_memory_descriptor_t *desc; + grub_efi_uintn_t desc_size; + + map_size = 0; + if (grub_efi_get_memory_map (&map_size, NULL, NULL, &desc_size, 0) < 0) + return 0; + + memory_map = grub_malloc (map_size); + if (memory_map == NULL) + return grub_errno; + if (grub_efi_get_memory_map (&map_size, memory_map, NULL, &desc_size, 0) <= 0) + goto fail; + + grub_printf + ("Type Physical start - end #Pages " + " Size Attributes\n"); + memory_map_end = ADD_MEMORY_DESCRIPTOR (memory_map, map_size); + for (desc = memory_map; + desc < memory_map_end; + desc = ADD_MEMORY_DESCRIPTOR (desc, desc_size)) + { + grub_efi_uint64_t size; + grub_efi_uint64_t attr; + static const char types_str[][9] = + { + "reserved", + "ldr-code", + "ldr-data", + "BS-code ", + "BS-data ", + "RT-code ", + "RT-data ", + "conv-mem", + "unusable", + "ACPI-rec", + "ACPI-nvs", + "MMIO ", + "IO-ports", + "PAL-code", + "persist ", + }; + if (desc->type < ARRAY_SIZE (types_str)) + grub_printf ("%s ", types_str[desc->type]); + else + grub_printf ("Unk %02x ", desc->type); + + grub_printf (" %016" PRIxGRUB_UINT64_T "-%016" PRIxGRUB_UINT64_T + " %08" PRIxGRUB_UINT64_T, + desc->physical_start, + desc->physical_start + (desc->num_pages << 12) - 1, + desc->num_pages); + + size = desc->num_pages << 12; /* 4 KiB page size */ + /* + * Since size is a multiple of 4 KiB, no need to handle units + * of just Bytes (which would use a mask of 0x3ff). + * + * 14 characters would support the largest possible number of 4 KiB + * pages that are not a multiple of larger units (e.g., MiB): + * 17592186044415 (0xffffff_fffff000), but that uses a lot of + * whitespace for a rare case. 6 characters usually suffices; + * columns will be off if not, but this is preferable to rounding. + */ + if (size & 0xfffff) + grub_printf (" %6" PRIuGRUB_UINT64_T "KiB", size >> 10); + else if (size & 0x3fffffff) + grub_printf (" %6" PRIuGRUB_UINT64_T "MiB", size >> 20); + else if (size & 0xffffffffff) + grub_printf (" %6" PRIuGRUB_UINT64_T "GiB", size >> 30); + else if (size & 0x3ffffffffffff) + grub_printf (" %6" PRIuGRUB_UINT64_T "TiB", size >> 40); + else if (size & 0xfffffffffffffff) + grub_printf (" %6" PRIuGRUB_UINT64_T "PiB", size >> 50); + else + grub_printf (" %6" PRIuGRUB_UINT64_T "EiB", size >> 60); + + attr = desc->attribute; + if (attr & GRUB_EFI_MEMORY_RUNTIME) + grub_printf (" RT"); + if (attr & GRUB_EFI_MEMORY_UC) + grub_printf (" UC"); + if (attr & GRUB_EFI_MEMORY_WC) + grub_printf (" WC"); + if (attr & GRUB_EFI_MEMORY_WT) + grub_printf (" WT"); + if (attr & GRUB_EFI_MEMORY_WB) + grub_printf (" WB"); + if (attr & GRUB_EFI_MEMORY_UCE) + grub_printf (" UCE"); + if (attr & GRUB_EFI_MEMORY_WP) + grub_printf (" WP"); + if (attr & GRUB_EFI_MEMORY_RP) + grub_printf (" RP"); + if (attr & GRUB_EFI_MEMORY_XP) + grub_printf (" XP"); + if (attr & GRUB_EFI_MEMORY_NV) + grub_printf (" NV"); + if (attr & GRUB_EFI_MEMORY_MORE_RELIABLE) + grub_printf (" MR"); + if (attr & GRUB_EFI_MEMORY_RO) + grub_printf (" RO"); + + grub_printf ("\n"); + } + + fail: + grub_free (memory_map); + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsefimmap) +{ + cmd = grub_register_command ("lsefimmap", grub_cmd_lsefimmap, + "", "Display EFI memory map."); +} + +GRUB_MOD_FINI(lsefimmap) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c new file mode 100644 index 000000000..ffb24fc3b --- /dev/null +++ b/grub-core/commands/efi/lsefisystab.c @@ -0,0 +1,129 @@ +/* lsefisystab.c - Display EFI systab. */ +/* + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct guid_mapping +{ + grub_guid_t guid; + const char *name; +}; + +static const struct guid_mapping guid_mappings[] = + { + { GRUB_EFI_ACPI_20_TABLE_GUID, "ACPI-2.0"}, + { GRUB_EFI_ACPI_TABLE_GUID, "ACPI-1.0"}, + { GRUB_EFI_CONFORMANCE_PROFILES_TABLE_GUID, "CONFORMANCE PROFILES"}, + { GRUB_EFI_CRC32_GUIDED_SECTION_EXTRACTION_GUID, + "CRC32 GUIDED SECTION EXTRACTION"}, + { GRUB_EFI_DEBUG_IMAGE_INFO_TABLE_GUID, "DEBUG IMAGE INFO"}, + { GRUB_EFI_DEVICE_TREE_GUID, "DEVICE TREE"}, + { GRUB_EFI_DXE_SERVICES_TABLE_GUID, "DXE SERVICES"}, + { GRUB_EFI_HCDP_TABLE_GUID, "HCDP"}, + { GRUB_EFI_HOB_LIST_GUID, "HOB LIST"}, + { GRUB_EFI_IMAGE_SECURITY_DATABASE_GUID, "IMAGE EXECUTION INFORMATION"}, + { GRUB_EFI_LZMA_CUSTOM_DECOMPRESS_GUID, "LZMA CUSTOM DECOMPRESS"}, + { GRUB_EFI_MEMORY_TYPE_INFORMATION_GUID, "MEMORY TYPE INFO"}, + { GRUB_EFI_MPS_TABLE_GUID, "MPS"}, + { GRUB_EFI_RT_PROPERTIES_TABLE_GUID, "RT PROPERTIES"}, + { GRUB_EFI_SAL_TABLE_GUID, "SAL"}, + { GRUB_EFI_SMBIOS_TABLE_GUID, "SMBIOS"}, + { GRUB_EFI_SMBIOS3_TABLE_GUID, "SMBIOS3"}, + { GRUB_EFI_SYSTEM_RESOURCE_TABLE_GUID, "SYSTEM RESOURCE TABLE"}, + { GRUB_EFI_TIANO_CUSTOM_DECOMPRESS_GUID, "TIANO CUSTOM DECOMPRESS"}, + { GRUB_EFI_TSC_FREQUENCY_GUID, "TSC FREQUENCY"}, + }; + +static grub_err_t +grub_cmd_lsefisystab (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + const grub_efi_system_table_t *st = grub_efi_system_table; + const grub_efi_uint32_t major_rev = st->hdr.revision >> 16; + const grub_efi_uint32_t minor_rev_upper = (st->hdr.revision & 0xffff) / 10; + const grub_efi_uint32_t minor_rev_lower = (st->hdr.revision & 0xffff) % 10; + grub_efi_configuration_table_t *t; + unsigned int i; + + grub_printf ("Address: %p\n", st); + grub_printf ("Signature: %016" PRIxGRUB_UINT64_T " revision: %u.%u", + st->hdr.signature, major_rev, minor_rev_upper); + if (minor_rev_lower) + grub_printf (".%u", minor_rev_lower); + grub_printf ("\n"); + { + char *vendor; + grub_uint16_t *vendor_utf16; + grub_printf ("Vendor: "); + + for (vendor_utf16 = st->firmware_vendor; *vendor_utf16; vendor_utf16++); + /* Allocate extra 3 bytes to simplify math. */ + vendor = grub_calloc (4, vendor_utf16 - st->firmware_vendor + 1); + if (!vendor) + return grub_errno; + *grub_utf16_to_utf8 ((grub_uint8_t *) vendor, st->firmware_vendor, + vendor_utf16 - st->firmware_vendor) = 0; + grub_printf ("%s", vendor); + grub_free (vendor); + } + + grub_printf (", Version=%x\n", st->firmware_revision); + + grub_printf ("%lld tables:\n", (long long) st->num_table_entries); + t = st->configuration_table; + for (i = 0; i < st->num_table_entries; i++) + { + unsigned int j; + + grub_printf ("%p ", t->vendor_table); + + grub_printf ("%pG", &t->vendor_guid); + + for (j = 0; j < ARRAY_SIZE (guid_mappings); j++) + if (grub_memcmp (&guid_mappings[j].guid, &t->vendor_guid, + sizeof (grub_guid_t)) == 0) + grub_printf (" %s", guid_mappings[j].name); + + grub_printf ("\n"); + t++; + } + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsefisystab) +{ + cmd = grub_register_command ("lsefisystab", grub_cmd_lsefisystab, + "", "Display EFI system tables."); +} + +GRUB_MOD_FINI(lsefisystab) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/lssal.c b/grub-core/commands/efi/lssal.c new file mode 100644 index 000000000..7248bdc29 --- /dev/null +++ b/grub-core/commands/efi/lssal.c @@ -0,0 +1,163 @@ +/* lssal.c - Display EFI SAL systab. */ +/* + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +disp_sal (void *table) +{ + struct grub_efi_sal_system_table *t = table; + void *desc; + grub_uint32_t len, l, i; + + grub_printf ("SAL rev: %02x, signature: %x, len:%x\n", + t->sal_rev, t->signature, t->total_table_len); + grub_printf ("nbr entry: %d, chksum: %02x, SAL version A: %02x B: %02x\n", + t->entry_count, t->checksum, + t->sal_a_version, t->sal_b_version); + grub_printf ("OEM-ID: %-32s\n", t->oem_id); + grub_printf ("Product-ID: %-32s\n", t->product_id); + + desc = t->entries; + len = t->total_table_len - sizeof (struct grub_efi_sal_system_table); + if (t->total_table_len <= sizeof (struct grub_efi_sal_system_table)) + return; + for (i = 0; i < t->entry_count; i++) + { + switch (*(grub_uint8_t *) desc) + { + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_ENTRYPOINT_DESCRIPTOR: + { + struct grub_efi_sal_system_table_entrypoint_descriptor *c = desc; + l = sizeof (*c); + grub_printf (" Entry point: PAL=%016" PRIxGRUB_UINT64_T + " SAL=%016" PRIxGRUB_UINT64_T " GP=%016" + PRIxGRUB_UINT64_T "\n", + c->pal_proc_addr, c->sal_proc_addr, + c->global_data_ptr); + } + break; + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_MEMORY_DESCRIPTOR: + { + struct grub_efi_sal_system_table_memory_descriptor *c = desc; + l = sizeof (*c); + grub_printf (" Memory descriptor entry addr=%016" PRIxGRUB_UINT64_T + " len=%" PRIuGRUB_UINT64_T "KB\n", + c->addr, c->len * 4); + grub_printf (" sal_used=%d attr=%x AR=%x attr_mask=%x " + "type=%x usage=%x\n", + c->sal_used, c->attr, c->ar, c->attr_mask, c->mem_type, + c->usage); + } + break; + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_PLATFORM_FEATURES: + { + struct grub_efi_sal_system_table_platform_features *c = desc; + l = sizeof (*c); + grub_printf (" Platform features: %02x", c->flags); + if (c->flags & GRUB_EFI_SAL_SYSTEM_TABLE_PLATFORM_FEATURE_BUSLOCK) + grub_printf (" BusLock"); + if (c->flags & GRUB_EFI_SAL_SYSTEM_TABLE_PLATFORM_FEATURE_IRQREDIRECT) + grub_printf (" IrqRedirect"); + if (c->flags & GRUB_EFI_SAL_SYSTEM_TABLE_PLATFORM_FEATURE_IPIREDIRECT) + + grub_printf (" IPIRedirect"); + if (c->flags & GRUB_EFI_SAL_SYSTEM_TABLE_PLATFORM_FEATURE_ITCDRIFT) + + grub_printf (" ITCDrift"); + grub_printf ("\n"); + } + break; + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_TRANSLATION_REGISTER_DESCRIPTOR: + { + struct grub_efi_sal_system_table_translation_register_descriptor *c + = desc; + l = sizeof (*c); + grub_printf (" TR type=%d num=%d va=%016" PRIxGRUB_UINT64_T + " pte=%016" PRIxGRUB_UINT64_T "\n", + c->register_type, c->register_number, + c->addr, c->page_size); + } + break; + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_PURGE_TRANSLATION_COHERENCE: + { + struct grub_efi_sal_system_table_purge_translation_coherence *c + = desc; + l = sizeof (*c); + grub_printf (" PTC coherence nbr=%d addr=%016" PRIxGRUB_UINT64_T "\n", + c->ndomains, c->coherence); + } + break; + case GRUB_EFI_SAL_SYSTEM_TABLE_TYPE_AP_WAKEUP: + { + struct grub_efi_sal_system_table_ap_wakeup *c = desc; + l = sizeof (*c); + grub_printf (" AP wake-up: mec=%d vect=%" PRIxGRUB_UINT64_T "\n", + c->mechanism, c->vector); + } + break; + default: + grub_printf (" unknown entry 0x%x\n", *(grub_uint8_t *)desc); + return; + } + desc = (grub_uint8_t *)desc + l; + if (len <= l) + return; + len -= l; + } +} + +static grub_err_t +grub_cmd_lssal (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + static grub_guid_t guid = GRUB_EFI_SAL_TABLE_GUID; + void *table = grub_efi_find_configuration_table (&guid); + + if (table == NULL) + { + grub_printf ("SAL not found\n"); + return GRUB_ERR_NONE; + } + + disp_sal (table); + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lssal) +{ + cmd = grub_register_command ("lssal", grub_cmd_lssal, "", + "Display SAL system table."); +} + +GRUB_MOD_FINI(lssal) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/smbios.c b/grub-core/commands/efi/smbios.c new file mode 100644 index 000000000..717e5fc1d --- /dev/null +++ b/grub-core/commands/efi/smbios.c @@ -0,0 +1,37 @@ +/* smbios.c - get smbios tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include + +struct grub_smbios_eps * +grub_machine_smbios_get_eps (void) +{ + static grub_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; + + return (struct grub_smbios_eps *) grub_efi_find_configuration_table (&smbios_guid); +} + +struct grub_smbios_eps3 * +grub_machine_smbios_get_eps3 (void) +{ + static grub_guid_t smbios3_guid = GRUB_EFI_SMBIOS3_TABLE_GUID; + + return (struct grub_smbios_eps3 *) grub_efi_find_configuration_table (&smbios3_guid); +} diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c new file mode 100644 index 000000000..cbac69866 --- /dev/null +++ b/grub-core/commands/efi/tpm.c @@ -0,0 +1,334 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + * + * EFI TPM support code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef TCG_PCR_EVENT grub_tpm_event_t; + +static grub_guid_t tpm_guid = EFI_TPM_GUID; +static grub_guid_t tpm2_guid = EFI_TPM2_GUID; +static grub_guid_t cc_measurement_guid = GRUB_EFI_CC_MEASUREMENT_PROTOCOL_GUID; + +static grub_efi_handle_t *grub_tpm_handle; +static grub_uint8_t grub_tpm_version; + +static grub_int8_t tpm1_present = -1; +static grub_int8_t tpm2_present = -1; + +static grub_efi_boolean_t +grub_tpm1_present (grub_efi_tpm_protocol_t *tpm) +{ + grub_efi_status_t status; + TCG_EFI_BOOT_SERVICE_CAPABILITY caps; + grub_uint32_t flags; + grub_efi_physical_address_t eventlog, lastevent; + + if (tpm1_present != -1) + return (grub_efi_boolean_t) tpm1_present; + + caps.Size = (grub_uint8_t) sizeof (caps); + + status = tpm->status_check (tpm, &caps, &flags, &eventlog, &lastevent); + + if (status != GRUB_EFI_SUCCESS || caps.TPMDeactivatedFlag + || !caps.TPMPresentFlag) + tpm1_present = 0; + else + tpm1_present = 1; + + grub_dprintf ("tpm", "tpm1%s present\n", tpm1_present ? "" : " NOT"); + + return (grub_efi_boolean_t) tpm1_present; +} + +static grub_efi_boolean_t +grub_tpm2_present (grub_efi_tpm2_protocol_t *tpm) +{ + grub_efi_status_t status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + + caps.Size = (grub_uint8_t) sizeof (caps); + + if (tpm2_present != -1) + return (grub_efi_boolean_t) tpm2_present; + + status = tpm->get_capability (tpm, &caps); + + if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag) + tpm2_present = 0; + else + tpm2_present = 1; + + grub_dprintf ("tpm", "tpm2%s present\n", tpm2_present ? "" : " NOT"); + + return (grub_efi_boolean_t) tpm2_present; +} + +static grub_efi_boolean_t +grub_tpm_handle_find (grub_efi_handle_t *tpm_handle, + grub_efi_uint8_t *protocol_version) +{ + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + + if (grub_tpm_handle != NULL) + { + *tpm_handle = grub_tpm_handle; + *protocol_version = grub_tpm_version; + return 1; + } + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm_guid, NULL, + &num_handles); + if (handles && num_handles > 0) + { + grub_tpm_handle = handles[0]; + *tpm_handle = handles[0]; + grub_tpm_version = 1; + *protocol_version = 1; + grub_dprintf ("tpm", "TPM handle Found, version: 1\n"); + return 1; + } + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL, + &num_handles); + if (handles && num_handles > 0) + { + grub_tpm_handle = handles[0]; + *tpm_handle = handles[0]; + grub_tpm_version = 2; + *protocol_version = 2; + grub_dprintf ("tpm", "TPM handle Found, version: 2\n"); + return 1; + } + + return 0; +} + +static grub_err_t +grub_efi_log_event_status (grub_efi_status_t status) +{ + switch (status) + { + case GRUB_EFI_SUCCESS: + return GRUB_ERR_NONE; + case GRUB_EFI_DEVICE_ERROR: + return grub_error (GRUB_ERR_IO, N_("command failed")); + case GRUB_EFI_INVALID_PARAMETER: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid parameter")); + case GRUB_EFI_BUFFER_TOO_SMALL: + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("output buffer too small")); + case GRUB_EFI_NOT_FOUND: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); + default: + return grub_error (grub_is_tpm_fail_fatal () ? GRUB_ERR_UNKNOWN_DEVICE : GRUB_ERR_NONE, N_("unknown TPM error")); + } +} + +static grub_err_t +grub_tpm1_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, + grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_tpm_event_t *event; + grub_efi_status_t status; + grub_efi_tpm_protocol_t *tpm; + grub_efi_physical_address_t lastevent; + grub_uint32_t algorithm; + grub_uint32_t eventnum = 0; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm1_present (tpm)) + return 0; + + event = grub_zalloc (sizeof (*event) + grub_strlen (description) + 1); + if (!event) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->PCRIndex = pcr; + event->EventType = EV_IPL; + event->EventSize = grub_strlen (description) + 1; + grub_strcpy ((char *) event->Event, description); + + algorithm = TCG_ALG_SHA; + status = tpm->log_extend_event (tpm, (grub_addr_t) buf, (grub_uint64_t) size, + algorithm, event, &eventnum, &lastevent); + grub_free (event); + + return grub_efi_log_event_status (status); +} + +static grub_err_t +grub_tpm2_log_event (grub_efi_handle_t tpm_handle, unsigned char *buf, + grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + EFI_TCG2_EVENT *event; + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!grub_tpm2_present (tpm)) + return 0; + + event = + grub_zalloc (sizeof (EFI_TCG2_EVENT) + grub_strlen (description) + 1); + if (!event) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("cannot allocate TPM event buffer")); + + event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); + event->Header.HeaderVersion = 1; + event->Header.PCRIndex = pcr; + event->Header.EventType = EV_IPL; + event->Size = + sizeof (*event) - sizeof (event->Event) + grub_strlen (description) + 1; + grub_strcpy ((char *) event->Event, description); + + status = tpm->hash_log_extend_event (tpm, 0, (grub_addr_t) buf, + (grub_uint64_t) size, event); + grub_free (event); + + return grub_efi_log_event_status (status); +} + +static void +grub_cc_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_efi_cc_event_t *event; + grub_efi_status_t status; + grub_efi_cc_protocol_t *cc; + grub_efi_cc_mr_index_t mr; + + cc = grub_efi_locate_protocol (&cc_measurement_guid, NULL); + if (cc == NULL) + return; + + status = cc->map_pcr_to_mr_index (cc, pcr, &mr); + if (status != GRUB_EFI_SUCCESS) + { + grub_efi_log_event_status (status); + return; + } + + event = grub_zalloc (sizeof (grub_efi_cc_event_t) + + grub_strlen (description) + 1); + if (event == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate CC event buffer")); + return; + } + + event->Header.HeaderSize = sizeof (grub_efi_cc_event_header_t); + event->Header.HeaderVersion = GRUB_EFI_CC_EVENT_HEADER_VERSION; + event->Header.MrIndex = mr; + event->Header.EventType = EV_IPL; + event->Size = sizeof (*event) + grub_strlen (description) + 1; + grub_strcpy ((char *) event->Event, description); + + status = cc->hash_log_extend_event (cc, 0, + (grub_efi_physical_address_t)(grub_addr_t) buf, + (grub_efi_uint64_t) size, event); + grub_free (event); + + if (status != GRUB_EFI_SUCCESS) + grub_efi_log_event_status (status); +} + +grub_err_t +grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_efi_handle_t tpm_handle; + grub_efi_uint8_t protocol_version; + + grub_cc_log_event(buf, size, pcr, description); + + if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) + return 0; + + grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", + pcr, size, description); + + if (protocol_version == 1) + return grub_tpm1_log_event (tpm_handle, buf, size, pcr, description); + else + return grub_tpm2_log_event (tpm_handle, buf, size, pcr, description); +} + +int +grub_tpm_present (void) +{ + grub_efi_handle_t tpm_handle; + grub_efi_uint8_t protocol_version; + grub_efi_cc_protocol_t *cc; + + /* + * When confidential computing measurement protocol is enabled + * we assume the TPM is present. + */ + cc = grub_efi_locate_protocol (&cc_measurement_guid, NULL); + if (cc != NULL) + return 1; + + if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) + return 0; + + if (protocol_version == 1) + { + grub_efi_tpm_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!tpm) + { + grub_dprintf ("tpm", "Cannot open TPM protocol\n"); + return 0; + } + return grub_tpm1_present (tpm); + } + else + { + grub_efi_tpm2_protocol_t *tpm; + + tpm = grub_efi_open_protocol (tpm_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!tpm) + { + grub_dprintf ("tpm", "Cannot open TPM protocol\n"); + return 0; + } + return grub_tpm2_present (tpm); + } +} diff --git a/grub-core/commands/eval.c b/grub-core/commands/eval.c new file mode 100644 index 000000000..f826a4f22 --- /dev/null +++ b/grub-core/commands/eval.c @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_eval (grub_command_t cmd __attribute__((__unused__)), + int argc, char *argv[]) +{ + int i; + grub_size_t size = argc; /* +1 for final zero */ + char *str, *p; + grub_err_t ret; + + if (argc == 0) + return GRUB_ERR_NONE; + + for (i = 0; i < argc; i++) + size += grub_strlen (argv[i]); + + str = p = grub_malloc (size); + if (!str) + return grub_errno; + + for (i = 0; i < argc; i++) + { + p = grub_stpcpy (p, argv[i]); + *p++ = ' '; + } + *--p = '\0'; + + ret = grub_script_execute_sourcecode (str); + grub_free (str); + return ret; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(eval) +{ + cmd = grub_register_command ("eval", grub_cmd_eval, N_("STRING ..."), + N_("Evaluate arguments as GRUB commands")); +} + +GRUB_MOD_FINI(eval) +{ + grub_unregister_command (cmd); +} + diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c new file mode 100644 index 000000000..c236be13a --- /dev/null +++ b/grub-core/commands/extcmd.c @@ -0,0 +1,144 @@ +/* extcmd.c - support extended command */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_err_t +grub_extcmd_dispatcher (struct grub_command *cmd, int argc, char **args, + struct grub_script *script) +{ + int new_argc; + char **new_args; + struct grub_arg_list *state; + struct grub_extcmd_context context; + grub_err_t ret; + grub_extcmd_t ext = cmd->data; + + context.state = 0; + context.extcmd = ext; + context.script = script; + + if (! ext->options) + { + ret = (ext->func) (&context, argc, args); + return ret; + } + + state = grub_arg_list_alloc (ext, argc, args); + if (state == NULL) + return grub_errno; + + if (grub_arg_parse (ext, argc, args, state, &new_args, &new_argc)) + { + context.state = state; + ret = (ext->func) (&context, new_argc, new_args); + grub_free (new_args); + grub_free (state); + return ret; + } + + grub_free (state); + return grub_errno; +} + +static grub_err_t +grub_extcmd_dispatch (struct grub_command *cmd, int argc, char **args) +{ + return grub_extcmd_dispatcher (cmd, argc, args, 0); +} + +grub_extcmd_t +grub_register_extcmd_prio (const char *name, grub_extcmd_func_t func, + grub_command_flags_t flags, const char *summary, + const char *description, + const struct grub_arg_option *parser, + int prio) +{ + grub_extcmd_t ext; + grub_command_t cmd; + + ext = (grub_extcmd_t) grub_malloc (sizeof (*ext)); + if (! ext) + return 0; + + cmd = grub_register_command_prio (name, grub_extcmd_dispatch, + summary, description, prio); + if (! cmd) + { + grub_free (ext); + return 0; + } + + cmd->flags = (flags | GRUB_COMMAND_FLAG_EXTCMD); + cmd->data = ext; + + ext->cmd = cmd; + ext->func = func; + ext->options = parser; + ext->data = 0; + + return ext; +} + +grub_extcmd_t +grub_register_extcmd (const char *name, grub_extcmd_func_t func, + grub_command_flags_t flags, const char *summary, + const char *description, + const struct grub_arg_option *parser) +{ + return grub_register_extcmd_prio (name, func, flags, + summary, description, parser, 1); +} + +static grub_err_t +grub_extcmd_lockdown (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_ACCESS_DENIED, + N_("%s: the command is not allowed when lockdown is enforced"), + ctxt->extcmd->cmd->name); +} + +grub_extcmd_t +grub_register_extcmd_lockdown (const char *name, grub_extcmd_func_t func, + grub_command_flags_t flags, const char *summary, + const char *description, + const struct grub_arg_option *parser) +{ + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + func = grub_extcmd_lockdown; + + return grub_register_extcmd (name, func, flags, summary, description, parser); +} + +void +grub_unregister_extcmd (grub_extcmd_t ext) +{ + grub_unregister_command (ext->cmd); + grub_free (ext); +} diff --git a/grub-core/commands/file.c b/grub-core/commands/file.c new file mode 100644 index 000000000..19602d757 --- /dev/null +++ b/grub-core/commands/file.c @@ -0,0 +1,707 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = { + {"is-i386-xen-pae-domu", 0, 0, + N_("Check if FILE can be booted as i386 PAE Xen unprivileged guest kernel"), + 0, 0}, + {"is-x86_64-xen-domu", 0, 0, + N_("Check if FILE can be booted as x86_64 Xen unprivileged guest kernel"), 0, 0}, + {"is-x86-xen-dom0", 0, 0, + N_("Check if FILE can be used as Xen x86 privileged guest kernel"), 0, 0}, + {"is-x86-multiboot", 0, 0, + N_("Check if FILE can be used as x86 multiboot kernel"), 0, 0}, + {"is-x86-multiboot2", 0, 0, + N_("Check if FILE can be used as x86 multiboot2 kernel"), 0, 0}, + {"is-arm-linux", 0, 0, + N_("Check if FILE is ARM Linux"), 0, 0}, + {"is-arm64-linux", 0, 0, + N_("Check if FILE is ARM64 Linux"), 0, 0}, + {"is-ia64-linux", 0, 0, + N_("Check if FILE is IA64 Linux"), 0, 0}, + {"is-mips-linux", 0, 0, + N_("Check if FILE is MIPS Linux"), 0, 0}, + {"is-mipsel-linux", 0, 0, + N_("Check if FILE is MIPSEL Linux"), 0, 0}, + {"is-sparc64-linux", 0, 0, + N_("Check if FILE is SPARC64 Linux"), 0, 0}, + {"is-powerpc-linux", 0, 0, + N_("Check if FILE is POWERPC Linux"), 0, 0}, + {"is-x86-linux", 0, 0, + N_("Check if FILE is x86 Linux"), 0, 0}, + {"is-x86-linux32", 0, 0, + N_("Check if FILE is x86 Linux supporting 32-bit protocol"), 0, 0}, + {"is-x86-kfreebsd", 0, 0, + N_("Check if FILE is x86 kFreeBSD"), 0, 0}, + {"is-i386-kfreebsd", 0, 0, + N_("Check if FILE is i386 kFreeBSD"), 0, 0}, + {"is-x86_64-kfreebsd", 0, 0, + N_("Check if FILE is x86_64 kFreeBSD"), 0, 0}, + + {"is-x86-knetbsd", 0, 0, + N_("Check if FILE is x86 kNetBSD"), 0, 0}, + {"is-i386-knetbsd", 0, 0, + N_("Check if FILE is i386 kNetBSD"), 0, 0}, + {"is-x86_64-knetbsd", 0, 0, + N_("Check if FILE is x86_64 kNetBSD"), 0, 0}, + + {"is-i386-efi", 0, 0, + N_("Check if FILE is i386 EFI file"), 0, 0}, + {"is-x86_64-efi", 0, 0, + N_("Check if FILE is x86_64 EFI file"), 0, 0}, + {"is-ia64-efi", 0, 0, + N_("Check if FILE is IA64 EFI file"), 0, 0}, + {"is-arm64-efi", 0, 0, + N_("Check if FILE is ARM64 EFI file"), 0, 0}, + {"is-arm-efi", 0, 0, + N_("Check if FILE is ARM EFI file"), 0, 0}, + {"is-riscv32-efi", 0, 0, + N_("Check if FILE is RISC-V 32bit EFI file"), 0, 0}, + {"is-riscv64-efi", 0, 0, + N_("Check if FILE is RISC-V 64bit EFI file"), 0, 0}, + {"is-hibernated-hiberfil", 0, 0, + N_("Check if FILE is hiberfil.sys in hibernated state"), 0, 0}, + {"is-x86_64-xnu", 0, 0, + N_("Check if FILE is x86_64 XNU (Mac OS X kernel)"), 0, 0}, + {"is-i386-xnu", 0, 0, + N_("Check if FILE is i386 XNU (Mac OS X kernel)"), 0, 0}, + {"is-xnu-hibr", 0, 0, + N_("Check if FILE is XNU (Mac OS X kernel) hibernated image"), 0, 0}, + {"is-x86-bios-bootsector", 0, 0, + N_("Check if FILE is BIOS bootsector"), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +enum +{ + IS_PAE_DOMU, + IS_64_DOMU, + IS_DOM0, + IS_MULTIBOOT, + IS_MULTIBOOT2, + IS_ARM_LINUX, + IS_ARM64_LINUX, + IS_IA64_LINUX, + IS_MIPS_LINUX, + IS_MIPSEL_LINUX, + IS_SPARC64_LINUX, + IS_POWERPC_LINUX, + IS_X86_LINUX, + IS_X86_LINUX32, + IS_X86_KFREEBSD, + IS_X86_KFREEBSD32, + IS_X86_KFREEBSD64, + IS_X86_KNETBSD, + IS_X86_KNETBSD32, + IS_X86_KNETBSD64, + IS_32_EFI, + IS_64_EFI, + IS_IA_EFI, + IS_ARM64_EFI, + IS_ARM_EFI, + IS_RISCV32_EFI, + IS_RISCV64_EFI, + IS_HIBERNATED, + IS_XNU64, + IS_XNU32, + IS_XNU_HIBR, + IS_BIOS_BOOTSECTOR, + OPT_TYPE_MIN = IS_PAE_DOMU, + OPT_TYPE_MAX = IS_BIOS_BOOTSECTOR +}; + + +static grub_err_t +grub_cmd_file (grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_file_t file = 0; + grub_elf_t elf = 0; + grub_err_t err; + int type = -1, i; + int ret = 0; + grub_macho_t macho = 0; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + for (i = OPT_TYPE_MIN; i <= OPT_TYPE_MAX; i++) + if (ctxt->state[i].set) + { + if (type == -1) + { + type = i; + continue; + } + return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple types specified"); + } + if (type == -1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no type specified"); + + file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL); + if (!file) + return grub_errno; + switch (type) + { + case IS_BIOS_BOOTSECTOR: + { + grub_uint16_t sig; + if (grub_file_size (file) != 512) + break; + if (grub_file_seek (file, 510) == (grub_size_t) -1) + break; + if (grub_file_read (file, &sig, 2) != 2) + break; + if (sig != grub_cpu_to_le16_compile_time (0xaa55)) + break; + ret = 1; + break; + } + case IS_IA64_LINUX: + { + Elf64_Ehdr ehdr; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_ident[EI_CLASS] != ELFCLASS64 + || ehdr.e_ident[EI_DATA] != ELFDATA2LSB + || ehdr.e_machine != grub_cpu_to_le16_compile_time (EM_IA_64)) + break; + + ret = 1; + + break; + } + + case IS_SPARC64_LINUX: + { + Elf64_Ehdr ehdr; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_ident[EI_CLASS] != ELFCLASS64 + || ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + break; + + if (ehdr.e_machine != grub_cpu_to_le16_compile_time (EM_SPARCV9) + || ehdr.e_type != grub_cpu_to_be16_compile_time (ET_EXEC)) + break; + + ret = 1; + + break; + } + + case IS_POWERPC_LINUX: + { + Elf32_Ehdr ehdr; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_ident[EI_DATA] != ELFDATA2MSB + || (ehdr.e_machine != grub_cpu_to_le16_compile_time (EM_PPC) + && ehdr.e_machine != + grub_cpu_to_le16_compile_time (EM_PPC64))) + break; + + if (ehdr.e_type != grub_cpu_to_be16_compile_time (ET_EXEC) + && ehdr.e_type != grub_cpu_to_be16_compile_time (ET_DYN)) + break; + + ret = 1; + + break; + } + + case IS_MIPS_LINUX: + { + Elf32_Ehdr ehdr; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_ident[EI_DATA] != ELFDATA2MSB + || ehdr.e_machine != grub_cpu_to_be16_compile_time (EM_MIPS) + || ehdr.e_type != grub_cpu_to_be16_compile_time (ET_EXEC)) + break; + + ret = 1; + + break; + } + + case IS_X86_KNETBSD: + case IS_X86_KNETBSD32: + case IS_X86_KNETBSD64: + { + int is32, is64; + + elf = grub_elf_file (file, file->name); + + if (elf == NULL) + break; + if (elf->ehdr.ehdr32.e_type != grub_cpu_to_le16_compile_time (ET_EXEC) + || elf->ehdr.ehdr32.e_ident[EI_DATA] != ELFDATA2LSB) + break; + + is32 = grub_elf_is_elf32 (elf); + is64 = grub_elf_is_elf64 (elf); + if (!is32 && !is64) + break; + if (!is32 && type == IS_X86_KNETBSD32) + break; + if (!is64 && type == IS_X86_KNETBSD64) + break; + if (is64) + ret = grub_file_check_netbsd64 (elf); + if (is32) + ret = grub_file_check_netbsd32 (elf); + break; + } + + case IS_X86_KFREEBSD: + case IS_X86_KFREEBSD32: + case IS_X86_KFREEBSD64: + { + Elf32_Ehdr ehdr; + int is32, is64; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_type != grub_cpu_to_le16_compile_time (ET_EXEC) + || ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + break; + + if (ehdr.e_ident[EI_OSABI] != ELFOSABI_FREEBSD) + break; + + is32 = (ehdr.e_machine == grub_cpu_to_le16_compile_time (EM_386) + && ehdr.e_ident[EI_CLASS] == ELFCLASS32); + is64 = (ehdr.e_machine == grub_cpu_to_le16_compile_time (EM_X86_64) + && ehdr.e_ident[EI_CLASS] == ELFCLASS64); + if (!is32 && !is64) + break; + if (!is32 && (type == IS_X86_KFREEBSD32 || type == IS_X86_KNETBSD32)) + break; + if (!is64 && (type == IS_X86_KFREEBSD64 || type == IS_X86_KNETBSD64)) + break; + ret = 1; + + break; + } + + + case IS_MIPSEL_LINUX: + { + Elf32_Ehdr ehdr; + + if (grub_file_read (file, &ehdr, sizeof (ehdr)) != sizeof (ehdr)) + break; + + if (ehdr.e_ident[EI_MAG0] != ELFMAG0 + || ehdr.e_ident[EI_MAG1] != ELFMAG1 + || ehdr.e_ident[EI_MAG2] != ELFMAG2 + || ehdr.e_ident[EI_MAG3] != ELFMAG3 + || ehdr.e_ident[EI_VERSION] != EV_CURRENT + || ehdr.e_version != EV_CURRENT) + break; + + if (ehdr.e_machine != grub_cpu_to_le16_compile_time (EM_MIPS) + || ehdr.e_type != grub_cpu_to_le16_compile_time (ET_EXEC)) + break; + + ret = 1; + + break; + } + case IS_ARM_LINUX: + { + struct linux_arch_kernel_header lh; + + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + break; + /* Short forward branch in A32 state (for Raspberry pi kernels). */ + if (lh.code0 == grub_cpu_to_le32_compile_time (0xea000006)) + { + ret = 1; + break; + } + + if (lh.magic == + grub_cpu_to_le32_compile_time (GRUB_LINUX_ARM_MAGIC_SIGNATURE)) + { + ret = 1; + break; + } + break; + } + case IS_ARM64_LINUX: + { + struct linux_arch_kernel_header lh; + + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + break; + + /* + * The PE/COFF header can be anywhere in the file. Load it from the correct + * offset if it is not where it is expected. + */ + if ((grub_uint8_t *) &lh + lh.hdr_offset != (grub_uint8_t *) &lh.pe_image_header) + { + if (grub_file_seek (file, lh.hdr_offset) == (grub_off_t) -1 + || grub_file_read (file, &lh.pe_image_header, sizeof (struct grub_pe_image_header)) + != sizeof (struct grub_pe_image_header)) + return grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); + } + + if (lh.pe_image_header.coff_header.machine == grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_ARM64)) + { + ret = 1; + break; + } + break; + } + case IS_PAE_DOMU ... IS_DOM0: + { + struct grub_xen_file_info xen_inf; + elf = grub_xen_file (file); + if (!elf) + break; + err = grub_xen_get_info (elf, &xen_inf); + if (err) + break; + /* Unfortuntely no way to check if kernel supports dom0. */ + if (type == IS_DOM0) + ret = 1; + if (type == IS_PAE_DOMU) + ret = (xen_inf.arch == GRUB_XEN_FILE_I386_PAE + || xen_inf.arch == GRUB_XEN_FILE_I386_PAE_BIMODE); + if (type == IS_64_DOMU) + ret = (xen_inf.arch == GRUB_XEN_FILE_X86_64); + break; + } + case IS_MULTIBOOT: + case IS_MULTIBOOT2: + { + grub_uint32_t *buffer; + grub_ssize_t len; + grub_size_t search_size; + grub_uint32_t *header; + grub_uint32_t magic; + grub_size_t step; + + if (type == IS_MULTIBOOT2) + { + search_size = 32768; + magic = grub_cpu_to_le32_compile_time (0xe85250d6); + step = 2; + } + else + { + search_size = 8192; + magic = grub_cpu_to_le32_compile_time (0x1BADB002); + step = 1; + } + + buffer = grub_malloc (search_size); + if (!buffer) + break; + + len = grub_file_read (file, buffer, search_size); + if (len < 32) + { + grub_free (buffer); + break; + } + + /* Look for the multiboot header in the buffer. The header should + be at least 12 bytes and aligned on a 4-byte boundary. */ + for (header = buffer; + ((char *) header <= + (char *) buffer + len - (type == IS_MULTIBOOT2 ? 16 : 12)); + header += step) + { + if (header[0] == magic + && !(grub_le_to_cpu32 (header[0]) + + grub_le_to_cpu32 (header[1]) + + grub_le_to_cpu32 (header[2]) + + (type == IS_MULTIBOOT2 + ? grub_le_to_cpu32 (header[3]) : 0))) + { + ret = 1; + break; + } + } + + grub_free (buffer); + break; + } + case IS_X86_LINUX32: + case IS_X86_LINUX: + { + struct linux_i386_kernel_header lh; + if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + break; + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) + break; + + if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + break; + + /* FIXME: some really old kernels (< 1.3.73) will fail this. */ + if (lh.header != + grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) + || grub_le_to_cpu16 (lh.version) < 0x0200) + break; + + if (type == IS_X86_LINUX) + { + ret = 1; + break; + } + + /* FIXME: 2.03 is not always good enough (Linux 2.4 can be 2.03 and + still not support 32-bit boot. */ + if (lh.header != + grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) + || grub_le_to_cpu16 (lh.version) < 0x0203) + break; + + if (!(lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL)) + break; + ret = 1; + break; + } + case IS_HIBERNATED: + { + grub_uint8_t hibr_file_magic[4]; + if (grub_file_read (file, &hibr_file_magic, sizeof (hibr_file_magic)) + != sizeof (hibr_file_magic)) + break; + if (grub_memcmp ("hibr", hibr_file_magic, sizeof (hibr_file_magic)) == + 0 + || grub_memcmp ("HIBR", hibr_file_magic, + sizeof (hibr_file_magic)) == 0) + ret = 1; + break; + } + case IS_XNU64: + case IS_XNU32: + { + macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, + (type == IS_XNU64)); + if (!macho) + break; + /* FIXME: more checks? */ + ret = 1; + break; + } + case IS_XNU_HIBR: + { + struct grub_xnu_hibernate_header hibhead; + if (grub_file_read (file, &hibhead, sizeof (hibhead)) + != sizeof (hibhead)) + break; + if (hibhead.magic != + grub_cpu_to_le32_compile_time (GRUB_XNU_HIBERNATE_MAGIC)) + break; + ret = 1; + break; + } + case IS_32_EFI: + case IS_64_EFI: + case IS_IA_EFI: + case IS_ARM64_EFI: + case IS_ARM_EFI: + case IS_RISCV32_EFI: + case IS_RISCV64_EFI: + { + char signature[4]; + grub_uint32_t pe_offset; + struct grub_pe32_coff_header coff_head; + + if (grub_file_read (file, signature, 2) != 2) + break; + if (signature[0] != 'M' || signature[1] != 'Z') + break; + if ((grub_ssize_t) grub_file_seek (file, 0x3c) == -1) + break; + if (grub_file_read (file, &pe_offset, 4) != 4) + break; + if ((grub_ssize_t) grub_file_seek (file, grub_le_to_cpu32 (pe_offset)) + == -1) + break; + if (grub_file_read (file, signature, 4) != 4) + break; + if (signature[0] != 'P' || signature[1] != 'E' + || signature[2] != '\0' || signature[3] != '\0') + break; + + if (grub_file_read (file, &coff_head, sizeof (coff_head)) + != sizeof (coff_head)) + break; + if (type == IS_32_EFI + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_I386)) + break; + if (type == IS_64_EFI + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_X86_64)) + break; + if (type == IS_IA_EFI + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_IA64)) + break; + if (type == IS_ARM64_EFI + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_ARM64)) + break; + if (type == IS_ARM_EFI + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_ARMTHUMB_MIXED)) + break; + if ((type == IS_RISCV32_EFI || type == IS_RISCV64_EFI) + && coff_head.machine != + grub_cpu_to_le16_compile_time (GRUB_PE32_MACHINE_RISCV64)) + /* TODO: Determine bitness dynamically */ + break; + if (type == IS_IA_EFI || type == IS_64_EFI || type == IS_ARM64_EFI || + type == IS_RISCV32_EFI || type == IS_RISCV64_EFI) + { + struct grub_pe64_optional_header o64; + if (grub_file_read (file, &o64, sizeof (o64)) != sizeof (o64)) + break; + if (o64.magic != + grub_cpu_to_le16_compile_time (GRUB_PE32_PE64_MAGIC)) + break; + if (o64.subsystem != + grub_cpu_to_le16_compile_time + (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION)) + break; + ret = 1; + break; + } + if (type == IS_32_EFI || type == IS_ARM_EFI) + { + struct grub_pe32_optional_header o32; + if (grub_file_read (file, &o32, sizeof (o32)) != sizeof (o32)) + break; + if (o32.magic != + grub_cpu_to_le16_compile_time (GRUB_PE32_PE32_MAGIC)) + break; + if (o32.subsystem != + grub_cpu_to_le16_compile_time + (GRUB_PE32_SUBSYSTEM_EFI_APPLICATION)) + break; + ret = 1; + break; + } + break; + } + } + + if (elf) + grub_elf_close (elf); + else if (macho) + grub_macho_close (macho); + else if (file) + grub_file_close (file); + + if (!ret && (grub_errno == GRUB_ERR_BAD_OS || grub_errno == GRUB_ERR_NONE)) + /* TRANSLATORS: it's a standalone boolean value, + opposite of "true". */ + grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); + return grub_errno; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(file) +{ + cmd = grub_register_extcmd ("file", grub_cmd_file, 0, + N_("OPTIONS FILE"), + N_("Check if FILE is of specified type."), + options); +} + +GRUB_MOD_FINI(file) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/file32.c b/grub-core/commands/file32.c new file mode 100644 index 000000000..0861c458d --- /dev/null +++ b/grub-core/commands/file32.c @@ -0,0 +1,5 @@ +#define GRUB_TARGET_WORDSIZE 32 +#define XX 32 +#define ehdrXX ehdr32 +#define grub_file_check_netbsdXX grub_file_check_netbsd32 +#include "fileXX.c" diff --git a/grub-core/commands/file64.c b/grub-core/commands/file64.c new file mode 100644 index 000000000..90890d481 --- /dev/null +++ b/grub-core/commands/file64.c @@ -0,0 +1,5 @@ +#define GRUB_TARGET_WORDSIZE 64 +#define XX 64 +#define ehdrXX ehdr64 +#define grub_file_check_netbsdXX grub_file_check_netbsd64 +#include "fileXX.c" diff --git a/grub-core/commands/fileXX.c b/grub-core/commands/fileXX.c new file mode 100644 index 000000000..c17d26ce6 --- /dev/null +++ b/grub-core/commands/fileXX.c @@ -0,0 +1,74 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wcast-align" + +int +grub_file_check_netbsdXX (grub_elf_t elf) +{ + Elf_Shdr *s, *s0; + + grub_size_t shnum = elf->ehdr.ehdrXX.e_shnum; + grub_size_t shentsize = elf->ehdr.ehdrXX.e_shentsize; + grub_size_t shsize = shnum * shentsize; + grub_off_t stroff; + + if (!shnum || !shentsize) + return 0; + + s0 = grub_malloc (shsize); + if (!s0) + return 0; + + if (grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_shoff) == (grub_off_t) -1) + goto fail; + + if (grub_file_read (elf->file, s0, shsize) != (grub_ssize_t) shsize) + goto fail; + + s = (Elf_Shdr *) ((char *) s0 + elf->ehdr.ehdrXX.e_shstrndx * shentsize); + stroff = s->sh_offset; + + for (s = s0; s < (Elf_Shdr *) ((char *) s0 + shnum * shentsize); + s = (Elf_Shdr *) ((char *) s + shentsize)) + { + char name[sizeof(".note.netbsd.ident")]; + grub_memset (name, 0, sizeof (name)); + if (grub_file_seek (elf->file, stroff + s->sh_name) == (grub_off_t) -1) + goto fail; + + if (grub_file_read (elf->file, name, sizeof (name)) != (grub_ssize_t) sizeof (name)) + { + if (grub_errno) + goto fail; + continue; + } + if (grub_memcmp (name, ".note.netbsd.ident", + sizeof(".note.netbsd.ident")) != 0) + continue; + grub_free (s0); + return 1; + } + fail: + grub_free (s0); + return 0; +} diff --git a/commands/gptsync.c b/grub-core/commands/gptsync.c similarity index 86% rename from commands/gptsync.c rename to grub-core/commands/gptsync.c index d217b5d5c..444e24874 100644 --- a/commands/gptsync.c +++ b/grub-core/commands/gptsync.c @@ -29,17 +29,19 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + /* Convert a LBA address to a CHS address in the INT 13 format. */ /* Taken from grub1. */ /* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63. Is it a problem? */ static void -lba_to_chs (int lba, grub_uint8_t *cl, grub_uint8_t *ch, +lba_to_chs (grub_uint32_t lba, grub_uint8_t *cl, grub_uint8_t *ch, grub_uint8_t *dh) { - int cylinder, head, sector; - int sectors = 63, heads = 255, cylinders = 1024; + grub_uint32_t cylinder, head, sector; + grub_uint32_t sectors = 63, heads = 255, cylinders = 1024; sector = lba % sectors + 1; head = (lba / sectors) % heads; @@ -65,6 +67,7 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), struct grub_partition *partition; grub_disk_addr_t first_sector; int numactive = 0; + int i; if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); @@ -98,20 +101,22 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), } /* Check if it is valid. */ - if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) + if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE)) { grub_device_close (dev); return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); } /* Make sure the MBR is a protective MBR and not a normal MBR. */ - if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) + for (i = 0; i < 4; i++) + if (mbr.entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK) + break; + if (i == 4) { grub_device_close (dev); return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found"); } - int i; first_sector = dev->disk->total_sectors; for (i = 1; i < argc; i++) { @@ -131,15 +136,16 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), if (! partition) { grub_device_close (dev); - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition"); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + N_("no such partition")); } if (partition->start + partition->len > 0xffffffff) { grub_device_close (dev); return grub_error (GRUB_ERR_OUT_OF_RANGE, - "only partitions resding in the first 2TB " - "can be presen in hybrid MBR"); + "only partitions residing in the first 2TB " + "can be present in hybrid MBR"); } @@ -210,7 +216,7 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), first_sector--; mbr.entries[0].flag = 0; mbr.entries[0].type = GRUB_PC_PARTITION_TYPE_GPT_DISK; - mbr.entries[0].start = grub_cpu_to_le32 (1); + mbr.entries[0].start = grub_cpu_to_le32_compile_time (1); lba_to_chs (1, &(mbr.entries[0].start_sector), &(mbr.entries[0].start_cylinder), @@ -221,7 +227,7 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), &(mbr.entries[0].end_head)); mbr.entries[0].length = grub_cpu_to_le32 (first_sector); - mbr.signature = grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE); + mbr.signature = grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE); if (grub_disk_write (dev->disk, 0, 0, sizeof (mbr), &mbr)) { @@ -229,7 +235,9 @@ grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } - grub_printf ("New MBR is written to '%s'\n", args[0]); + grub_device_close (dev); + + grub_printf_ (N_("New MBR is written to `%s'\n"), args[0]); return GRUB_ERR_NONE; } @@ -242,9 +250,11 @@ GRUB_MOD_INIT(gptsync) (void) mod; /* To stop warning. */ cmd = grub_register_command ("gptsync", grub_cmd_gptsync, N_("DEVICE [PARTITION[+/-[TYPE]]] ..."), + /* TRANSLATORS: MBR type is one-byte partition + type id. */ N_("Fill hybrid MBR of GPT drive DEVICE. " - "specified partitions will be a part " - "of hybrid mbr. Up to 3 partitions are " + "Specified partitions will be a part " + "of hybrid MBR. Up to 3 partitions are " "allowed. TYPE is an MBR type. " "+ means that partition is active. " "Only one partition can be active.")); diff --git a/commands/halt.c b/grub-core/commands/halt.c similarity index 90% rename from commands/halt.c rename to grub-core/commands/halt.c index 3400115a0..8f2e88040 100644 --- a/commands/halt.c +++ b/grub-core/commands/halt.c @@ -22,13 +22,14 @@ #include #include -static grub_err_t +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t __attribute__ ((noreturn)) grub_cmd_halt (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { grub_halt (); - return 0; } static grub_command_t cmd; @@ -36,7 +37,7 @@ static grub_command_t cmd; GRUB_MOD_INIT(halt) { cmd = grub_register_command ("halt", grub_cmd_halt, - 0, N_("Halts the computer. This command does" + 0, N_("Halts the computer. This command does" " not work on all firmware implementations.")); } diff --git a/commands/hashsum.c b/grub-core/commands/hashsum.c similarity index 55% rename from commands/hashsum.c rename to grub-core/commands/hashsum.c index 951479fa7..d56b5b0bb 100644 --- a/commands/hashsum.c +++ b/grub-core/commands/hashsum.c @@ -24,21 +24,28 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = { - {"hash", 'h', 0, "Specify hash to use.", "HASH", ARG_TYPE_STRING}, - {"check", 'c', 0, "Check hash list file.", "FILE", ARG_TYPE_STRING}, - {"prefix", 'p', 0, "Base directory for hash list.", "DIRECTORY", + {"hash", 'h', 0, N_("Specify hash to use."), N_("HASH"), ARG_TYPE_STRING}, + {"check", 'c', 0, N_("Check hashes of files with hash list FILE."), + N_("FILE"), ARG_TYPE_STRING}, + {"prefix", 'p', 0, N_("Base directory for hash list."), N_("DIR"), ARG_TYPE_STRING}, - {"keep-going", 'k', 0, "Don't stop after first error.", 0, 0}, + {"keep-going", 'k', 0, N_("Don't stop after first error."), 0, 0}, + {"uncompress", 'u', 0, N_("Uncompress file before checksumming."), 0, 0}, {0, 0, 0, 0, 0, 0} }; -struct { const char *name; const char *hashname; } aliases[] = +static struct { const char *name; const char *hashname; } aliases[] = { {"sha256sum", "sha256"}, {"sha512sum", "sha512"}, + {"sha1sum", "sha1"}, {"md5sum", "md5"}, + {"crc", "crc32"}, }; static inline int @@ -56,17 +63,23 @@ hextoval (char c) static grub_err_t hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result) { - grub_uint8_t context[hash->contextsize]; - grub_uint8_t readbuf[4096]; + void *context; + grub_uint8_t *readbuf; +#define BUF_SIZE 4096 + readbuf = grub_malloc (BUF_SIZE); + if (!readbuf) + return grub_errno; + context = grub_zalloc (hash->contextsize); + if (!readbuf || !context) + goto fail; - grub_memset (context, 0, sizeof (context)); hash->init (context); while (1) { grub_ssize_t r; - r = grub_file_read (file, readbuf, sizeof (readbuf)); + r = grub_file_read (file, readbuf, BUF_SIZE); if (r < 0) - return grub_errno; + goto fail; if (r == 0) break; hash->write (context, readbuf, r); @@ -74,51 +87,78 @@ hash_file (grub_file_t file, const gcry_md_spec_t *hash, void *result) hash->final (context); grub_memcpy (result, hash->read (context), hash->mdlen); + grub_free (readbuf); + grub_free (context); + return GRUB_ERR_NONE; + + fail: + grub_free (readbuf); + grub_free (context); + return grub_errno; } static grub_err_t check_list (const gcry_md_spec_t *hash, const char *hashfilename, - const char *prefix, int keep) + const char *prefix, int keep, int uncompress) { grub_file_t hashlist, file; char *buf = NULL; - grub_uint8_t expected[hash->mdlen]; - grub_uint8_t actual[hash->mdlen]; + grub_uint8_t expected[GRUB_CRYPTO_MAX_MDLEN]; + grub_uint8_t actual[GRUB_CRYPTO_MAX_MDLEN]; grub_err_t err; unsigned i; unsigned unread = 0, mismatch = 0; - hashlist = grub_file_open (hashfilename); + if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return grub_error (GRUB_ERR_BUG, "mdlen is too long"); + + hashlist = grub_file_open (hashfilename, GRUB_FILE_TYPE_HASHLIST); if (!hashlist) return grub_errno; - + while (grub_free (buf), (buf = grub_file_getline (hashlist))) { const char *p = buf; + while (grub_isspace (p[0])) + p++; for (i = 0; i < hash->mdlen; i++) { int high, low; high = hextoval (*p++); low = hextoval (*p++); if (high < 0 || low < 0) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + { + grub_free (buf); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + } expected[i] = (high << 4) | low; } - if (*p++ != ' ' || *p++ != ' ') - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + if ((p[0] != ' ' && p[0] != '\t') || (p[1] != ' ' && p[1] != '\t')) + { + grub_free (buf); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid hash list"); + } + p += 2; if (prefix) { char *filename; - + filename = grub_xasprintf ("%s/%s", prefix, p); if (!filename) - return grub_errno; - file = grub_file_open (filename); + { + grub_free (buf); + return grub_errno; + } + file = grub_file_open (filename, GRUB_FILE_TYPE_TO_HASH + | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS + : GRUB_FILE_TYPE_NONE)); grub_free (filename); } else - file = grub_file_open (p); + file = grub_file_open (p, GRUB_FILE_TYPE_TO_HASH + | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS + : GRUB_FILE_TYPE_NONE)); if (!file) { grub_file_close (hashlist); @@ -129,7 +169,7 @@ check_list (const gcry_md_spec_t *hash, const char *hashfilename, grub_file_close (file); if (err) { - grub_printf ("%s: READ ERROR\n", p); + grub_printf_ (N_("%s: READ ERROR\n"), p); if (!keep) { grub_file_close (hashlist); @@ -143,7 +183,7 @@ check_list (const gcry_md_spec_t *hash, const char *hashfilename, } if (grub_crypto_memcmp (expected, actual, hash->mdlen) != 0) { - grub_printf ("%s: HASH MISMATCH\n", p); + grub_printf_ (N_("%s: HASH MISMATCH\n"), p); if (!keep) { grub_file_close (hashlist); @@ -152,9 +192,9 @@ check_list (const gcry_md_spec_t *hash, const char *hashfilename, "hash of '%s' mismatches", p); } mismatch++; - continue; + continue; } - grub_printf ("%s: OK\n", p); + grub_printf_ (N_("%s: OK\n"), p); } if (mismatch || unread) return grub_error (GRUB_ERR_TEST_FAILURE, @@ -164,19 +204,20 @@ check_list (const gcry_md_spec_t *hash, const char *hashfilename, } static grub_err_t -grub_cmd_hashsum (struct grub_extcmd *cmd, +grub_cmd_hashsum (struct grub_extcmd_context *ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; const char *hashname = NULL; const char *prefix = NULL; const gcry_md_spec_t *hash; unsigned i; int keep = state[3].set; + int uncompress = state[4].set; unsigned unread = 0; for (i = 0; i < ARRAY_SIZE (aliases); i++) - if (grub_strcmp (cmd->cmd->name, aliases[i].name) == 0) + if (grub_strcmp (ctxt->extcmd->cmd->name, aliases[i].name) == 0) hashname = aliases[i].hashname; if (state[0].set) hashname = state[0].arg; @@ -188,6 +229,9 @@ grub_cmd_hashsum (struct grub_extcmd *cmd, if (!hash) return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown hash"); + if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return grub_error (GRUB_ERR_BUG, "mdlen is too long"); + if (state[2].set) prefix = state[2].arg; @@ -196,16 +240,18 @@ grub_cmd_hashsum (struct grub_extcmd *cmd, if (argc != 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, "--check is incompatible with file list"); - return check_list (hash, state[1].arg, prefix, keep); + return check_list (hash, state[1].arg, prefix, keep, uncompress); } for (i = 0; i < (unsigned) argc; i++) { - grub_uint8_t result[hash->mdlen]; + GRUB_PROPERLY_ALIGNED_ARRAY (result, GRUB_CRYPTO_MAX_MDLEN); grub_file_t file; grub_err_t err; unsigned j; - file = grub_file_open (args[i]); + file = grub_file_open (args[i], GRUB_FILE_TYPE_TO_HASH + | (!uncompress ? GRUB_FILE_TYPE_NO_DECOMPRESS + : GRUB_FILE_TYPE_NONE)); if (!file) { if (!keep) @@ -227,43 +273,53 @@ grub_cmd_hashsum (struct grub_extcmd *cmd, continue; } for (j = 0; j < hash->mdlen; j++) - grub_printf ("%02x", result[j]); + grub_printf ("%02x", ((grub_uint8_t *) result)[j]); grub_printf (" %s\n", args[i]); } if (unread) - return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read.", + return grub_error (GRUB_ERR_TEST_FAILURE, "%d files couldn't be read", unread); return GRUB_ERR_NONE; } -static grub_extcmd_t cmd, cmd_md5, cmd_sha256, cmd_sha512; +static grub_extcmd_t cmd, cmd_md5, cmd_sha1, cmd_sha256, cmd_sha512, cmd_crc; GRUB_MOD_INIT(hashsum) { - cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, - GRUB_COMMAND_FLAG_BOTH, - "hashsum -h HASH [-c FILE [-p PREFIX]] " - "[FILE1 [FILE2 ...]]", - "Compute or check hash checksum.", + cmd = grub_register_extcmd ("hashsum", grub_cmd_hashsum, 0, + N_("-h HASH [-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + /* TRANSLATORS: "hash checksum" is just to + be a bit more precise, you can treat it as + just "hash". */ + N_("Compute or check hash checksum."), options); - cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, - GRUB_COMMAND_FLAG_BOTH, - "md5sum [-c FILE [-p PREFIX]] " - "[FILE1 [FILE2 ...]]", - "Compute or check hash checksum.", + cmd_md5 = grub_register_extcmd ("md5sum", grub_cmd_hashsum, 0, + N_("[-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + N_("Compute or check hash checksum."), options); - cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, - GRUB_COMMAND_FLAG_BOTH, - "sha256sum [-c FILE [-p PREFIX]] " - "[FILE1 [FILE2 ...]]", - "Compute or check hash checksum.", + cmd_sha1 = grub_register_extcmd ("sha1sum", grub_cmd_hashsum, 0, + N_("[-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + N_("Compute or check hash checksum."), + options); + cmd_sha256 = grub_register_extcmd ("sha256sum", grub_cmd_hashsum, 0, + N_("[-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + N_("Compute or check hash checksum."), options); - cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, - GRUB_COMMAND_FLAG_BOTH, - "sha512sum [-c FILE [-p PREFIX]] " - "[FILE1 [FILE2 ...]]", - "Compute or check hash checksum.", + cmd_sha512 = grub_register_extcmd ("sha512sum", grub_cmd_hashsum, 0, + N_("[-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + N_("Compute or check hash checksum."), + options); + + cmd_crc = grub_register_extcmd ("crc", grub_cmd_hashsum, 0, + N_("[-c FILE [-p PREFIX]] " + "[FILE1 [FILE2 ...]]"), + N_("Compute or check hash checksum."), options); } @@ -271,6 +327,8 @@ GRUB_MOD_FINI(hashsum) { grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd_md5); + grub_unregister_extcmd (cmd_sha1); grub_unregister_extcmd (cmd_sha256); grub_unregister_extcmd (cmd_sha512); + grub_unregister_extcmd (cmd_crc); } diff --git a/commands/hdparm.c b/grub-core/commands/hdparm.c similarity index 70% rename from commands/hdparm.c rename to grub-core/commands/hdparm.c index a3f8bbff0..96f2336f3 100644 --- a/commands/hdparm.c +++ b/grub-core/commands/hdparm.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -26,14 +27,16 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static const struct grub_arg_option options[] = { {"apm", 'B', 0, N_("Set Advanced Power Management\n" "(1=low, ..., 254=high, 255=off)."), 0, ARG_TYPE_INT}, - {"power", 'C', 0, N_("Check power mode."), 0, ARG_TYPE_NONE}, + {"power", 'C', 0, N_("Display power mode."), 0, ARG_TYPE_NONE}, {"security-freeze", 'F', 0, N_("Freeze ATA security settings until reset."), 0, ARG_TYPE_NONE}, - {"health", 'H', 0, N_("Check SMART health status."), 0, ARG_TYPE_NONE}, + {"health", 'H', 0, N_("Display SMART health status."), 0, ARG_TYPE_NONE}, {"aam", 'M', 0, N_("Set Automatic Acoustic Management\n" "(0=off, 128=quiet, ..., 254=fast)."), 0, ARG_TYPE_INT}, @@ -44,7 +47,7 @@ static const struct grub_arg_option options[] = { {"sleep", 'Y', 0, N_("Set drive to sleep mode."), 0, ARG_TYPE_NONE}, {"identify", 'i', 0, N_("Print drive identity and settings."), 0, ARG_TYPE_NONE}, - {"dumpid", 'I', 0, N_("Dump contents of ATA IDENTIFY sector."), + {"dumpid", 'I', 0, N_("Show raw contents of ATA IDENTIFY sector."), 0, ARG_TYPE_NONE}, {"smart", -1, 0, N_("Disable/enable SMART (0/1)."), 0, ARG_TYPE_INT}, {"quiet", 'q', 0, N_("Do not print messages."), 0, ARG_TYPE_NONE}, @@ -61,60 +64,64 @@ enum grub_ata_smart_commands static int quiet = 0; static grub_err_t -grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd, +grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd, grub_uint8_t features, grub_uint8_t sectors, void * buffer, int size) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = cmd; - apt.taskfile[GRUB_ATA_REG_FEATURES] = features; - apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors; + apt.taskfile.cmd = cmd; + apt.taskfile.features = features; + apt.taskfile.sectors = sectors; + apt.taskfile.disk = 0xE0; + apt.buffer = buffer; apt.size = size; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt, 0)) return grub_errno; return GRUB_ERR_NONE; } static int -grub_hdparm_do_check_powermode_cmd (grub_disk_t disk) +grub_hdparm_do_check_powermode_cmd (grub_ata_t ata) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE; + apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE; + apt.taskfile.disk = 0xE0; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt, 0)) return -1; - return apt.taskfile[GRUB_ATA_REG_SECTORS]; + return apt.taskfile.sectors; } static int -grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features) +grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features) { struct grub_disk_ata_pass_through_parms apt; grub_memset (&apt, 0, sizeof (apt)); - apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART; - apt.taskfile[GRUB_ATA_REG_FEATURES] = features; - apt.taskfile[GRUB_ATA_REG_LBAMID] = 0x4f; - apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2; + apt.taskfile.cmd = GRUB_ATA_CMD_SMART; + apt.taskfile.features = features; + apt.taskfile.lba_mid = 0x4f; + apt.taskfile.lba_high = 0xc2; + apt.taskfile.disk = 0xE0; - if (grub_disk_ata_pass_through (disk, &apt)) + if (ata->dev->readwrite (ata, &apt, 0)) return -1; if (features == GRUB_ATA_FEAT_SMART_STATUS) { - if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0x4f - && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2) + if ( apt.taskfile.lba_mid == 0x4f + && apt.taskfile.lba_high == 0xc2) return 0; /* Good SMART status. */ - else if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0xf4 - && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c) + else if ( apt.taskfile.lba_mid == 0xf4 + && apt.taskfile.lba_high == 0x2c) return 1; /* Bad SMART status. */ else return -1; @@ -124,12 +131,12 @@ grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features) static grub_err_t grub_hdparm_simple_cmd (const char * msg, - grub_disk_t disk, grub_uint8_t cmd) + grub_ata_t ata, grub_uint8_t cmd) { if (! quiet && msg) grub_printf ("%s", msg); - grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0); + grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, 0, 0, NULL, 0); if (! quiet && msg) grub_printf ("%s\n", ! err ? "" : ": not supported"); @@ -138,7 +145,7 @@ grub_hdparm_simple_cmd (const char * msg, static grub_err_t grub_hdparm_set_val_cmd (const char * msg, int val, - grub_disk_t disk, grub_uint8_t cmd, + grub_ata_t ata, grub_uint8_t cmd, grub_uint8_t features, grub_uint8_t sectors) { if (! quiet && msg && *msg) @@ -149,7 +156,7 @@ grub_hdparm_set_val_cmd (const char * msg, int val, grub_printf ("Disable %s", msg); } - grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors, + grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors, NULL, 0); if (! quiet && msg) @@ -158,22 +165,20 @@ grub_hdparm_set_val_cmd (const char * msg, int val, } static const char * -le16_to_char (char *dest, const grub_uint16_t * src16, unsigned bytes) +le16_to_char (grub_uint16_t *dest, const grub_uint16_t * src16, unsigned bytes) { - grub_uint16_t * dest16 = (grub_uint16_t *) dest; unsigned i; for (i = 0; i < bytes / 2; i++) - dest16[i] = grub_be_to_cpu16 (src16[i]); - return dest; + dest[i] = grub_swap_bytes16 (src16[i]); + dest[i] = 0; + return (char *) dest; } static void -grub_hdparm_print_identify (const char * idbuf) +grub_hdparm_print_identify (const grub_uint16_t * idw) { - const grub_uint16_t * idw = (const grub_uint16_t *) idbuf; - /* Print identity strings. */ - char tmp[40]; + grub_uint16_t tmp[21]; grub_printf ("Model: \"%.40s\"\n", le16_to_char (tmp, &idw[27], 40)); grub_printf ("Firmware: \"%.8s\"\n", le16_to_char (tmp, &idw[23], 8)); grub_printf ("Serial: \"%.20s\"\n", le16_to_char (tmp, &idw[10], 20)); @@ -270,21 +275,25 @@ static int get_int_arg (const struct grub_arg_list *state) } static grub_err_t -grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? +grub_cmd_hdparm (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; + struct grub_ata *ata; + const char *diskname; /* Check command line. */ if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); - grub_size_t len = grub_strlen (args[0]); - if (! (args[0][0] == '(' && args[0][len - 1] == ')')) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name"); - args[0][len - 1] = 0; - - if (! grub_disk_ata_pass_through) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available"); + if (args[0][0] == '(') + { + grub_size_t len = grub_strlen (args[0]); + if (args[0][len - 1] == ')') + args[0][len - 1] = 0; + diskname = &args[0][1]; + } + else + diskname = &args[0][0]; int i = 0; int apm = get_int_arg (&state[i++]); @@ -301,25 +310,43 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? quiet = state[i++].set; /* Open disk. */ - grub_disk_t disk = grub_disk_open (&args[0][1]); + grub_disk_t disk = grub_disk_open (diskname); if (! disk) return grub_errno; - if (disk->partition) + switch (disk->dev->id) { + case GRUB_DISK_DEVICE_ATA_ID: + ata = disk->data; + break; + case GRUB_DISK_DEVICE_SCSI_ID: + if (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF) + == GRUB_SCSI_SUBSYSTEM_PATA + || (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF) + == GRUB_SCSI_SUBSYSTEM_AHCI)) + { + ata = ((struct grub_scsi *) disk->data)->data; + break; + } + /* FALLTHROUGH */ + default: grub_disk_close (disk); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed"); + return grub_error (GRUB_ERR_IO, "not an ATA device"); } + /* Change settings. */ if (aam >= 0) grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1), - disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam); + ata, GRUB_ATA_CMD_SET_FEATURES, + (aam ? 0x42 : 0xc2), aam); if (apm >= 0) grub_hdparm_set_val_cmd ("Advanced Power Management", - (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES, - (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0)); + (apm != 255 ? apm : -1), ata, + GRUB_ATA_CMD_SET_FEATURES, + (apm != 255 ? 0x05 : 0x85), + (apm != 255 ? apm : 0)); if (standby_tout >= 0) { @@ -330,28 +357,28 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? grub_printf (")"); } /* The IDLE cmd sets disk to idle mode and configures standby timer. */ - grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout); + grub_hdparm_set_val_cmd ("", -1, ata, GRUB_ATA_CMD_IDLE, 0, standby_tout); } if (enable_smart >= 0) { if (! quiet) grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis")); - int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ? + int err = grub_hdparm_do_smart_cmd (ata, (enable_smart ? GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE)); if (! quiet) grub_printf ("%s\n", err ? ": not supported" : ""); } if (sec_freeze) - grub_hdparm_simple_cmd ("Freeze security settings", disk, + grub_hdparm_simple_cmd ("Freeze security settings", ata, GRUB_ATA_CMD_SECURITY_FREEZE_LOCK); /* Print/dump IDENTIFY. */ if (ident || dumpid) { - char buf[GRUB_DISK_SECTOR_SIZE]; - if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE, + grub_uint16_t buf[GRUB_DISK_SECTOR_SIZE / 2]; + if (grub_hdparm_do_ata_cmd (ata, GRUB_ATA_CMD_IDENTIFY_DEVICE, 0, 0, buf, sizeof (buf))) grub_printf ("Cannot read ATA IDENTIFY data\n"); else @@ -359,7 +386,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? if (ident) grub_hdparm_print_identify (buf); if (dumpid) - hexdump (0, buf, sizeof (buf)); + hexdump (0, (char *) buf, sizeof (buf)); } } @@ -367,7 +394,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? if (power) { grub_printf ("Disk power mode is: "); - int mode = grub_hdparm_do_check_powermode_cmd (disk); + int mode = grub_hdparm_do_check_powermode_cmd (ata); if (mode < 0) grub_printf ("unknown\n"); else @@ -383,7 +410,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? { if (! quiet) grub_printf ("SMART status is: "); - int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS); + int err = grub_hdparm_do_smart_cmd (ata, GRUB_ATA_FEAT_SMART_STATUS); if (! quiet) grub_printf ("%s\n", (err < 0 ? "unknown" : err == 0 ? "OK" : "*BAD*")); @@ -392,11 +419,11 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state???? /* Change power mode. */ if (standby_now) - grub_hdparm_simple_cmd ("Set disk to standby mode", disk, + grub_hdparm_simple_cmd ("Set disk to standby mode", ata, GRUB_ATA_CMD_STANDBY_IMMEDIATE); if (sleep_now) - grub_hdparm_simple_cmd ("Set disk to sleep mode", disk, + grub_hdparm_simple_cmd ("Set disk to sleep mode", ata, GRUB_ATA_CMD_SLEEP); grub_disk_close (disk); @@ -409,10 +436,9 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(hdparm) { - cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm, - GRUB_COMMAND_FLAG_BOTH, - N_("[OPTIONS] DISK"), - N_("Get/set ATA disk parameters."), options); + cmd = grub_register_extcmd_lockdown ("hdparm", grub_cmd_hdparm, 0, + N_("[OPTIONS] DISK"), + N_("Get/set ATA disk parameters."), options); } GRUB_MOD_FINI(hdparm) diff --git a/grub-core/commands/help.c b/grub-core/commands/help.c new file mode 100644 index 000000000..113d0d0ca --- /dev/null +++ b/grub-core/commands/help.c @@ -0,0 +1,155 @@ +/* help.c - command to show a help text. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_help (grub_extcmd_context_t ctxt __attribute__ ((unused)), int argc, + char **args) +{ + int cnt = 0; + char *currarg; + + if (argc == 0) + { + grub_command_t cmd; + FOR_COMMANDS(cmd) + { + if ((cmd->prio & GRUB_COMMAND_FLAG_ACTIVE)) + { + struct grub_term_output *term; + const char *summary_translated = _(cmd->summary); + char *command_help; + grub_uint32_t *unicode_command_help; + grub_uint32_t *unicode_last_position; + + command_help = grub_xasprintf ("%s %s", cmd->name, summary_translated); + if (!command_help) + break; + + grub_utf8_to_ucs4_alloc (command_help, &unicode_command_help, + &unicode_last_position); + + FOR_ACTIVE_TERM_OUTPUTS(term) + { + unsigned stringwidth; + grub_uint32_t *unicode_last_screen_position; + + unicode_last_screen_position = unicode_command_help; + + stringwidth = 0; + + while (unicode_last_screen_position < unicode_last_position && + stringwidth < ((grub_term_width (term) / 2) - 2)) + { + struct grub_unicode_glyph glyph; + unicode_last_screen_position + += grub_unicode_aglomerate_comb (unicode_last_screen_position, + unicode_last_position + - unicode_last_screen_position, + &glyph); + + stringwidth + += grub_term_getcharwidth (term, &glyph); + } + + grub_print_ucs4 (unicode_command_help, + unicode_last_screen_position, 0, 0, term); + if (!(cnt % 2)) + grub_print_spaces (term, grub_term_width (term) / 2 + - stringwidth); + } + + if (cnt % 2) + grub_printf ("\n"); + cnt++; + + grub_free (command_help); + grub_free (unicode_command_help); + } + } + if (!(cnt % 2)) + grub_printf ("\n"); + } + else + { + int i; + grub_command_t cmd_iter, cmd, cmd_next; + + for (i = 0; i < argc; i++) + { + currarg = args[i]; + + FOR_COMMANDS_SAFE (cmd_iter, cmd_next) + { + if (!(cmd_iter->prio & GRUB_COMMAND_FLAG_ACTIVE)) + continue; + + if (grub_strncmp (cmd_iter->name, currarg, + grub_strlen (currarg)) != 0) + continue; + if (cmd_iter->flags & GRUB_COMMAND_FLAG_DYNCMD) + cmd = grub_dyncmd_get_cmd (cmd_iter); + else + cmd = cmd_iter; + if (!cmd) + { + grub_print_error (); + continue; + } + if (cnt++ > 0) + grub_printf ("\n\n"); + + if ((cmd->flags & GRUB_COMMAND_FLAG_EXTCMD) && + ! (cmd->flags & GRUB_COMMAND_FLAG_DYNCMD)) + grub_arg_show_help ((grub_extcmd_t) cmd->data); + else + grub_printf ("%s %s %s\n%s\n", _("Usage:"), cmd->name, + _(cmd->summary), _(cmd->description)); + } + } + } + + grub_printf ("\n\nTo enable less(1)-like paging, \"set pager=1\".\n"); + + return 0; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(help) +{ + cmd = grub_register_extcmd ("help", grub_cmd_help, 0, + N_("[PATTERN ...]"), + N_("Show a help message."), 0); +} + +GRUB_MOD_FINI(help) +{ + grub_unregister_extcmd (cmd); +} diff --git a/commands/hexdump.c b/grub-core/commands/hexdump.c similarity index 81% rename from commands/hexdump.c rename to grub-core/commands/hexdump.c index c1d4ecba9..d6f61d98a 100644 --- a/commands/hexdump.c +++ b/grub-core/commands/hexdump.c @@ -21,10 +21,12 @@ #include #include #include -#include #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = { {"skip", 's', 0, N_("Skip offset bytes from the beginning of file."), 0, @@ -34,23 +36,27 @@ static const struct grub_arg_option options[] = { }; static grub_err_t -grub_cmd_hexdump (grub_extcmd_t cmd, int argc, char **args) +grub_cmd_hexdump (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; char buf[GRUB_DISK_SECTOR_SIZE * 4]; grub_ssize_t size, length; grub_disk_addr_t skip; int namelen; if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); namelen = grub_strlen (args[0]); skip = (state[0].set) ? grub_strtoull (state[0].arg, 0, 0) : 0; length = (state[1].set) ? grub_strtoul (state[1].arg, 0, 0) : 256; if (!grub_strcmp (args[0], "(mem)")) - hexdump (skip, (char *) (grub_addr_t) skip, length); + { + if (grub_is_lockdown() == GRUB_LOCKDOWN_ENABLED) + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("memory reading is disabled in lockdown mode")); + hexdump (skip, (char *) (grub_addr_t) skip, length); + } else if ((args[0][0] == '(') && (args[0][namelen - 1] == ')')) { grub_disk_t disk; @@ -89,7 +95,7 @@ grub_cmd_hexdump (grub_extcmd_t cmd, int argc, char **args) { grub_file_t file; - file = grub_gzfile_open (args[0], 1); + file = grub_file_open (args[0], GRUB_FILE_TYPE_HEXCAT); if (! file) return 0; @@ -120,10 +126,9 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT (hexdump) { - cmd = grub_register_extcmd ("hexdump", grub_cmd_hexdump, - GRUB_COMMAND_FLAG_BOTH, + cmd = grub_register_extcmd ("hexdump", grub_cmd_hexdump, 0, N_("[OPTIONS] FILE_OR_DEVICE"), - N_("Dump the contents of a file or memory."), + N_("Show raw contents of a file or memory."), options); } diff --git a/commands/lsmmap.c b/grub-core/commands/i386/cmosdump.c similarity index 53% rename from commands/lsmmap.c rename to grub-core/commands/i386/cmosdump.c index d5eef1ce9..626485ccb 100644 --- a/commands/lsmmap.c +++ b/grub-core/commands/i386/cmosdump.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2009,2013 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 @@ -16,38 +16,49 @@ * along with GRUB. If not, see . */ -#include #include -#include #include +#include +#include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_err_t -grub_cmd_lsmmap (grub_command_t cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) - +grub_cmd_cmosdump (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { - 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) - { - grub_printf ("base_addr = 0x%llx, length = 0x%llx, type = 0x%x\n", - (long long) addr, (long long) size, type); - return 0; - } - grub_machine_mmap_iterate (hook); + int i; - return 0; + for (i = 0; i < 256; i++) + { + grub_err_t err; + grub_uint8_t value; + if ((i & 0xf) == 0) + grub_printf ("%02x: ", i); + + err = grub_cmos_read (i, &value); + if (err) + return err; + + grub_printf ("%02x ", value); + if ((i & 0xf) == 0xf) + grub_printf ("\n"); + } + return GRUB_ERR_NONE; } static grub_command_t cmd; + -GRUB_MOD_INIT(lsmmap) +GRUB_MOD_INIT(cmosdump) { - cmd = grub_register_command ("lsmmap", grub_cmd_lsmmap, - 0, N_("List memory map provided by firmware.")); + cmd = grub_register_command ("cmosdump", grub_cmd_cmosdump, + 0, + N_("Show raw dump of the CMOS contents.")); } -GRUB_MOD_FINI(lsmmap) +GRUB_MOD_FINI(cmosdump) { grub_unregister_command (cmd); } diff --git a/grub-core/commands/i386/cmostest.c b/grub-core/commands/i386/cmostest.c new file mode 100644 index 000000000..1f0c5341d --- /dev/null +++ b/grub-core/commands/i386/cmostest.c @@ -0,0 +1,124 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +parse_args (int argc, char *argv[], int *byte, int *bit) +{ + const char *rest; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "address required"); + + *byte = grub_strtoul (argv[0], &rest, 0); + if (*rest != ':') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "address required"); + + *bit = grub_strtoul (rest + 1, 0, 0); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_cmostest (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + int byte = 0, bit = 0; + grub_err_t err; + grub_uint8_t value; + + err = parse_args (argc, argv, &byte, &bit); + if (err) + return err; + + err = grub_cmos_read (byte, &value); + if (err) + return err; + + if (value & (1 << bit)) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); +} + +static grub_err_t +grub_cmd_cmosclean (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + int byte = 0, bit = 0; + grub_err_t err; + grub_uint8_t value; + + err = parse_args (argc, argv, &byte, &bit); + if (err) + return err; + err = grub_cmos_read (byte, &value); + if (err) + return err; + + return grub_cmos_write (byte, value & (~(1 << bit))); +} + +static grub_err_t +grub_cmd_cmosset (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + int byte = 0, bit = 0; + grub_err_t err; + grub_uint8_t value; + + err = parse_args (argc, argv, &byte, &bit); + if (err) + return err; + err = grub_cmos_read (byte, &value); + if (err) + return err; + + return grub_cmos_write (byte, value | (1 << bit)); +} + +static grub_command_t cmd, cmd_clean, cmd_set; + + +GRUB_MOD_INIT(cmostest) +{ + cmd = grub_register_command_lockdown ("cmostest", grub_cmd_cmostest, + N_("BYTE:BIT"), + N_("Test bit at BYTE:BIT in CMOS.")); + cmd_clean = grub_register_command_lockdown ("cmosclean", grub_cmd_cmosclean, + N_("BYTE:BIT"), + N_("Clear bit at BYTE:BIT in CMOS.")); + cmd_set = grub_register_command_lockdown ("cmosset", grub_cmd_cmosset, + N_("BYTE:BIT"), + /* TRANSLATORS: A bit may be either set (1) or clear (0). */ + N_("Set bit at BYTE:BIT in CMOS.")); +} + +GRUB_MOD_FINI(cmostest) +{ + grub_unregister_command (cmd); + grub_unregister_command (cmd_clean); + grub_unregister_command (cmd_set); +} diff --git a/grub-core/commands/i386/coreboot/cb_timestamps.c b/grub-core/commands/i386/coreboot/cb_timestamps.c new file mode 100644 index 000000000..c44abb3fa --- /dev/null +++ b/grub-core/commands/i386/coreboot/cb_timestamps.c @@ -0,0 +1,126 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint32_t +tsc2ms (grub_uint64_t tsc) +{ + grub_uint64_t ah = tsc >> 32; + grub_uint64_t al = tsc & 0xffffffff; + + return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; +} + +static const char *descs[] = { + [1] = "romstage", + [2] = "before RAM init", + [3] = "after RAM init", + [4] = "end of romstage", + [5] = "start of verified boot", + [6] = "end of verified boot", + [8] = "start of RAM copy", + [9] = "end of RAM copy", + [10] = "start of ramstage", + [11] = "start of bootblock", + [12] = "end of bootblock", + [13] = "starting to load romstage", + [14] = "finished loading romstage", + [15] = "starting LZMA decompress (ignore for x86)", + [16] = "finished LZMA decompress (ignore for x86)", + [30] = "device enumerate", + [40] = "device configure", + [50] = "device enable", + [60] = "device initialize", + [70] = "device done", + [75] = "CBMEM POST", + [80] = "writing tables", + [90] = "loading payload", + [98] = "wake jump", + [99] = "selfboot jump", +}; + +static int +iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, + void *data) +{ + int *available = data; + grub_uint64_t last_tsc = 0; + struct grub_linuxbios_timestamp_table *ts_table; + unsigned i; + + if (table_item->tag != GRUB_LINUXBIOS_MEMBER_TIMESTAMPS) + return 0; + + *available = 1; + ts_table = (struct grub_linuxbios_timestamp_table *) (grub_addr_t) + *(grub_uint64_t *) (table_item + 1); + + for (i = 0; i < ts_table->used; i++) + { + grub_uint32_t tmabs = tsc2ms (ts_table->entries[i].tsc); + grub_uint32_t tmrel = tsc2ms (ts_table->entries[i].tsc - last_tsc); + last_tsc = ts_table->entries[i].tsc; + + grub_printf ("%3d.%03ds %2d.%03ds %02d %s\n", + tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000, + ts_table->entries[i].id, + (ts_table->entries[i].id < ARRAY_SIZE (descs) + && descs[ts_table->entries[i].id]) + ? descs[ts_table->entries[i].id] : ""); + } + return 1; +} + + +static grub_err_t +grub_cmd_coreboot_boottime (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + int available = 0; + + grub_linuxbios_table_iterate (iterate_linuxbios_table, &available); + if (!available) + { + grub_puts_ (N_("No boot time statistics is available\n")); + return 0; + } + return 0; +} + +static grub_command_t cmd_boottime; + +GRUB_MOD_INIT(cbtime) +{ + cmd_boottime = + grub_register_command ("coreboot_boottime", grub_cmd_coreboot_boottime, + 0, N_("Show coreboot boot time statistics.")); +} + +GRUB_MOD_FINI(cbtime) +{ + grub_unregister_command (cmd_boottime); +} diff --git a/grub-core/commands/i386/coreboot/cbls.c b/grub-core/commands/i386/coreboot/cbls.c new file mode 100644 index 000000000..50b7b8750 --- /dev/null +++ b/grub-core/commands/i386/coreboot/cbls.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const char *console_descs[] = { + "8250 UART", + "VGA", + "BTEXT", + "log buffer console", + "SROM", + "EHCI debug", + "memory-mapped 8250 UART" +}; + +static const char *descs[] = { + [GRUB_LINUXBIOS_MEMBER_MEMORY] = "memory map (`lsmmap' to list)", + [GRUB_LINUXBIOS_MEMBER_MAINBOARD] = "mainboard", + [4] = "version", + [5] = "extra version", + [6] = "build", + [7] = "compile time", + [8] = "compile by", + [9] = "compile host", + [0xa] = "compile domain", + [0xb] = "compiler", + [0xc] = "linker", + [0xd] = "assembler", + [0xf] = "serial", + [GRUB_LINUXBIOS_MEMBER_CONSOLE] = "console", + [GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER] = "framebuffer", + [0x13] = "GPIO", + [0x15] = "VDAT", + [GRUB_LINUXBIOS_MEMBER_TIMESTAMPS] = "timestamps (`coreboot_boottime' to list)", + [GRUB_LINUXBIOS_MEMBER_CBMEMC] = "CBMEM console (`cbmemc' to list)", + [0x18] = "MRC cache", + [0x19] = "VBNV", + [0xc8] = "CMOS option table", + [0xc9] = "CMOS option", + [0xca] = "CMOS option enum", + [0xcb] = "CMOS option defaults", + [0xcc] = "CMOS checksum", +}; + +static int +iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, + void *data __attribute__ ((unused))) +{ + if (table_item->tag < ARRAY_SIZE (descs) && descs[table_item->tag]) + grub_printf ("tag=%02x size=%02x %s", + table_item->tag, table_item->size, descs[table_item->tag]); + else + grub_printf ("tag=%02x size=%02x", + table_item->tag, table_item->size); + + switch (table_item->tag) + { + case GRUB_LINUXBIOS_MEMBER_FRAMEBUFFER: + { + struct grub_linuxbios_table_framebuffer *fb; + fb = (struct grub_linuxbios_table_framebuffer *) (table_item + 1); + + grub_printf (": %dx%dx%d pitch=%d lfb=0x%llx %d/%d/%d/%d %d/%d/%d/%d", + fb->width, fb->height, + fb->bpp, fb->pitch, + (unsigned long long) fb->lfb, + fb->red_mask_size, fb->green_mask_size, + fb->blue_mask_size, fb->reserved_mask_size, + fb->red_field_pos, fb->green_field_pos, + fb->blue_field_pos, fb->reserved_field_pos); + break; + } + case GRUB_LINUXBIOS_MEMBER_MAINBOARD: + { + struct grub_linuxbios_mainboard *mb; + mb = (struct grub_linuxbios_mainboard *) (table_item + 1); + grub_printf (": vendor=`%s' part_number=`%s'", + mb->strings + mb->vendor, + mb->strings + mb->part_number); + break; + } + case 0x04 ... 0x0d: + grub_printf (": `%s'", (char *) (table_item + 1)); + break; + case GRUB_LINUXBIOS_MEMBER_CONSOLE: + { + grub_uint16_t *val = (grub_uint16_t *) (table_item + 1); + grub_printf (": id=%d", *val); + if (*val < ARRAY_SIZE (console_descs) + && console_descs[*val]) + grub_printf (" %s", console_descs[*val]); + } + } + grub_printf ("\n"); + + return 0; +} + + +static grub_err_t +grub_cmd_lscoreboot (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_linuxbios_table_iterate (iterate_linuxbios_table, 0); + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(cbls) +{ + cmd = + grub_register_command ("lscoreboot", grub_cmd_lscoreboot, + 0, N_("List coreboot tables.")); +} + +GRUB_MOD_FINI(cbls) +{ + grub_unregister_command (cmd); +} diff --git a/commands/i386/cpuid.c b/grub-core/commands/i386/cpuid.c similarity index 63% rename from commands/i386/cpuid.c rename to grub-core/commands/i386/cpuid.c index 6eebf91a1..42b984154 100644 --- a/commands/i386/cpuid.c +++ b/grub-core/commands/i386/cpuid.c @@ -27,28 +27,48 @@ #include #include -#define cpuid(num,a,b,c,d) \ - asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "0" (num)) +GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = { - {"long-mode", 'l', 0, N_("Check for long mode flag (default)."), 0, 0}, + /* TRANSLATORS: "(default)" at the end means that this option is used if + no argument is specified. */ + {"long-mode", 'l', 0, N_("Check if CPU supports 64-bit (long) mode (default)."), 0, 0}, + {"pae", 'p', 0, N_("Check if CPU supports Physical Address Extension."), 0, 0}, {0, 0, 0, 0, 0, 0} }; -#define bit_LM (1 << 29) +enum + { + MODE_LM = 0, + MODE_PAE = 1 + }; -unsigned char grub_cpuid_has_longmode = 0; +enum + { + bit_PAE = (1 << 6), + }; +enum + { + bit_LM = (1 << 29) + }; + +unsigned char grub_cpuid_has_longmode = 0, grub_cpuid_has_pae = 0; static grub_err_t -grub_cmd_cpuid (grub_extcmd_t cmd __attribute__ ((unused)), +grub_cmd_cpuid (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - return grub_cpuid_has_longmode ? GRUB_ERR_NONE - : grub_error (GRUB_ERR_TEST_FAILURE, "false"); + int val = 0; + if (ctxt->state[MODE_PAE].set) + val = grub_cpuid_has_pae; + else + val = grub_cpuid_has_longmode; + return val ? GRUB_ERR_NONE + /* TRANSLATORS: it's a standalone boolean value, + opposite of "true". */ + : grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } static grub_extcmd_t cmd; @@ -58,6 +78,7 @@ GRUB_MOD_INIT(cpuid) #ifdef __x86_64__ /* grub-emu */ grub_cpuid_has_longmode = 1; + grub_cpuid_has_pae = 1; #else unsigned int eax, ebx, ecx, edx; unsigned int max_level; @@ -72,23 +93,29 @@ GRUB_MOD_INIT(cpuid) goto done; /* Check the highest input value for eax. */ - cpuid (0, eax, ebx, ecx, edx); + grub_cpuid (0, eax, ebx, ecx, edx); /* We only look at the first four characters. */ max_level = eax; if (max_level == 0) goto done; - cpuid (0x80000000, eax, ebx, ecx, edx); + if (max_level >= 1) + { + grub_cpuid (1, eax, ebx, ecx, edx); + grub_cpuid_has_pae = !!(edx & bit_PAE); + } + + grub_cpuid (0x80000000, eax, ebx, ecx, edx); ext_level = eax; if (ext_level < 0x80000000) goto done; - cpuid (0x80000001, eax, ebx, ecx, edx); + grub_cpuid (0x80000001, eax, ebx, ecx, edx); grub_cpuid_has_longmode = !!(edx & bit_LM); done: #endif - cmd = grub_register_extcmd ("cpuid", grub_cmd_cpuid, GRUB_COMMAND_FLAG_BOTH, + cmd = grub_register_extcmd ("cpuid", grub_cmd_cpuid, 0, "[-l]", N_("Check for CPU features."), options); } diff --git a/commands/i386/pc/drivemap.c b/grub-core/commands/i386/pc/drivemap.c similarity index 87% rename from commands/i386/pc/drivemap.c rename to grub-core/commands/i386/pc/drivemap.c index 3baacba49..a7ee4c9bd 100644 --- a/commands/i386/pc/drivemap.c +++ b/grub-core/commands/i386/pc/drivemap.c @@ -24,16 +24,19 @@ #include #include #include -#include #include #include +#include +#include - -/* Real mode IVT slot (seg:off far pointer) for interrupt 0x13. */ -static grub_uint32_t *const int13slot = UINT_TO_PTR (4 * 0x13); +GRUB_MOD_LICENSE ("GPLv3+"); /* Remember to update enum opt_idxs accordingly. */ static const struct grub_arg_option options[] = { + /* TRANSLATORS: In this file "mapping" refers to a change GRUB makes so if + your language doesn't have an equivalent of "mapping" you can + use the word like "rerouting". + */ {"list", 'l', 0, N_("Show the current mappings."), 0, 0}, {"reset", 'r', 0, N_("Reset all mappings to the default values."), 0, 0}, {"swap", 's', 0, N_("Perform both direct and reverse mappings."), 0, 0}, @@ -68,7 +71,7 @@ typedef struct drivemap_node grub_uint8_t redirto; } drivemap_node_t; -typedef struct __attribute__ ((packed)) int13map_node +typedef struct GRUB_PACKED int13map_node { grub_uint8_t disknum; grub_uint8_t mapto; @@ -105,8 +108,7 @@ drivemap_set (grub_uint8_t newdrive, grub_uint8_t redirto) { mapping = grub_malloc (sizeof (drivemap_node_t)); if (! mapping) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "cannot allocate map entry, not enough memory"); + return grub_errno; mapping->newdrive = newdrive; mapping->redirto = redirto; mapping->next = map_head; @@ -176,11 +178,14 @@ list_mappings (void) /* Show: list mappings. */ if (! map_head) { - grub_printf ("No drives have been remapped\n"); + grub_puts_ (N_("No drives have been remapped")); return GRUB_ERR_NONE; } - grub_printf ("OS disk #num ------> GRUB/BIOS device\n"); + /* TRANSLATORS: This is the header of mapping list. + On the left is how OS will see the disks and + on the right current GRUB vision. */ + grub_puts_ (N_("OS disk #num ------> GRUB/BIOS device")); drivemap_node_t *curnode = map_head; while (curnode) { @@ -196,13 +201,13 @@ list_mappings (void) } static grub_err_t -grub_cmd_drivemap (struct grub_extcmd *cmd, int argc, char **args) +grub_cmd_drivemap (struct grub_extcmd_context *ctxt, int argc, char **args) { - if (cmd->state[OPTIDX_LIST].set) + if (ctxt->state[OPTIDX_LIST].set) { return list_mappings (); } - else if (cmd->state[OPTIDX_RESET].set) + else if (ctxt->state[OPTIDX_RESET].set) { /* Reset: just delete all mappings, freeing their memory. */ drivemap_node_t *curnode = map_head; @@ -216,7 +221,7 @@ grub_cmd_drivemap (struct grub_extcmd *cmd, int argc, char **args) map_head = 0; return GRUB_ERR_NONE; } - else if (!cmd->state[OPTIDX_SWAP].set && argc == 0) + else if (!ctxt->state[OPTIDX_SWAP].set && argc == 0) { /* No arguments */ return list_mappings (); @@ -228,7 +233,7 @@ grub_cmd_drivemap (struct grub_extcmd *cmd, int argc, char **args) grub_err_t err; if (argc != 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); err = tryparse_diskstring (args[0], &mapfrom); if (err != GRUB_ERR_NONE) @@ -248,11 +253,11 @@ grub_cmd_drivemap (struct grub_extcmd *cmd, int argc, char **args) } /* Set the mapping for the disk (overwrites any existing mapping). */ grub_dprintf ("drivemap", "%s %s (%02x) = %s (%02x)\n", - cmd->state[OPTIDX_SWAP].set ? "Swapping" : "Mapping", + ctxt->state[OPTIDX_SWAP].set ? "Swapping" : "Mapping", args[1], mapto, args[0], mapfrom); err = drivemap_set (mapto, mapfrom); /* If -s, perform the reverse mapping too (only if the first was OK). */ - if (cmd->state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE) + if (ctxt->state[OPTIDX_SWAP].set && err == GRUB_ERR_NONE) err = drivemap_set (mapfrom, mapto); return err; } @@ -272,6 +277,8 @@ install_int13_handler (int noret __attribute__ ((unused))) grub_uint8_t *handler_base = 0; /* Address of the map within the deployed bundle. */ int13map_node_t *handler_map; + /* Real mode IVT slot (seg:off far pointer) for interrupt 0x13. */ + grub_uint32_t *int13slot = (grub_uint32_t *) grub_absolute_pointer (4 * 0x13); int i; int entries = 0; @@ -304,9 +311,9 @@ install_int13_handler (int noret __attribute__ ((unused))) total_size = INT13H_OFFSET (&grub_drivemap_mapstart) + (entries + 1) * sizeof (int13map_node_t); grub_dprintf ("drivemap", "Payload is %u bytes long\n", total_size); - handler_base = grub_mmap_malign_and_register (16, total_size, + handler_base = grub_mmap_malign_and_register (16, ALIGN_UP (total_size, 16), &drivemap_mmap, - GRUB_MACHINE_MEMORY_RESERVED, + GRUB_MEMORY_RESERVED, GRUB_MMAP_MALLOC_LOW); if (! handler_base) return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reserve " @@ -346,6 +353,9 @@ install_int13_handler (int noret __attribute__ ((unused))) static grub_err_t uninstall_int13_handler (void) { + /* Real mode IVT slot (seg:off far pointer) for interrupt 0x13. */ + grub_uint32_t *int13slot = (grub_uint32_t *) grub_absolute_pointer (4 * 0x13); + if (! grub_drivemap_oldhandler) return GRUB_ERR_NONE; @@ -361,7 +371,7 @@ uninstall_int13_handler (void) static int grub_get_root_biosnumber_drivemap (void) { - char *biosnum; + const char *biosnum; int ret = -1; grub_device_t dev; @@ -401,12 +411,10 @@ GRUB_MOD_INIT (drivemap) { grub_get_root_biosnumber_saved = grub_get_root_biosnumber; grub_get_root_biosnumber = grub_get_root_biosnumber_drivemap; - cmd = grub_register_extcmd ("drivemap", grub_cmd_drivemap, - GRUB_COMMAND_FLAG_BOTH, - "drivemap" - N_("-l | -r | [-s] grubdev osdisk."), - N_("Manage the BIOS drive mappings."), - options); + cmd = grub_register_extcmd ("drivemap", grub_cmd_drivemap, 0, + N_("-l | -r | [-s] grubdev osdisk."), + N_("Manage the BIOS drive mappings."), + options); drivemap_hook = grub_loader_register_preboot_hook (&install_int13_handler, &uninstall_int13_handler, diff --git a/commands/i386/pc/drivemap_int13h.S b/grub-core/commands/i386/pc/drivemap_int13h.S similarity index 80% rename from commands/i386/pc/drivemap_int13h.S rename to grub-core/commands/i386/pc/drivemap_int13h.S index 440349685..3c87521b6 100644 --- a/commands/i386/pc/drivemap_int13h.S +++ b/grub-core/commands/i386/pc/drivemap_int13h.S @@ -19,7 +19,7 @@ #include -#define INT13H_OFFSET(x) ((x) - EXT_C(grub_drivemap_handler)) +#define INT13H_OFFSET(x) ((x) - LOCAL (base)) .code16 @@ -27,6 +27,7 @@ /* The replacement int13 handler. Preserve all registers. */ FUNCTION(grub_drivemap_handler) +LOCAL (base): /* Save %dx for future restore. */ push %dx /* Push flags. Used to simulate interrupt with original flags. */ @@ -35,11 +36,11 @@ FUNCTION(grub_drivemap_handler) /* Map the drive number (always in DL). */ push %ax push %bx -#ifdef APPLE_CC - grub_drivemap_mapstart_ofs = INT13H_OFFSET(EXT_C(grub_drivemap_mapstart)) - movw $grub_drivemap_mapstart_ofs, %bx +#ifdef __APPLE__ + LOCAL(mapstart_offset) = INT13H_OFFSET(LOCAL (mapstart)) + movw $LOCAL(mapstart_offset), %bx #else - movw $INT13H_OFFSET(EXT_C(grub_drivemap_mapstart)), %bx + movw $INT13H_OFFSET(LOCAL (mapstart)), %bx #endif more_remaining: @@ -66,11 +67,11 @@ not_found: popf pushf -#ifdef APPLE_CC - grub_drivemap_oldhandler_ofs = INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) - lcall *%cs:grub_drivemap_oldhandler_ofs +#ifdef __APPLE__ + LOCAL(oldhandler_offset) = INT13H_OFFSET (LOCAL (oldhandler)) + lcall *%cs:LOCAL(oldhandler_offset) #else - lcall *%cs:INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) + lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler)) #endif push %bp @@ -94,10 +95,10 @@ norestore: popf pushf -#ifdef APPLE_CC - lcall *%cs:grub_drivemap_oldhandler_ofs +#ifdef __APPLE__ + lcall *%cs:LOCAL(oldhandler_offset) #else - lcall *%cs:INT13H_OFFSET (EXT_C (grub_drivemap_oldhandler)) + lcall *%cs:INT13H_OFFSET (LOCAL (oldhandler)) #endif push %bp @@ -111,9 +112,13 @@ norestore: /* Far pointer to the old handler. Stored as a CS:IP in the style of real-mode IVT entries (thus PI:SC in mem). */ VARIABLE(grub_drivemap_oldhandler) +LOCAL (oldhandler): .word 0x0, 0x0 /* This label MUST be at the end of the copied block, since the installer code reserves additional space for mappings at runtime and copies them over it. */ -.align 2 + .align 2 + VARIABLE(grub_drivemap_mapstart) +LOCAL (mapstart): + .byte 0 diff --git a/grub-core/commands/i386/pc/halt.c b/grub-core/commands/i386/pc/halt.c new file mode 100644 index 000000000..e87e8dccd --- /dev/null +++ b/grub-core/commands/i386/pc/halt.c @@ -0,0 +1,126 @@ +/* halt.c - command to halt the computer. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + {"no-apm", 'n', 0, N_("Do not use APM to halt the computer."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +static inline void __attribute__ ((noreturn)) +stop (void) +{ + while (1) + { + asm volatile ("hlt"); + } +} +/* + * Halt the system, using APM if possible. If NO_APM is true, don't use + * APM even if it is available. + */ +void __attribute__ ((noreturn)) +grub_halt (int no_apm) +{ + struct grub_bios_int_registers regs; + + grub_acpi_halt (); + + if (no_apm) + stop (); + + /* detect APM */ + regs.eax = 0x5300; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + stop (); + + /* disconnect APM first */ + regs.eax = 0x5304; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + /* connect APM */ + regs.eax = 0x5301; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + stop (); + + /* set APM protocol level - 1.1 or bust. (this covers APM 1.2 also) */ + regs.eax = 0x530E; + regs.ebx = 0; + regs.ecx = 0x0101; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + stop (); + + /* set the power state to off */ + regs.eax = 0x5307; + regs.ebx = 1; + regs.ecx = 3; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + /* shouldn't reach here */ + stop (); +} + +static grub_err_t __attribute__ ((noreturn)) +grub_cmd_halt (grub_extcmd_context_t ctxt, + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) + +{ + struct grub_arg_list *state = ctxt->state; + int no_apm = 0; + + if (state[0].set) + no_apm = 1; + grub_halt (no_apm); +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(halt) +{ + cmd = grub_register_extcmd ("halt", grub_cmd_halt, 0, "[-n]", + N_("Halt the system, if possible using APM."), + options); +} + +GRUB_MOD_FINI(halt) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/i386/pc/lsapm.c b/grub-core/commands/i386/pc/lsapm.c new file mode 100644 index 000000000..8f49880da --- /dev/null +++ b/grub-core/commands/i386/pc/lsapm.c @@ -0,0 +1,115 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +int +grub_apm_get_info (struct grub_apm_info *info) +{ + struct grub_bios_int_registers regs; + + /* detect APM */ + regs.eax = 0x5300; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + return 0; + info->version = regs.eax & 0xffff; + info->flags = regs.ecx & 0xffff; + + /* disconnect APM first */ + regs.eax = 0x5304; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + /* connect APM */ + regs.eax = 0x5303; + regs.ebx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + return 0; + + info->cseg = regs.eax & 0xffff; + info->offset = regs.ebx; + info->cseg_16 = regs.ecx & 0xffff; + info->dseg = regs.edx & 0xffff; + info->cseg_len = regs.esi >> 16; + info->cseg_16_len = regs.esi & 0xffff; + info->dseg_len = regs.edi; + + return 1; +} + +static grub_err_t +grub_cmd_lsapm (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) +{ + struct grub_apm_info info; + if (!grub_apm_get_info (&info)) + return grub_error (GRUB_ERR_IO, N_("no APM found")); + + grub_printf_ (N_("Version %u.%u\n" + "32-bit CS = 0x%x, len = 0x%x, offset = 0x%x\n" + "16-bit CS = 0x%x, len = 0x%x\n" + "DS = 0x%x, len = 0x%x\n"), + info.version >> 8, info.version & 0xff, + info.cseg, info.cseg_len, info.offset, + info.cseg_16, info.cseg_16_len, + info.dseg, info.dseg_len); + grub_xputs (info.flags & GRUB_APM_FLAGS_16BITPROTECTED_SUPPORTED + ? _("16-bit protected interface supported\n") + : _("16-bit protected interface unsupported\n")); + grub_xputs (info.flags & GRUB_APM_FLAGS_32BITPROTECTED_SUPPORTED + ? _("32-bit protected interface supported\n") + : _("32-bit protected interface unsupported\n")); + grub_xputs (info.flags & GRUB_APM_FLAGS_CPUIDLE_SLOWS_DOWN + ? _("CPU Idle slows down processor\n") + : _("CPU Idle doesn't slow down processor\n")); + grub_xputs (info.flags & GRUB_APM_FLAGS_DISABLED + ? _("APM disabled\n") : _("APM enabled\n")); + grub_xputs (info.flags & GRUB_APM_FLAGS_DISENGAGED + ? _("APM disengaged\n") : _("APM engaged\n")); + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsapm) +{ + cmd = grub_register_command ("lsapm", grub_cmd_lsapm, 0, + N_("Show APM information.")); +} + +GRUB_MOD_FINI(lsapm) +{ + grub_unregister_command (cmd); +} + + diff --git a/grub-core/commands/i386/pc/play.c b/grub-core/commands/i386/pc/play.c new file mode 100644 index 000000000..7ff8cd633 --- /dev/null +++ b/grub-core/commands/i386/pc/play.c @@ -0,0 +1,197 @@ +/* play.c - command to play a tune */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +/* Lots of this file is borrowed from GNU/Hurd generic-speaker driver. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define BASE_TEMPO (60 * 1000) + + +#define T_REST ((grub_uint16_t) 0) +#define T_FINE ((grub_uint16_t) -1) + +struct note +{ + grub_uint16_t pitch; + grub_uint16_t duration; +}; + +/* Returns whether playing should continue. */ +static int +play (unsigned tempo, struct note *note) +{ + grub_uint64_t to; + + if (note->pitch == T_FINE || grub_getkey_noblock () != GRUB_TERM_NO_KEY) + return 1; + + grub_dprintf ("play", "pitch = %d, duration = %d\n", note->pitch, + note->duration); + + switch (note->pitch) + { + case T_REST: + grub_speaker_beep_off (); + break; + + default: + grub_speaker_beep_on (note->pitch); + break; + } + + to = grub_get_time_ms () + BASE_TEMPO * note->duration / tempo; + while ((grub_get_time_ms () <= to) + && (grub_getkey_noblock () == GRUB_TERM_NO_KEY)); + + return 0; +} + +static grub_err_t +grub_cmd_play (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + /* TRANSLATORS: It's musical notes, not the notes + you take. Play command expects arguments which can + be either a filename or tempo+notes. + This error happens if none is specified. */ + N_("filename or tempo and notes expected")); + + if (argc == 1) + { + struct note buf; + grub_uint32_t tempo; + grub_file_t file; + + file = grub_file_open (args[0], GRUB_FILE_TYPE_AUDIO); + + if (! file) + return grub_errno; + + if (grub_file_read (file, &tempo, sizeof (tempo)) != sizeof (tempo)) + { + grub_file_close (file); + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + args[0]); + return grub_errno; + } + + if (!tempo) + { + grub_file_close (file); + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid tempo in %s"), + args[0]); + return grub_errno; + } + + tempo = grub_le_to_cpu32 (tempo); + grub_dprintf ("play","tempo = %d\n", tempo); + + while (grub_file_read (file, &buf, + sizeof (struct note)) == sizeof (struct note)) + { + buf.pitch = grub_le_to_cpu16 (buf.pitch); + buf.duration = grub_le_to_cpu16 (buf.duration); + + if (play (tempo, &buf)) + break; + } + + grub_file_close (file); + } + else + { + const char *end; + unsigned tempo; + struct note note; + int i; + + tempo = grub_strtoul (args[0], &end, 0); + + if (!tempo) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Invalid tempo in %s"), + args[0]); + return grub_errno; + } + + if (*end) + /* Was not a number either, assume it was supposed to be a file name. */ + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), args[0]); + + grub_dprintf ("play","tempo = %d\n", tempo); + + for (i = 1; i + 1 < argc; i += 2) + { + note.pitch = grub_strtoul (args[i], &end, 0); + if (grub_errno) + break; + if (*end) + { + grub_error (GRUB_ERR_BAD_NUMBER, N_("unrecognized number")); + break; + } + + note.duration = grub_strtoul (args[i + 1], &end, 0); + if (grub_errno) + break; + if (*end) + { + grub_error (GRUB_ERR_BAD_NUMBER, N_("unrecognized number")); + break; + } + + if (play (tempo, ¬e)) + break; + } + } + + grub_speaker_beep_off (); + + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(play) +{ + cmd = grub_register_command ("play", grub_cmd_play, + N_("FILE | TEMPO [PITCH1 DURATION1] [PITCH2 DURATION2] ... "), + N_("Play a tune.")); +} + +GRUB_MOD_FINI(play) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/i386/pc/sendkey.c b/grub-core/commands/i386/pc/sendkey.c new file mode 100644 index 000000000..282bb5d42 --- /dev/null +++ b/grub-core/commands/i386/pc/sendkey.c @@ -0,0 +1,387 @@ +/* sendkey.c - fake keystroke. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv2+"); + +static char sendkey[0x20]; +/* Length of sendkey. */ +static int keylen = 0; +static int noled = 0; +static const struct grub_arg_option options[] = + { + {"num", 'n', 0, N_("set numlock mode"), "[on|off]", ARG_TYPE_STRING}, + {"caps", 'c', 0, N_("set capslock mode"), "[on|off]", ARG_TYPE_STRING}, + {"scroll", 's', 0, N_("set scrolllock mode"), "[on|off]", ARG_TYPE_STRING}, + {"insert", 0, 0, N_("set insert mode"), "[on|off]", ARG_TYPE_STRING}, + {"pause", 0, 0, N_("set pause mode"), "[on|off]", ARG_TYPE_STRING}, + {"left-shift", 0, 0, N_("press left shift"), "[on|off]", ARG_TYPE_STRING}, + {"right-shift", 0, 0, N_("press right shift"), "[on|off]", ARG_TYPE_STRING}, + {"sysrq", 0, 0, N_("press SysRq"), "[on|off]", ARG_TYPE_STRING}, + {"numkey", 0, 0, N_("press NumLock key"), "[on|off]", ARG_TYPE_STRING}, + {"capskey", 0, 0, N_("press CapsLock key"), "[on|off]", ARG_TYPE_STRING}, + {"scrollkey", 0, 0, N_("press ScrollLock key"), "[on|off]", ARG_TYPE_STRING}, + {"insertkey", 0, 0, N_("press Insert key"), "[on|off]", ARG_TYPE_STRING}, + {"left-alt", 0, 0, N_("press left alt"), "[on|off]", ARG_TYPE_STRING}, + {"right-alt", 0, 0, N_("press right alt"), "[on|off]", ARG_TYPE_STRING}, + {"left-ctrl", 0, 0, N_("press left ctrl"), "[on|off]", ARG_TYPE_STRING}, + {"right-ctrl", 0, 0, N_("press right ctrl"), "[on|off]", ARG_TYPE_STRING}, + {"no-led", 0, 0, N_("don't update LED state"), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; +static int simple_flag_offsets[] += {5, 6, 4, 7, 11, 1, 0, 10, 13, 14, 12, 15, 9, 3, 8, 2}; + +static grub_uint32_t andmask = 0xffffffff, ormask = 0; + +struct +keysym +{ + const char *unshifted_name; /* the name in unshifted state */ + const char *shifted_name; /* the name in shifted state */ + unsigned char unshifted_ascii; /* the ascii code in unshifted state */ + unsigned char shifted_ascii; /* the ascii code in shifted state */ + unsigned char keycode; /* keyboard scancode */ +}; + +/* The table for key symbols. If the "shifted" member of an entry is + NULL, the entry does not have shifted state. Copied from GRUB Legacy setkey fuction */ +static struct keysym keysym_table[] = +{ + {"escape", 0, 0x1b, 0, 0x01}, + {"1", "exclam", '1', '!', 0x02}, + {"2", "at", '2', '@', 0x03}, + {"3", "numbersign", '3', '#', 0x04}, + {"4", "dollar", '4', '$', 0x05}, + {"5", "percent", '5', '%', 0x06}, + {"6", "caret", '6', '^', 0x07}, + {"7", "ampersand", '7', '&', 0x08}, + {"8", "asterisk", '8', '*', 0x09}, + {"9", "parenleft", '9', '(', 0x0a}, + {"0", "parenright", '0', ')', 0x0b}, + {"minus", "underscore", '-', '_', 0x0c}, + {"equal", "plus", '=', '+', 0x0d}, + {"backspace", 0, '\b', 0, 0x0e}, + {"tab", 0, '\t', 0, 0x0f}, + {"q", "Q", 'q', 'Q', 0x10}, + {"w", "W", 'w', 'W', 0x11}, + {"e", "E", 'e', 'E', 0x12}, + {"r", "R", 'r', 'R', 0x13}, + {"t", "T", 't', 'T', 0x14}, + {"y", "Y", 'y', 'Y', 0x15}, + {"u", "U", 'u', 'U', 0x16}, + {"i", "I", 'i', 'I', 0x17}, + {"o", "O", 'o', 'O', 0x18}, + {"p", "P", 'p', 'P', 0x19}, + {"bracketleft", "braceleft", '[', '{', 0x1a}, + {"bracketright", "braceright", ']', '}', 0x1b}, + {"enter", 0, '\r', 0, 0x1c}, + {"control", 0, 0, 0, 0x1d}, + {"a", "A", 'a', 'A', 0x1e}, + {"s", "S", 's', 'S', 0x1f}, + {"d", "D", 'd', 'D', 0x20}, + {"f", "F", 'f', 'F', 0x21}, + {"g", "G", 'g', 'G', 0x22}, + {"h", "H", 'h', 'H', 0x23}, + {"j", "J", 'j', 'J', 0x24}, + {"k", "K", 'k', 'K', 0x25}, + {"l", "L", 'l', 'L', 0x26}, + {"semicolon", "colon", ';', ':', 0x27}, + {"quote", "doublequote", '\'', '"', 0x28}, + {"backquote", "tilde", '`', '~', 0x29}, + {"shift", 0, 0, 0, 0x2a}, + {"backslash", "bar", '\\', '|', 0x2b}, + {"z", "Z", 'z', 'Z', 0x2c}, + {"x", "X", 'x', 'X', 0x2d}, + {"c", "C", 'c', 'C', 0x2e}, + {"v", "V", 'v', 'V', 0x2f}, + {"b", "B", 'b', 'B', 0x30}, + {"n", "N", 'n', 'N', 0x31}, + {"m", "M", 'm', 'M', 0x32}, + {"comma", "less", ',', '<', 0x33}, + {"period", "greater", '.', '>', 0x34}, + {"slash", "question", '/', '?', 0x35}, + {"rshift", 0, 0, 0, 0x36}, + {"numasterisk", 0, '*', 0, 0x37}, + {"alt", 0, 0, 0, 0x38}, + {"space", 0, ' ', 0, 0x39}, + {"capslock", 0, 0, 0, 0x3a}, + {"F1", 0, 0, 0, 0x3b}, + {"F2", 0, 0, 0, 0x3c}, + {"F3", 0, 0, 0, 0x3d}, + {"F4", 0, 0, 0, 0x3e}, + {"F5", 0, 0, 0, 0x3f}, + {"F6", 0, 0, 0, 0x40}, + {"F7", 0, 0, 0, 0x41}, + {"F8", 0, 0, 0, 0x42}, + {"F9", 0, 0, 0, 0x43}, + {"F10", 0, 0, 0, 0x44}, + {"num7", "numhome", '7', 0, 0x47}, + {"num8", "numup", '8', 0, 0x48}, + {"num9", "numpgup", '9', 0, 0x49}, + {"numminus", 0, '-', 0, 0x4a}, + {"num4", "numleft", '4', 0, 0x4b}, + {"num5", "numcenter", '5', 0, 0x4c}, + {"num6", "numright", '6', 0, 0x4d}, + {"numplus", 0, '-', 0, 0x4e}, + {"num1", "numend", '1', 0, 0x4f}, + {"num2", "numdown", '2', 0, 0x50}, + {"num3", "numpgdown", '3', 0, 0x51}, + {"num0", "numinsert", '0', 0, 0x52}, + {"numperiod", "numdelete", 0, 0x7f, 0x53}, + {"F11", 0, 0, 0, 0x57}, + {"F12", 0, 0, 0, 0x58}, + {"numenter", 0, '\r', 0, 0xe0}, + {"numslash", 0, '/', 0, 0xe0}, + {"delete", 0, 0x7f, 0, 0xe0}, + {"insert", 0, 0xe0, 0, 0x52}, + {"home", 0, 0xe0, 0, 0x47}, + {"end", 0, 0xe0, 0, 0x4f}, + {"pgdown", 0, 0xe0, 0, 0x51}, + {"pgup", 0, 0xe0, 0, 0x49}, + {"down", 0, 0xe0, 0, 0x50}, + {"up", 0, 0xe0, 0, 0x48}, + {"left", 0, 0xe0, 0, 0x4b}, + {"right", 0, 0xe0, 0, 0x4d} +}; + +/* Set a simple flag in flags variable + OUTOFFSET - offset of flag in FLAGS, + OP - action id +*/ +static void +grub_sendkey_set_simple_flag (int outoffset, int op) +{ + if (op == 2) + { + andmask |= (1 << outoffset); + ormask &= ~(1 << outoffset); + } + else + { + andmask &= (~(1 << outoffset)); + if (op == 1) + ormask |= (1 << outoffset); + else + ormask &= ~(1 << outoffset); + } +} + +static int +grub_sendkey_parse_op (struct grub_arg_list state) +{ + if (! state.set) + return 2; + + if (grub_strcmp (state.arg, "off") == 0 || grub_strcmp (state.arg, "0") == 0 + || grub_strcmp (state.arg, "unpress") == 0) + return 0; + + if (grub_strcmp (state.arg, "on") == 0 || grub_strcmp (state.arg, "1") == 0 + || grub_strcmp (state.arg, "press") == 0) + return 1; + + return 2; +} + +static grub_uint32_t oldflags; + +static grub_err_t +grub_sendkey_postboot (void) +{ + /* For convention: pointer to flags. */ + grub_uint32_t *flags = grub_absolute_pointer (0x417); + + *flags = oldflags; + + *((volatile char *) grub_absolute_pointer (0x41a)) = 0x1e; + *((volatile char *) grub_absolute_pointer (0x41c)) = 0x1e; + + return GRUB_ERR_NONE; +} + +/* Set keyboard buffer to our sendkey */ +static grub_err_t +grub_sendkey_preboot (int noret __attribute__ ((unused))) +{ + /* For convention: pointer to flags. */ + grub_uint32_t *flags = grub_absolute_pointer (0x417); + + oldflags = *flags; + + /* Set the sendkey. */ + *((volatile char *) grub_absolute_pointer (0x41a)) = 0x1e; + *((volatile char *) grub_absolute_pointer (0x41c)) = keylen + 0x1e; + grub_memcpy ((char *) 0x41e, sendkey, 0x20); + + /* Transform "any ctrl" to "right ctrl" flag. */ + if (*flags & (1 << 8)) + *flags &= ~(1 << 2); + + /* Transform "any alt" to "right alt" flag. */ + if (*flags & (1 << 9)) + *flags &= ~(1 << 3); + + *flags = (*flags & andmask) | ormask; + + /* Transform "right ctrl" to "any ctrl" flag. */ + if (*flags & (1 << 8)) + *flags |= (1 << 2); + + /* Transform "right alt" to "any alt" flag. */ + if (*flags & (1 << 9)) + *flags |= (1 << 3); + + /* Write new LED state */ + if (!noled) + { + int value = 0; + int failed; + /* Try 5 times */ + for (failed = 0; failed < 5; failed++) + { + value = 0; + /* Send command change LEDs */ + grub_outb (0xed, 0x60); + + /* Wait */ + do + value = grub_inb (0x60); + while ((value != 0xfa) && (value != 0xfe)); + + if (value == 0xfa) + { + /* Set new LEDs*/ + grub_outb ((*flags >> 4) & 7, 0x60); + break; + } + } + } + return GRUB_ERR_NONE; +} + +/* Helper for grub_cmd_sendkey. */ +static int +find_key_code (char *key) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(keysym_table); i++) + { + if (keysym_table[i].unshifted_name + && grub_strcmp (key, keysym_table[i].unshifted_name) == 0) + return keysym_table[i].keycode; + else if (keysym_table[i].shifted_name + && grub_strcmp (key, keysym_table[i].shifted_name) == 0) + return keysym_table[i].keycode; + } + + return 0; +} + +/* Helper for grub_cmd_sendkey. */ +static int +find_ascii_code (char *key) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(keysym_table); i++) + { + if (keysym_table[i].unshifted_name + && grub_strcmp (key, keysym_table[i].unshifted_name) == 0) + return keysym_table[i].unshifted_ascii; + else if (keysym_table[i].shifted_name + && grub_strcmp (key, keysym_table[i].shifted_name) == 0) + return keysym_table[i].shifted_ascii; + } + + return 0; +} + +static grub_err_t +grub_cmd_sendkey (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + + andmask = 0xffffffff; + ormask = 0; + + { + int i; + + keylen = 0; + + for (i = 0; i < argc && keylen < 0x20; i++) + { + int key_code; + + key_code = find_key_code (args[i]); + if (key_code) + { + sendkey[keylen++] = find_ascii_code (args[i]); + sendkey[keylen++] = key_code; + } + } + } + + { + unsigned i; + for (i = 0; i < ARRAY_SIZE(simple_flag_offsets); i++) + grub_sendkey_set_simple_flag (simple_flag_offsets[i], + grub_sendkey_parse_op(state[i])); + } + + /* Set noled. */ + noled = (state[ARRAY_SIZE(simple_flag_offsets)].set); + + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; +static struct grub_preboot *preboot_hook; + +GRUB_MOD_INIT (sendkey) +{ + cmd = grub_register_extcmd ("sendkey", grub_cmd_sendkey, 0, + N_("[KEYSTROKE1] [KEYSTROKE2] ..."), + /* TRANSLATORS: It can emulate multiple + keypresses. */ + N_("Emulate a keystroke sequence"), options); + + preboot_hook + = grub_loader_register_preboot_hook (grub_sendkey_preboot, + grub_sendkey_postboot, + GRUB_LOADER_PREBOOT_HOOK_PRIO_CONSOLE); +} + +GRUB_MOD_FINI (sendkey) +{ + grub_unregister_extcmd (cmd); + grub_loader_unregister_preboot_hook (preboot_hook); +} diff --git a/grub-core/commands/i386/pc/smbios.c b/grub-core/commands/i386/pc/smbios.c new file mode 100644 index 000000000..069d66367 --- /dev/null +++ b/grub-core/commands/i386/pc/smbios.c @@ -0,0 +1,52 @@ +/* smbios.c - get smbios tables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include +#include + +struct grub_smbios_eps * +grub_machine_smbios_get_eps (void) +{ + grub_uint8_t *ptr; + + grub_dprintf ("smbios", "Looking for SMBIOS EPS. Scanning BIOS\n"); + + for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16) + if (grub_memcmp (ptr, "_SM_", 4) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps)) == 0) + return (struct grub_smbios_eps *) ptr; + + return 0; +} + +struct grub_smbios_eps3 * +grub_machine_smbios_get_eps3 (void) +{ + grub_uint8_t *ptr; + + grub_dprintf ("smbios", "Looking for SMBIOS3 EPS. Scanning BIOS\n"); + + for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16) + if (grub_memcmp (ptr, "_SM3_", 5) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_smbios_eps3)) == 0) + return (struct grub_smbios_eps3 *) ptr; + + return 0; +} diff --git a/grub-core/commands/i386/rdmsr.c b/grub-core/commands/i386/rdmsr.c new file mode 100644 index 000000000..2e42f6197 --- /dev/null +++ b/grub-core/commands/i386/rdmsr.c @@ -0,0 +1,91 @@ +/* rdmsr.c - Read CPU model-specific registers. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 Free Software Foundation, Inc. + * Based on gcc/gcc/config/i386/driver-i386.c + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE("GPLv3+"); + +static grub_extcmd_t cmd_read; + +static const struct grub_arg_option options[] = +{ + {0, 'v', 0, N_("Save read value into variable VARNAME."), + N_("VARNAME"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_msr_read (grub_extcmd_context_t ctxt, int argc, char **argv) +{ + grub_err_t err; + grub_uint32_t addr; + grub_uint64_t value; + const char *ptr; + char buf[sizeof("1122334455667788")]; + + err = grub_cpu_is_msr_supported (); + + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("RDMSR is unsupported")); + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + grub_errno = GRUB_ERR_NONE; + ptr = argv[0]; + addr = grub_strtoul (ptr, &ptr, 0); + + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (*ptr != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); + + value = grub_rdmsr (addr); + + if (ctxt->state[0].set) + { + grub_snprintf (buf, sizeof(buf), "%llx", (unsigned long long) value); + grub_env_set (ctxt->state[0].arg, buf); + } + else + grub_printf ("0x%llx\n", (unsigned long long) value); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(rdmsr) +{ + cmd_read = grub_register_extcmd ("rdmsr", grub_cmd_msr_read, 0, N_("ADDR"), + N_("Read a CPU model specific register."), + options); +} + +GRUB_MOD_FINI(rdmsr) +{ + grub_unregister_extcmd (cmd_read); +} diff --git a/grub-core/commands/i386/wrmsr.c b/grub-core/commands/i386/wrmsr.c new file mode 100644 index 000000000..7fbedaed9 --- /dev/null +++ b/grub-core/commands/i386/wrmsr.c @@ -0,0 +1,83 @@ +/* wrmsr.c - Write CPU model-specific registers. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 Free Software Foundation, Inc. + * Based on gcc/gcc/config/i386/driver-i386.c + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE("GPLv3+"); + +static grub_command_t cmd_write; + +static grub_err_t +grub_cmd_msr_write (grub_command_t cmd __attribute__ ((unused)), int argc, char **argv) +{ + grub_err_t err; + grub_uint32_t addr; + grub_uint64_t value; + const char *ptr; + + err = grub_cpu_is_msr_supported (); + + if (err != GRUB_ERR_NONE) + return grub_error (err, N_("WRMSR is unsupported")); + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + grub_errno = GRUB_ERR_NONE; + ptr = argv[0]; + addr = grub_strtoul (ptr, &ptr, 0); + + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (*ptr != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); + + ptr = argv[1]; + value = grub_strtoull (ptr, &ptr, 0); + + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + if (*ptr != '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid argument")); + + grub_wrmsr (addr, value); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(wrmsr) +{ + cmd_write = grub_register_command_lockdown ("wrmsr", grub_cmd_msr_write, N_("ADDR VALUE"), + N_("Write a value to a CPU model specific register.")); +} + +GRUB_MOD_FINI(wrmsr) +{ + grub_unregister_command (cmd_write); +} diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c new file mode 100644 index 000000000..4958b04a9 --- /dev/null +++ b/grub-core/commands/ieee1275/ibmvtpm.c @@ -0,0 +1,117 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * Copyright (C) 2022 IBM Corporation + * + * 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 . + * + * IBM vTPM support code. + */ + +#include +#include +#include +#include +#include +#include +#include + +static int +ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex, + grub_uint32_t eventtype, + const char *description, + grub_size_t description_size, + void *buf, grub_size_t size) +{ + struct tpm_2hash_ext_log + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t description_size; + grub_ieee1275_cell_t description; + grub_ieee1275_cell_t eventtype; + grub_ieee1275_cell_t pcrindex; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t rc; + }; + struct tpm_2hash_ext_log args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2); + args.method = (grub_ieee1275_cell_t) "2hash-ext-log"; + args.ihandle = grub_ieee1275_tpm_ihandle; + args.pcrindex = pcrindex; + args.eventtype = eventtype; + args.description = (grub_ieee1275_cell_t) description; + args.description_size = description_size; + args.buf = (grub_ieee1275_cell_t) buf; + args.size = (grub_ieee1275_cell_t) size; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + /* + * catch_result is set if firmware does not support 2hash-ext-log + * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure + */ + if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE) + return -1; + + return 0; +} + +static grub_err_t +tpm2_log_event (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + static int error_displayed = 0; + int rc; + + rc = ibmvtpm_2hash_ext_log (pcr, EV_IPL, + description, grub_strlen(description) + 1, + buf, size); + if (rc && !error_displayed) + { + error_displayed++; + return grub_error (GRUB_ERR_BAD_DEVICE, + "2HASH-EXT-LOG failed: Firmware is likely too old.\n"); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, + const char *description) +{ + grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n", + pcr, size, description); + + if (grub_ieee1275_tpm_ihandle != GRUB_IEEE1275_IHANDLE_INVALID) + return tpm2_log_event (buf, size, pcr, description); + + return GRUB_ERR_NONE; +} + +int +grub_tpm_present (void) +{ + /* + * Call tpm_init() "late" rather than from GRUB_MOD_INIT() so that device nodes + * can be found. + */ + return grub_ieee1275_tpm_init() == GRUB_ERR_NONE; +} diff --git a/commands/ieee1275/suspend.c b/grub-core/commands/ieee1275/suspend.c similarity index 91% rename from commands/ieee1275/suspend.c rename to grub-core/commands/ieee1275/suspend.c index f096cc9ba..b50548574 100644 --- a/commands/ieee1275/suspend.c +++ b/grub-core/commands/ieee1275/suspend.c @@ -24,12 +24,14 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_err_t grub_cmd_suspend (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - grub_printf ("Run 'go' to resume GRUB.\n"); + grub_puts_ (N_("Run `go' to resume GRUB.")); grub_ieee1275_enter (); grub_cls (); return 0; @@ -40,7 +42,7 @@ static grub_command_t cmd; GRUB_MOD_INIT(ieee1275_suspend) { cmd = grub_register_command ("suspend", grub_cmd_suspend, - 0, N_("Return to Open Firmware prompt.")); + 0, N_("Return to IEEE1275 prompt.")); } GRUB_MOD_FINI(ieee1275_suspend) diff --git a/grub-core/commands/iorw.c b/grub-core/commands/iorw.c new file mode 100644 index 000000000..584baec8f --- /dev/null +++ b/grub-core/commands/iorw.c @@ -0,0 +1,156 @@ +/* memrw.c - command to read / write physical memory */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_extcmd_t cmd_read_byte, cmd_read_word, cmd_read_dword; +static grub_command_t cmd_write_byte, cmd_write_word, cmd_write_dword; + +static const struct grub_arg_option options[] = + { + {0, 'v', 0, N_("Save read value into variable VARNAME."), + N_("VARNAME"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + + +static grub_err_t +grub_cmd_read (grub_extcmd_context_t ctxt, int argc, char **argv) +{ + grub_port_t addr; + grub_uint32_t value = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + addr = grub_strtoul (argv[0], 0, 0); + switch (ctxt->extcmd->cmd->name[sizeof ("in") - 1]) + { + case 'l': + value = grub_inl (addr); + break; + + case 'w': + value = grub_inw (addr); + break; + + case 'b': + value = grub_inb (addr); + break; + } + + if (ctxt->state[0].set) + { + char buf[sizeof ("XXXXXXXX")]; + grub_snprintf (buf, sizeof (buf), "%x", value); + grub_env_set (ctxt->state[0].arg, buf); + } + else + grub_printf ("0x%x\n", value); + + return 0; +} + +static grub_err_t +grub_cmd_write (grub_command_t cmd, int argc, char **argv) +{ + grub_port_t addr; + grub_uint32_t value; + grub_uint32_t mask = 0xffffffff; + + if (argc != 2 && argc != 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + addr = grub_strtoul (argv[0], 0, 0); + value = grub_strtoul (argv[1], 0, 0); + if (argc == 3) + mask = grub_strtoul (argv[2], 0, 0); + value &= mask; + switch (cmd->name[sizeof ("out") - 1]) + { + case 'l': + if (mask != 0xffffffff) + grub_outl ((grub_inl (addr) & ~mask) | value, addr); + else + grub_outl (value, addr); + break; + + case 'w': + if ((mask & 0xffff) != 0xffff) + grub_outw ((grub_inw (addr) & ~mask) | value, addr); + else + grub_outw (value, addr); + break; + + case 'b': + if ((mask & 0xff) != 0xff) + grub_outb ((grub_inb (addr) & ~mask) | value, addr); + else + grub_outb (value, addr); + break; + } + + return 0; +} + +GRUB_MOD_INIT(memrw) +{ + cmd_read_byte = + grub_register_extcmd ("inb", grub_cmd_read, 0, + N_("PORT"), N_("Read 8-bit value from PORT."), + options); + cmd_read_word = + grub_register_extcmd ("inw", grub_cmd_read, 0, + N_("PORT"), N_("Read 16-bit value from PORT."), + options); + cmd_read_dword = + grub_register_extcmd ("inl", grub_cmd_read, 0, + N_("PORT"), N_("Read 32-bit value from PORT."), + options); + cmd_write_byte = + grub_register_command_lockdown ("outb", grub_cmd_write, + N_("PORT VALUE [MASK]"), + N_("Write 8-bit VALUE to PORT.")); + cmd_write_word = + grub_register_command_lockdown ("outw", grub_cmd_write, + N_("PORT VALUE [MASK]"), + N_("Write 16-bit VALUE to PORT.")); + cmd_write_dword = + grub_register_command_lockdown ("outl", grub_cmd_write, + N_("ADDR VALUE [MASK]"), + N_("Write 32-bit VALUE to PORT.")); +} + +GRUB_MOD_FINI(memrw) +{ + grub_unregister_extcmd (cmd_read_byte); + grub_unregister_extcmd (cmd_read_word); + grub_unregister_extcmd (cmd_read_dword); + grub_unregister_command (cmd_write_byte); + grub_unregister_command (cmd_write_word); + grub_unregister_command (cmd_write_dword); +} diff --git a/grub-core/commands/keylayouts.c b/grub-core/commands/keylayouts.c new file mode 100644 index 000000000..aa3ba34f2 --- /dev/null +++ b/grub-core/commands/keylayouts.c @@ -0,0 +1,307 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +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', GRUB_TERM_ESC, GRUB_TERM_BACKSPACE, GRUB_TERM_TAB, ' ', '-', '=', '[', + /* 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, GRUB_TERM_ESC | GRUB_TERM_SHIFT, + /* 0x2a */ GRUB_TERM_BACKSPACE | GRUB_TERM_SHIFT, GRUB_TERM_TAB | 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 (code >= GRUB_KEYBOARD_LAYOUTS_ARRAY_SIZE) + return 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); + return GRUB_TERM_NO_KEY; + } + + 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, N_("variable `%s' isn't set"), "prefix"); + filename = grub_xasprintf ("%s/layouts/%s.gkb", prefix, argv[0]); + if (!filename) + return grub_errno; + } + else + filename = argv[0]; + + file = grub_file_open (filename, GRUB_FILE_TYPE_KEYBOARD_LAYOUT); + if (! file) + goto fail; + + if (grub_file_read (file, magic, sizeof (magic)) != sizeof (magic)) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("premature end of file %s"), + filename); + 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, N_("premature end of file %s"), + filename); + goto fail; + } + + if (version != grub_cpu_to_le32_compile_time (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, N_("premature end of file %s"), + filename); + 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); +} diff --git a/commands/keystatus.c b/grub-core/commands/keystatus.c similarity index 72% rename from commands/keystatus.c rename to grub-core/commands/keystatus.c index 838792889..ff3f58781 100644 --- a/commands/keystatus.c +++ b/grub-core/commands/keystatus.c @@ -23,31 +23,33 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static const struct grub_arg_option options[] = { + /* TRANSLATORS: "Check" in a sense that if this key is pressed then + "true" is returned, otherwise "false". */ {"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} }; -#define grub_cur_term_input grub_term_get_current_input () - static grub_err_t -grub_cmd_keystatus (grub_extcmd_t cmd, +grub_cmd_keystatus (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; int expect_mods = 0; int mods; if (state[0].set) - expect_mods |= GRUB_TERM_STATUS_SHIFT; + expect_mods |= (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT); if (state[1].set) - expect_mods |= GRUB_TERM_STATUS_CTRL; + expect_mods |= (GRUB_TERM_STATUS_LCTRL | GRUB_TERM_STATUS_RCTRL); if (state[2].set) - expect_mods |= GRUB_TERM_STATUS_ALT; + expect_mods |= (GRUB_TERM_STATUS_LALT | GRUB_TERM_STATUS_RALT); grub_dprintf ("keystatus", "expect_mods: %d\n", expect_mods); @@ -60,11 +62,11 @@ grub_cmd_keystatus (grub_extcmd_t cmd, FOR_ACTIVE_TERM_INPUTS (term) if (!term->getkeystatus) - return grub_error (GRUB_ERR_TEST_FAILURE, "false"); + return grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); else nterms++; if (!nterms) - return grub_error (GRUB_ERR_TEST_FAILURE, "false"); + return grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); return 0; } @@ -73,16 +75,16 @@ grub_cmd_keystatus (grub_extcmd_t cmd, if (mods >= 0 && (mods & expect_mods) != 0) return 0; else - return grub_error (GRUB_ERR_TEST_FAILURE, "false"); + return grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } static grub_extcmd_t cmd; GRUB_MOD_INIT(keystatus) { - cmd = grub_register_extcmd ("keystatus", grub_cmd_keystatus, - GRUB_COMMAND_FLAG_BOTH, - N_("[--shift] [--ctrl] [--alt]"), + cmd = grub_register_extcmd ("keystatus", grub_cmd_keystatus, 0, + "[--shift] [--ctrl] [--alt]", + /* TRANSLATORS: there are 3 modifiers. */ N_("Check key modifier status."), options); } diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c new file mode 100644 index 000000000..3bf9fe2e4 --- /dev/null +++ b/grub-core/commands/legacycfg.c @@ -0,0 +1,911 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Helper for legacy_file. */ +static grub_err_t +legacy_file_getline (char **line, int cont __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + *line = 0; + return GRUB_ERR_NONE; +} + +static grub_err_t +legacy_file (const char *filename) +{ + grub_file_t file; + char *entryname = NULL, *entrysrc = NULL; + grub_menu_t menu; + char *suffix = grub_strdup (""); + + if (!suffix) + return grub_errno; + + file = grub_file_open (filename, GRUB_FILE_TYPE_CONFIG); + if (! file) + { + grub_free (suffix); + return grub_errno; + } + + menu = grub_env_get_menu (); + if (! menu) + { + menu = grub_zalloc (sizeof (*menu)); + if (! menu) + { + grub_free (suffix); + return grub_errno; + } + + grub_env_set_menu (menu); + } + + while (1) + { + char *buf = grub_file_getline (file); + char *parsed = NULL; + + if (!buf && grub_errno) + { + grub_file_close (file); + grub_free (suffix); + return grub_errno; + } + + if (!buf) + break; + + { + char *oldname = NULL; + char *newsuffix; + char *ptr; + + for (ptr = buf; *ptr && grub_isspace (*ptr); ptr++); + + oldname = entryname; + parsed = grub_legacy_parse (ptr, &entryname, &newsuffix); + grub_free (buf); + buf = NULL; + if (newsuffix) + { + char *t; + grub_size_t sz; + + if (grub_add (grub_strlen (suffix), grub_strlen (newsuffix), &sz) || + grub_add (sz, 1, &sz)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + goto fail_0; + } + + t = suffix; + suffix = grub_realloc (suffix, sz); + if (!suffix) + { + grub_free (t); + + fail_0: + grub_free (entrysrc); + grub_free (parsed); + grub_free (newsuffix); + grub_free (suffix); + return grub_errno; + } + grub_memcpy (suffix + grub_strlen (suffix), newsuffix, + grub_strlen (newsuffix) + 1); + grub_free (newsuffix); + newsuffix = NULL; + } + if (oldname != entryname && oldname) + { + const char **args = grub_malloc (sizeof (args[0])); + if (!args) + { + grub_file_close (file); + return grub_errno; + } + args[0] = oldname; + grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", + NULL, NULL, + entrysrc, 0); + grub_free (args); + entrysrc[0] = 0; + grub_free (oldname); + } + } + + if (parsed && !entryname) + { + grub_normal_parse_line (parsed, legacy_file_getline, NULL); + grub_print_error (); + grub_free (parsed); + parsed = NULL; + } + else if (parsed) + { + if (!entrysrc) + entrysrc = parsed; + else + { + char *t; + grub_size_t sz; + + if (grub_add (grub_strlen (entrysrc), grub_strlen (parsed), &sz) || + grub_add (sz, 1, &sz)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + goto fail_1; + } + + t = entrysrc; + entrysrc = grub_realloc (entrysrc, sz); + if (!entrysrc) + { + grub_free (t); + + fail_1: + grub_free (parsed); + grub_free (suffix); + return grub_errno; + } + grub_memcpy (entrysrc + grub_strlen (entrysrc), parsed, + grub_strlen (parsed) + 1); + grub_free (parsed); + parsed = NULL; + } + } + } + grub_file_close (file); + + if (entryname) + { + const char **args = grub_malloc (sizeof (args[0])); + if (!args) + { + grub_free (suffix); + grub_free (entrysrc); + return grub_errno; + } + args[0] = entryname; + grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, + NULL, NULL, entrysrc, 0); + grub_free (args); + } + + grub_normal_parse_line (suffix, legacy_file_getline, NULL); + grub_print_error (); + grub_free (suffix); + grub_free (entrysrc); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_legacy_source (struct grub_command *cmd, + int argc, char **args) +{ + int new_env, extractor; + grub_err_t ret; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + extractor = (cmd->name[0] == 'e'); + new_env = (cmd->name[extractor ? (sizeof ("extract_legacy_entries_") - 1) + : (sizeof ("legacy_") - 1)] == 'c'); + + if (new_env) + grub_cls (); + + if (new_env && !extractor) + grub_env_context_open (); + if (extractor) + grub_env_extractor_open (!new_env); + + ret = legacy_file (args[0]); + + if (new_env) + { + grub_menu_t menu; + menu = grub_env_get_menu (); + if (menu && menu->size) + grub_show_menu (menu, 1, 0); + if (!extractor) + grub_env_context_close (); + } + if (extractor) + grub_env_extractor_close (!new_env); + + return ret; +} + +static enum + { + GUESS_IT, LINUX, MULTIBOOT, KFREEBSD, KNETBSD, KOPENBSD + } kernel_type; + +static grub_err_t +grub_cmd_legacy_kernel (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + int i; +#ifdef TODO + int no_mem_option = 0; +#endif + struct grub_command *cmd; + char **cutargs; + int cutargc; + grub_err_t err = GRUB_ERR_NONE; + + for (i = 0; i < 2; i++) + { + /* FIXME: really support this. */ + if (argc >= 1 && grub_strcmp (args[0], "--no-mem-option") == 0) + { +#ifdef TODO + no_mem_option = 1; +#endif + argc--; + args++; + continue; + } + + /* linux16 handles both zImages and bzImages. */ + if (argc >= 1 && (grub_strcmp (args[0], "--type=linux") == 0 + || grub_strcmp (args[0], "--type=biglinux") == 0)) + { + kernel_type = LINUX; + argc--; + args++; + continue; + } + + if (argc >= 1 && grub_strcmp (args[0], "--type=multiboot") == 0) + { + kernel_type = MULTIBOOT; + argc--; + args++; + continue; + } + + if (argc >= 1 && grub_strcmp (args[0], "--type=freebsd") == 0) + { + kernel_type = KFREEBSD; + argc--; + args++; + continue; + } + + if (argc >= 1 && grub_strcmp (args[0], "--type=openbsd") == 0) + { + kernel_type = KOPENBSD; + argc--; + args++; + continue; + } + + if (argc >= 1 && grub_strcmp (args[0], "--type=netbsd") == 0) + { + kernel_type = KNETBSD; + argc--; + args++; + continue; + } + } + + if (argc < 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + cutargs = grub_calloc (argc - 1, sizeof (cutargs[0])); + if (!cutargs) + return grub_errno; + cutargc = argc - 1; + grub_memcpy (cutargs + 1, args + 2, sizeof (cutargs[0]) * (argc - 2)); + cutargs[0] = args[0]; + + do + { + /* First try Linux. */ + if (kernel_type == GUESS_IT || kernel_type == LINUX) + { +#ifdef GRUB_MACHINE_PCBIOS + cmd = grub_command_find ("linux16"); +#else + cmd = grub_command_find ("linux"); +#endif + if (cmd) + { + if (!(cmd->func) (cmd, cutargc, cutargs)) + { + kernel_type = LINUX; + goto out; + } + } + grub_errno = GRUB_ERR_NONE; + } + + /* Then multiboot. */ + if (kernel_type == GUESS_IT || kernel_type == MULTIBOOT) + { + cmd = grub_command_find ("multiboot"); + if (cmd) + { + if (!(cmd->func) (cmd, argc, args)) + { + kernel_type = MULTIBOOT; + goto out; + } + } + grub_errno = GRUB_ERR_NONE; + } + + { + int bsd_device = -1; + int bsd_slice = -1; + int bsd_part = -1; + { + grub_device_t dev; + const char *hdbiasstr; + int hdbias = 0; + hdbiasstr = grub_env_get ("legacy_hdbias"); + if (hdbiasstr) + { + hdbias = grub_strtoul (hdbiasstr, 0, 0); + grub_errno = GRUB_ERR_NONE; + } + dev = grub_device_open (0); + if (dev && dev->disk + && dev->disk->dev->id == GRUB_DISK_DEVICE_BIOSDISK_ID + && dev->disk->id >= 0x80 && dev->disk->id <= 0x90) + { + struct grub_partition *part = dev->disk->partition; + bsd_device = dev->disk->id - 0x80 - hdbias; + if (part && (grub_strcmp (part->partmap->name, "netbsd") == 0 + || grub_strcmp (part->partmap->name, "openbsd") == 0 + || grub_strcmp (part->partmap->name, "bsd") == 0)) + { + bsd_part = part->number; + part = part->parent; + } + if (part && grub_strcmp (part->partmap->name, "msdos") == 0) + bsd_slice = part->number; + } + if (dev) + grub_device_close (dev); + } + + /* k*BSD didn't really work well with grub-legacy. */ + if (kernel_type == GUESS_IT || kernel_type == KFREEBSD) + { + char buf[sizeof("adXXXXXXXXXXXXsXXXXXXXXXXXXYYY")]; + if (bsd_device != -1) + { + if (bsd_slice != -1 && bsd_part != -1) + grub_snprintf(buf, sizeof(buf), "ad%ds%d%c", bsd_device, + bsd_slice, 'a' + bsd_part); + else if (bsd_slice != -1) + grub_snprintf(buf, sizeof(buf), "ad%ds%d", bsd_device, + bsd_slice); + else + grub_snprintf(buf, sizeof(buf), "ad%d", bsd_device); + grub_env_set ("kFreeBSD.vfs.root.mountfrom", buf); + } + else + grub_env_unset ("kFreeBSD.vfs.root.mountfrom"); + cmd = grub_command_find ("kfreebsd"); + if (cmd) + { + if (!(cmd->func) (cmd, cutargc, cutargs)) + { + kernel_type = KFREEBSD; + goto out; + } + } + grub_errno = GRUB_ERR_NONE; + } + { + char **bsdargs; + int bsdargc; + char bsddevname[sizeof ("wdXXXXXXXXXXXXY")]; + int found = 0; + + if (bsd_device == -1) + { + bsdargs = cutargs; + bsdargc = cutargc; + } + else + { + char rbuf[3] = "-r"; + bsdargc = cutargc + 2; + bsdargs = grub_calloc (bsdargc, sizeof (bsdargs[0])); + if (!bsdargs) + { + err = grub_errno; + goto out; + } + grub_memcpy (bsdargs, args, argc * sizeof (bsdargs[0])); + bsdargs[argc] = rbuf; + bsdargs[argc + 1] = bsddevname; + grub_snprintf (bsddevname, sizeof (bsddevname), + "wd%d%c", bsd_device, + bsd_part != -1 ? bsd_part + 'a' : 'c'); + } + if (kernel_type == GUESS_IT || kernel_type == KNETBSD) + { + cmd = grub_command_find ("knetbsd"); + if (cmd) + { + if (!(cmd->func) (cmd, bsdargc, bsdargs)) + { + kernel_type = KNETBSD; + found = 1; + goto free_bsdargs; + } + } + grub_errno = GRUB_ERR_NONE; + } + if (kernel_type == GUESS_IT || kernel_type == KOPENBSD) + { + cmd = grub_command_find ("kopenbsd"); + if (cmd) + { + if (!(cmd->func) (cmd, bsdargc, bsdargs)) + { + kernel_type = KOPENBSD; + found = 1; + goto free_bsdargs; + } + } + grub_errno = GRUB_ERR_NONE; + } + +free_bsdargs: + if (bsdargs != cutargs) + grub_free (bsdargs); + if (found) + goto out; + } + } + } + while (0); + + err = grub_error (GRUB_ERR_BAD_OS, "couldn't load file %s", + args[0]); +out: + grub_free (cutargs); + return err; +} + +static grub_err_t +grub_cmd_legacy_initrd (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_command *cmd; + + if (kernel_type == LINUX) + { +#ifdef GRUB_MACHINE_PCBIOS + cmd = grub_command_find ("initrd16"); +#else + cmd = grub_command_find ("initrd"); +#endif + if (!cmd) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"), +#ifdef GRUB_MACHINE_PCBIOS + "initrd16" +#else + "initrd" +#endif + ); + + return cmd->func (cmd, argc ? 1 : 0, args); + } + if (kernel_type == MULTIBOOT) + { + cmd = grub_command_find ("module"); + if (!cmd) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"), + "module"); + + return cmd->func (cmd, argc, args); + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); +} + +static grub_err_t +grub_cmd_legacy_initrdnounzip (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_command *cmd; + + if (kernel_type == LINUX) + { + cmd = grub_command_find ("initrd16"); + if (!cmd) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"), + "initrd16"); + + return cmd->func (cmd, argc, args); + } + if (kernel_type == MULTIBOOT) + { + char **newargs; + grub_err_t err; + char nounzipbuf[10] = "--nounzip"; + + cmd = grub_command_find ("module"); + if (!cmd) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"), + "module"); + + newargs = grub_calloc (argc + 1, sizeof (newargs[0])); + if (!newargs) + return grub_errno; + grub_memcpy (newargs + 1, args, argc * sizeof (newargs[0])); + newargs[0] = nounzipbuf; + + err = cmd->func (cmd, argc + 1, newargs); + grub_free (newargs); + return err; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("you need to load the kernel first")); +} + +static grub_err_t +check_password_deny (const char *user __attribute__ ((unused)), + const char *entered __attribute__ ((unused)), + void *password __attribute__ ((unused))) +{ + return GRUB_ACCESS_DENIED; +} + +#define MD5_HASHLEN 16 + +struct legacy_md5_password +{ + grub_uint8_t *salt; + int saltlen; + grub_uint8_t hash[MD5_HASHLEN]; +}; + +static int +check_password_md5_real (const char *entered, + struct legacy_md5_password *pw) +{ + grub_size_t enteredlen = grub_strlen (entered); + unsigned char alt_result[MD5_HASHLEN]; + unsigned char *digest; + grub_uint8_t *ctx; + grub_size_t i; + int ret; + + ctx = grub_zalloc (GRUB_MD_MD5->contextsize); + if (!ctx) + return 0; + + GRUB_MD_MD5->init (ctx); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + grub_memcpy (alt_result, digest, MD5_HASHLEN); + + GRUB_MD_MD5->init (ctx); + GRUB_MD_MD5->write (ctx, entered, enteredlen); + GRUB_MD_MD5->write (ctx, pw->salt, pw->saltlen); /* include the $1$ header */ + for (i = enteredlen; i > 16; i -= 16) + GRUB_MD_MD5->write (ctx, alt_result, 16); + GRUB_MD_MD5->write (ctx, alt_result, i); + + for (i = enteredlen; i > 0; i >>= 1) + GRUB_MD_MD5->write (ctx, entered + ((i & 1) ? enteredlen : 0), 1); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + + for (i = 0; i < 1000; i++) + { + grub_memcpy (alt_result, digest, 16); + + GRUB_MD_MD5->init (ctx); + if ((i & 1) != 0) + GRUB_MD_MD5->write (ctx, entered, enteredlen); + else + GRUB_MD_MD5->write (ctx, alt_result, 16); + + if (i % 3 != 0) + GRUB_MD_MD5->write (ctx, pw->salt + 3, pw->saltlen - 3); + + if (i % 7 != 0) + GRUB_MD_MD5->write (ctx, entered, enteredlen); + + if ((i & 1) != 0) + GRUB_MD_MD5->write (ctx, alt_result, 16); + else + GRUB_MD_MD5->write (ctx, entered, enteredlen); + digest = GRUB_MD_MD5->read (ctx); + GRUB_MD_MD5->final (ctx); + } + + ret = (grub_crypto_memcmp (digest, pw->hash, MD5_HASHLEN) == 0); + grub_free (ctx); + return ret; +} + +static grub_err_t +check_password_md5 (const char *user, + const char *entered, + void *password) +{ + if (!check_password_md5_real (entered, password)) + return GRUB_ACCESS_DENIED; + + grub_auth_authenticate (user); + + return GRUB_ERR_NONE; +} + +static inline int +ib64t (char c) +{ + if (c == '.') + return 0; + if (c == '/') + return 1; + if (c >= '0' && c <= '9') + return c - '0' + 2; + if (c >= 'A' && c <= 'Z') + return c - 'A' + 12; + if (c >= 'a' && c <= 'z') + return c - 'a' + 38; + return -1; +} + +static struct legacy_md5_password * +parse_legacy_md5 (int argc, char **args) +{ + const char *salt, *saltend; + struct legacy_md5_password *pw = NULL; + int i; + const char *p; + + if (grub_memcmp (args[0], "--md5", sizeof ("--md5")) != 0) + goto fail; + if (argc == 1) + goto fail; + if (grub_strlen(args[1]) <= 3) + goto fail; + salt = args[1]; + saltend = grub_strchr (salt + 3, '$'); + if (!saltend) + goto fail; + pw = grub_malloc (sizeof (*pw)); + if (!pw) + goto fail; + + p = saltend + 1; + for (i = 0; i < 5; i++) + { + int n; + grub_uint32_t w = 0; + + for (n = 0; n < 4; n++) + { + int ww = ib64t(*p++); + if (ww == -1) + goto fail; + w |= ww << (n * 6); + } + pw->hash[i == 4 ? 5 : 12+i] = w & 0xff; + pw->hash[6+i] = (w >> 8) & 0xff; + pw->hash[i] = (w >> 16) & 0xff; + } + { + int n; + grub_uint32_t w = 0; + for (n = 0; n < 2; n++) + { + int ww = ib64t(*p++); + if (ww == -1) + goto fail; + w |= ww << (6 * n); + } + if (w >= 0x100) + goto fail; + pw->hash[11] = w; + } + + pw->saltlen = saltend - salt; + pw->salt = (grub_uint8_t *) grub_strndup (salt, pw->saltlen); + if (!pw->salt) + goto fail; + + return pw; + + fail: + grub_free (pw); + return NULL; +} + +static grub_err_t +grub_cmd_legacy_password (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + struct legacy_md5_password *pw = NULL; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + if (args[0][0] != '-' || args[0][1] != '-') + return grub_normal_set_password ("legacy", args[0]); + + pw = parse_legacy_md5 (argc, args); + + if (pw) + return grub_auth_register_authentication ("legacy", check_password_md5, pw); + else + /* This is to imitate minor difference between grub-legacy in GRUB2. + If 2 password commands are executed in a row and second one fails + on GRUB2 the password of first one is used, whereas in grub-legacy + authenthication is denied. In case of no password command was executed + early both versions deny any access. */ + return grub_auth_register_authentication ("legacy", check_password_deny, + NULL); +} + +int +grub_legacy_check_md5_password (int argc, char **args, + char *entered) +{ + struct legacy_md5_password *pw = NULL; + int ret; + + if (args[0][0] != '-' || args[0][1] != '-') + { + char correct[GRUB_AUTH_MAX_PASSLEN]; + + grub_memset (correct, 0, sizeof (correct)); + grub_strncpy (correct, args[0], sizeof (correct)); + + return grub_crypto_memcmp (entered, correct, GRUB_AUTH_MAX_PASSLEN) == 0; + } + + pw = parse_legacy_md5 (argc, args); + + if (!pw) + return 0; + + ret = check_password_md5_real (entered, pw); + grub_free (pw); + return ret; +} + +static grub_err_t +grub_cmd_legacy_check_password (struct grub_command *mycmd __attribute__ ((unused)), + int argc, char **args) +{ + char entered[GRUB_AUTH_MAX_PASSLEN]; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + grub_puts_ (N_("Enter password: ")); + if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN)) + return GRUB_ACCESS_DENIED; + + if (!grub_legacy_check_md5_password (argc, args, + entered)) + return GRUB_ACCESS_DENIED; + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_source, cmd_configfile; +static grub_command_t cmd_source_extract, cmd_configfile_extract; +static grub_command_t cmd_kernel, cmd_initrd, cmd_initrdnounzip; +static grub_command_t cmd_password, cmd_check_password; + +GRUB_MOD_INIT(legacycfg) +{ + cmd_source + = grub_register_command ("legacy_source", + grub_cmd_legacy_source, + N_("FILE"), + /* TRANSLATORS: "legacy config" means + "config as used by grub-legacy". */ + N_("Parse legacy config in same context")); + cmd_configfile + = grub_register_command ("legacy_configfile", + grub_cmd_legacy_source, + N_("FILE"), + N_("Parse legacy config in new context")); + cmd_source_extract + = grub_register_command ("extract_legacy_entries_source", + grub_cmd_legacy_source, + N_("FILE"), + N_("Parse legacy config in same context taking only menu entries")); + cmd_configfile_extract + = grub_register_command ("extract_legacy_entries_configfile", + grub_cmd_legacy_source, + N_("FILE"), + N_("Parse legacy config in new context taking only menu entries")); + + cmd_kernel = grub_register_command ("legacy_kernel", + grub_cmd_legacy_kernel, + N_("[--no-mem-option] [--type=TYPE] FILE [ARG ...]"), + N_("Simulate grub-legacy `kernel' command")); + + cmd_initrd = grub_register_command ("legacy_initrd", + grub_cmd_legacy_initrd, + N_("FILE [ARG ...]"), + N_("Simulate grub-legacy `initrd' command")); + cmd_initrdnounzip = grub_register_command ("legacy_initrd_nounzip", + grub_cmd_legacy_initrdnounzip, + N_("FILE [ARG ...]"), + N_("Simulate grub-legacy `modulenounzip' command")); + + cmd_password = grub_register_command ("legacy_password", + grub_cmd_legacy_password, + N_("[--md5] PASSWD [FILE]"), + N_("Simulate grub-legacy `password' command")); + + cmd_check_password = grub_register_command ("legacy_check_password", + grub_cmd_legacy_check_password, + N_("[--md5] PASSWD [FILE]"), + N_("Simulate grub-legacy `password' command in menu entry mode")); + +} + +GRUB_MOD_FINI(legacycfg) +{ + grub_unregister_command (cmd_source); + grub_unregister_command (cmd_configfile); + grub_unregister_command (cmd_source_extract); + grub_unregister_command (cmd_configfile_extract); + + grub_unregister_command (cmd_kernel); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_initrdnounzip); + + grub_unregister_command (cmd_password); + grub_unregister_command (cmd_check_password); +} diff --git a/commands/loadenv.c b/grub-core/commands/loadenv.c similarity index 55% rename from commands/loadenv.c rename to grub-core/commands/loadenv.c index 51b88cbc9..166445849 100644 --- a/commands/loadenv.c +++ b/grub-core/commands/loadenv.c @@ -28,46 +28,55 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static const struct grub_arg_option options[] = { + /* TRANSLATORS: This option is used to override default filename + for loading and storing environment. */ {"file", 'f', 0, N_("Specify filename."), 0, ARG_TYPE_PATHNAME}, + {"skip-sig", 's', 0, + N_("Skip signature-checking of the environment file."), 0, ARG_TYPE_NONE}, {0, 0, 0, 0, 0, 0} }; +/* Opens 'filename' with compression filters disabled. Optionally disables the + PUBKEY filter (that insists upon properly signed files) as well. PUBKEY + filter is restored before the function returns. */ static grub_file_t -open_envblk_file (char *filename) +open_envblk_file (char *filename, + enum grub_file_type type) { grub_file_t file; + char *buf = 0; if (! filename) { - char *prefix; + const char *prefix; + int len; prefix = grub_env_get ("prefix"); - if (prefix) + if (! prefix) { - int len; - - len = grub_strlen (prefix); - filename = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG)); - if (! filename) - return 0; - - grub_strcpy (filename, prefix); - filename[len] = '/'; - grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG); - file = grub_file_open (filename); - grub_free (filename); - return file; - } - else - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, "prefix is not found"); + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); return 0; } + + len = grub_strlen (prefix); + buf = grub_malloc (len + 1 + sizeof (GRUB_ENVBLK_DEFCFG)); + if (! buf) + return 0; + filename = buf; + + grub_strcpy (filename, prefix); + filename[len] = '/'; + grub_strcpy (filename + len + 1, GRUB_ENVBLK_DEFCFG); } - return grub_file_open (filename); + file = grub_file_open (filename, type); + + grub_free (buf); + return file; } static grub_envblk_t @@ -89,8 +98,6 @@ read_envblk_file (grub_file_t file) ret = grub_file_read (file, buf + offset, size); if (ret <= 0) { - if (grub_errno == GRUB_ERR_NONE) - grub_error (GRUB_ERR_FILE_READ_ERROR, "cannot read"); grub_free (buf); return 0; } @@ -110,23 +117,59 @@ read_envblk_file (grub_file_t file) return envblk; } -static grub_err_t -grub_cmd_load_env (grub_extcmd_t cmd, - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) +struct grub_env_whitelist { - struct grub_arg_list *state = cmd->state; + grub_size_t len; + char **list; +}; +typedef struct grub_env_whitelist grub_env_whitelist_t; + +static int +test_whitelist_membership (const char* name, + const grub_env_whitelist_t* whitelist) +{ + grub_size_t i; + + for (i = 0; i < whitelist->len; i++) + if (grub_strcmp (name, whitelist->list[i]) == 0) + return 1; /* found it */ + + return 0; /* not found */ +} + +/* Helper for grub_cmd_load_env. */ +static int +set_var (const char *name, const char *value, void *whitelist) +{ + if (! whitelist) + { + grub_env_set (name, value); + return 0; + } + + if (test_whitelist_membership (name, + (const grub_env_whitelist_t *) whitelist)) + grub_env_set (name, value); + + return 0; +} + +static grub_err_t +grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; grub_file_t file; grub_envblk_t envblk; + grub_env_whitelist_t whitelist; - auto int set_var (const char *name, const char *value); - int set_var (const char *name, const char *value) - { - grub_env_set (name, value); - return 0; - } + whitelist.len = argc; + whitelist.list = args; - file = open_envblk_file ((state[0].set) ? state[0].arg : 0); + /* state[0] is the -f flag; state[1] is the --skip-sig flag */ + file = open_envblk_file ((state[0].set) ? state[0].arg : 0, + GRUB_FILE_TYPE_LOADENV + | (state[1].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE)); if (! file) return grub_errno; @@ -134,7 +177,8 @@ grub_cmd_load_env (grub_extcmd_t cmd, if (! envblk) goto fail; - grub_envblk_iterate (envblk, set_var); + /* argc > 0 indicates caller provided a whitelist of variables to read. */ + grub_envblk_iterate (envblk, argc > 0 ? &whitelist : 0, set_var); grub_envblk_close (envblk); fail: @@ -142,24 +186,28 @@ grub_cmd_load_env (grub_extcmd_t cmd, return grub_errno; } +/* Print all variables in current context. */ +static int +print_var (const char *name, const char *value, + void *hook_data __attribute__ ((unused))) +{ + grub_printf ("%s=%s\n", name, value); + return 0; +} + static grub_err_t -grub_cmd_list_env (grub_extcmd_t cmd, +grub_cmd_list_env (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; grub_file_t file; grub_envblk_t envblk; - /* Print all variables in current context. */ - auto int print_var (const char *name, const char *value); - int print_var (const char *name, const char *value) - { - grub_printf ("%s=%s\n", name, value); - return 0; - } - - file = open_envblk_file ((state[0].set) ? state[0].arg : 0); + file = open_envblk_file ((state[0].set) ? state[0].arg : 0, + GRUB_FILE_TYPE_LOADENV + | (state[1].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE : GRUB_FILE_TYPE_NONE)); if (! file) return grub_errno; @@ -167,7 +215,7 @@ grub_cmd_list_env (grub_extcmd_t cmd, if (! envblk) goto fail; - grub_envblk_iterate (envblk, print_var); + grub_envblk_iterate (envblk, NULL, print_var); grub_envblk_close (envblk); fail: @@ -212,10 +260,19 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, for (p = blocklists; p; p = p->next) { struct blocklist *q; + /* Check if any pair of blocks overlap. */ for (q = p->next; q; q = q->next) { - /* Check if any pair of blocks overlap. */ - if (p->sector == q->sector) + grub_disk_addr_t s1, s2; + grub_disk_addr_t e1, e2; + + s1 = p->sector; + e1 = s1 + ((p->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); + + s2 = q->sector; + e2 = s2 + ((q->length + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); + + if (s1 < e2 && s2 < e1) { /* This might be actually valid, but it is unbelievable that any filesystem makes such a silly allocation. */ @@ -235,15 +292,22 @@ check_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, /* One more sanity check. Re-read all sectors by blocklists, and compare those with the data read via a file. */ disk = file->device->disk; - if (disk->partition) - part_start = grub_partition_get_start (disk->partition); - else - part_start = 0; + + part_start = grub_partition_get_start (disk->partition); buf = grub_envblk_buffer (envblk); + char *blockbuf = NULL; + grub_size_t blockbuf_len = 0; for (p = blocklists, index = 0; p; index += p->length, p = p->next) { - char blockbuf[GRUB_DISK_SECTOR_SIZE]; + if (p->length > blockbuf_len) + { + grub_free (blockbuf); + blockbuf_len = 2 * p->length; + blockbuf = grub_malloc (blockbuf_len); + if (!blockbuf) + return grub_errno; + } if (grub_disk_read (disk, p->sector - part_start, p->offset, p->length, blockbuf)) @@ -268,10 +332,7 @@ write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, buf = grub_envblk_buffer (envblk); disk = file->device->disk; - if (disk->partition) - part_start = grub_partition_get_start (disk->partition); - else - part_start = 0; + part_start = grub_partition_get_start (disk->partition); index = 0; for (p = blocklists; p; index += p->length, p = p->next) @@ -284,49 +345,56 @@ write_blocklists (grub_envblk_t envblk, struct blocklist *blocklists, return 1; } -static grub_err_t -grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args) +/* Context for grub_cmd_save_env. */ +struct grub_cmd_save_env_ctx { - struct grub_arg_list *state = cmd->state; + struct blocklist *head, *tail; +}; + +/* Store blocklists in a linked list. */ +static grub_err_t +save_env_read_hook (grub_disk_addr_t sector, unsigned offset, unsigned length, + char *buf __attribute__ ((unused)), void *data) +{ + struct grub_cmd_save_env_ctx *ctx = data; + struct blocklist *block; + + block = grub_malloc (sizeof (*block)); + if (! block) + return GRUB_ERR_NONE; + + block->sector = sector; + block->offset = offset; + block->length = length; + + /* Slightly complicated, because the list should be FIFO. */ + block->next = 0; + if (ctx->tail) + ctx->tail->next = block; + ctx->tail = block; + if (! ctx->head) + ctx->head = block; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_save_env (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; grub_file_t file; grub_envblk_t envblk; - struct blocklist *head = 0; - struct blocklist *tail = 0; - - /* Store blocklists in a linked list. */ - auto void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector, - unsigned offset, - unsigned length); - void NESTED_FUNC_ATTR read_hook (grub_disk_addr_t sector, - unsigned offset, unsigned length) - { - struct blocklist *block; - - if (offset + length > GRUB_DISK_SECTOR_SIZE) - /* Seemingly a bug. */ - return; - - block = grub_malloc (sizeof (*block)); - if (! block) - return; - - block->sector = sector; - block->offset = offset; - block->length = length; - - /* Slightly complicated, because the list should be FIFO. */ - block->next = 0; - if (tail) - tail->next = block; - tail = block; - if (! head) - head = block; - } + struct grub_cmd_save_env_ctx ctx = { + .head = 0, + .tail = 0 + }; if (! argc) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no variable is specified"); - file = open_envblk_file ((state[0].set) ? state[0].arg : 0); + file = open_envblk_file ((state[0].set) ? state[0].arg : 0, + GRUB_FILE_TYPE_SAVEENV + | GRUB_FILE_TYPE_SKIP_SIGNATURE); if (! file) return grub_errno; @@ -336,18 +404,19 @@ grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args) return grub_error (GRUB_ERR_BAD_DEVICE, "disk device required"); } - file->read_hook = read_hook; + file->read_hook = save_env_read_hook; + file->read_hook_data = &ctx; envblk = read_envblk_file (file); file->read_hook = 0; if (! envblk) goto fail; - if (check_blocklists (envblk, head, file)) + if (check_blocklists (envblk, ctx.head, file)) goto fail; while (argc) { - char *value; + const char *value; value = grub_env_get (args[0]); if (value) @@ -358,17 +427,19 @@ grub_cmd_save_env (grub_extcmd_t cmd, int argc, char **args) goto fail; } } + else + grub_envblk_delete (envblk, args[0]); argc--; args++; } - write_blocklists (envblk, head, file); + write_blocklists (envblk, ctx.head, file); fail: if (envblk) grub_envblk_close (envblk); - free_blocklists (head); + free_blocklists (ctx.head); grub_file_close (file); return grub_errno; } @@ -378,20 +449,16 @@ static grub_extcmd_t cmd_load, cmd_list, cmd_save; GRUB_MOD_INIT(loadenv) { cmd_load = - grub_register_extcmd ("load_env", grub_cmd_load_env, - GRUB_COMMAND_FLAG_BOTH, - N_("[-f FILE]"), + grub_register_extcmd ("load_env", grub_cmd_load_env, 0, + N_("[-f FILE] [-s|--skip-sig] [variable_name_to_whitelist] [...]"), N_("Load variables from environment block file."), options); cmd_list = - grub_register_extcmd ("list_env", grub_cmd_list_env, - GRUB_COMMAND_FLAG_BOTH, - N_("[-f FILE]"), + grub_register_extcmd ("list_env", grub_cmd_list_env, 0, N_("[-f FILE]"), N_("List variables from environment block file."), options); cmd_save = - grub_register_extcmd ("save_env", grub_cmd_save_env, - GRUB_COMMAND_FLAG_BOTH, + grub_register_extcmd ("save_env", grub_cmd_save_env, 0, N_("[-f FILE] variable_name [...]"), N_("Save variables to environment block file."), options); diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c new file mode 100644 index 000000000..c3bcb089a --- /dev/null +++ b/grub-core/commands/ls.c @@ -0,0 +1,311 @@ +/* ls.c - command to list files and devices */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + {"long", 'l', 0, N_("Show a long list with more detailed information."), 0, 0}, + {"human-readable", 'h', 0, N_("Print sizes in a human readable format."), 0, 0}, + {"all", 'a', 0, N_("List all files."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +/* Helper for grub_ls_list_devices. */ +static int +grub_ls_print_devices (const char *name, void *data) +{ + int *longlist = data; + + if (*longlist) + grub_normal_print_device_info (name); + else + grub_printf ("(%s) ", name); + + return 0; +} + +static grub_err_t +grub_ls_list_devices (int longlist) +{ + grub_device_iterate (grub_ls_print_devices, &longlist); + grub_xputs ("\n"); + +#if 0 + { + grub_net_app_level_t proto; + int first = 1; + FOR_NET_APP_LEVEL (proto) + { + if (first) + grub_puts_ (N_("Network protocols:")); + first = 0; + grub_printf ("%s ", proto->name); + } + grub_xputs ("\n"); + } +#endif + + grub_refresh (); + + return 0; +} + +/* Context for grub_ls_list_files. */ +struct grub_ls_list_files_ctx +{ + char *dirname; + char *filename; + int all; + int human; + int longlist; + int print_dirhdr; +}; + +/* Helper for grub_ls_list_files. */ +static int +print_file (const char *filename, const struct grub_dirhook_info *info, + void *data) +{ + char *pathname = NULL; + struct grub_ls_list_files_ctx *ctx = data; + + if ((! ctx->all) && (filename[0] == '.')) + return 0; + + if ((ctx->filename != NULL) && (grub_strcmp (filename, ctx->filename) != 0)) + return 0; + + if (ctx->print_dirhdr) + { + grub_printf ("%s:\n", ctx->dirname); + ctx->print_dirhdr = 0; + } + + if (! ctx->longlist) + { + if (ctx->filename != NULL) + grub_xputs (ctx->dirname); + grub_printf ("%s%s ", filename, info->dir ? "/" : ""); + return 0; + } + + if (! info->dir) + { + grub_file_t file; + + if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/') + pathname = grub_xasprintf ("%s%s", ctx->dirname, filename); + else + pathname = grub_xasprintf ("%s/%s", ctx->dirname, filename); + + if (!pathname) + return 1; + + /* XXX: For ext2fs symlinks are detected as files while they + should be reported as directories. */ + file = grub_file_open (pathname, GRUB_FILE_TYPE_GET_SIZE + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (file) + { + if (! ctx->human) + grub_printf ("%-12llu", (unsigned long long) file->size); + else + grub_printf ("%-12s", grub_get_human_size (file->size, + GRUB_HUMAN_SIZE_SHORT)); + grub_file_close (file); + } + else + grub_xputs ("????????????"); + + grub_errno = GRUB_ERR_NONE; + } + else + grub_printf ("%-12s", _("DIR")); + + if (info->mtimeset) + { + struct grub_datetime datetime; + grub_unixtime2datetime (info->mtime, &datetime); + if (ctx->human) + grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ", + datetime.year, datetime.month, datetime.day, + datetime.hour, datetime.minute, + datetime.second, + grub_get_weekday_name (&datetime)); + else + grub_printf (" %04d%02d%02d%02d%02d%02d ", + datetime.year, datetime.month, + datetime.day, datetime.hour, + datetime.minute, datetime.second); + } + /* + * Only print the full path when listing a file path given as an argument + * to ls, i.e. when ctx->filename != NULL. File listings that are printed + * due to showing the contents of a directory do not need a full path because + * the full path to the directory will have already been printed. + */ + grub_printf ("%s%s\n", (ctx->filename != NULL) ? pathname : filename, + info->dir ? "/" : ""); + + grub_free (pathname); + + return 0; +} + +static grub_err_t +grub_ls_list_files (char *dirname, int longlist, int all, int human, int dirhdr) +{ + char *device_name; + grub_fs_t fs; + const char *path; + grub_device_t dev; + + device_name = grub_file_get_device_name (dirname); + dev = grub_device_open (device_name); + if (! dev) + goto fail; + + fs = grub_fs_probe (dev); + path = grub_strchr (dirname, ')'); + if (! path) + path = dirname; + else + path++; + + if (! path && ! device_name) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); + goto fail; + } + + if (! *path && device_name) + { + if (grub_errno == GRUB_ERR_UNKNOWN_FS) + grub_errno = GRUB_ERR_NONE; + +#ifdef GRUB_MACHINE_IEEE1275 + /* + * Close device to prevent a double open in grub_normal_print_device_info(). + * Otherwise it may lead to hangs on some IEEE 1275 platforms. + */ + grub_device_close (dev); + dev = NULL; +#endif + + grub_normal_print_device_info (device_name); + } + else if (fs) + { + struct grub_ls_list_files_ctx ctx = { + .dirname = dirname, + .filename = NULL, + .all = all, + .human = human, + .longlist = longlist, + .print_dirhdr = dirhdr + }; + + (fs->fs_dir) (dev, path, print_file, &ctx); + + if (grub_errno == GRUB_ERR_BAD_FILE_TYPE + && path[grub_strlen (path) - 1] != '/') + { + /* + * Reset errno as it is currently set, but will cause subsequent code + * to think there is an error. + */ + grub_errno = GRUB_ERR_NONE; + + /* PATH might be a regular file. */ + ctx.print_dirhdr = 0; + ctx.filename = grub_strrchr (dirname, '/'); + if (ctx.filename == NULL) + goto fail; + ++(ctx.filename); + + ctx.dirname = grub_strndup (dirname, ctx.filename - dirname); + if (ctx.dirname == NULL) + goto fail; + + (fs->fs_dir) (dev, ctx.dirname + (path - dirname), print_file, &ctx); + + grub_free (ctx.dirname); + } + + if (grub_errno == GRUB_ERR_NONE) + grub_xputs ("\n"); + + grub_refresh (); + } + + fail: + if (dev) + grub_device_close (dev); + + grub_free (device_name); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_ls (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + int i; + + if (argc == 0) + grub_ls_list_devices (state[0].set); + else + for (i = 0; i < argc; i++) + grub_ls_list_files (args[i], state[0].set, state[2].set, state[1].set, + argc > 1); + + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(ls) +{ + cmd = grub_register_extcmd ("ls", grub_cmd_ls, 0, + N_("[-l|-h|-a] [FILE ...]"), + N_("List devices and files."), options); +} + +GRUB_MOD_FINI(ls) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/lsacpi.c b/grub-core/commands/lsacpi.c new file mode 100644 index 000000000..f7a1a1b05 --- /dev/null +++ b/grub-core/commands/lsacpi.c @@ -0,0 +1,314 @@ +/* acpi.c - Display acpi tables. */ +/* + * 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wcast-align" + +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +print_strn (grub_uint8_t *str, grub_size_t len) +{ + for (; *str && len; str++, len--) + grub_printf ("%c", *str); + for (len++; len; len--) + grub_printf (" "); +} + +#define print_field(x) print_strn(x, sizeof (x)) + +static void +disp_acpi_table (struct grub_acpi_table_header *t) +{ + print_field (t->signature); + grub_printf ("%4" PRIuGRUB_UINT32_T "B rev=%u chksum=0x%02x (%s) OEM=", t->length, t->revision, t->checksum, + grub_byte_checksum (t, t->length) == 0 ? "valid" : "invalid"); + print_field (t->oemid); + print_field (t->oemtable); + grub_printf ("OEMrev=%08" PRIxGRUB_UINT32_T " ", t->oemrev); + print_field (t->creator_id); + grub_printf (" %08" PRIxGRUB_UINT32_T "\n", t->creator_rev); +} + +static void +disp_madt_table (struct grub_acpi_madt *t) +{ + struct grub_acpi_madt_entry_header *d; + grub_uint32_t len; + + disp_acpi_table (&t->hdr); + grub_printf ("Local APIC=%08" PRIxGRUB_UINT32_T " Flags=%08" + PRIxGRUB_UINT32_T "\n", + t->lapic_addr, t->flags); + len = t->hdr.length - sizeof (struct grub_acpi_madt); + d = t->entries; + for (;len > 0; len -= d->len, d = (void *) ((grub_uint8_t *) d + d->len)) + { + switch (d->type) + { + case GRUB_ACPI_MADT_ENTRY_TYPE_LAPIC: + { + struct grub_acpi_madt_entry_lapic *dt = (void *) d; + grub_printf (" LAPIC ACPI_ID=%02x APIC_ID=%02x Flags=%08x\n", + dt->acpiid, dt->apicid, dt->flags); + if (dt->hdr.len != sizeof (*dt)) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + break; + } + + case GRUB_ACPI_MADT_ENTRY_TYPE_IOAPIC: + { + struct grub_acpi_madt_entry_ioapic *dt = (void *) d; + grub_printf (" IOAPIC ID=%02x address=%08x GSI=%08x\n", + dt->id, dt->address, dt->global_sys_interrupt); + if (dt->hdr.len != sizeof (*dt)) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + if (dt->pad) + grub_printf (" non-zero pad: %02x\n", dt->pad); + break; + } + + case GRUB_ACPI_MADT_ENTRY_TYPE_INTERRUPT_OVERRIDE: + { + struct grub_acpi_madt_entry_interrupt_override *dt = (void *) d; + grub_printf (" Int Override bus=%x src=%x GSI=%08x Flags=%04x\n", + dt->bus, dt->source, dt->global_sys_interrupt, + dt->flags); + if (dt->hdr.len != sizeof (*dt)) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + } + break; + + case GRUB_ACPI_MADT_ENTRY_TYPE_LAPIC_NMI: + { + struct grub_acpi_madt_entry_lapic_nmi *dt = (void *) d; + grub_printf (" LAPIC_NMI ACPI_ID=%02x Flags=%04x lint=%02x\n", + dt->acpiid, dt->flags, dt->lint); + if (dt->hdr.len != sizeof (*dt)) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + break; + } + + case GRUB_ACPI_MADT_ENTRY_TYPE_SAPIC: + { + struct grub_acpi_madt_entry_sapic *dt = (void *) d; + grub_printf (" IOSAPIC Id=%02x GSI=%08x Addr=%016" PRIxGRUB_UINT64_T + "\n", + dt->id, dt->global_sys_interrupt_base, + dt->addr); + if (dt->hdr.len != sizeof (*dt)) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + if (dt->pad) + grub_printf (" non-zero pad: %02x\n", dt->pad); + + } + break; + case GRUB_ACPI_MADT_ENTRY_TYPE_LSAPIC: + { + struct grub_acpi_madt_entry_lsapic *dt = (void *) d; + grub_printf (" LSAPIC ProcId=%02x ID=%02x EID=%02x Flags=%x", + dt->cpu_id, dt->id, dt->eid, dt->flags); + if (dt->flags & GRUB_ACPI_MADT_ENTRY_SAPIC_FLAGS_ENABLED) + grub_printf (" Enabled\n"); + else + grub_printf (" Disabled\n"); + if (d->len > sizeof (struct grub_acpi_madt_entry_sapic)) + grub_printf (" UID val=%08x, Str=%s\n", dt->cpu_uid, + dt->cpu_uid_str); + if (dt->hdr.len != sizeof (*dt) + grub_strlen ((char *) dt->cpu_uid_str) + 1) + grub_printf (" table size mismatch %d != %d\n", dt->hdr.len, + (int) sizeof (*dt)); + if (dt->pad[0] || dt->pad[1] || dt->pad[2]) + grub_printf (" non-zero pad: %02x%02x%02x\n", dt->pad[0], dt->pad[1], dt->pad[2]); + } + break; + case GRUB_ACPI_MADT_ENTRY_TYPE_PLATFORM_INT_SOURCE: + { + struct grub_acpi_madt_entry_platform_int_source *dt = (void *) d; + static const char * const platint_type[] = + {"Nul", "PMI", "INIT", "CPEI"}; + + grub_printf (" Platform INT flags=%04x type=%02x (%s)" + " ID=%02x EID=%02x\n", + dt->flags, dt->inttype, + (dt->inttype < ARRAY_SIZE (platint_type)) + ? platint_type[dt->inttype] : "??", dt->cpu_id, + dt->cpu_eid); + grub_printf (" IOSAPIC Vec=%02x GSI=%08x source flags=%08x\n", + dt->sapic_vector, dt->global_sys_int, dt->src_flags); + } + break; + default: + grub_printf (" type=%x l=%u ", d->type, d->len); + grub_printf (" ??\n"); + } + } +} + +static void +disp_acpi_xsdt_table (struct grub_acpi_table_header *t) +{ + grub_uint32_t len; + grub_uint64_t *desc; + + disp_acpi_table (t); + len = t->length - sizeof (*t); + desc = (grub_uint64_t *) (t + 1); + for (; len >= sizeof (*desc); desc++, len -= sizeof (*desc)) + { +#if GRUB_CPU_SIZEOF_VOID_P == 4 + if (*desc >= (1ULL << 32)) + { + grub_printf ("Unreachable table\n"); + continue; + } +#endif + t = (struct grub_acpi_table_header *) (grub_addr_t) *desc; + + if (t == NULL) + continue; + + if (grub_memcmp (t->signature, GRUB_ACPI_MADT_SIGNATURE, + sizeof (t->signature)) == 0) + disp_madt_table ((struct grub_acpi_madt *) t); + else + disp_acpi_table (t); + } +} + +static void +disp_acpi_rsdt_table (struct grub_acpi_table_header *t) +{ + grub_uint32_t len; + grub_uint32_t *desc; + + disp_acpi_table (t); + len = t->length - sizeof (*t); + desc = (grub_uint32_t *) (t + 1); + for (; len >= sizeof (*desc); desc++, len -= sizeof (*desc)) + { + t = (struct grub_acpi_table_header *) (grub_addr_t) *desc; + + if (t == NULL) + continue; + + if (grub_memcmp (t->signature, GRUB_ACPI_MADT_SIGNATURE, + sizeof (t->signature)) == 0) + disp_madt_table ((struct grub_acpi_madt *) t); + else + disp_acpi_table (t); + } +} + +static void +disp_acpi_rsdpv1 (struct grub_acpi_rsdp_v10 *rsdp) +{ + print_field (rsdp->signature); + grub_printf ("chksum:%02x (%s), OEM-ID: ", rsdp->checksum, grub_byte_checksum (rsdp, sizeof (*rsdp)) == 0 ? "valid" : "invalid"); + print_field (rsdp->oemid); + grub_printf ("rev=%d\n", rsdp->revision); + grub_printf ("RSDT=%08" PRIxGRUB_UINT32_T "\n", rsdp->rsdt_addr); +} + +static void +disp_acpi_rsdpv2 (struct grub_acpi_rsdp_v20 *rsdp) +{ + disp_acpi_rsdpv1 (&rsdp->rsdpv1); + grub_printf ("len=%d chksum=%02x (%s) XSDT=%016" PRIxGRUB_UINT64_T "\n", rsdp->length, rsdp->checksum, grub_byte_checksum (rsdp, rsdp->length) == 0 ? "valid" : "invalid", + rsdp->xsdt_addr); + if (rsdp->length != sizeof (*rsdp)) + grub_printf (" length mismatch %d != %d\n", rsdp->length, + (int) sizeof (*rsdp)); + if (rsdp->reserved[0] || rsdp->reserved[1] || rsdp->reserved[2]) + grub_printf (" non-zero reserved %02x%02x%02x\n", rsdp->reserved[0], rsdp->reserved[1], rsdp->reserved[2]); +} + +static const struct grub_arg_option options[] = { + {"v1", '1', 0, N_("Show version 1 tables only."), 0, ARG_TYPE_NONE}, + {"v2", '2', 0, N_("Show version 2 and version 3 tables only."), 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_lsacpi (struct grub_extcmd_context *ctxt, + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + if (!ctxt->state[1].set) + { + struct grub_acpi_rsdp_v10 *rsdp1 = grub_acpi_get_rsdpv1 (); + if (!rsdp1) + grub_printf ("No RSDPv1\n"); + else + { + grub_printf ("RSDPv1 signature:"); + disp_acpi_rsdpv1 (rsdp1); + disp_acpi_rsdt_table ((void *) (grub_addr_t) rsdp1->rsdt_addr); + } + } + + if (!ctxt->state[0].set) + { + struct grub_acpi_rsdp_v20 *rsdp2 = grub_acpi_get_rsdpv2 (); + if (!rsdp2) + grub_printf ("No RSDPv2\n"); + else + { +#if GRUB_CPU_SIZEOF_VOID_P == 4 + if (rsdp2->xsdt_addr >= (1ULL << 32)) + grub_printf ("Unreachable RSDPv2\n"); + else +#endif + { + grub_printf ("RSDPv2 signature:"); + disp_acpi_rsdpv2 (rsdp2); + disp_acpi_xsdt_table ((void *) (grub_addr_t) rsdp2->xsdt_addr); + grub_printf ("\n"); + } + } + } + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(lsapi) +{ + cmd = grub_register_extcmd ("lsacpi", grub_cmd_lsacpi, 0, "[-1|-2]", + N_("Show ACPI information."), options); +} + +GRUB_MOD_FINI(lsacpi) +{ + grub_unregister_extcmd (cmd); +} + + diff --git a/grub-core/commands/lsmmap.c b/grub-core/commands/lsmmap.c new file mode 100644 index 000000000..816ee47d1 --- /dev/null +++ b/grub-core/commands/lsmmap.c @@ -0,0 +1,85 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#ifndef GRUB_MACHINE_EMU +static const char *names[] = + { + [GRUB_MEMORY_AVAILABLE] = N_("available RAM"), + [GRUB_MEMORY_RESERVED] = N_("reserved RAM"), + /* TRANSLATORS: this refers to memory where ACPI tables are stored + and which can be used by OS once it loads ACPI tables. */ + [GRUB_MEMORY_ACPI] = N_("ACPI reclaimable RAM"), + /* TRANSLATORS: this refers to memory which ACPI-compliant OS + is required to save accross hibernations. */ + [GRUB_MEMORY_NVS] = N_("ACPI non-volatile storage RAM"), + [GRUB_MEMORY_BADRAM] = N_("faulty RAM (BadRAM)"), + [GRUB_MEMORY_PERSISTENT] = N_("persistent RAM"), + [GRUB_MEMORY_PERSISTENT_LEGACY] = N_("persistent RAM (legacy)"), + [GRUB_MEMORY_COREBOOT_TABLES] = N_("RAM holding coreboot tables"), + [GRUB_MEMORY_CODE] = N_("RAM holding firmware code") + }; + +/* Helper for grub_cmd_lsmmap. */ +static int +lsmmap_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + if (type < (int) ARRAY_SIZE (names) && type >= 0 && names[type]) + grub_printf_ (N_("base_addr = 0x%llx, length = 0x%llx, %s\n"), + (long long) addr, (long long) size, _(names[type])); + else + grub_printf_ (N_("base_addr = 0x%llx, length = 0x%llx, type = 0x%x\n"), + (long long) addr, (long long) size, type); + return 0; +} +#endif + +static grub_err_t +grub_cmd_lsmmap (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) + +{ +#ifndef GRUB_MACHINE_EMU + grub_machine_mmap_iterate (lsmmap_hook, NULL); +#endif + + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsmmap) +{ + cmd = grub_register_command ("lsmmap", grub_cmd_lsmmap, + 0, N_("List memory map provided by firmware.")); +} + +GRUB_MOD_FINI(lsmmap) +{ + grub_unregister_command (cmd); +} diff --git a/commands/lspci.c b/grub-core/commands/lspci.c similarity index 93% rename from commands/lspci.c rename to grub-core/commands/lspci.c index a69bb35ad..b3cdab1b3 100644 --- a/commands/lspci.c +++ b/grub-core/commands/lspci.c @@ -22,12 +22,15 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct grub_pci_classname { int class; int subclass; - char *desc; + const char *desc; }; static const struct grub_pci_classname grub_pci_classes[] = @@ -124,8 +127,9 @@ static const struct grub_arg_option options[] = static int iospace; -static int NESTED_FUNC_ATTR -grub_lspci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) +static int +grub_lspci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) { grub_uint32_t class; const char *sclass; @@ -167,7 +171,7 @@ grub_lspci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) if (space == 0) continue; - + switch (space & GRUB_PCI_ADDR_SPACE_MASK) { case GRUB_PCI_ADDR_SPACE_IO: @@ -191,13 +195,13 @@ grub_lspci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) (space & GRUB_PCI_ADDR_MEM_MASK), space & GRUB_PCI_ADDR_MEM_PREFETCH ? "prefetchable" : "non-prefetchable"); - + } else grub_printf ("\t32-bit memory space %d at 0x%016llx [%s]\n", (unsigned) ((reg - GRUB_PCI_REG_ADDRESSES) / sizeof (grub_uint32_t)) - 1, - (unsigned long long) + (unsigned long long) (space & GRUB_PCI_ADDR_MEM_MASK), space & GRUB_PCI_ADDR_MEM_PREFETCH ? "prefetchable" : "non-prefetchable"); @@ -211,12 +215,12 @@ grub_lspci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) } static grub_err_t -grub_cmd_lspci (grub_extcmd_t cmd, +grub_cmd_lspci (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - iospace = cmd->state[0].set; - grub_pci_iterate (grub_lspci_iter); + iospace = ctxt->state[0].set; + grub_pci_iterate (grub_lspci_iter, NULL); return GRUB_ERR_NONE; } @@ -224,8 +228,8 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(lspci) { - cmd = grub_register_extcmd ("lspci", grub_cmd_lspci, GRUB_COMMAND_FLAG_BOTH, - "[-i]", N_("List PCI devices."), options); + cmd = grub_register_extcmd ("lspci", grub_cmd_lspci, 0, "[-i]", + N_("List PCI devices."), options); } GRUB_MOD_FINI(lspci) diff --git a/grub-core/commands/macbless.c b/grub-core/commands/macbless.c new file mode 100644 index 000000000..3d761452a --- /dev/null +++ b/grub-core/commands/macbless.c @@ -0,0 +1,233 @@ +/* hfspbless.c - set the hfs+ boot directory. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2007,2008,2009,2012,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct find_node_context +{ + grub_uint64_t inode_found; + char *dirname; + enum + { FOUND_NONE, FOUND_FILE, FOUND_DIR } found; +}; + +static int +find_inode (const char *filename, + const struct grub_dirhook_info *info, void *data) +{ + struct find_node_context *ctx = data; + if (!info->inodeset) + return 0; + + if ((grub_strcmp (ctx->dirname, filename) == 0 + || (info->case_insensitive + && grub_strcasecmp (ctx->dirname, filename) == 0))) + { + ctx->inode_found = info->inode; + ctx->found = info->dir ? FOUND_DIR : FOUND_FILE; + } + return 0; +} + +grub_err_t +grub_mac_bless_inode (grub_device_t dev, grub_uint32_t inode, int is_dir, + int intel) +{ + grub_err_t err; + union + { + struct grub_hfs_sblock hfs; + struct grub_hfsplus_volheader hfsplus; + } volheader; + grub_disk_addr_t embedded_offset; + + if (intel && is_dir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "can't bless a directory for mactel"); + if (!intel && !is_dir) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "can't bless a file for mac PPC"); + + /* Read the bootblock. */ + err = grub_disk_read (dev->disk, GRUB_HFSPLUS_SBLOCK, 0, sizeof (volheader), + (char *) &volheader); + if (err) + return err; + + embedded_offset = 0; + if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC) + { + int extent_start; + int ablk_size; + int ablk_start; + + /* See if there's an embedded HFS+ filesystem. */ + if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC) + { + if (intel) + volheader.hfs.intel_bootfile = grub_be_to_cpu32 (inode); + else + volheader.hfs.ppc_bootdir = grub_be_to_cpu32 (inode); + return GRUB_ERR_NONE; + } + + /* Calculate the offset needed to translate HFS+ sector numbers. */ + extent_start = + grub_be_to_cpu16 (volheader.hfs.embed_extent.first_block); + ablk_size = grub_be_to_cpu32 (volheader.hfs.blksz); + ablk_start = grub_be_to_cpu16 (volheader.hfs.first_block); + embedded_offset = (ablk_start + + ((grub_uint64_t) extent_start) + * (ablk_size >> GRUB_DISK_SECTOR_BITS)); + + err = + grub_disk_read (dev->disk, embedded_offset + GRUB_HFSPLUS_SBLOCK, 0, + sizeof (volheader), (char *) &volheader); + if (err) + return err; + } + + if ((grub_be_to_cpu16 (volheader.hfsplus.magic) != GRUB_HFSPLUS_MAGIC) + && (grub_be_to_cpu16 (volheader.hfsplus.magic) != GRUB_HFSPLUSX_MAGIC)) + return grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); + if (intel) + volheader.hfsplus.intel_bootfile = grub_be_to_cpu32 (inode); + else + volheader.hfsplus.ppc_bootdir = grub_be_to_cpu32 (inode); + + return grub_disk_write (dev->disk, embedded_offset + GRUB_HFSPLUS_SBLOCK, 0, + sizeof (volheader), (char *) &volheader); +} + +grub_err_t +grub_mac_bless_file (grub_device_t dev, const char *path_in, int intel) +{ + grub_fs_t fs; + + char *path, *tail; + struct find_node_context ctx; + + fs = grub_fs_probe (dev); + if (!fs || (grub_strcmp (fs->name, "hfsplus") != 0 + && grub_strcmp (fs->name, "hfs") != 0)) + return grub_error (GRUB_ERR_BAD_FS, "no suitable FS found"); + + path = grub_strdup (path_in); + if (!path) + return grub_errno; + + tail = path + grub_strlen (path) - 1; + + /* Remove trailing '/'. */ + while (tail != path && *tail == '/') + *(tail--) = 0; + + tail = grub_strrchr (path, '/'); + ctx.found = 0; + + if (tail) + { + *tail = 0; + ctx.dirname = tail + 1; + + (fs->fs_dir) (dev, *path == 0 ? "/" : path, find_inode, &ctx); + } + else + { + ctx.dirname = path + 1; + (fs->fs_dir) (dev, "/", find_inode, &ctx); + } + if (!ctx.found) + { + grub_free (path); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + path_in); + } + grub_free (path); + + return grub_mac_bless_inode (dev, (grub_uint32_t) ctx.inode_found, + (ctx.found == FOUND_DIR), intel); +} + +static grub_err_t +grub_cmd_macbless (grub_command_t cmd, int argc, char **args) +{ + char *device_name; + char *path = 0; + grub_device_t dev = 0; + grub_err_t err; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + device_name = grub_file_get_device_name (args[0]); + dev = grub_device_open (device_name); + + path = grub_strchr (args[0], ')'); + if (!path) + path = args[0]; + else + path = path + 1; + + if (!path || *path == 0 || !dev) + { + if (dev) + grub_device_close (dev); + + grub_free (device_name); + + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); + } + + err = grub_mac_bless_file (dev, path, cmd->name[3] == 't'); + + grub_device_close (dev); + grub_free (device_name); + return err; +} + +static grub_command_t cmd, cmd_ppc; + +GRUB_MOD_INIT(macbless) +{ + cmd = grub_register_command ("mactelbless", grub_cmd_macbless, + N_("FILE"), + N_("Bless FILE of HFS or HFS+ partition for intel macs.")); + cmd_ppc = + grub_register_command ("macppcbless", grub_cmd_macbless, N_("DIR"), + N_("Bless DIR of HFS or HFS+ partition for PPC macs.")); +} + +GRUB_MOD_FINI(macbless) +{ + grub_unregister_command (cmd); + grub_unregister_command (cmd_ppc); +} diff --git a/commands/memrw.c b/grub-core/commands/memrw.c similarity index 64% rename from commands/memrw.c rename to grub-core/commands/memrw.c index 6a4d43be2..3542683d1 100644 --- a/commands/memrw.c +++ b/grub-core/commands/memrw.c @@ -22,6 +22,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static grub_extcmd_t cmd_read_byte, cmd_read_word, cmd_read_dword; static grub_command_t cmd_write_byte, cmd_write_word, cmd_write_dword; @@ -29,23 +32,23 @@ static grub_command_t cmd_write_byte, cmd_write_word, cmd_write_dword; static const struct grub_arg_option options[] = { {0, 'v', 0, N_("Save read value into variable VARNAME."), - "VARNAME", ARG_TYPE_STRING}, + N_("VARNAME"), ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; static grub_err_t -grub_cmd_read (grub_extcmd_t cmd, int argc, char **argv) +grub_cmd_read (grub_extcmd_context_t ctxt, int argc, char **argv) { - grub_target_addr_t addr; + grub_addr_t addr; grub_uint32_t value = 0; char buf[sizeof ("XXXXXXXX")]; if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid number of arguments"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); addr = grub_strtoul (argv[0], 0, 0); - switch (cmd->cmd->name[sizeof ("read_") - 1]) + switch (ctxt->extcmd->cmd->name[sizeof ("read_") - 1]) { case 'd': value = *((volatile grub_uint32_t *) addr); @@ -60,10 +63,10 @@ grub_cmd_read (grub_extcmd_t cmd, int argc, char **argv) break; } - if (cmd->state[0].set) + if (ctxt->state[0].set) { grub_snprintf (buf, sizeof (buf), "%x", value); - grub_env_set (cmd->state[0].arg, buf); + grub_env_set (ctxt->state[0].arg, buf); } else grub_printf ("0x%x\n", value); @@ -74,12 +77,12 @@ grub_cmd_read (grub_extcmd_t cmd, int argc, char **argv) static grub_err_t grub_cmd_write (grub_command_t cmd, int argc, char **argv) { - grub_target_addr_t addr; + grub_addr_t addr; grub_uint32_t value; grub_uint32_t mask = 0xffffffff; if (argc != 2 && argc != 3) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid number of arguments"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); addr = grub_strtoul (argv[0], 0, 0); value = grub_strtoul (argv[1], 0, 0); @@ -119,23 +122,32 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { cmd_read_byte = - grub_register_extcmd ("read_byte", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH, - N_("ADDR"), N_("Read byte from ADDR."), options); + grub_register_extcmd_lockdown ("read_byte", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 8-bit value from ADDR."), + options); cmd_read_word = - grub_register_extcmd ("read_word", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH, - N_("ADDR"), N_("Read word from ADDR."), options); + grub_register_extcmd_lockdown ("read_word", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 16-bit value from ADDR."), + options); cmd_read_dword = - grub_register_extcmd ("read_dword", grub_cmd_read, GRUB_COMMAND_FLAG_BOTH, - N_("ADDR"), N_("Read dword from ADDR."), options); + grub_register_extcmd_lockdown ("read_dword", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 32-bit value from ADDR."), + options); cmd_write_byte = - grub_register_command ("write_byte", grub_cmd_write, - N_("ADDR VALUE [MASK]"), N_("Write byte VALUE to ADDR.")); + grub_register_command_lockdown ("write_byte", grub_cmd_write, + N_("ADDR VALUE [MASK]"), + N_("Write 8-bit VALUE to ADDR.")); cmd_write_word = - grub_register_command ("write_word", grub_cmd_write, - N_("ADDR VALUE [MASK]"), N_("Write word VALUE to ADDR.")); + grub_register_command_lockdown ("write_word", grub_cmd_write, + N_("ADDR VALUE [MASK]"), + N_("Write 16-bit VALUE to ADDR.")); cmd_write_dword = - grub_register_command ("write_dword", grub_cmd_write, - N_("ADDR VALUE [MASK]"), N_("Write dword VALUE to ADDR.")); + grub_register_command_lockdown ("write_dword", grub_cmd_write, + N_("ADDR VALUE [MASK]"), + N_("Write 32-bit VALUE to ADDR.")); } GRUB_MOD_FINI(memrw) diff --git a/grub-core/commands/memtools.c b/grub-core/commands/memtools.c new file mode 100644 index 000000000..ae0a9bec3 --- /dev/null +++ b/grub-core/commands/memtools.c @@ -0,0 +1,152 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Free Software Foundation, Inc. + * Copyright (C) 2022 IBM Corporation + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_lsmem (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) + +{ +#ifndef GRUB_MACHINE_EMU + grub_mm_dump (0); +#endif + + return 0; +} + +static grub_err_t +grub_cmd_lsfreemem (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) + +{ +#ifndef GRUB_MACHINE_EMU + grub_mm_dump_free (); +#endif + + return 0; +} + + +static grub_err_t +grub_cmd_stress_big_allocs (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + int i, max_mb, blocks_alloced; + void *mem; + void **blocklist; + + grub_printf ("Test 1: increasingly sized allocs to 1GB block\n"); + for (i = 1; i < 1024; i++) + { + grub_printf ("%4d MB . ", i); + mem = grub_malloc (i * 1024 * 1024); + if (mem == NULL) + { + grub_printf ("failed\n"); + break; + } + else + grub_free (mem); + + if (i % 7 == 0) + grub_printf ("\n"); + } + + max_mb = i - 1; + grub_printf ("\nMax sized allocation we did was %d MB\n", max_mb); + + grub_printf ("\nTest 2: 1MB at a time, max 4GB\n"); + blocklist = grub_calloc (4096, sizeof (void *)); + for (i = 0; i < 4096; i++) + { + blocklist[i] = grub_malloc (1024 * 1024); + if (blocklist[i] == NULL) + { + grub_printf ("Ran out of memory at iteration %d\n", i); + break; + } + } + blocks_alloced = i; + for (i = 0; i < blocks_alloced; i++) + grub_free (blocklist[i]); + + grub_printf ("\nTest 3: 1MB aligned 900kB + 100kB\n"); + /* grub_mm_debug=1;*/ + for (i = 0; i < 4096; i += 2) + { + blocklist[i] = grub_memalign (1024 * 1024, 900 * 1024); + if (blocklist[i] == NULL) + { + grub_printf ("Failed big allocation, iteration %d\n", i); + blocks_alloced = i; + break; + } + + blocklist[i + 1] = grub_malloc (100 * 1024); + if (blocklist[i + 1] == NULL) + { + grub_printf ("Failed small allocation, iteration %d\n", i); + blocks_alloced = i + 1; + break; + } + grub_printf ("."); + } + for (i = 0; i < blocks_alloced; i++) + grub_free (blocklist[i]); + + grub_free (blocklist); + +#if defined(__powerpc__) + grub_printf ("\nA reboot may now be required.\n"); +#endif + + grub_errno = GRUB_ERR_NONE; + return GRUB_ERR_NONE; +} + +static grub_command_t cmd_lsmem, cmd_lsfreemem, cmd_sba; + +GRUB_MOD_INIT (memtools) +{ + cmd_lsmem = grub_register_command ("lsmem", grub_cmd_lsmem, + 0, N_("List free and allocated memory blocks.")); + cmd_lsfreemem = grub_register_command ("lsfreemem", grub_cmd_lsfreemem, + 0, N_("List free memory blocks.")); + cmd_sba = grub_register_command ("stress_big_allocs", grub_cmd_stress_big_allocs, + 0, N_("Stress test large allocations.")); +} + +GRUB_MOD_FINI (memtools) +{ + grub_unregister_command (cmd_lsmem); + grub_unregister_command (cmd_lsfreemem); + grub_unregister_command (cmd_sba); +} diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c new file mode 100644 index 000000000..720e6d8ea --- /dev/null +++ b/grub-core/commands/menuentry.c @@ -0,0 +1,337 @@ +/* menuentry.c - menuentry command */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct grub_arg_option options[] = + { + {"class", 1, GRUB_ARG_OPTION_REPEATABLE, + N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING}, + {"users", 2, 0, + N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"), + ARG_TYPE_STRING}, + {"hotkey", 3, 0, + N_("Keyboard key to quickly boot this entry."), N_("KEYBOARD_KEY"), ARG_TYPE_STRING}, + {"source", 4, 0, + N_("Use STRING as menu entry body."), N_("STRING"), ARG_TYPE_STRING}, + {"id", 0, 0, N_("Menu entry identifier."), N_("STRING"), ARG_TYPE_STRING}, + /* TRANSLATORS: menu entry can either be bootable by anyone or only by + handful of users. By default when security is active only superusers can + boot a given menu entry. With --unrestricted (this option) + anyone can boot it. */ + {"unrestricted", 0, 0, N_("This entry can be booted by any user."), + 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} + }; + +static struct +{ + const char *name; + int key; +} hotkey_aliases[] = + { + {"backspace", GRUB_TERM_BACKSPACE}, + {"tab", GRUB_TERM_TAB}, + {"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, + char **classes, const char *id, + const char *users, const char *hotkey, + const char *prefix, const char *sourcecode, + int submenu) +{ + int menu_hotkey = 0; + char **menu_args = NULL; + char *menu_users = NULL; + char *menu_title = NULL; + char *menu_sourcecode = NULL; + char *menu_id = NULL; + struct grub_menu_entry_class *menu_classes = NULL; + + grub_menu_t menu; + grub_menu_entry_t *last; + + menu = grub_env_get_menu (); + if (! menu) + return grub_error (GRUB_ERR_MENU, "no menu context"); + + last = &menu->entry_list; + + menu_sourcecode = grub_xasprintf ("%s%s", prefix ?: "", sourcecode); + if (! menu_sourcecode) + return grub_errno; + + if (classes && classes[0]) + { + int i; + for (i = 0; classes[i]; i++); /* count # of menuentry classes */ + menu_classes = grub_zalloc (sizeof (struct grub_menu_entry_class) + * (i + 1)); + if (! menu_classes) + goto fail; + + for (i = 0; classes[i]; i++) + { + menu_classes[i].name = grub_strdup (classes[i]); + if (! menu_classes[i].name) + goto fail; + menu_classes[i].next = classes[i + 1] ? &menu_classes[i + 1] : NULL; + } + } + + if (users) + { + menu_users = grub_strdup (users); + if (! menu_users) + goto fail; + } + + if (hotkey) + { + unsigned i; + for (i = 0; i < ARRAY_SIZE (hotkey_aliases); i++) + if (grub_strcmp (hotkey, hotkey_aliases[i].name) == 0) + { + menu_hotkey = hotkey_aliases[i].key; + break; + } + if (i == ARRAY_SIZE (hotkey_aliases)) + menu_hotkey = hotkey[0]; + } + + if (! argc) + { + grub_error (GRUB_ERR_MENU, "menuentry is missing title"); + goto fail; + } + + menu_title = grub_strdup (args[0]); + if (! menu_title) + goto fail; + + menu_id = grub_strdup (id ? : menu_title); + if (! menu_id) + goto fail; + + /* Save argc, args to pass as parameters to block arg later. */ + menu_args = grub_calloc (argc + 1, sizeof (char *)); + if (! menu_args) + goto fail; + + { + int i; + for (i = 0; i < argc; i++) + { + menu_args[i] = grub_strdup (args[i]); + if (! menu_args[i]) + goto fail; + } + menu_args[argc] = NULL; + } + + /* Add the menu entry at the end of the list. */ + while (*last) + last = &(*last)->next; + + *last = grub_zalloc (sizeof (**last)); + if (! *last) + goto fail; + + (*last)->title = menu_title; + (*last)->id = menu_id; + (*last)->hotkey = menu_hotkey; + (*last)->classes = menu_classes; + if (menu_users) + (*last)->restricted = 1; + (*last)->users = menu_users; + (*last)->argc = argc; + (*last)->args = menu_args; + (*last)->sourcecode = menu_sourcecode; + (*last)->submenu = submenu; + + menu->size++; + return GRUB_ERR_NONE; + + fail: + + grub_free (menu_sourcecode); + { + int i; + for (i = 0; menu_classes && menu_classes[i].name; i++) + grub_free (menu_classes[i].name); + grub_free (menu_classes); + } + + { + int i; + for (i = 0; menu_args && menu_args[i]; i++) + grub_free (menu_args[i]); + grub_free (menu_args); + } + + grub_free (menu_users); + grub_free (menu_title); + grub_free (menu_id); + return grub_errno; +} + +static char * +setparams_prefix (int argc, char **args) +{ + int i; + int j; + char *p; + char *result; + grub_size_t len = 10; + + /* Count resulting string length */ + for (i = 0; i < argc; i++) + { + len += 3; /* 3 = 1 space + 2 quotes */ + p = args[i]; + while (*p) + len += (*p++ == '\'' ? 4 : 1); + } + + result = grub_malloc (len + 2); + if (! result) + return 0; + + grub_strcpy (result, "setparams"); + p = result + 9; + + for (j = 0; j < argc; j++) + { + *p++ = ' '; + *p++ = '\''; + p = grub_strchrsub (p, args[j], '\'', "'\\''"); + *p++ = '\''; + } + *p++ = '\n'; + *p = '\0'; + return result; +} + +static grub_err_t +grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) +{ + char ch; + char *src; + char *prefix; + unsigned len; + grub_err_t r; + const char *users; + + if (! argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing arguments"); + + if (ctxt->state[3].set && ctxt->script) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "multiple menuentry definitions"); + + if (! ctxt->state[3].set && ! ctxt->script) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); + + if (ctxt->state[1].set) + users = ctxt->state[1].arg; + else if (ctxt->state[5].set) + users = NULL; + else + users = ""; + + if (! ctxt->script) + return grub_normal_add_menu_entry (argc, (const char **) args, + (ctxt->state[0].set ? ctxt->state[0].args + : NULL), + ctxt->state[4].arg, + users, + ctxt->state[2].arg, 0, + ctxt->state[3].arg, + ctxt->extcmd->cmd->name[0] == 's'); + + src = args[argc - 1]; + args[argc - 1] = NULL; + + len = grub_strlen(src); + ch = src[len - 1]; + src[len - 1] = '\0'; + + prefix = setparams_prefix (argc - 1, args); + if (! prefix) + return grub_errno; + + r = grub_normal_add_menu_entry (argc - 1, (const char **) args, + ctxt->state[0].args, ctxt->state[4].arg, + users, + ctxt->state[2].arg, prefix, src + 1, + ctxt->extcmd->cmd->name[0] == 's'); + + src[len - 1] = ch; + args[argc - 1] = src; + grub_free (prefix); + return r; +} + +static grub_extcmd_t cmd, cmd_sub; + +void +grub_menu_init (void) +{ + cmd = grub_register_extcmd ("menuentry", grub_cmd_menuentry, + GRUB_COMMAND_FLAG_BLOCKS + | GRUB_COMMAND_ACCEPT_DASH + | GRUB_COMMAND_FLAG_EXTRACTOR, + N_("BLOCK"), N_("Define a menu entry."), options); + cmd_sub = grub_register_extcmd ("submenu", grub_cmd_menuentry, + GRUB_COMMAND_FLAG_BLOCKS + | GRUB_COMMAND_ACCEPT_DASH + | GRUB_COMMAND_FLAG_EXTRACTOR, + N_("BLOCK"), N_("Define a submenu."), + options); +} + +void +grub_menu_fini (void) +{ + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_sub); +} diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c new file mode 100644 index 000000000..ff4ff021c --- /dev/null +++ b/grub-core/commands/minicmd.c @@ -0,0 +1,238 @@ +/* minicmd.c - commands for the rescue mode */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_EFI +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* cat FILE */ +static grub_err_t +grub_mini_cmd_cat (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + char buf[GRUB_DISK_SECTOR_SIZE]; + grub_ssize_t size; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_CAT); + if (! file) + return grub_errno; + + while ((size = grub_file_read (file, buf, sizeof (buf))) > 0) + { + 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 + { + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + grub_printf ("<%x>", (int) c); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); + } + } + } + + grub_xputs ("\n"); + grub_refresh (); + grub_file_close (file); + + return 0; +} + +/* help */ +static grub_err_t +grub_mini_cmd_help (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_command_t p; + + for (p = grub_command_list; p; p = p->next) + grub_printf ("%s (%d%c)\t%s\n", p->name, + p->prio & GRUB_COMMAND_PRIO_MASK, + (p->prio & GRUB_COMMAND_FLAG_ACTIVE) ? '+' : '-', + p->description); + + return 0; +} + +/* dump ADDRESS [SIZE] */ +static grub_err_t +grub_mini_cmd_dump (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_uint8_t *addr; + grub_size_t size = 4; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no address specified"); + +#if GRUB_CPU_SIZEOF_VOID_P == GRUB_CPU_SIZEOF_LONG +#define grub_strtoaddr grub_strtoul +#else +#define grub_strtoaddr grub_strtoull +#endif + + addr = (grub_uint8_t *) grub_strtoaddr (argv[0], 0, 0); + if (grub_errno) + return grub_errno; + + if (argc > 1) + size = (grub_size_t) grub_strtoaddr (argv[1], 0, 0); + + while (size--) + { + grub_printf ("%x%x ", *addr >> 4, *addr & 0xf); + addr++; + } + + return 0; +} + +/* rmmod MODULE */ +static grub_err_t +grub_mini_cmd_rmmod (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_dl_t mod; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified"); + + mod = grub_dl_get (argv[0]); + if (! mod) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such module"); + + if (grub_dl_is_persistent (mod)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot unload persistent module"); + + if (grub_dl_ref_count (mod) > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot unload referenced module"); + + grub_dl_unref (mod); + grub_dl_unload (mod); + + return 0; +} + +/* lsmod */ +static grub_err_t +grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ + grub_dl_t mod; + + /* TRANSLATORS: this is module list header. Name + is module name, Ref Count is a reference counter + (how many modules or open descriptors use it). + Dependencies are the other modules it uses. + */ + grub_printf_ (N_("Name\tRef Count\tDependencies\n")); + FOR_DL_MODULES (mod) + { + grub_dl_dep_t dep; + + grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count); + for (dep = mod->dep; dep; dep = dep->next) + { + if (dep != mod->dep) + grub_xputs (","); + + grub_printf ("%s", dep->mod->name); + } + grub_xputs ("\n"); + } + + return 0; +} + +/* exit */ +static grub_err_t __attribute__ ((noreturn)) +grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char *argv[] __attribute__ ((unused))) +{ +#ifdef GRUB_MACHINE_EFI + /* + * The "exit" command is often used to launch the next boot application. + * So, erase the secrets. + */ + grub_cryptodisk_erasesecrets (); +#endif + grub_exit (); + /* Not reached. */ +} + +static grub_command_t cmd_cat, cmd_help; +static grub_command_t cmd_dump, cmd_rmmod, cmd_lsmod, cmd_exit; + +GRUB_MOD_INIT(minicmd) +{ + cmd_cat = + grub_register_command ("cat", grub_mini_cmd_cat, + N_("FILE"), N_("Show the contents of a file.")); + cmd_help = + grub_register_command ("help", grub_mini_cmd_help, + 0, N_("Show this message.")); + cmd_dump = + grub_register_command_lockdown ("dump", grub_mini_cmd_dump, + N_("ADDR [SIZE]"), N_("Show memory contents.")); + cmd_rmmod = + grub_register_command ("rmmod", grub_mini_cmd_rmmod, + N_("MODULE"), N_("Remove a module.")); + cmd_lsmod = + grub_register_command ("lsmod", grub_mini_cmd_lsmod, + 0, N_("Show loaded modules.")); + cmd_exit = + grub_register_command ("exit", grub_mini_cmd_exit, + 0, N_("Exit from GRUB.")); +} + +GRUB_MOD_FINI(minicmd) +{ + grub_unregister_command (cmd_cat); + grub_unregister_command (cmd_help); + grub_unregister_command (cmd_dump); + grub_unregister_command (cmd_rmmod); + grub_unregister_command (cmd_lsmod); + grub_unregister_command (cmd_exit); +} diff --git a/grub-core/commands/mips/loongson/lsspd.c b/grub-core/commands/mips/loongson/lsspd.c new file mode 100644 index 000000000..97569816d --- /dev/null +++ b/grub-core/commands/mips/loongson/lsspd.c @@ -0,0 +1,103 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_lsspd (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_pci_device_t dev; + grub_port_t smbbase; + int i; + grub_err_t err; + + if (!grub_cs5536_find (&dev)) + { + grub_puts_ (N_("No CS5536 found")); + return GRUB_ERR_NONE; + } + grub_printf_ (N_("CS5536 at %d:%d.%d\n"), grub_pci_get_bus (dev), + grub_pci_get_device (dev), grub_pci_get_function (dev)); + + err = grub_cs5536_init_smbus (dev, 0x7fff, &smbbase); + if (err) + return err; + + /* TRANSLATORS: System management bus is often used to access components like + RAM (info only, not data) or batteries. I/O space is where in memory + its ports are. */ + grub_printf_ (N_("System management bus controller I/O space is at 0x%x\n"), + smbbase); + + for (i = GRUB_SMB_RAM_START_ADDR; + i < GRUB_SMB_RAM_START_ADDR + GRUB_SMB_RAM_NUM_MAX; i++) + { + struct grub_smbus_spd spd; + grub_memset (&spd, 0, sizeof (spd)); + /* TRANSLATORS: it's shown in a report in a way + like number 1: ... number 2: ... + */ + grub_printf_ (N_("RAM slot number %d\n"), i); + err = grub_cs5536_read_spd (smbbase, i, &spd); + if (err) + { + grub_print_error (); + continue; + } + grub_printf_ (N_("Written SPD bytes: %d B.\n"), spd.written_size); + grub_printf_ (N_("Total flash size: %d B.\n"), + 1 << spd.log_total_flash_size); + if (spd.memory_type == GRUB_SMBUS_SPD_MEMORY_TYPE_DDR2) + { + char str[sizeof (spd.ddr2.part_number) + 1]; + grub_puts_ (N_("Memory type: DDR2.")); + grub_memcpy (str, spd.ddr2.part_number, + sizeof (spd.ddr2.part_number)); + str[sizeof (spd.ddr2.part_number)] = 0; + grub_printf_ (N_("Part no: %s.\n"), str); + } + else + grub_puts_ (N_("Memory type: Unknown.")); + } + + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(lsspd) +{ + cmd = grub_register_command ("lsspd", grub_cmd_lsspd, 0, + N_("Print Memory information.")); +} + +GRUB_MOD_FINI(lsspd) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c new file mode 100644 index 000000000..580c8d3b0 --- /dev/null +++ b/grub-core/commands/nativedisk.c @@ -0,0 +1,332 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const char *modnames_def[] = { + /* FIXME: autogenerate this. */ +#if defined (__i386__) || defined (__x86_64__) || defined (GRUB_MACHINE_MIPS_LOONGSON) + "pata", "ahci", "usbms", "ohci", "uhci", "ehci" +#elif defined (GRUB_MACHINE_MIPS_QEMU_MIPS) + "pata" +#else +#error "Fill this" +#endif + }; + +static grub_err_t +get_uuid (const char *name, char **uuid, int getnative) +{ + grub_device_t dev; + grub_fs_t fs = 0; + + *uuid = 0; + + dev = grub_device_open (name); + if (!dev) + return grub_errno; + + if (!dev->disk) + { + grub_dprintf ("nativedisk", "Skipping non-disk\n"); + grub_device_close (dev); + return 0; + } + + switch (dev->disk->dev->id) + { + /* Firmware disks. */ + case GRUB_DISK_DEVICE_BIOSDISK_ID: + case GRUB_DISK_DEVICE_OFDISK_ID: + case GRUB_DISK_DEVICE_OBDISK_ID: + case GRUB_DISK_DEVICE_EFIDISK_ID: + case GRUB_DISK_DEVICE_NAND_ID: + case GRUB_DISK_DEVICE_ARCDISK_ID: + case GRUB_DISK_DEVICE_HOSTDISK_ID: + case GRUB_DISK_DEVICE_UBOOTDISK_ID: + break; + + /* Native disks. */ + case GRUB_DISK_DEVICE_ATA_ID: + case GRUB_DISK_DEVICE_SCSI_ID: + case GRUB_DISK_DEVICE_XEN: + if (getnative) + break; + /* FALLTHROUGH */ + + /* Virtual disks. */ + /* GRUB dynamically generated files. */ + case GRUB_DISK_DEVICE_PROCFS_ID: + /* To access through host OS routines (grub-emu only). */ + case GRUB_DISK_DEVICE_HOST_ID: + /* To access coreboot roms. */ + case GRUB_DISK_DEVICE_CBFSDISK_ID: + /* GRUB-only memdisk. Can't match any of firmware devices. */ + case GRUB_DISK_DEVICE_MEMDISK_ID: + grub_dprintf ("nativedisk", "Skipping native disk %s\n", + dev->disk->name); + grub_device_close (dev); + return 0; + + /* FIXME: those probably need special handling. */ + case GRUB_DISK_DEVICE_LOOPBACK_ID: + case GRUB_DISK_DEVICE_DISKFILTER_ID: + case GRUB_DISK_DEVICE_CRYPTODISK_ID: + break; + } + if (dev) + fs = grub_fs_probe (dev); + if (!fs) + { + grub_device_close (dev); + return grub_errno; + } + if (!fs->fs_uuid || fs->fs_uuid (dev, uuid) || !*uuid) + { + grub_device_close (dev); + + if (!grub_errno) + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("%s does not support UUIDs"), fs->name); + + return grub_errno; + } + grub_device_close (dev); + return GRUB_ERR_NONE; +} + +struct search_ctx +{ + char *root_uuid; + char *prefix_uuid; + const char *prefix_path; + int prefix_found, root_found; +}; + +static int +iterate_device (const char *name, void *data) +{ + struct search_ctx *ctx = data; + char *cur_uuid; + + if (get_uuid (name, &cur_uuid, 1)) + { + if (grub_errno == GRUB_ERR_UNKNOWN_FS) + grub_errno = 0; + grub_print_error (); + return 0; + } + + grub_dprintf ("nativedisk", "checking %s: %s\n", name, + cur_uuid); + if (ctx->prefix_uuid && grub_strcasecmp (cur_uuid, ctx->prefix_uuid) == 0) + { + char *prefix; + prefix = grub_xasprintf ("(%s)/%s", name, ctx->prefix_path); + grub_env_set ("prefix", prefix); + grub_free (prefix); + ctx->prefix_found = 1; + } + if (ctx->root_uuid && grub_strcasecmp (cur_uuid, ctx->root_uuid) == 0) + { + grub_env_set ("root", name); + ctx->root_found = 1; + } + return ctx->prefix_found && ctx->root_found; +} + +static grub_err_t +grub_cmd_nativedisk (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args_in) +{ + char *uuid_root = 0, *uuid_prefix, *prefdev = 0; + const char *prefix = 0; + const char *path_prefix = 0; + int mods_loaded = 0; + grub_dl_t *mods; + const char **args; + int i; + + if (argc == 0) + { + argc = ARRAY_SIZE (modnames_def); + args = modnames_def; + } + else + args = (const char **) args_in; + + prefix = grub_env_get ("prefix"); + + if (! prefix) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); + + if (prefix) + path_prefix = (prefix[0] == '(') ? grub_strchr (prefix, ')') : NULL; + if (path_prefix) + path_prefix++; + else + path_prefix = prefix; + + mods = grub_calloc (argc, sizeof (mods[0])); + if (!mods) + return grub_errno; + + if (get_uuid (NULL, &uuid_root, 0)) + { + grub_free (mods); + return grub_errno; + } + + prefdev = grub_file_get_device_name (prefix); + if (grub_errno) + { + grub_print_error (); + prefdev = 0; + } + + if (get_uuid (prefdev, &uuid_prefix, 0)) + { + grub_free (uuid_root); + grub_free (prefdev); + grub_free (mods); + return grub_errno; + } + + grub_dprintf ("nativedisk", "uuid_prefix = %s, uuid_root = %s\n", + uuid_prefix, uuid_root); + + for (mods_loaded = 0; mods_loaded < argc; mods_loaded++) + { + char *filename; + grub_dl_t mod; + grub_file_t file = NULL; + grub_ssize_t size; + void *core = 0; + + mod = grub_dl_get (args[mods_loaded]); + if (mod) + { + mods[mods_loaded] = 0; + continue; + } + + filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s.mod", + prefix, args[mods_loaded]); + if (! filename) + goto fail; + + file = grub_file_open (filename, + GRUB_FILE_TYPE_GRUB_MODULE); + grub_free (filename); + if (! file) + goto fail; + + size = grub_file_size (file); + core = grub_malloc (size); + if (! core) + { + grub_file_close (file); + goto fail; + } + + if (grub_file_read (file, core, size) != (grub_ssize_t) size) + { + grub_file_close (file); + grub_free (core); + goto fail; + } + + grub_file_close (file); + + mods[mods_loaded] = grub_dl_load_core_noinit (core, size); + if (! mods[mods_loaded]) + goto fail; + } + + for (i = 0; i < argc; i++) + if (mods[i]) + grub_dl_init (mods[i]); + + if (uuid_prefix || uuid_root) + { + struct search_ctx ctx; + grub_fs_autoload_hook_t saved_autoload; + + /* No need to autoload FS since obviously we already have the necessary fs modules. */ + saved_autoload = grub_fs_autoload_hook; + grub_fs_autoload_hook = 0; + + ctx.root_uuid = uuid_root; + ctx.prefix_uuid = uuid_prefix; + ctx.prefix_path = path_prefix; + ctx.prefix_found = !uuid_prefix; + ctx.root_found = !uuid_root; + + /* FIXME: try to guess the correct values. */ + grub_device_iterate (iterate_device, &ctx); + + grub_fs_autoload_hook = saved_autoload; + } + grub_free (uuid_root); + grub_free (uuid_prefix); + grub_free (prefdev); + grub_free (mods); + + return GRUB_ERR_NONE; + + fail: + grub_free (uuid_root); + grub_free (uuid_prefix); + grub_free (prefdev); + + for (i = 0; i < mods_loaded; i++) + if (mods[i]) + { + mods[i]->fini = 0; + grub_dl_unload (mods[i]); + } + grub_free (mods); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(nativedisk) +{ + cmd = grub_register_command ("nativedisk", grub_cmd_nativedisk, N_("[MODULE1 MODULE2 ...]"), + N_("Switch to native disk drivers. If no modules are specified default set (pata,ahci,usbms,ohci,uhci,ehci) is used")); +} + +GRUB_MOD_FINI(nativedisk) +{ + grub_unregister_command (cmd); +} diff --git a/commands/parttool.c b/grub-core/commands/parttool.c similarity index 75% rename from commands/parttool.c rename to grub-core/commands/parttool.c index 5ad6133ca..ff45c65e6 100644 --- a/commands/parttool.c +++ b/grub-core/commands/parttool.c @@ -31,13 +31,15 @@ #include #include +GRUB_MOD_LICENSE ("GPLv2+"); + static struct grub_parttool *parts = 0; static int curhandle = 0; static grub_dl_t mymod; static char helpmsg[] = - "Perform COMMANDS on partition.\n" - "Use \"parttool PARTITION help\" for the list " - "of available commands."; + N_("Perform COMMANDS on partition.\n" + "Use `parttool PARTITION help' for the list " + "of available commands."); int grub_parttool_register(const char *part_name, @@ -57,7 +59,13 @@ grub_parttool_register(const char *part_name, for (nargs = 0; args[nargs].name != 0; nargs++); cur->nargs = nargs; cur->args = (struct grub_parttool_argdesc *) - grub_malloc ((nargs + 1) * sizeof (struct grub_parttool_argdesc)); + grub_calloc (nargs + 1, sizeof (struct grub_parttool_argdesc)); + if (!cur->args) + { + grub_free (cur); + curhandle--; + return -1; + } grub_memcpy (cur->args, args, (nargs + 1) * sizeof (struct grub_parttool_argdesc)); @@ -92,6 +100,50 @@ grub_parttool_unregister (int handle) grub_dl_unref (mymod); } +static grub_err_t +show_help (grub_device_t dev) +{ + int found = 0; + struct grub_parttool *cur; + + for (cur = parts; cur; cur = cur->next) + if (grub_strcmp (dev->disk->partition->partmap->name, cur->name) == 0) + { + struct grub_parttool_argdesc *curarg; + found = 1; + for (curarg = cur->args; curarg->name; curarg++) + { + int spacing = 20; + + spacing -= grub_strlen (curarg->name); + grub_printf ("%s", curarg->name); + + switch (curarg->type) + { + case GRUB_PARTTOOL_ARG_BOOL: + grub_printf ("+/-"); + spacing -= 3; + break; + + case GRUB_PARTTOOL_ARG_VAL: + grub_xputs (_("=VAL")); + spacing -= 4; + break; + + case GRUB_PARTTOOL_ARG_END: + break; + } + while (spacing-- > 0) + grub_printf (" "); + grub_puts_ (curarg->desc); + } + } + if (! found) + grub_printf_ (N_("Sorry, no parttool is available for %s\n"), + dev->disk->partition->partmap->name); + return GRUB_ERR_NONE; +} + static grub_err_t grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) @@ -102,51 +154,9 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), int i, j; grub_err_t err = GRUB_ERR_NONE; - auto grub_err_t show_help (void); - grub_err_t show_help (void) - { - int found = 0; - for (cur = parts; cur; cur = cur->next) - if (grub_strcmp (dev->disk->partition->partmap->name, cur->name) == 0) - { - struct grub_parttool_argdesc *curarg; - found = 1; - for (curarg = cur->args; curarg->name; curarg++) - { - int spacing = 20; - - spacing -= grub_strlen (curarg->name); - grub_printf ("%s", curarg->name); - - switch (curarg->type) - { - case GRUB_PARTTOOL_ARG_BOOL: - grub_printf ("+/-"); - spacing -= 3; - break; - - case GRUB_PARTTOOL_ARG_VAL: - grub_printf ("=VAL"); - spacing -= 4; - break; - - case GRUB_PARTTOOL_ARG_END: - break; - } - while (spacing-- > 0) - grub_printf (" "); - grub_printf ("%s\n", curarg->desc); - } - } - if (! found) - grub_printf ("Sorry no parttool is available for %s\n", - dev->disk->partition->partmap->name); - return GRUB_ERR_NONE; - } - if (argc < 1) { - grub_printf ("%s\n", helpmsg); + grub_puts_ (helpmsg); return grub_error (GRUB_ERR_BAD_ARGUMENT, "too few arguments"); } @@ -175,7 +185,7 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), } /* Load modules. */ -#ifndef GRUB_UTIL + if (! grub_no_modules) { const char *prefix; prefix = grub_env_get ("prefix"); @@ -183,12 +193,13 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), { char *filename; - filename = grub_xasprintf ("%s/parttool.lst", prefix); + filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM + "/parttool.lst", prefix); if (filename) { grub_file_t file; - file = grub_file_open (filename); + file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE_LIST); if (file) { char *buf = 0; @@ -202,6 +213,8 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), break; name = buf; + while (grub_isspace (name[0])) + name++; if (! grub_isgraph (name[0])) continue; @@ -211,8 +224,9 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), continue; *p = '\0'; - while (*++p == ' ') - ; + p++; + while (*p == ' ' || *p == '\t') + p++; if (! grub_isgraph (*p)) continue; @@ -233,16 +247,23 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), /* Ignore errors. */ grub_errno = GRUB_ERR_NONE; } -#endif if (argc == 1) - return show_help (); + { + err = show_help (dev); + grub_device_close (dev); + return err; + } for (i = 1; i < argc; i++) if (grub_strcmp (args[i], "help") == 0) - return show_help (); + { + err = show_help (dev); + grub_device_close (dev); + return err; + } - parsed = (int *) grub_zalloc (argc * sizeof (int)); + parsed = (int *) grub_calloc (argc, sizeof (int)); for (i = 1; i < argc; i++) if (! parsed[i]) @@ -267,16 +288,20 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), break; } if (! cur) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unrecognised argument %s", + { + grub_free (parsed); + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown argument `%s'"), args[i]); + } ptool = cur; pargs = (struct grub_parttool_args *) - grub_zalloc (ptool->nargs * sizeof (struct grub_parttool_args)); + grub_calloc (ptool->nargs, sizeof (struct grub_parttool_args)); for (j = i; j < argc; j++) if (! parsed[j]) { for (curarg = ptool->args; curarg->name; curarg++) - if (grub_strncmp (curarg->name, args[i], + if (grub_strncmp (curarg->name, args[j], grub_strlen (curarg->name)) == 0 && ((curarg->type == GRUB_PARTTOOL_ARG_BOOL && (args[j][grub_strlen (curarg->name)] == '+' @@ -290,7 +315,7 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)), switch (curarg->type) { case GRUB_PARTTOOL_ARG_BOOL: - pargs[curarg - ptool->args].bool + pargs[curarg - ptool->args].b = (args[j][grub_strlen (curarg->name)] != '-'); break; diff --git a/commands/password.c b/grub-core/commands/password.c similarity index 83% rename from commands/password.c rename to grub-core/commands/password.c index 04285254e..6d42c9b02 100644 --- a/commands/password.c +++ b/grub-core/commands/password.c @@ -26,6 +26,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_dl_t my_mod; static grub_err_t @@ -40,26 +42,22 @@ check_password (const char *user, const char *entered, return GRUB_ERR_NONE; } -static grub_err_t -grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) +grub_err_t +grub_normal_set_password (const char *user, const char *password) { grub_err_t err; char *pass; int copylen; - if (argc != 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "two arguments expected"); - pass = grub_zalloc (GRUB_AUTH_MAX_PASSLEN); if (!pass) return grub_errno; - copylen = grub_strlen (args[1]); + copylen = grub_strlen (password); if (copylen >= GRUB_AUTH_MAX_PASSLEN) copylen = GRUB_AUTH_MAX_PASSLEN - 1; - grub_memcpy (pass, args[1], copylen); + grub_memcpy (pass, password, copylen); - err = grub_auth_register_authentication (args[0], check_password, pass); + err = grub_auth_register_authentication (user, check_password, pass); if (err) { grub_free (pass); @@ -69,6 +67,15 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), return GRUB_ERR_NONE; } +static grub_err_t +grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + return grub_normal_set_password (args[0], args[1]); +} + static grub_command_t cmd; GRUB_MOD_INIT(password) diff --git a/commands/password_pbkdf2.c b/grub-core/commands/password_pbkdf2.c similarity index 78% rename from commands/password_pbkdf2.c rename to grub-core/commands/password_pbkdf2.c index 51c8ea794..bcb902f97 100644 --- a/commands/password_pbkdf2.c +++ b/grub-core/commands/password_pbkdf2.c @@ -24,6 +24,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; @@ -42,6 +45,7 @@ check_password (const char *user, const char *entered, void *pin) grub_uint8_t *buf; struct pbkdf2_password *pass = pin; gcry_err_code_t err; + grub_err_t ret; buf = grub_malloc (pass->buflen); if (!buf) @@ -52,17 +56,17 @@ check_password (const char *user, const char *entered, void *pin) pass->salt, pass->saltlen, pass->c, buf, pass->buflen); if (err) + ret = grub_crypto_gcry_error (err); + else if (grub_crypto_memcmp (buf, pass->expected, pass->buflen) != 0) + ret = GRUB_ACCESS_DENIED; + else { - grub_free (buf); - return grub_crypto_gcry_error (err); + grub_auth_authenticate (user); + ret = GRUB_ERR_NONE; } - if (grub_crypto_memcmp (buf, pass->expected, pass->buflen) != 0) - return GRUB_ACCESS_DENIED; - - grub_auth_authenticate (user); - - return GRUB_ERR_NONE; + grub_free (buf); + return ret; } static inline int @@ -82,16 +86,16 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { grub_err_t err; - char *ptr, *ptr2; + const char *ptr, *ptr2; grub_uint8_t *ptro; struct pbkdf2_password *pass; if (argc != 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Two arguments expected."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); if (grub_memcmp (args[1], "grub.pbkdf2.sha512.", sizeof ("grub.pbkdf2.sha512.") - 1) != 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid PBKDF2 password")); ptr = args[1] + sizeof ("grub.pbkdf2.sha512.") - 1; @@ -100,10 +104,15 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), return grub_errno; pass->c = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + { + grub_free (pass); + return grub_errno; + } if (*ptr != '.') { grub_free (pass); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid PBKDF2 password")); } ptr++; @@ -111,7 +120,7 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), if (!ptr2 || ((ptr2 - ptr) & 1) || grub_strlen (ptr2 + 1) & 1) { grub_free (pass); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Incorrect PBKDF2 password."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid PBKDF2 password")); } pass->saltlen = (ptr2 - ptr) >> 1; @@ -134,7 +143,11 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), grub_free (pass->salt); grub_free (pass); return grub_error (GRUB_ERR_BAD_ARGUMENT, - "Incorrect PBKDF2 password."); + /* TRANSLATORS: it means that the string which + was supposed to be a password hash doesn't + have a correct format, not to password + mismatch. */ + N_("invalid PBKDF2 password")); } *ptro = (hex1 << 4) | hex2; @@ -149,7 +162,7 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } ptr = ptr2 + 1; - ptr2 += grub_strlen (ptr2); + ptr2 += grub_strlen (ptr2); while (ptr < ptr2) { int hex1, hex2; @@ -163,7 +176,7 @@ grub_cmd_password (grub_command_t cmd __attribute__ ((unused)), grub_free (pass->salt); grub_free (pass); return grub_error (GRUB_ERR_BAD_ARGUMENT, - "Incorrect PBKDF2 password."); + N_("invalid PBKDF2 password")); } *ptro = (hex1 << 4) | hex2; @@ -186,8 +199,8 @@ GRUB_MOD_INIT(password_pbkdf2) { my_mod = mod; cmd = grub_register_command ("password_pbkdf2", grub_cmd_password, - "password_pbkdf2 USER PBKDF2_PASSWORD", - "Set user password (PBKDF2). "); + N_("USER PBKDF2_PASSWORD"), + N_("Set user password (PBKDF2). ")); } GRUB_MOD_FINI(password_pbkdf2) diff --git a/grub-core/commands/pcidump.c b/grub-core/commands/pcidump.c new file mode 100644 index 000000000..f72628fce --- /dev/null +++ b/grub-core/commands/pcidump.c @@ -0,0 +1,174 @@ +/* lspci.c - List PCI devices. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct iter_cxt +{ + grub_uint32_t pciid_check_mask, pciid_check_value; + int bus, device, function; + int check_bus, check_device, check_function; +}; + +static const struct grub_arg_option options[] = + { + {0, 'd', 0, N_("Select device by vendor and device IDs."), + N_("[vendor]:[device]"), ARG_TYPE_STRING}, + {0, 's', 0, N_("Select device by its position on the bus."), + N_("[bus]:[slot][.func]"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +static int +grub_pcidump_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data) +{ + struct iter_cxt *ctx = data; + grub_pci_address_t addr; + int i; + + if ((pciid & ctx->pciid_check_mask) != ctx->pciid_check_value) + return 0; + + if (ctx->check_bus && grub_pci_get_bus (dev) != ctx->bus) + return 0; + + if (ctx->check_device && grub_pci_get_device (dev) != ctx->device) + return 0; + + if (ctx->check_function && grub_pci_get_function (dev) != ctx->function) + return 0; + + for (i = 0; i < 256; i += 4) + { + addr = grub_pci_make_address (dev, i); + grub_printf ("%08x ", grub_pci_read (addr)); + if ((i & 0xc) == 0xc) + grub_printf ("\n"); + } + + return 0; +} + +static grub_err_t +grub_cmd_pcidump (grub_extcmd_context_t ctxt, + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) +{ + const char *ptr; + struct iter_cxt ctx = + { + .pciid_check_value = 0, + .pciid_check_mask = 0, + .check_bus = 0, + .check_device = 0, + .check_function = 0, + .bus = 0, + .function = 0, + .device = 0 + }; + + if (ctxt->state[0].set) + { + ptr = ctxt->state[0].arg; + ctx.pciid_check_value |= (grub_strtoul (ptr, &ptr, 16) & 0xffff); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = ctxt->state[0].arg; + } + else + ctx.pciid_check_mask |= 0xffff; + if (grub_errno) + return grub_errno; + if (*ptr != ':') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("missing `%c' symbol"), ':'); + ptr++; + ctx.pciid_check_value |= (grub_strtoul (ptr, &ptr, 16) & 0xffff) << 16; + if (grub_errno == GRUB_ERR_BAD_NUMBER) + grub_errno = GRUB_ERR_NONE; + else + ctx.pciid_check_mask |= 0xffff0000; + } + + ctx.pciid_check_value &= ctx.pciid_check_mask; + + if (ctxt->state[1].set) + { + const char *optr; + + ptr = ctxt->state[1].arg; + optr = ptr; + ctx.bus = grub_strtoul (ptr, &ptr, 16); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = optr; + } + else + ctx.check_bus = 1; + if (grub_errno) + return grub_errno; + if (*ptr != ':') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("missing `%c' symbol"), ':'); + ptr++; + optr = ptr; + ctx.device = grub_strtoul (ptr, &ptr, 16); + if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_errno = GRUB_ERR_NONE; + ptr = optr; + } + else + ctx.check_device = 1; + if (*ptr == '.') + { + ptr++; + ctx.function = grub_strtoul (ptr, &ptr, 16); + if (grub_errno) + return grub_errno; + ctx.check_function = 1; + } + } + + grub_pci_iterate (grub_pcidump_iter, &ctx); + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(pcidump) +{ + cmd = grub_register_extcmd ("pcidump", grub_cmd_pcidump, 0, + N_("[-s POSITION] [-d DEVICE]"), + N_("Show raw dump of the PCI configuration space."), options); +} + +GRUB_MOD_FINI(pcidump) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c new file mode 100644 index 000000000..5fadc33c4 --- /dev/null +++ b/grub-core/commands/pgp.c @@ -0,0 +1,1020 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + OPTION_SKIP_SIG = 0 + }; + +static const struct grub_arg_option options[] = + { + {"skip-sig", 's', 0, + N_("Skip signature-checking of the public key file."), 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +read_packet_header (grub_file_t sig, grub_uint8_t *out_type, grub_size_t *len) +{ + grub_uint8_t type; + grub_uint8_t l; + grub_uint16_t l16; + grub_uint32_t l32; + + /* New format. */ + switch (grub_file_read (sig, &type, sizeof (type))) + { + case 1: + break; + case 0: + { + *out_type = 0xff; + return 0; + } + default: + if (grub_errno) + return grub_errno; + /* TRANSLATORS: it's about GNUPG signatures. */ + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + } + + if (type == 0) + { + *out_type = 0xfe; + return 0; + } + + if (!(type & 0x80)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + if (type & 0x40) + { + *out_type = (type & 0x3f); + if (grub_file_read (sig, &l, sizeof (l)) != 1) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + if (l < 192) + { + *len = l; + return 0; + } + if (l < 224) + { + *len = (l - 192) << GRUB_CHAR_BIT; + if (grub_file_read (sig, &l, sizeof (l)) != 1) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len |= l; + return 0; + } + if (l == 255) + { + if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu32 (l32); + return 0; + } + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + } + *out_type = ((type >> 2) & 0xf); + switch (type & 0x3) + { + case 0: + if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = l; + return 0; + case 1: + if (grub_file_read (sig, &l16, sizeof (l16)) != sizeof (l16)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu16 (l16); + return 0; + case 2: + if (grub_file_read (sig, &l32, sizeof (l32)) != sizeof (l32)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + *len = grub_be_to_cpu32 (l32); + return 0; + } + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); +} + +struct signature_v4_header +{ + grub_uint8_t type; + grub_uint8_t pkeyalgo; + grub_uint8_t hash; + grub_uint16_t hashed_sub; +} GRUB_PACKED; + +const char *hashes[] = { + [0x01] = "md5", + [0x02] = "sha1", + [0x03] = "ripemd160", + [0x08] = "sha256", + [0x09] = "sha384", + [0x0a] = "sha512", + [0x0b] = "sha224" +}; + +struct gcry_pk_spec *grub_crypto_pk_dsa; +struct gcry_pk_spec *grub_crypto_pk_ecdsa; +struct gcry_pk_spec *grub_crypto_pk_rsa; + +static int +dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); +static int +rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); + +struct +{ + const char *name; + grub_size_t nmpisig; + grub_size_t nmpipub; + struct gcry_pk_spec **algo; + int (*pad) (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk); + const char *module; +} pkalgos[] = + { + [1] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, + [3] = { "rsa", 1, 2, &grub_crypto_pk_rsa, rsa_pad, "gcry_rsa" }, + [17] = { "dsa", 2, 4, &grub_crypto_pk_dsa, dsa_pad, "gcry_dsa" }, + }; + +struct grub_public_key +{ + struct grub_public_key *next; + struct grub_public_subkey *subkeys; +}; + +struct grub_public_subkey +{ + struct grub_public_subkey *next; + grub_uint8_t type; + grub_uint32_t fingerprint[5]; + gcry_mpi_t mpis[10]; +}; + +static void +free_pk (struct grub_public_key *pk) +{ + struct grub_public_subkey *nsk, *sk; + for (sk = pk->subkeys; sk; sk = nsk) + { + grub_size_t i; + for (i = 0; i < ARRAY_SIZE (sk->mpis); i++) + if (sk->mpis[i]) + gcry_mpi_release (sk->mpis[i]); + nsk = sk->next; + grub_free (sk); + } + grub_free (pk); +} + +#define READBUF_SIZE 4096 + +struct grub_public_key * +grub_load_public_key (grub_file_t f) +{ + grub_err_t err; + struct grub_public_key *ret; + struct grub_public_subkey **last = 0; + void *fingerprint_context = NULL; + grub_uint8_t *buffer = NULL; + + ret = grub_zalloc (sizeof (*ret)); + if (!ret) + { + grub_free (fingerprint_context); + return NULL; + } + + buffer = grub_zalloc (READBUF_SIZE); + fingerprint_context = grub_zalloc (GRUB_MD_SHA1->contextsize); + + if (!buffer || !fingerprint_context) + goto fail; + + last = &ret->subkeys; + + while (1) + { + grub_uint8_t type; + grub_size_t len; + grub_uint8_t v, pk; + grub_uint32_t creation_time; + grub_off_t pend; + struct grub_public_subkey *sk; + grub_size_t i; + grub_uint16_t len_be; + + err = read_packet_header (f, &type, &len); + + if (err) + goto fail; + if (type == 0xfe) + continue; + if (type == 0xff) + { + grub_free (fingerprint_context); + grub_free (buffer); + return ret; + } + + grub_dprintf ("crypt", "len = %x\n", (int) len); + + pend = grub_file_tell (f) + len; + if (type != 6 && type != 14 + && type != 5 && type != 7) + { + grub_file_seek (f, pend); + continue; + } + + if (grub_file_read (f, &v, sizeof (v)) != sizeof (v)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "v = %x\n", v); + + if (v != 4) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + if (grub_file_read (f, &creation_time, sizeof (creation_time)) != sizeof (creation_time)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "time = %x\n", creation_time); + + if (grub_file_read (f, &pk, sizeof (pk)) != sizeof (pk)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + goto fail; + } + + grub_dprintf ("crypt", "pk = %x\n", pk); + + if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) + { + grub_file_seek (f, pend); + continue; + } + + sk = grub_zalloc (sizeof (struct grub_public_subkey)); + if (!sk) + goto fail; + + grub_memset (fingerprint_context, 0, GRUB_MD_SHA1->contextsize); + GRUB_MD_SHA1->init (fingerprint_context); + GRUB_MD_SHA1->write (fingerprint_context, "\x99", 1); + len_be = grub_cpu_to_be16 (len); + GRUB_MD_SHA1->write (fingerprint_context, &len_be, sizeof (len_be)); + GRUB_MD_SHA1->write (fingerprint_context, &v, sizeof (v)); + GRUB_MD_SHA1->write (fingerprint_context, &creation_time, sizeof (creation_time)); + GRUB_MD_SHA1->write (fingerprint_context, &pk, sizeof (pk)); + + for (i = 0; i < pkalgos[pk].nmpipub; i++) + { + grub_uint16_t l; + grub_size_t lb; + if (grub_file_read (f, &l, sizeof (l)) != sizeof (l)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + + lb = (grub_be_to_cpu16 (l) + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT; + if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + if (grub_file_read (f, buffer + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + grub_memcpy (buffer, &l, sizeof (l)); + + GRUB_MD_SHA1->write (fingerprint_context, buffer, lb + sizeof (grub_uint16_t)); + + if (gcry_mpi_scan (&sk->mpis[i], GCRYMPI_FMT_PGP, + buffer, lb + sizeof (grub_uint16_t), 0)) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + break; + } + } + + if (i < pkalgos[pk].nmpipub) + { + grub_free (sk); + goto fail; + } + + GRUB_MD_SHA1->final (fingerprint_context); + + grub_memcpy (sk->fingerprint, GRUB_MD_SHA1->read (fingerprint_context), 20); + + *last = sk; + last = &sk->next; + + grub_dprintf ("crypt", "actual pos: %x, expected: %x\n", (int)grub_file_tell (f), (int)pend); + + grub_file_seek (f, pend); + } + fail: + free_pk (ret); + grub_free (fingerprint_context); + grub_free (buffer); + return NULL; +} + +struct grub_public_key *grub_pk_trusted; + +struct grub_public_subkey * +grub_crypto_pk_locate_subkey (grub_uint64_t keyid, struct grub_public_key *pkey) +{ + struct grub_public_subkey *sk; + for (sk = pkey->subkeys; sk; sk = sk->next) + if (grub_memcmp (sk->fingerprint + 3, &keyid, 8) == 0) + return sk; + return 0; +} + +struct grub_public_subkey * +grub_crypto_pk_locate_subkey_in_trustdb (grub_uint64_t keyid) +{ + struct grub_public_key *pkey; + struct grub_public_subkey *sk; + for (pkey = grub_pk_trusted; pkey; pkey = pkey->next) + { + sk = grub_crypto_pk_locate_subkey (keyid, pkey); + if (sk) + return sk; + } + return 0; +} + + +static int +dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk) +{ + unsigned nbits = gcry_mpi_get_nbits (sk->mpis[1]); + grub_dprintf ("crypt", "must be %u bits got %d bits\n", nbits, + (int)(8 * hash->mdlen)); + return gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, hval, + nbits / 8 < (unsigned) hash->mdlen ? nbits / 8 + : (unsigned) hash->mdlen, 0); +} + +static int +rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, + const gcry_md_spec_t *hash, struct grub_public_subkey *sk) +{ + grub_size_t tlen, emlen, fflen; + grub_uint8_t *em, *emptr; + unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); + int ret; + tlen = hash->mdlen + hash->asnlen; + emlen = (nbits + 7) / 8; + if (emlen < tlen + 11) + return 1; + + em = grub_malloc (emlen); + if (!em) + return 1; + + em[0] = 0x00; + em[1] = 0x01; + fflen = emlen - tlen - 3; + for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) + *emptr = 0xff; + *emptr++ = 0x00; + grub_memcpy (emptr, hash->asnoid, hash->asnlen); + emptr += hash->asnlen; + grub_memcpy (emptr, hval, hash->mdlen); + + ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); + grub_free (em); + return ret; +} + +struct grub_pubkey_context +{ + grub_file_t sig; + struct signature_v4_header v4; + grub_uint8_t v; + const gcry_md_spec_t *hash; + void *hash_context; +}; + +static grub_err_t +grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig) +{ + grub_size_t len; + grub_uint8_t h; + grub_uint8_t t; + grub_uint8_t pk; + grub_err_t err; + grub_uint8_t type = 0; + + grub_memset (ctxt, 0, sizeof (*ctxt)); + + err = read_packet_header (sig, &type, &len); + if (err) + return err; + + if (type != 0x2) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (ctxt->v != 4) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + h = ctxt->v4.hash; + t = ctxt->v4.type; + pk = ctxt->v4.pkeyalgo; + + if (t != 0) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + if (h >= ARRAY_SIZE (hashes) || hashes[h] == NULL) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "unknown hash"); + + if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + + ctxt->hash = grub_crypto_lookup_md_by_name (hashes[h]); + if (!ctxt->hash) + return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]); + + grub_dprintf ("crypt", "alive\n"); + + ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize); + if (!ctxt->hash_context) + return grub_errno; + + ctxt->hash->init (ctxt->hash_context); + ctxt->sig = sig; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pubkey_write (void *ctxt_, void *buf, grub_size_t size) +{ + struct grub_pubkey_context *ctxt = ctxt_; + ctxt->hash->write (ctxt->hash_context, buf, size); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_verify_signature_real (struct grub_pubkey_context *ctxt, + struct grub_public_key *pkey) +{ + gcry_mpi_t mpis[10]; + grub_uint8_t pk = ctxt->v4.pkeyalgo; + grub_size_t i; + grub_uint8_t *readbuf = NULL; + unsigned char *hval; + grub_ssize_t rem = grub_be_to_cpu16 (ctxt->v4.hashed_sub); + grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6); + grub_uint8_t s; + grub_uint16_t unhashed_sub; + grub_ssize_t r; + grub_uint8_t hash_start[2]; + gcry_mpi_t hmpi; + grub_uint64_t keyid = 0; + struct grub_public_subkey *sk; + + readbuf = grub_malloc (READBUF_SIZE); + if (!readbuf) + goto fail; + + ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); + ctxt->hash->write (ctxt->hash_context, &ctxt->v4, sizeof (ctxt->v4)); + while (rem) + { + r = grub_file_read (ctxt->sig, readbuf, + rem < READBUF_SIZE ? rem : READBUF_SIZE); + if (r < 0) + goto fail; + if (r == 0) + break; + ctxt->hash->write (ctxt->hash_context, readbuf, r); + rem -= r; + } + ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v)); + s = 0xff; + ctxt->hash->write (ctxt->hash_context, &s, sizeof (s)); + ctxt->hash->write (ctxt->hash_context, &headlen, sizeof (headlen)); + r = grub_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub)); + if (r != sizeof (unhashed_sub)) + goto fail; + { + grub_uint8_t *ptr; + grub_uint32_t l; + rem = grub_be_to_cpu16 (unhashed_sub); + if (rem > READBUF_SIZE) + goto fail; + r = grub_file_read (ctxt->sig, readbuf, rem); + if (r != rem) + goto fail; + for (ptr = readbuf; ptr < readbuf + rem; ptr += l) + { + if (*ptr < 192) + l = *ptr++; + else if (*ptr < 255) + { + if (ptr + 1 >= readbuf + rem) + break; + l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192; + ptr += 2; + } + else + { + if (ptr + 5 >= readbuf + rem) + break; + l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1)); + ptr += 5; + } + if (*ptr == 0x10 && l >= 8) + keyid = grub_get_unaligned64 (ptr + 1); + } + } + + ctxt->hash->final (ctxt->hash_context); + + grub_dprintf ("crypt", "alive\n"); + + hval = ctxt->hash->read (ctxt->hash_context); + + if (grub_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start)) + goto fail; + if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0) + goto fail; + + grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (ctxt->sig)); + + for (i = 0; i < pkalgos[pk].nmpisig; i++) + { + grub_uint16_t l; + grub_size_t lb; + grub_dprintf ("crypt", "alive\n"); + if (grub_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + lb = (grub_be_to_cpu16 (l) + 7) / 8; + grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l)); + if (lb > READBUF_SIZE - sizeof (grub_uint16_t)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + if (grub_file_read (ctxt->sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb) + goto fail; + grub_dprintf ("crypt", "alive\n"); + grub_memcpy (readbuf, &l, sizeof (l)); + grub_dprintf ("crypt", "alive\n"); + + if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP, + readbuf, lb + sizeof (grub_uint16_t), 0)) + goto fail; + grub_dprintf ("crypt", "alive\n"); + } + + if (pkey) + sk = grub_crypto_pk_locate_subkey (keyid, pkey); + else + sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid); + if (!sk) + { + /* TRANSLATORS: %08x is 32-bit key id. */ + grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("public key %08" PRIxGRUB_UINT64_T " not found"), keyid); + goto fail; + } + + if (pkalgos[pk].pad (&hmpi, hval, ctxt->hash, sk)) + goto fail; + if (!*pkalgos[pk].algo) + { + grub_dl_load (pkalgos[pk].module); + grub_errno = GRUB_ERR_NONE; + } + + if (!*pkalgos[pk].algo) + { + grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"), + pkalgos[pk].module); + goto fail; + } + if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0)) + goto fail; + + grub_free (readbuf); + + return GRUB_ERR_NONE; + + fail: + grub_free (readbuf); + if (!grub_errno) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature")); + return grub_errno; +} + +static void +grub_pubkey_close_real (struct grub_pubkey_context *ctxt) +{ + if (ctxt->sig) + grub_file_close (ctxt->sig); + if (ctxt->hash_context) + grub_free (ctxt->hash_context); +} + +static void +grub_pubkey_close (void *ctxt) +{ + grub_pubkey_close_real (ctxt); + grub_free (ctxt); +} + +grub_err_t +grub_verify_signature (grub_file_t f, const char *fsig, + struct grub_public_key *pkey) +{ + grub_file_t sig; + grub_err_t err; + struct grub_pubkey_context ctxt; + grub_uint8_t *readbuf = NULL; + + sig = grub_file_open (fsig, + GRUB_FILE_TYPE_SIGNATURE + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (!sig) + return grub_errno; + + err = grub_verify_signature_init (&ctxt, sig); + if (err) + { + grub_file_close (sig); + return err; + } + + readbuf = grub_zalloc (READBUF_SIZE); + if (!readbuf) + goto fail; + + while (1) + { + grub_ssize_t r; + r = grub_file_read (f, readbuf, READBUF_SIZE); + if (r < 0) + goto fail; + if (r == 0) + break; + err = grub_pubkey_write (&ctxt, readbuf, r); + if (err) + return err; + } + + grub_verify_signature_real (&ctxt, pkey); + fail: + grub_pubkey_close_real (&ctxt); + return grub_errno; +} + +static grub_err_t +grub_cmd_trust (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + grub_file_t pkf; + struct grub_public_key *pk = NULL; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + pkf = grub_file_open (args[0], + GRUB_FILE_TYPE_PUBLIC_KEY_TRUST + | GRUB_FILE_TYPE_NO_DECOMPRESS + | (ctxt->state[OPTION_SKIP_SIG].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE + : GRUB_FILE_TYPE_NONE)); + if (!pkf) + return grub_errno; + pk = grub_load_public_key (pkf); + if (!pk) + { + grub_file_close (pkf); + return grub_errno; + } + grub_file_close (pkf); + + pk->next = grub_pk_trusted; + grub_pk_trusted = pk; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_list (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_public_key *pk = NULL; + struct grub_public_subkey *sk = NULL; + + for (pk = grub_pk_trusted; pk; pk = pk->next) + for (sk = pk->subkeys; sk; sk = sk->next) + { + unsigned i; + for (i = 0; i < 20; i += 2) + grub_printf ("%02x%02x ", ((grub_uint8_t *) sk->fingerprint)[i], + ((grub_uint8_t *) sk->fingerprint)[i + 1]); + grub_printf ("\n"); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_distrust (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_uint32_t keyid, keyid_be; + struct grub_public_key **pkey; + struct grub_public_subkey *sk; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + keyid = grub_strtoull (args[0], 0, 16); + if (grub_errno) + return grub_errno; + keyid_be = grub_cpu_to_be32 (keyid); + + for (pkey = &grub_pk_trusted; *pkey; pkey = &((*pkey)->next)) + { + struct grub_public_key *next; + for (sk = (*pkey)->subkeys; sk; sk = sk->next) + if (grub_memcmp (sk->fingerprint + 4, &keyid_be, 4) == 0) + break; + if (!sk) + continue; + next = (*pkey)->next; + free_pk (*pkey); + *pkey = next; + return GRUB_ERR_NONE; + } + /* TRANSLATORS: %08x is 32-bit key id. */ + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"), keyid); +} + +static grub_err_t +grub_cmd_verify_signature (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + grub_file_t f = NULL; + grub_err_t err = GRUB_ERR_NONE; + struct grub_public_key *pk = NULL; + + grub_dprintf ("crypt", "alive\n"); + + if (argc < 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + grub_dprintf ("crypt", "alive\n"); + + if (argc > 2) + { + grub_file_t pkf; + pkf = grub_file_open (args[2], + GRUB_FILE_TYPE_PUBLIC_KEY + | GRUB_FILE_TYPE_NO_DECOMPRESS + | (ctxt->state[OPTION_SKIP_SIG].set + ? GRUB_FILE_TYPE_SKIP_SIGNATURE + : GRUB_FILE_TYPE_NONE)); + if (!pkf) + return grub_errno; + pk = grub_load_public_key (pkf); + if (!pk) + { + grub_file_close (pkf); + return grub_errno; + } + grub_file_close (pkf); + } + + f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); + if (!f) + { + err = grub_errno; + goto fail; + } + + err = grub_verify_signature (f, args[1], pk); + fail: + if (f) + grub_file_close (f); + if (pk) + free_pk (pk); + return err; +} + +static int sec = 0; + +static grub_err_t +grub_pubkey_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)), + void **context, enum grub_verify_flags *flags) +{ + grub_file_t sig; + char *fsuf, *ptr; + grub_err_t err; + struct grub_pubkey_context *ctxt; + + if (!sec) + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + + fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig")); + if (!fsuf) + return grub_errno; + ptr = grub_stpcpy (fsuf, io->name); + grub_memcpy (ptr, ".sig", sizeof (".sig")); + + sig = grub_file_open (fsuf, GRUB_FILE_TYPE_SIGNATURE); + grub_free (fsuf); + if (!sig) + return grub_errno; + + ctxt = grub_malloc (sizeof (*ctxt)); + if (!ctxt) + { + grub_file_close (sig); + return grub_errno; + } + err = grub_verify_signature_init (ctxt, sig); + if (err) + { + grub_free (ctxt); + grub_file_close (sig); + return err; + } + *context = ctxt; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pubkey_fini (void *ctxt) +{ + return grub_verify_signature_real (ctxt, NULL); +} + +static char * +grub_env_write_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + sec = (*val == '1') || (*val == 'e'); + return grub_strdup (sec ? "enforce" : "no"); +} + +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + + +/* Filesystem descriptor. */ +struct grub_fs pseudo_fs = + { + .name = "pseudo", + .fs_read = pseudo_read + }; + +struct grub_file_verifier grub_pubkey_verifier = + { + .name = "pgp", + .init = grub_pubkey_init, + .fini = grub_pubkey_fini, + .write = grub_pubkey_write, + .close = grub_pubkey_close, + }; + +static grub_extcmd_t cmd, cmd_trust; +static grub_command_t cmd_distrust, cmd_list; + +GRUB_MOD_INIT(pgp) +{ + const char *val; + struct grub_module_header *header; + + val = grub_env_get ("check_signatures"); + if (val && (val[0] == '1' || val[0] == 'e')) + sec = 1; + else + sec = 0; + + grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec); + grub_env_export ("check_signatures"); + + grub_pk_trusted = 0; + FOR_MODULES (header) + { + struct grub_file pseudo_file; + struct grub_public_key *pk = NULL; + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + + /* Not an ELF module, skip. */ + if (header->type != OBJ_TYPE_PUBKEY) + continue; + + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = (header->size - sizeof (struct grub_module_header)); + pseudo_file.data = (char *) header + sizeof (struct grub_module_header); + + pk = grub_load_public_key (&pseudo_file); + if (!pk) + grub_fatal ("error loading initial key: %s\n", grub_errmsg); + + pk->next = grub_pk_trusted; + grub_pk_trusted = pk; + } + + if (!val) + grub_env_set ("check_signatures", grub_pk_trusted ? "enforce" : "no"); + + cmd = grub_register_extcmd ("verify_detached", grub_cmd_verify_signature, 0, + N_("[-s|--skip-sig] FILE SIGNATURE_FILE [PUBKEY_FILE]"), + N_("Verify detached signature."), + options); + cmd_trust = grub_register_extcmd ("trust", grub_cmd_trust, 0, + N_("[-s|--skip-sig] PUBKEY_FILE"), + N_("Add PUBKEY_FILE to trusted keys."), + options); + cmd_list = grub_register_command ("list_trusted", grub_cmd_list, + 0, + N_("Show the list of trusted keys.")); + cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust, + N_("PUBKEY_ID"), + N_("Remove PUBKEY_ID from trusted keys.")); + + grub_verifier_register (&grub_pubkey_verifier); +} + +GRUB_MOD_FINI(pgp) +{ + grub_register_variable_hook ("check_signatures", NULL, NULL); + grub_env_unset ("check_signatures"); + grub_verifier_unregister (&grub_pubkey_verifier); + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_trust); + grub_unregister_command (cmd_list); + grub_unregister_command (cmd_distrust); +} diff --git a/commands/probe.c b/grub-core/commands/probe.c similarity index 50% rename from commands/probe.c rename to grub-core/commands/probe.c index c2cc599e9..be9637f33 100644 --- a/commands/probe.c +++ b/grub-core/commands/probe.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,23 +32,29 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = { - {"set", 's', GRUB_ARG_OPTION_OPTIONAL, - N_("Set a variable to return value."), "VAR", ARG_TYPE_STRING}, + {"set", 's', 0, + N_("Set a variable to return value."), N_("VARNAME"), ARG_TYPE_STRING}, + /* TRANSLATORS: It's a driver that is currently in use to access + the diven disk. */ {"driver", 'd', 0, N_("Determine driver."), 0, 0}, {"partmap", 'p', 0, N_("Determine partition map type."), 0, 0}, {"fs", 'f', 0, N_("Determine filesystem type."), 0, 0}, {"fs-uuid", 'u', 0, N_("Determine filesystem UUID."), 0, 0}, {"label", 'l', 0, N_("Determine filesystem label."), 0, 0}, + {"part-uuid", 0, 0, N_("Determine partition UUID."), 0, 0}, {0, 0, 0, 0, 0, 0} }; static grub_err_t -grub_cmd_probe (grub_extcmd_t cmd, int argc, char **args) +grub_cmd_probe (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; grub_device_t dev; grub_fs_t fs; char *ptr; @@ -66,19 +73,20 @@ grub_cmd_probe (grub_extcmd_t cmd, int argc, char **args) else dev = grub_device_open (args[0]); if (! dev) - return grub_error (GRUB_ERR_BAD_DEVICE, "couldn't open device"); + return grub_errno; if (state[1].set) { const char *val = "none"; if (dev->net) - val = dev->net->dev->name; + val = dev->net->protocol->name; if (dev->disk) val = dev->disk->dev->name; if (state[0].set) grub_env_set (state[0].arg, val); else grub_printf ("%s", val); + grub_device_close (dev); return GRUB_ERR_NONE; } if (state[2].set) @@ -90,59 +98,141 @@ grub_cmd_probe (grub_extcmd_t cmd, int argc, char **args) grub_env_set (state[0].arg, val); else grub_printf ("%s", val); + grub_device_close (dev); + return GRUB_ERR_NONE; + } + if (state[6].set) + { + /* AAAABBBB-CCCC-DDDD-EEEE-FFFFFFFFFFFF + null terminator */ + char val[37] = "none"; + if (dev->disk && dev->disk->partition) + { + struct grub_partition *p = dev->disk->partition; + grub_disk_t disk = grub_disk_open(dev->disk->name); + + if (!disk) + { + grub_device_close (dev); + return grub_errno; + } + + if (grub_strcmp(dev->disk->partition->partmap->name, "gpt") == 0) + { + struct grub_gpt_partentry entry; + grub_guid_t *guid; + + if (grub_disk_read(disk, p->offset, p->index, sizeof(entry), &entry)) + { + grub_error_push (); + grub_disk_close (disk); + grub_device_close (dev); + grub_error_pop (); + return grub_errno; + } + guid = &entry.guid; + guid->data1 = grub_le_to_cpu32 (guid->data1); + guid->data2 = grub_le_to_cpu16 (guid->data2); + guid->data3 = grub_le_to_cpu16 (guid->data3); + grub_snprintf (val, sizeof(val), "%pG", guid); + } + else if (grub_strcmp(dev->disk->partition->partmap->name, "msdos") == 0) + { + grub_uint32_t nt_disk_sig; + + if (grub_disk_read(disk, 0, GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC, + sizeof(nt_disk_sig), &nt_disk_sig) == 0) + grub_snprintf (val, sizeof(val), "%08x-%02x", + grub_le_to_cpu32(nt_disk_sig), 1 + p->number); + } + grub_disk_close(disk); + } + if (state[0].set) + grub_env_set (state[0].arg, val); + else + grub_printf ("%s", val); + grub_device_close (dev); return GRUB_ERR_NONE; } fs = grub_fs_probe (dev); if (! fs) - return grub_error (GRUB_ERR_UNKNOWN_FS, "unrecognised fs"); + { + grub_error_push (); + grub_device_close (dev); + grub_error_pop (); + return grub_errno; + } if (state[3].set) { if (state[0].set) grub_env_set (state[0].arg, fs->name); else grub_printf ("%s", fs->name); + grub_device_close (dev); return GRUB_ERR_NONE; } if (state[4].set) { char *uuid; - if (! fs->uuid) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "uuid for this FS isn't supported yet"); - err = fs->uuid (dev, &uuid); + if (! fs->fs_uuid) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("%s does not support UUIDs"), fs->name); + } + err = fs->fs_uuid (dev, &uuid); if (err) - return err; + { + grub_device_close (dev); + return err; + } if (! uuid) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "uuid for this FS isn't supported yet"); + { + grub_device_close (dev); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("%s does not support UUIDs"), fs->name); + } if (state[0].set) grub_env_set (state[0].arg, uuid); else grub_printf ("%s", uuid); grub_free (uuid); + grub_device_close (dev); return GRUB_ERR_NONE; } if (state[5].set) { char *label; - if (! fs->label) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "label for this FS isn't supported yet"); - err = fs->label (dev, &label); + if (! fs->fs_label) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("filesystem `%s' does not support labels"), + fs->name); + } + err = fs->fs_label (dev, &label); if (err) - return err; + { + grub_device_close (dev); + return err; + } if (! label) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "uuid for this FS isn't supported yet"); + { + grub_device_close (dev); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("filesystem `%s' does not support labels"), + fs->name); + } if (state[0].set) grub_env_set (state[0].arg, label); else grub_printf ("%s", label); grub_free (label); + grub_device_close (dev); return GRUB_ERR_NONE; } + grub_device_close (dev); return grub_error (GRUB_ERR_BAD_ARGUMENT, "unrecognised target"); } @@ -150,8 +240,7 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT (probe) { - cmd = grub_register_extcmd ("probe", grub_cmd_probe, GRUB_COMMAND_FLAG_BOTH, - N_("[DEVICE]"), + cmd = grub_register_extcmd ("probe", grub_cmd_probe, 0, N_("DEVICE"), N_("Retrieve device info."), options); } diff --git a/commands/read.c b/grub-core/commands/read.c similarity index 55% rename from commands/read.c rename to grub-core/commands/read.c index 8a7c4a01f..8d72e45c9 100644 --- a/commands/read.c +++ b/grub-core/commands/read.c @@ -23,19 +23,29 @@ #include #include #include -#include +#include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + {"silent", 's', 0, N_("Do not echo input"), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; static char * -grub_getline (void) +grub_getline (int silent) { - int i; + grub_size_t i; char *line; char *tmp; - char c; + int c; + grub_size_t alloc_size; i = 0; - line = grub_malloc (1 + i + sizeof('\0')); + line = grub_malloc (1 + sizeof('\0')); if (! line) return NULL; @@ -45,11 +55,23 @@ grub_getline (void) if ((c == '\n') || (c == '\r')) break; - line[i] = c; - if (grub_isprint (c)) - grub_putchar (c); - i++; - tmp = grub_realloc (line, 1 + i + sizeof('\0')); + if (!grub_isprint (c)) + continue; + + line[i] = (char) c; + if (!silent) + grub_printf ("%c", c); + if (grub_add (i, 1, &i)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + if (grub_add (i, 1 + sizeof('\0'), &alloc_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + tmp = grub_realloc (line, alloc_size); if (! tmp) { grub_free (line); @@ -63,9 +85,11 @@ grub_getline (void) } static grub_err_t -grub_cmd_read (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) +grub_cmd_read (grub_extcmd_context_t ctxt, int argc, char **args) { - char *line = grub_getline (); + struct grub_arg_list *state = ctxt->state; + char *line = grub_getline (state[0].set); + if (! line) return grub_errno; if (argc > 0) @@ -75,16 +99,16 @@ grub_cmd_read (grub_command_t cmd __attribute__ ((unused)), int argc, char **arg return 0; } -static grub_command_t cmd; +static grub_extcmd_t cmd; GRUB_MOD_INIT(read) { - cmd = grub_register_command ("read", grub_cmd_read, - N_("[ENVVAR]"), - N_("Set variable with user input.")); + cmd = grub_register_extcmd ("read", grub_cmd_read, 0, + N_("[-s] [ENVVAR]"), + N_("Set variable with user input."), options); } GRUB_MOD_FINI(read) { - grub_unregister_command (cmd); + grub_unregister_extcmd (cmd); } diff --git a/commands/reboot.c b/grub-core/commands/reboot.c similarity index 94% rename from commands/reboot.c rename to grub-core/commands/reboot.c index eedd53c91..46d364c99 100644 --- a/commands/reboot.c +++ b/grub-core/commands/reboot.c @@ -22,13 +22,14 @@ #include #include -static grub_err_t +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t __attribute__ ((noreturn)) grub_cmd_reboot (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { grub_reboot (); - return 0; } static grub_command_t cmd; diff --git a/grub-core/commands/regexp.c b/grub-core/commands/regexp.c new file mode 100644 index 000000000..246af39f0 --- /dev/null +++ b/grub-core/commands/regexp.c @@ -0,0 +1,168 @@ +/* regexp.c -- The regexp command. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + { "set", 's', GRUB_ARG_OPTION_REPEATABLE, + /* TRANSLATORS: in regexp you can mark some + groups with parentheses. These groups are + then numbered and you can save some of + them in variables. In other programs + those components are often referenced with + back slash, e.g. \1. Compare + sed -e 's,\([a-z][a-z]*\),lowercase=\1,g' + The whole matching component is saved in VARNAME, not its number. + */ + N_("Store matched component NUMBER in VARNAME."), + N_("[NUMBER:]VARNAME"), ARG_TYPE_STRING }, + { 0, 0, 0, 0, 0, 0 } + }; + +static grub_err_t +setvar (char *str, char *v, regmatch_t *m) +{ + char ch; + grub_err_t err; + ch = str[m->rm_eo]; + str[m->rm_eo] = '\0'; + err = grub_env_set (v, str + m->rm_so); + str[m->rm_eo] = ch; + return err; +} + +static grub_err_t +set_matches (char **varnames, char *str, grub_size_t nmatches, + regmatch_t *matches) +{ + int i; + char *p; + const char * q; + grub_err_t err; + unsigned long j; + + for (i = 0; varnames && varnames[i]; i++) + { + err = GRUB_ERR_NONE; + p = grub_strchr (varnames[i], ':'); + if (! p) + { + /* varname w/o index defaults to 1 */ + if (nmatches < 2 || matches[1].rm_so == -1) + grub_env_unset (varnames[i]); + else + err = setvar (str, varnames[i], &matches[1]); + } + else + { + j = grub_strtoul (varnames[i], &q, 10); + if (q != p) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid variable name format %s", varnames[i]); + + if (nmatches <= j || matches[j].rm_so == -1) + grub_env_unset (p + 1); + else + err = setvar (str, p + 1, &matches[j]); + } + + if (err != GRUB_ERR_NONE) + return err; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_regexp (grub_extcmd_context_t ctxt, int argc, char **args) +{ + regex_t regex; + int ret; + grub_size_t s; + char *comperr; + grub_err_t err; + regmatch_t *matches = 0; + + if (argc != 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected")); + + ret = regcomp (®ex, args[0], REG_EXTENDED); + if (ret) + goto fail; + + matches = grub_calloc (regex.re_nsub + 1, sizeof (*matches)); + if (! matches) + goto fail; + + ret = regexec (®ex, args[1], regex.re_nsub + 1, matches, 0); + if (!ret) + { + err = set_matches (ctxt->state[0].args, args[1], + regex.re_nsub + 1, matches); + regfree (®ex); + grub_free (matches); + return err; + } + + fail: + grub_free (matches); + s = regerror (ret, ®ex, 0, 0); + comperr = grub_malloc (s); + if (!comperr) + { + regfree (®ex); + return grub_errno; + } + regerror (ret, ®ex, comperr, s); + err = grub_error (GRUB_ERR_TEST_FAILURE, "%s", comperr); + regfree (®ex); + grub_free (comperr); + return err; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(regexp) +{ + cmd = grub_register_extcmd ("regexp", grub_cmd_regexp, 0, + /* TRANSLATORS: This are two arguments. So it's + two separate units to translate and pay + attention not to reverse them. */ + N_("REGEXP STRING"), + N_("Test if REGEXP matches STRING."), options); + + /* Setup GRUB script wildcard translator. */ + grub_wildcard_translator = &grub_filename_translator; +} + +GRUB_MOD_FINI(regexp) +{ + grub_unregister_extcmd (cmd); + grub_wildcard_translator = 0; +} diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c new file mode 100644 index 000000000..49b679e80 --- /dev/null +++ b/grub-core/commands/search.c @@ -0,0 +1,412 @@ +/* search.c - search devices based on a file or a filesystem label */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct cache_entry +{ + struct cache_entry *next; + char *key; + char *value; +}; + +static struct cache_entry *cache; + +/* Context for FUNC_NAME. */ +struct search_ctx +{ + const char *key; + const char *var; + enum search_flags flags; + char **hints; + unsigned nhints; + int count; + int is_cache; +}; + +static bool +is_unencrypted_disk (grub_disk_t disk) +{ + grub_command_t cmd; + char *disk_str; + int disk_str_len; + int res; + + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + return false; /* This is (crypto) disk. */ + + if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID) + { + char opt[] = "--quiet"; + char *args[2]; + + cmd = grub_command_find ("cryptocheck"); + if (cmd == NULL) /* No diskfilter module loaded for some reason. */ + return true; + + disk_str_len = grub_strlen (disk->name) + 2 + 1; + disk_str = grub_malloc (disk_str_len); + if (disk_str == NULL) /* Something is wrong, better report as unencrypted. */ + return true; + + grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name); + args[0] = opt; + args[1] = disk_str; + res = cmd->func (cmd, 2, args); + grub_free (disk_str); + return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */ + } + return true; +} + +/* Helper for FUNC_NAME. */ +static int +iterate_device (const char *name, void *data) +{ + struct search_ctx *ctx = data; + int found = 0; + + /* Skip floppy drives when requested. */ + if (ctx->flags & SEARCH_FLAGS_NO_FLOPPY && + name[0] == 'f' && name[1] == 'd' && name[2] >= '0' && name[2] <= '9') + return 0; + + /* Limit to EFI disks when requested. */ + if (ctx->flags & SEARCH_FLAGS_EFIDISK_ONLY) + { + grub_device_t dev; + + dev = grub_device_open (name); + if (dev == NULL) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (dev->disk == NULL || dev->disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_device_close (dev); + } + + /* Limit to encrypted disks when requested. */ + if (ctx->flags & SEARCH_FLAGS_CRYPTODISK_ONLY) + { + grub_device_t dev; + + dev = grub_device_open (name); + if (dev == NULL) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (dev->disk == NULL || is_unencrypted_disk (dev->disk) == true) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_device_close (dev); + } + +#ifdef DO_SEARCH_FS_UUID +#define compare_fn grub_strcasecmp +#else +#define compare_fn grub_strcmp +#endif + +#ifdef DO_SEARCH_FILE + { + char *buf; + grub_file_t file; + + buf = grub_xasprintf ("(%s)%s", name, ctx->key); + if (! buf) + return 1; + + file = grub_file_open (buf, GRUB_FILE_TYPE_FS_SEARCH + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (file) + { + found = 1; + grub_file_close (file); + } + grub_free (buf); + } +#else + { + /* SEARCH_FS_UUID or SEARCH_LABEL */ + grub_device_t dev; + grub_fs_t fs; + char *quid; + + dev = grub_device_open (name); + if (dev) + { + fs = grub_fs_probe (dev); + +#ifdef DO_SEARCH_FS_UUID +#define read_fn fs_uuid +#else +#define read_fn fs_label +#endif + + if (fs && fs->read_fn) + { + fs->read_fn (dev, &quid); + + if (grub_errno == GRUB_ERR_NONE && quid) + { + if (compare_fn (quid, ctx->key) == 0) + found = 1; + + grub_free (quid); + } + } + + grub_device_close (dev); + } + } +#endif + + if (!ctx->is_cache && found && ctx->count == 0) + { + struct cache_entry *cache_ent; + cache_ent = grub_malloc (sizeof (*cache_ent)); + if (cache_ent) + { + cache_ent->key = grub_strdup (ctx->key); + cache_ent->value = grub_strdup (name); + if (cache_ent->value && cache_ent->key) + { + cache_ent->next = cache; + cache = cache_ent; + } + else + { + grub_free (cache_ent->value); + grub_free (cache_ent->key); + grub_free (cache_ent); + grub_errno = GRUB_ERR_NONE; + } + } + else + grub_errno = GRUB_ERR_NONE; + } + + if (found) + { + ctx->count++; + if (ctx->var) + grub_env_set (ctx->var, name); + else + grub_printf (" %s", name); + } + + grub_errno = GRUB_ERR_NONE; + return (found && ctx->var); +} + +/* Helper for FUNC_NAME. */ +static int +part_hook (grub_disk_t disk, const grub_partition_t partition, void *data) +{ + struct search_ctx *ctx = data; + char *partition_name, *devname; + int ret; + + partition_name = grub_partition_get_name (partition); + if (! partition_name) + return 1; + + devname = grub_xasprintf ("%s,%s", disk->name, partition_name); + grub_free (partition_name); + if (!devname) + return 1; + ret = iterate_device (devname, ctx); + grub_free (devname); + + return ret; +} + +/* Helper for FUNC_NAME. */ +static void +try (struct search_ctx *ctx) +{ + unsigned i; + struct cache_entry **prev; + struct cache_entry *cache_ent; + + for (prev = &cache, cache_ent = *prev; cache_ent; + prev = &cache_ent->next, cache_ent = *prev) + if (compare_fn (cache_ent->key, ctx->key) == 0) + break; + if (cache_ent) + { + ctx->is_cache = 1; + if (iterate_device (cache_ent->value, ctx)) + { + ctx->is_cache = 0; + return; + } + ctx->is_cache = 0; + /* Cache entry was outdated. Remove it. */ + if (!ctx->count) + { + *prev = cache_ent->next; + grub_free (cache_ent->key); + grub_free (cache_ent->value); + grub_free (cache_ent); + } + } + + for (i = 0; i < ctx->nhints; i++) + { + char *end; + if (!ctx->hints[i][0]) + continue; + end = ctx->hints[i] + grub_strlen (ctx->hints[i]) - 1; + if (*end == ',') + *end = 0; + if (iterate_device (ctx->hints[i], ctx)) + { + if (!*end) + *end = ','; + return; + } + if (!*end) + { + grub_device_t dev; + int ret; + dev = grub_device_open (ctx->hints[i]); + if (!dev) + { + if (!*end) + *end = ','; + continue; + } + if (!dev->disk) + { + grub_device_close (dev); + if (!*end) + *end = ','; + continue; + } + ret = grub_partition_iterate (dev->disk, part_hook, ctx); + if (!*end) + *end = ','; + grub_device_close (dev); + if (ret) + return; + } + } + grub_device_iterate (iterate_device, ctx); +} + +void +FUNC_NAME (const char *key, const char *var, enum search_flags flags, + char **hints, unsigned nhints) +{ + struct search_ctx ctx = { + .key = key, + .var = var, + .flags = flags, + .hints = hints, + .nhints = nhints, + .count = 0, + .is_cache = 0 + }; + grub_fs_autoload_hook_t saved_autoload; + + /* First try without autoloading if we're setting variable. */ + if (var) + { + saved_autoload = grub_fs_autoload_hook; + grub_fs_autoload_hook = 0; + try (&ctx); + + /* Restore autoload hook. */ + grub_fs_autoload_hook = saved_autoload; + + /* Retry with autoload if nothing found. */ + if (grub_errno == GRUB_ERR_NONE && ctx.count == 0) + try (&ctx); + } + else + try (&ctx); + + if (grub_errno == GRUB_ERR_NONE && ctx.count == 0) + grub_error (GRUB_ERR_FILE_NOT_FOUND, "no such device: %s", key); +} + +static grub_err_t +grub_cmd_do_search (grub_command_t cmd __attribute__ ((unused)), int argc, + char **args) +{ + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + FUNC_NAME (args[0], argc == 1 ? 0 : args[1], 0, (args + 2), + argc > 2 ? argc - 2 : 0); + + return grub_errno; +} + +static grub_command_t cmd; + +#ifdef DO_SEARCH_FILE +GRUB_MOD_INIT(search_fs_file) +#elif defined (DO_SEARCH_FS_UUID) +GRUB_MOD_INIT(search_fs_uuid) +#else +GRUB_MOD_INIT(search_label) +#endif +{ + cmd = + grub_register_command (COMMAND_NAME, grub_cmd_do_search, + N_("NAME [VARIABLE] [HINTS]"), + HELP_MESSAGE); +} + +#ifdef DO_SEARCH_FILE +GRUB_MOD_FINI(search_fs_file) +#elif defined (DO_SEARCH_FS_UUID) +GRUB_MOD_FINI(search_fs_uuid) +#else +GRUB_MOD_FINI(search_label) +#endif +{ + grub_unregister_command (cmd); +} diff --git a/commands/search_file.c b/grub-core/commands/search_file.c similarity index 89% rename from commands/search_file.c rename to grub-core/commands/search_file.c index 73ce89ccc..cc2a5f939 100644 --- a/commands/search_file.c +++ b/grub-core/commands/search_file.c @@ -1,6 +1,5 @@ #define DO_SEARCH_FILE 1 #define FUNC_NAME grub_search_fs_file #define COMMAND_NAME "search.file" -#define SEARCH_TARGET "file" #define HELP_MESSAGE N_("Search devices by file. If VARIABLE is specified, the first device found is set to a variable.") #include "search.c" diff --git a/commands/search_label.c b/grub-core/commands/search_label.c similarity index 85% rename from commands/search_label.c rename to grub-core/commands/search_label.c index ee9c792be..12e7c1831 100644 --- a/commands/search_label.c +++ b/grub-core/commands/search_label.c @@ -1,6 +1,5 @@ #define DO_SEARCH_FS_LABEL 1 #define FUNC_NAME grub_search_label #define COMMAND_NAME "search.fs_label" -#define SEARCH_TARGET "filesystem label" #define HELP_MESSAGE N_("Search devices by label. If VARIABLE is specified, the first device found is set to a variable.") #include "search.c" diff --git a/commands/search_uuid.c b/grub-core/commands/search_uuid.c similarity index 86% rename from commands/search_uuid.c rename to grub-core/commands/search_uuid.c index 52f83812c..541bcf54b 100644 --- a/commands/search_uuid.c +++ b/grub-core/commands/search_uuid.c @@ -1,6 +1,5 @@ #define DO_SEARCH_FS_UUID 1 #define FUNC_NAME grub_search_fs_uuid #define COMMAND_NAME "search.fs_uuid" -#define SEARCH_TARGET "filesystem UUID" #define HELP_MESSAGE N_("Search devices by UUID. If VARIABLE is specified, the first device found is set to a variable.") #include "search.c" diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c new file mode 100644 index 000000000..5f536006c --- /dev/null +++ b/grub-core/commands/search_wrap.c @@ -0,0 +1,231 @@ +/* search.c - search devices based on a file or a filesystem label */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + {"file", 'f', 0, N_("Search devices by a file."), 0, 0}, + {"label", 'l', 0, N_("Search devices by a filesystem label."), + 0, 0}, + {"fs-uuid", 'u', 0, N_("Search devices by a filesystem UUID."), + 0, 0}, + {"set", 's', GRUB_ARG_OPTION_OPTIONAL, + N_("Set a variable to the first device found."), N_("VARNAME"), + ARG_TYPE_STRING}, + {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, + {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, + {"cryptodisk-only", 0, 0, N_("Only probe encrypted disks."), 0, 0}, + {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT. If HINT ends in comma, " + "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, + {"hint-ieee1275", 0, GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT if currently running on IEEE1275. " + "If HINT ends in comma, also try subpartitions"), + N_("HINT"), ARG_TYPE_STRING}, + {"hint-bios", 0, GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT if currently running on BIOS. " + "If HINT ends in comma, also try subpartitions"), + N_("HINT"), ARG_TYPE_STRING}, + {"hint-baremetal", 0, GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT if direct hardware access is supported. " + "If HINT ends in comma, also try subpartitions"), + N_("HINT"), ARG_TYPE_STRING}, + {"hint-efi", 0, GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT if currently running on EFI. " + "If HINT ends in comma, also try subpartitions"), + N_("HINT"), ARG_TYPE_STRING}, + {"hint-arc", 0, GRUB_ARG_OPTION_REPEATABLE, + N_("First try the device HINT if currently running on ARC." + " If HINT ends in comma, also try subpartitions"), + N_("HINT"), ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +enum options + { + SEARCH_FILE, + SEARCH_LABEL, + SEARCH_FS_UUID, + SEARCH_SET, + SEARCH_NO_FLOPPY, + SEARCH_EFIDISK_ONLY, + SEARCH_CRYPTODISK_ONLY, + SEARCH_HINT, + SEARCH_HINT_IEEE1275, + SEARCH_HINT_BIOS, + SEARCH_HINT_BAREMETAL, + SEARCH_HINT_EFI, + SEARCH_HINT_ARC, + }; + +static grub_err_t +grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + const char *var = 0; + const char *id = 0; + int i = 0, j = 0, nhints = 0; + char **hints = NULL; + enum search_flags flags = SEARCH_FLAGS_NONE; + + if (state[SEARCH_HINT].set) + for (i = 0; state[SEARCH_HINT].args[i]; i++) + nhints++; + +#ifdef GRUB_MACHINE_IEEE1275 + if (state[SEARCH_HINT_IEEE1275].set) + for (i = 0; state[SEARCH_HINT_IEEE1275].args[i]; i++) + nhints++; +#endif + +#ifdef GRUB_MACHINE_EFI + if (state[SEARCH_HINT_EFI].set) + for (i = 0; state[SEARCH_HINT_EFI].args[i]; i++) + nhints++; +#endif + +#ifdef GRUB_MACHINE_PCBIOS + if (state[SEARCH_HINT_BIOS].set) + for (i = 0; state[SEARCH_HINT_BIOS].args[i]; i++) + nhints++; +#endif + +#ifdef GRUB_MACHINE_ARC + if (state[SEARCH_HINT_ARC].set) + for (i = 0; state[SEARCH_HINT_ARC].args[i]; i++) + nhints++; +#endif + + if (state[SEARCH_HINT_BAREMETAL].set) + for (i = 0; state[SEARCH_HINT_BAREMETAL].args[i]; i++) + nhints++; + + hints = grub_calloc (nhints, sizeof (hints[0])); + if (!hints) + return grub_errno; + j = 0; + + if (state[SEARCH_HINT].set) + for (i = 0; state[SEARCH_HINT].args[i]; i++) + hints[j++] = state[SEARCH_HINT].args[i]; + +#ifdef GRUB_MACHINE_IEEE1275 + if (state[SEARCH_HINT_IEEE1275].set) + for (i = 0; state[SEARCH_HINT_IEEE1275].args[i]; i++) + hints[j++] = state[SEARCH_HINT_IEEE1275].args[i]; +#endif + +#ifdef GRUB_MACHINE_EFI + if (state[SEARCH_HINT_EFI].set) + for (i = 0; state[SEARCH_HINT_EFI].args[i]; i++) + hints[j++] = state[SEARCH_HINT_EFI].args[i]; +#endif + +#ifdef GRUB_MACHINE_ARC + if (state[SEARCH_HINT_ARC].set) + for (i = 0; state[SEARCH_HINT_ARC].args[i]; i++) + hints[j++] = state[SEARCH_HINT_ARC].args[i]; +#endif + +#ifdef GRUB_MACHINE_PCBIOS + if (state[SEARCH_HINT_BIOS].set) + for (i = 0; state[SEARCH_HINT_BIOS].args[i]; i++) + hints[j++] = state[SEARCH_HINT_BIOS].args[i]; +#endif + + if (state[SEARCH_HINT_BAREMETAL].set) + for (i = 0; state[SEARCH_HINT_BAREMETAL].args[i]; i++) + hints[j++] = state[SEARCH_HINT_BAREMETAL].args[i]; + + /* Skip hints for future platforms. */ + for (j = 0; j < argc; j++) + if (grub_memcmp (args[j], "--hint-", sizeof ("--hint-") - 1) != 0) + break; + + if (state[SEARCH_SET].set) + var = state[SEARCH_SET].arg ? state[SEARCH_SET].arg : "root"; + + if (argc != j) + id = args[j]; + else if (state[SEARCH_SET].set && state[SEARCH_SET].arg) + { + id = state[SEARCH_SET].arg; + var = "root"; + } + else + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + goto out; + } + + if (state[SEARCH_NO_FLOPPY].set) + flags |= SEARCH_FLAGS_NO_FLOPPY; + + if (state[SEARCH_EFIDISK_ONLY].set) + flags |= SEARCH_FLAGS_EFIDISK_ONLY; + + if (state[SEARCH_CRYPTODISK_ONLY].set) + flags |= SEARCH_FLAGS_CRYPTODISK_ONLY; + + if (state[SEARCH_LABEL].set) + grub_search_label (id, var, flags, hints, nhints); + else if (state[SEARCH_FS_UUID].set) + grub_search_fs_uuid (id, var, flags, hints, nhints); + else if (state[SEARCH_FILE].set) + grub_search_fs_file (id, var, flags, hints, nhints); + else + grub_error (GRUB_ERR_INVALID_COMMAND, "unspecified search type"); + +out: + grub_free (hints); + return grub_errno; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(search) +{ + cmd = + grub_register_extcmd ("search", grub_cmd_search, + GRUB_COMMAND_FLAG_EXTRACTOR | GRUB_COMMAND_ACCEPT_DASH, + N_("[-f|-l|-u|-s|-n] [--cryptodisk-only] [--hint HINT [--hint HINT] ...]" + " NAME"), + N_("Search devices by file, filesystem label" + " or filesystem UUID." + " If --set is specified, the first device found is" + " set to a variable. If no variable name is" + " specified, `root' is used."), + options); +} + +GRUB_MOD_FINI(search) +{ + grub_unregister_extcmd (cmd); +} diff --git a/commands/setpci.c b/grub-core/commands/setpci.c similarity index 75% rename from commands/setpci.c rename to grub-core/commands/setpci.c index f780547a2..5917625f8 100644 --- a/commands/setpci.c +++ b/grub-core/commands/setpci.c @@ -23,6 +23,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct pci_register { @@ -31,7 +34,7 @@ struct pci_register unsigned size; }; -struct pci_register pci_registers[] = +static struct pci_register pci_registers[] = { {"VENDOR_ID", GRUB_PCI_REG_VENDOR , 2}, {"DEVICE_ID", GRUB_PCI_REG_DEVICE , 2}, @@ -63,12 +66,12 @@ struct pci_register pci_registers[] = static const struct grub_arg_option options[] = { - {0, 'd', 0, "Select device by vendor and device IDs.", - "[vendor]:[device]", ARG_TYPE_STRING}, - {0, 's', 0, "Select device by its position on the bus.", - "[bus]:[slot][.func]", ARG_TYPE_STRING}, - {0, 'v', 0, "Save read value into variable VARNAME.", - "VARNAME", ARG_TYPE_STRING}, + {0, 'd', 0, N_("Select device by vendor and device IDs."), + N_("[vendor]:[device]"), ARG_TYPE_STRING}, + {0, 's', 0, N_("Select device by its position on the bus."), + N_("[bus]:[slot][.func]"), ARG_TYPE_STRING}, + {0, 'v', 0, N_("Save read value into variable VARNAME."), + N_("VARNAME"), ARG_TYPE_STRING}, {0, 0, 0, 0, 0, 0} }; @@ -80,8 +83,9 @@ static int regsize; static grub_uint16_t regaddr; static const char *varname; -static int NESTED_FUNC_ATTR -grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) +static int +grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) { grub_uint32_t regval = 0; grub_pci_address_t addr; @@ -95,7 +99,7 @@ grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) if (check_device && grub_pci_get_device (dev) != device) return 0; - if (check_function && grub_pci_get_function (dev) != device) + if (check_function && grub_pci_get_function (dev) != function) return 0; addr = grub_pci_make_address (dev, regaddr); @@ -125,7 +129,7 @@ grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) if (!write_mask) { - grub_printf ("Register %x of %d:%d.%d is %x\n", regaddr, + grub_printf (_("Register %x of %x:%02x.%x is %x\n"), regaddr, grub_pci_get_bus (dev), grub_pci_get_device (dev), grub_pci_get_function (dev), @@ -154,7 +158,7 @@ grub_setpci_iter (grub_pci_device_t dev, grub_pci_id_t pciid) } static grub_err_t -grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) +grub_cmd_setpci (grub_extcmd_context_t ctxt, int argc, char **argv) { const char *ptr; unsigned i; @@ -162,24 +166,23 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) pciid_check_value = 0; pciid_check_mask = 0; - if (cmd->state[0].set) + if (ctxt->state[0].set) { - ptr = cmd->state[0].arg; - pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff); + ptr = ctxt->state[0].arg; + pciid_check_value |= (grub_strtoul (ptr, &ptr, 16) & 0xffff); if (grub_errno == GRUB_ERR_BAD_NUMBER) { grub_errno = GRUB_ERR_NONE; - ptr = cmd->state[0].arg; + ptr = ctxt->state[0].arg; } else pciid_check_mask |= 0xffff; if (grub_errno) return grub_errno; if (*ptr != ':') - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("missing `%c' symbol"), ':'); ptr++; - pciid_check_value |= (grub_strtoul (ptr, (char **) &ptr, 16) & 0xffff) - << 16; + pciid_check_value |= (grub_strtoul (ptr, &ptr, 16) & 0xffff) << 16; if (grub_errno == GRUB_ERR_BAD_NUMBER) grub_errno = GRUB_ERR_NONE; else @@ -190,13 +193,13 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) check_bus = check_device = check_function = 0; - if (cmd->state[1].set) + if (ctxt->state[1].set) { const char *optr; - - ptr = cmd->state[1].arg; + + ptr = ctxt->state[1].arg; optr = ptr; - bus = grub_strtoul (ptr, (char **) &ptr, 16); + bus = grub_strtoul (ptr, &ptr, 16); if (grub_errno == GRUB_ERR_BAD_NUMBER) { grub_errno = GRUB_ERR_NONE; @@ -207,10 +210,10 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) if (grub_errno) return grub_errno; if (*ptr != ':') - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Colon expected."); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("missing `%c' symbol"), ':'); ptr++; optr = ptr; - device = grub_strtoul (ptr, (char **) &ptr, 16); + device = grub_strtoul (ptr, &ptr, 16); if (grub_errno == GRUB_ERR_BAD_NUMBER) { grub_errno = GRUB_ERR_NONE; @@ -221,25 +224,22 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) if (*ptr == '.') { ptr++; - function = grub_strtoul (ptr, (char **) &ptr, 16); + function = grub_strtoul (ptr, &ptr, 16); if (grub_errno) return grub_errno; check_function = 1; } } - if (cmd->state[2].set) - varname = cmd->state[2].arg; + if (ctxt->state[2].set) + varname = ctxt->state[2].arg; else varname = NULL; write_mask = 0; - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Command expected."); - - if (argc > 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Only one command is supported."); + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); ptr = argv[0]; @@ -252,9 +252,9 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) if (i == ARRAY_SIZE (pci_registers)) { regsize = 0; - regaddr = grub_strtoul (ptr, (char **) &ptr, 16); + regaddr = grub_strtoul (ptr, &ptr, 16); if (grub_errno) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown register"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown register"); } else { @@ -269,7 +269,7 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) if (*ptr == '+') { ptr++; - regaddr += grub_strtoul (ptr, (char **) &ptr, 16); + regaddr += grub_strtoul (ptr, &ptr, 16); if (grub_errno) return grub_errno; } @@ -295,32 +295,31 @@ grub_cmd_setpci (grub_extcmd_t cmd, int argc, char **argv) if (!regsize) return grub_error (GRUB_ERR_BAD_ARGUMENT, - "Unknown register size."); + "unknown register size"); write_mask = 0; if (*ptr == '=') { ptr++; - regwrite = grub_strtoul (ptr, (char **) &ptr, 16); + regwrite = grub_strtoul (ptr, &ptr, 16); if (grub_errno) return grub_errno; write_mask = 0xffffffff; if (*ptr == ':') { ptr++; - write_mask = grub_strtoul (ptr, (char **) &ptr, 16); + write_mask = grub_strtoul (ptr, &ptr, 16); if (grub_errno) return grub_errno; - write_mask = 0xffffffff; } regwrite &= write_mask; } if (write_mask && varname) return grub_error (GRUB_ERR_BAD_ARGUMENT, - "Option -v isn't valid for writes."); + "option -v isn't valid for writes"); - grub_pci_iterate (grub_setpci_iter); + grub_pci_iterate (grub_setpci_iter, NULL); return GRUB_ERR_NONE; } @@ -328,10 +327,10 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(setpci) { - cmd = grub_register_extcmd ("setpci", grub_cmd_setpci, GRUB_COMMAND_FLAG_BOTH, - "setpci [-s POSITION] [-d DEVICE] [-v VAR] " - "[REGISTER][=VALUE[:MASK]]", - "Manipulate PCI devices.", options); + cmd = grub_register_extcmd_lockdown ("setpci", grub_cmd_setpci, 0, + N_("[-s POSITION] [-d DEVICE] [-v VAR] " + "REGISTER[=VALUE[:MASK]]"), + N_("Manipulate PCI devices."), options); } GRUB_MOD_FINI(setpci) diff --git a/commands/sleep.c b/grub-core/commands/sleep.c similarity index 82% rename from commands/sleep.c rename to grub-core/commands/sleep.c index 012181fa2..a1370b710 100644 --- a/commands/sleep.c +++ b/grub-core/commands/sleep.c @@ -22,18 +22,19 @@ #include #include #include -#include #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static const struct grub_arg_option options[] = { {"verbose", 'v', 0, N_("Verbose countdown."), 0, 0}, - {"interruptible", 'i', 0, N_("Interruptible with ESC."), 0, 0}, + {"interruptible", 'i', 0, N_("Allow to interrupt with ESC."), 0, 0}, {0, 0, 0, 0, 0, 0} }; -static grub_uint16_t *pos; +static struct grub_term_coordinate *pos; static void do_print (int n) @@ -42,6 +43,7 @@ do_print (int n) /* NOTE: Do not remove the trailing space characters. They are required to clear the line. */ grub_printf ("%d ", n); + grub_refresh (); } /* Based on grub_millisleep() from kern/generic/millisleep.c. */ @@ -53,21 +55,20 @@ grub_interruptible_millisleep (grub_uint32_t ms) start = grub_get_time_ms (); while (grub_get_time_ms () - start < ms) - if (grub_checkkey () >= 0 && - GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC) + if (grub_key_is_interrupt (grub_getkey_noblock ())) return 1; return 0; } static grub_err_t -grub_cmd_sleep (grub_extcmd_t cmd, int argc, char **args) +grub_cmd_sleep (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = cmd->state; + struct grub_arg_list *state = ctxt->state; int n; if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing operand"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); n = grub_strtoul (args[0], 0, 10); @@ -77,6 +78,8 @@ grub_cmd_sleep (grub_extcmd_t cmd, int argc, char **args) return 0; } + grub_refresh (); + pos = grub_term_save_pos (); for (; n; n--) @@ -102,7 +105,7 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(sleep) { - cmd = grub_register_extcmd ("sleep", grub_cmd_sleep, GRUB_COMMAND_FLAG_BOTH, + cmd = grub_register_extcmd ("sleep", grub_cmd_sleep, 0, N_("NUMBER_OF_SECONDS"), N_("Wait for a specified number of seconds."), options); diff --git a/grub-core/commands/smbios.c b/grub-core/commands/smbios.c new file mode 100644 index 000000000..1a9086ddd --- /dev/null +++ b/grub-core/commands/smbios.c @@ -0,0 +1,398 @@ +/* smbios.c - retrieve smbios information. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Abstract useful values found in either the SMBIOS3 or SMBIOS EPS. */ +static struct { + grub_addr_t start; + grub_addr_t end; + grub_uint16_t structures; +} table_desc; + +static grub_extcmd_t cmd; + +/* Locate the SMBIOS entry point structure depending on the hardware. */ +struct grub_smbios_eps * +grub_smbios_get_eps (void) +{ + static struct grub_smbios_eps *eps = NULL; + + if (eps != NULL) + return eps; + + eps = grub_machine_smbios_get_eps (); + + return eps; +} + +/* Locate the SMBIOS3 entry point structure depending on the hardware. */ +static struct grub_smbios_eps3 * +grub_smbios_get_eps3 (void) +{ + static struct grub_smbios_eps3 *eps = NULL; + + if (eps != NULL) + return eps; + + eps = grub_machine_smbios_get_eps3 (); + + return eps; +} + +static char * +linux_string (const char *value) +{ + char *out = grub_malloc( grub_strlen (value) + 1); + const char *src = value; + char *dst = out; + + for (; *src; src++) + if (*src > ' ' && *src < 127 && *src != ':') + *dst++ = *src; + + *dst = 0; + return out; +} + +/* + * These functions convert values from the various SMBIOS structure field types + * into a string formatted to be returned to the user. They expect that the + * structure and offset were already validated. When the requested data is + * successfully retrieved and formatted, the pointer to the string is returned; + * otherwise, NULL is returned on failure. Don't free the result. + */ + +static const char * +grub_smbios_format_byte (const grub_uint8_t *structure, grub_uint8_t offset) +{ + static char buffer[sizeof ("255")]; + + grub_snprintf (buffer, sizeof (buffer), "%u", structure[offset]); + + return (const char *)buffer; +} + +static const char * +grub_smbios_format_word (const grub_uint8_t *structure, grub_uint8_t offset) +{ + static char buffer[sizeof ("65535")]; + + grub_uint16_t value = grub_get_unaligned16 (structure + offset); + grub_snprintf (buffer, sizeof (buffer), "%u", value); + + return (const char *)buffer; +} + +static const char * +grub_smbios_format_dword (const grub_uint8_t *structure, grub_uint8_t offset) +{ + static char buffer[sizeof ("4294967295")]; + + grub_uint32_t value = grub_get_unaligned32 (structure + offset); + grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT32_T, value); + + return (const char *)buffer; +} + +static const char * +grub_smbios_format_qword (const grub_uint8_t *structure, grub_uint8_t offset) +{ + static char buffer[sizeof ("18446744073709551615")]; + + grub_uint64_t value = grub_get_unaligned64 (structure + offset); + grub_snprintf (buffer, sizeof (buffer), "%" PRIuGRUB_UINT64_T, value); + + return (const char *)buffer; +} + +static const char * +grub_smbios_get_string (const grub_uint8_t *structure, grub_uint8_t offset) +{ + const grub_uint8_t *ptr = structure + structure[1]; + const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end; + const grub_uint8_t referenced_string_number = structure[offset]; + grub_uint8_t i; + + /* A string referenced with zero is interpreted as unset. */ + if (referenced_string_number == 0) + return NULL; + + /* Search the string set. */ + for (i = 1; *ptr != 0 && ptr < table_end; i++) + if (i == referenced_string_number) + { + const char *str = (const char *)ptr; + while (*ptr++ != 0) + if (ptr >= table_end) + return NULL; /* The string isn't terminated. */ + return str; + } + else + while (*ptr++ != 0 && ptr < table_end); + + /* The string number is greater than the number of strings in the set. */ + return NULL; +} + +static const char * +grub_smbios_format_uuid (const grub_uint8_t *structure, grub_uint8_t offset) +{ + static char buffer[sizeof ("ffffffff-ffff-ffff-ffff-ffffffffffff")]; + const grub_uint8_t *f = structure + offset; /* little-endian fields */ + const grub_uint8_t *g = f + 8; /* byte-by-byte fields */ + + grub_snprintf (buffer, sizeof (buffer), + "%02x%02x%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x-%02x%02x%02x%02x%02x%02x", + f[3], f[2], f[1], f[0], f[5], f[4], f[7], f[6], + g[0], g[1], g[2], g[3], g[4], g[5], g[6], g[7]); + + return (const char *)buffer; +} + +/* List the field formatting functions and the number of bytes they need. */ +static const struct { + const char *(*format) (const grub_uint8_t *structure, grub_uint8_t offset); + grub_uint8_t field_length; +} field_extractors[] = { + {grub_smbios_format_byte, 1}, + {grub_smbios_format_word, 2}, + {grub_smbios_format_dword, 4}, + {grub_smbios_format_qword, 8}, + {grub_smbios_get_string, 1}, + {grub_smbios_format_uuid, 16} +}; + +/* List command options, with structure field getters ordered as above. */ +#define FIRST_GETTER_OPT (3) +#define SETTER_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors)) +#define LINUX_OPT (FIRST_GETTER_OPT + ARRAY_SIZE(field_extractors) + 1) + +static const struct grub_arg_option options[] = { + {"type", 't', 0, N_("Match structures with the given type."), + N_("type"), ARG_TYPE_INT}, + {"handle", 'h', 0, N_("Match structures with the given handle."), + N_("handle"), ARG_TYPE_INT}, + {"match", 'm', 0, N_("Select a structure when several match."), + N_("match"), ARG_TYPE_INT}, + {"get-byte", 'b', 0, N_("Get the byte's value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-word", 'w', 0, N_("Get two bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-dword", 'd', 0, N_("Get four bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-qword", 'q', 0, N_("Get eight bytes' value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-string", 's', 0, N_("Get the string specified at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"get-uuid", 'u', 0, N_("Get the UUID's value at the given offset."), + N_("offset"), ARG_TYPE_INT}, + {"set", '\0', 0, N_("Store the value in the given variable name."), + N_("variable"), ARG_TYPE_STRING}, + {"linux", '\0', 0, N_("Filter the result like linux does."), + N_("variable"), ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +/* + * Return a matching SMBIOS structure. + * + * This method can use up to three criteria for selecting a structure: + * - The "type" field (use -1 to ignore) + * - The "handle" field (use -1 to ignore) + * - Which to return if several match (use 0 to ignore) + * + * The return value is a pointer to the first matching structure. If no + * structures match the given parameters, NULL is returned. + */ +static const grub_uint8_t * +grub_smbios_match_structure (const grub_int16_t type, + const grub_int32_t handle, + const grub_uint16_t match) +{ + const grub_uint8_t *ptr = (const grub_uint8_t *)table_desc.start; + const grub_uint8_t *table_end = (const grub_uint8_t *)table_desc.end; + grub_uint16_t structures = table_desc.structures; + grub_uint16_t structure_count = 0; + grub_uint16_t matches = 0; + + while (ptr < table_end + && ptr[1] >= 4 /* Valid structures include the 4-byte header. */ + && (structure_count++ < structures || structures == 0)) + { + grub_uint16_t structure_handle = grub_get_unaligned16 (ptr + 2); + grub_uint8_t structure_type = ptr[0]; + + if ((handle < 0 || handle == structure_handle) + && (type < 0 || type == structure_type) + && (match == 0 || match == ++matches)) + return ptr; + else + { + ptr += ptr[1]; + while ((*ptr++ != 0 || *ptr++ != 0) && ptr < table_end); + } + + if (structure_type == GRUB_SMBIOS_TYPE_END_OF_TABLE) + break; + } + + return NULL; +} + +static grub_err_t +grub_cmd_smbios (grub_extcmd_context_t ctxt, + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) +{ + struct grub_arg_list *state = ctxt->state; + + grub_int16_t type = -1; + grub_int32_t handle = -1; + grub_uint16_t match = 0; + grub_uint8_t offset = 0; + + const grub_uint8_t *structure; + const char *value; + char *modified_value = NULL; + grub_int32_t option; + grub_int8_t field_type = -1; + grub_uint8_t i; + + if (table_desc.start == 0) + return grub_error (GRUB_ERR_IO, + N_("the SMBIOS entry point structure was not found")); + + /* Read the given filtering options. */ + if (state[0].set) + { + option = grub_strtol (state[0].arg, NULL, 0); + if (option < 0 || option > 255) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the type must be between 0 and 255")); + type = (grub_int16_t)option; + } + if (state[1].set) + { + option = grub_strtol (state[1].arg, NULL, 0); + if (option < 0 || option > 65535) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the handle must be between 0 and 65535")); + handle = (grub_int32_t)option; + } + if (state[2].set) + { + option = grub_strtol (state[2].arg, NULL, 0); + if (option <= 0 || option > 65535) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the match must be a positive integer")); + match = (grub_uint16_t)option; + } + + /* Determine the data type of the structure field to retrieve. */ + for (i = 0; i < ARRAY_SIZE(field_extractors); i++) + if (state[FIRST_GETTER_OPT + i].set) + { + if (field_type >= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("only one --get option is usable at a time")); + field_type = i; + } + + /* Require a choice of a structure field to return. */ + if (field_type < 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("one of the --get options is required")); + + /* Locate a matching SMBIOS structure. */ + structure = grub_smbios_match_structure (type, handle, match); + if (structure == NULL) + return grub_error (GRUB_ERR_IO, + N_("no structure matched the given options")); + + /* Ensure the requested byte offset is inside the structure. */ + option = grub_strtol (state[FIRST_GETTER_OPT + field_type].arg, NULL, 0); + if (option < 0 || option >= structure[1]) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("the given offset is outside the structure")); + + /* Ensure the requested data type at the offset is inside the structure. */ + offset = (grub_uint8_t)option; + if (offset + field_extractors[field_type].field_length > structure[1]) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("the field ends outside the structure")); + + /* Format the requested structure field into a readable string. */ + value = field_extractors[field_type].format (structure, offset); + if (value == NULL) + return grub_error (GRUB_ERR_IO, + N_("failed to retrieve the structure field")); + + if (state[LINUX_OPT].set) + value = modified_value = linux_string (value); + + /* Store or print the formatted value. */ + if (state[SETTER_OPT].set) + grub_env_set (state[SETTER_OPT].arg, value); + else + grub_printf ("%s\n", value); + + grub_free(modified_value); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(smbios) +{ + struct grub_smbios_eps3 *eps3; + struct grub_smbios_eps *eps; + + if ((eps3 = grub_smbios_get_eps3 ())) + { + table_desc.start = (grub_addr_t)eps3->table_address; + table_desc.end = table_desc.start + eps3->maximum_table_length; + table_desc.structures = 0; /* SMBIOS3 drops the structure count. */ + } + else if ((eps = grub_smbios_get_eps ())) + { + table_desc.start = (grub_addr_t)eps->intermediate.table_address; + table_desc.end = table_desc.start + eps->intermediate.table_length; + table_desc.structures = eps->intermediate.structures; + } + + cmd = grub_register_extcmd ("smbios", grub_cmd_smbios, 0, + N_("[-t type] [-h handle] [-m match] " + "(-b|-w|-d|-q|-s|-u) offset " + "[--set variable]"), + N_("Retrieve SMBIOS information."), options); +} + +GRUB_MOD_FINI(smbios) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/syslinuxcfg.c b/grub-core/commands/syslinuxcfg.c new file mode 100644 index 000000000..7be28fada --- /dev/null +++ b/grub-core/commands/syslinuxcfg.c @@ -0,0 +1,217 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Helper for syslinux_file. */ +static grub_err_t +syslinux_file_getline (char **line, int cont __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + *line = 0; + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option options[] = + { + {"root", 'r', 0, + N_("root directory of the syslinux disk [default=/]."), + N_("DIR"), ARG_TYPE_STRING}, + {"cwd", 'c', 0, + N_("current directory of syslinux [default is parent directory of input file]."), + N_("DIR"), ARG_TYPE_STRING}, + {"isolinux", 'i', 0, N_("assume input is an isolinux configuration file."), 0, 0}, + {"pxelinux", 'p', 0, N_("assume input is a pxelinux configuration file."), 0, 0}, + {"syslinux", 's', 0, N_("assume input is a syslinux configuration file."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +enum + { + OPTION_ROOT, + OPTION_CWD, + OPTION_ISOLINUX, + OPTION_PXELINUX, + OPTION_SYSLINUX + }; + +static grub_err_t +syslinux_file (grub_extcmd_context_t ctxt, const char *filename) +{ + char *result; + const char *root = ctxt->state[OPTION_ROOT].set ? ctxt->state[OPTION_ROOT].arg : "/"; + const char *cwd = ctxt->state[OPTION_CWD].set ? ctxt->state[OPTION_CWD].arg : NULL; + grub_syslinux_flavour_t flav = GRUB_SYSLINUX_UNKNOWN; + char *cwdf = NULL; + grub_menu_t menu; + + if (ctxt->state[OPTION_ISOLINUX].set) + flav = GRUB_SYSLINUX_ISOLINUX; + if (ctxt->state[OPTION_PXELINUX].set) + flav = GRUB_SYSLINUX_PXELINUX; + if (ctxt->state[OPTION_SYSLINUX].set) + flav = GRUB_SYSLINUX_SYSLINUX; + + if (!cwd) + { + char *p; + cwdf = grub_strdup (filename); + if (!cwdf) + return grub_errno; + p = grub_strrchr (cwdf, '/'); + if (!p) + { + grub_free (cwdf); + cwd = "/"; + cwdf = NULL; + } + else + { + *p = '\0'; + cwd = cwdf; + } + } + + grub_dprintf ("syslinux", + "transforming syslinux config %s, root = %s, cwd = %s\n", + filename, root, cwd); + + result = grub_syslinux_config_file (root, root, cwd, cwd, filename, flav); + if (!result) + return grub_errno; + + grub_dprintf ("syslinux", "syslinux config transformed\n"); + + menu = grub_env_get_menu (); + if (! menu) + { + menu = grub_zalloc (sizeof (*menu)); + if (! menu) + { + grub_free (result); + return grub_errno; + } + + grub_env_set_menu (menu); + } + + grub_normal_parse_line (result, syslinux_file_getline, NULL); + grub_print_error (); + grub_free (result); + grub_free (cwdf); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_syslinux_source (grub_extcmd_context_t ctxt, + int argc, char **args) +{ + int new_env, extractor; + grub_err_t ret; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + extractor = (ctxt->extcmd->cmd->name[0] == 'e'); + new_env = (ctxt->extcmd->cmd->name[extractor ? (sizeof ("extract_syslinux_entries_") - 1) + : (sizeof ("syslinux_") - 1)] == 'c'); + + if (new_env) + grub_cls (); + + if (new_env && !extractor) + grub_env_context_open (); + if (extractor) + grub_env_extractor_open (!new_env); + + ret = syslinux_file (ctxt, args[0]); + + if (new_env) + { + grub_menu_t menu; + menu = grub_env_get_menu (); + if (menu && menu->size) + grub_show_menu (menu, 1, 0); + if (!extractor) + grub_env_context_close (); + } + if (extractor) + grub_env_extractor_close (!new_env); + + return ret; +} + + +static grub_extcmd_t cmd_source, cmd_configfile; +static grub_extcmd_t cmd_source_extract, cmd_configfile_extract; + +GRUB_MOD_INIT(syslinuxcfg) +{ + cmd_source + = grub_register_extcmd ("syslinux_source", + grub_cmd_syslinux_source, 0, + N_("FILE"), + /* TRANSLATORS: "syslinux config" means + "config as used by syslinux". */ + N_("Execute syslinux config in same context"), + options); + cmd_configfile + = grub_register_extcmd ("syslinux_configfile", + grub_cmd_syslinux_source, 0, + N_("FILE"), + N_("Execute syslinux config in new context"), + options); + cmd_source_extract + = grub_register_extcmd ("extract_syslinux_entries_source", + grub_cmd_syslinux_source, 0, + N_("FILE"), + N_("Execute syslinux config in same context taking only menu entries"), + options); + cmd_configfile_extract + = grub_register_extcmd ("extract_syslinux_entries_configfile", + grub_cmd_syslinux_source, 0, + N_("FILE"), + N_("Execute syslinux config in new context taking only menu entries"), + options); +} + +GRUB_MOD_FINI(syslinuxcfg) +{ + grub_unregister_extcmd (cmd_source); + grub_unregister_extcmd (cmd_configfile); + grub_unregister_extcmd (cmd_source_extract); + grub_unregister_extcmd (cmd_configfile_extract); +} diff --git a/grub-core/commands/terminal.c b/grub-core/commands/terminal.c new file mode 100644 index 000000000..78a140099 --- /dev/null +++ b/grub-core/commands/terminal.c @@ -0,0 +1,285 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_term_autoload *grub_term_input_autoload = NULL; +struct grub_term_autoload *grub_term_output_autoload = NULL; + +struct abstract_terminal +{ + struct abstract_terminal *next; + struct abstract_terminal *prev; + const char *name; + grub_err_t (*init) (struct abstract_terminal *term); + grub_err_t (*fini) (struct abstract_terminal *term); +}; + +static grub_err_t +handle_command (int argc, char **args, struct abstract_terminal **enabled, + struct abstract_terminal **disabled, + struct grub_term_autoload *autoloads, + const char *active_str, + const char *available_str) +{ + int i; + struct abstract_terminal *term; + struct grub_term_autoload *aut; + + if (argc == 0) + { + grub_puts_ (active_str); + for (term = *enabled; term; term = term->next) + grub_printf ("%s ", term->name); + grub_printf ("\n"); + grub_puts_ (available_str); + for (term = *disabled; term; term = term->next) + grub_printf ("%s ", term->name); + /* This is quadratic but we don't expect mode than 30 terminal + modules ever. */ + for (aut = autoloads; aut; aut = aut->next) + { + for (term = *disabled; term; term = term->next) + if (grub_strcmp (term->name, aut->name) == 0 + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (term->name, aut->name, + grub_strlen (aut->name) - 1) == 0)) + break; + if (!term) + for (term = *enabled; term; term = term->next) + if (grub_strcmp (term->name, aut->name) == 0 + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (term->name, aut->name, + grub_strlen (aut->name) - 1) == 0)) + break; + if (!term) + grub_printf ("%s ", aut->name); + } + grub_printf ("\n"); + return GRUB_ERR_NONE; + } + i = 0; + + if (grub_strcmp (args[0], "--append") == 0 + || grub_strcmp (args[0], "--remove") == 0) + i++; + + if (i == argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("no terminal specified")); + + for (; i < argc; i++) + { + int again = 0; + while (1) + { + for (term = *disabled; term; term = term->next) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (term == 0) + for (term = *enabled; term; term = term->next) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (term) + break; + if (again) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("terminal `%s' isn't found"), + args[i]); + for (aut = autoloads; aut; aut = aut->next) + if (grub_strcmp (args[i], aut->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", aut->name) == 0) + || (aut->name[0] && aut->name[grub_strlen (aut->name) - 1] == '*' + && grub_memcmp (args[i], aut->name, + grub_strlen (aut->name) - 1) == 0)) + { + grub_dl_t mod; + mod = grub_dl_load (aut->modname); + if (mod) + grub_dl_ref (mod); + grub_errno = GRUB_ERR_NONE; + break; + } + if (grub_memcmp (args[i], "serial_usb", + sizeof ("serial_usb") - 1) == 0 + && grub_term_poll_usb) + { + grub_term_poll_usb (1); + again = 1; + continue; + } + if (!aut) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("terminal `%s' isn't found"), + args[i]); + again = 1; + } + } + + if (grub_strcmp (args[0], "--append") == 0) + { + for (i = 1; i < argc; i++) + { + for (term = *disabled; term; term = term->next) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (term) + { + if (term->init && term->init (term) != GRUB_ERR_NONE) + return grub_errno; + + grub_list_remove (GRUB_AS_LIST (term)); + grub_list_push (GRUB_AS_LIST_P (enabled), GRUB_AS_LIST (term)); + } + } + return GRUB_ERR_NONE; + } + + if (grub_strcmp (args[0], "--remove") == 0) + { + for (i = 1; i < argc; i++) + { + for (term = *enabled; term; term = term->next) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (term) + { + if (!term->next && term == *enabled) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "can't remove the last terminal"); + grub_list_remove (GRUB_AS_LIST (term)); + if (term->fini) + term->fini (term); + grub_list_push (GRUB_AS_LIST_P (disabled), GRUB_AS_LIST (term)); + } + } + return GRUB_ERR_NONE; + } + for (i = 0; i < argc; i++) + { + for (term = *disabled; term; term = term->next) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (term) + { + if (term->init && term->init (term) != GRUB_ERR_NONE) + return grub_errno; + + grub_list_remove (GRUB_AS_LIST (term)); + grub_list_push (GRUB_AS_LIST_P (enabled), GRUB_AS_LIST (term)); + } + } + + { + struct abstract_terminal *next; + for (term = *enabled; term; term = next) + { + next = term->next; + for (i = 0; i < argc; i++) + if (grub_strcmp (args[i], term->name) == 0 + || (grub_strcmp (args[i], "ofconsole") == 0 + && grub_strcmp ("console", term->name) == 0)) + break; + if (i == argc) + { + if (!term->next && term == *enabled) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "can't remove the last terminal"); + grub_list_remove (GRUB_AS_LIST (term)); + if (term->fini) + term->fini (term); + grub_list_push (GRUB_AS_LIST_P (disabled), GRUB_AS_LIST (term)); + } + } + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_terminal_input (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + (void) GRUB_FIELD_MATCH (grub_term_inputs, struct abstract_terminal *, next); + (void) GRUB_FIELD_MATCH (grub_term_inputs, struct abstract_terminal *, prev); + (void) GRUB_FIELD_MATCH (grub_term_inputs, struct abstract_terminal *, name); + (void) GRUB_FIELD_MATCH (grub_term_inputs, struct abstract_terminal *, init); + (void) GRUB_FIELD_MATCH (grub_term_inputs, struct abstract_terminal *, fini); + return handle_command (argc, args, + (struct abstract_terminal **) (void *) &grub_term_inputs, + (struct abstract_terminal **) (void *) &grub_term_inputs_disabled, + grub_term_input_autoload, + N_("Active input terminals:"), + N_("Available input terminals:")); +} + +static grub_err_t +grub_cmd_terminal_output (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + (void) GRUB_FIELD_MATCH (grub_term_outputs, struct abstract_terminal *, next); + (void) GRUB_FIELD_MATCH (grub_term_outputs, struct abstract_terminal *, prev); + (void) GRUB_FIELD_MATCH (grub_term_outputs, struct abstract_terminal *, name); + (void) GRUB_FIELD_MATCH (grub_term_outputs, struct abstract_terminal *, init); + (void) GRUB_FIELD_MATCH (grub_term_outputs, struct abstract_terminal *, fini); + return handle_command (argc, args, + (struct abstract_terminal **) (void *) &grub_term_outputs, + (struct abstract_terminal **) (void *) &grub_term_outputs_disabled, + grub_term_output_autoload, + N_("Active output terminals:"), + N_("Available output terminals:")); +} + +static grub_command_t cmd_terminal_input, cmd_terminal_output; + +GRUB_MOD_INIT(terminal) +{ + cmd_terminal_input = + grub_register_command ("terminal_input", grub_cmd_terminal_input, + N_("[--append|--remove] " + "[TERMINAL1] [TERMINAL2] ..."), + N_("List or select an input terminal.")); + cmd_terminal_output = + grub_register_command ("terminal_output", grub_cmd_terminal_output, + N_("[--append|--remove] " + "[TERMINAL1] [TERMINAL2] ..."), + N_("List or select an output terminal.")); +} + +GRUB_MOD_FINI(terminal) +{ + grub_unregister_command (cmd_terminal_input); + grub_unregister_command (cmd_terminal_output); +} diff --git a/commands/test.c b/grub-core/commands/test.c similarity index 57% rename from commands/test.c rename to grub-core/commands/test.c index 6995165cf..b585c3d70 100644 --- a/commands/test.c +++ b/grub-core/commands/test.c @@ -27,123 +27,139 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Set a limit on recursion to avoid stack overflow. */ +#define MAX_TEST_RECURSION_DEPTH 100 + /* A simple implementation for signed numbers. */ static int -grub_strtosl (char *arg, char **end, int base) +grub_strtosl (char *arg, const char ** const end, int base) { if (arg[0] == '-') return -grub_strtoul (arg + 1, end, base); return grub_strtoul (arg, end, base); } -/* Parse a test expression starting from *argn. */ -static int -test_parse (char **args, int *argn, int argc) +/* Context for test_parse. */ +struct test_parse_ctx { - int ret = 0, discard = 0, invert = 0; + int invert; + int or, and; int file_exists; struct grub_dirhook_info file_info; + char *filename; +}; - auto void update_val (int val); - auto void get_fileinfo (char *pathname); +/* Take care of discarding and inverting. */ +static void +update_val (int val, struct test_parse_ctx *ctx) +{ + ctx->and = ctx->and && (ctx->invert ? ! val : val); + ctx->invert = 0; +} - /* Take care of discarding and inverting. */ - void update_val (int val) - { - if (! discard) - ret = invert ? ! val : val; - invert = discard = 0; - } +/* A hook for iterating directories. */ +static int +find_file (const char *cur_filename, const struct grub_dirhook_info *info, + void *data) +{ + struct test_parse_ctx *ctx = data; - /* Check if file exists and fetch its information. */ - void get_fileinfo (char *path) - { - char *filename, *pathname; - char *device_name; - grub_fs_t fs; - grub_device_t dev; - - /* A hook for iterating directories. */ - auto int find_file (const char *cur_filename, - const struct grub_dirhook_info *info); - int find_file (const char *cur_filename, - const struct grub_dirhook_info *info) + if ((info->case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename) + : grub_strcmp (cur_filename, ctx->filename)) == 0) { - if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename) - : grub_strcmp (cur_filename, filename)) == 0) - { - file_info = *info; - file_exists = 1; - return 1; - } - return 0; + ctx->file_info = *info; + ctx->file_exists = 1; + return 1; + } + return 0; +} + +/* Check if file exists and fetch its information. */ +static void +get_fileinfo (char *path, struct test_parse_ctx *ctx) +{ + char *pathname; + char *device_name; + grub_fs_t fs; + grub_device_t dev; + + ctx->file_exists = 0; + device_name = grub_file_get_device_name (path); + dev = grub_device_open (device_name); + if (! dev) + { + grub_free (device_name); + return; } - file_exists = 0; - device_name = grub_file_get_device_name (path); - dev = grub_device_open (device_name); - if (! dev) - { - grub_free (device_name); - return; - } + fs = grub_fs_probe (dev); + if (! fs) + { + grub_free (device_name); + grub_device_close (dev); + return; + } - fs = grub_fs_probe (dev); - if (! fs) - { - grub_free (device_name); - grub_device_close (dev); - return; - } + pathname = grub_strchr (path, ')'); + if (! pathname) + pathname = path; + else + pathname++; - pathname = grub_strchr (path, ')'); - if (! pathname) - pathname = path; - else - pathname++; + /* Remove trailing '/'. */ + while (*pathname && pathname[grub_strlen (pathname) - 1] == '/') + pathname[grub_strlen (pathname) - 1] = 0; - /* Remove trailing '/'. */ - while (*pathname && pathname[grub_strlen (pathname) - 1] == '/') - pathname[grub_strlen (pathname) - 1] = 0; + /* Split into path and filename. */ + ctx->filename = grub_strrchr (pathname, '/'); + if (! ctx->filename) + { + path = grub_strdup ("/"); + ctx->filename = pathname; + } + else + { + ctx->filename++; + path = grub_strdup (pathname); + path[ctx->filename - pathname] = 0; + } - /* Split into path and filename. */ - filename = grub_strrchr (pathname, '/'); - if (! filename) - { - path = grub_strdup ("/"); - filename = pathname; - } - else - { - filename++; - path = grub_strdup (pathname); - path[filename - pathname] = 0; - } + /* It's the whole device. */ + if (! *pathname) + { + ctx->file_exists = 1; + grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info)); + /* Root is always a directory. */ + ctx->file_info.dir = 1; - /* It's the whole device. */ - if (! *pathname) - { - file_exists = 1; - grub_memset (&file_info, 0, sizeof (file_info)); - /* Root is always a directory. */ - file_info.dir = 1; + /* Fetch writing time. */ + ctx->file_info.mtimeset = 0; + if (fs->fs_mtime) + { + if (! fs->fs_mtime (dev, &ctx->file_info.mtime)) + ctx->file_info.mtimeset = 1; + grub_errno = GRUB_ERR_NONE; + } + } + else + (fs->fs_dir) (dev, path, find_file, ctx); - /* Fetch writing time. */ - file_info.mtimeset = 0; - if (fs->mtime) - { - if (! fs->mtime (dev, &file_info.mtime)) - file_info.mtimeset = 1; - grub_errno = GRUB_ERR_NONE; - } - } - else - (fs->dir) (dev, path, find_file); + grub_device_close (dev); + grub_free (path); + grub_free (device_name); +} - grub_device_close (dev); - grub_free (path); - grub_free (device_name); - } +/* Parse a test expression starting from *argn. */ +static int +test_parse (char **args, int *argn, int argc, int *depth) +{ + struct test_parse_ctx ctx = { + .and = 1, + .or = 0, + .invert = 0 + }; /* Here we have the real parsing. */ while (*argn < argc) @@ -155,14 +171,16 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "=") == 0 || grub_strcmp (args[*argn + 1], "==") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0, + &ctx); (*argn) += 3; continue; } if (grub_strcmp (args[*argn + 1], "!=") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0, + &ctx); (*argn) += 3; continue; } @@ -170,28 +188,32 @@ test_parse (char **args, int *argn, int argc) /* GRUB extension: lexicographical sorting. */ if (grub_strcmp (args[*argn + 1], "<") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0, + &ctx); (*argn) += 3; continue; } if (grub_strcmp (args[*argn + 1], "<=") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0, + &ctx); (*argn) += 3; continue; } if (grub_strcmp (args[*argn + 1], ">") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0, + &ctx); (*argn) += 3; continue; } if (grub_strcmp (args[*argn + 1], ">=") == 0) { - update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0); + update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0, + &ctx); (*argn) += 3; continue; } @@ -200,7 +222,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-eq") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - == grub_strtosl (args[*argn + 2], 0, 0)); + == grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -208,7 +230,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-ge") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - >= grub_strtosl (args[*argn + 2], 0, 0)); + >= grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -216,7 +238,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-gt") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - > grub_strtosl (args[*argn + 2], 0, 0)); + > grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -224,7 +246,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-le") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - <= grub_strtosl (args[*argn + 2], 0, 0)); + <= grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -232,7 +254,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-lt") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - < grub_strtosl (args[*argn + 2], 0, 0)); + < grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -240,7 +262,7 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-ne") == 0) { update_val (grub_strtosl (args[*argn], 0, 0) - != grub_strtosl (args[*argn + 2], 0, 0)); + != grub_strtosl (args[*argn + 2], 0, 0), &ctx); (*argn) += 3; continue; } @@ -263,10 +285,10 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn + 1], "-pgt") == 0) update_val (grub_strtoul (args[*argn] + i, 0, 0) - > grub_strtoul (args[*argn + 2] + i, 0, 0)); + > grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx); else update_val (grub_strtoul (args[*argn] + i, 0, 0) - < grub_strtoul (args[*argn + 2] + i, 0, 0)); + < grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx); (*argn) += 3; continue; } @@ -281,22 +303,24 @@ test_parse (char **args, int *argn, int argc) int bias = 0; /* Fetch fileinfo. */ - get_fileinfo (args[*argn]); - file1 = file_info; - file1exists = file_exists; - get_fileinfo (args[*argn + 2]); + get_fileinfo (args[*argn], &ctx); + file1 = ctx.file_info; + file1exists = ctx.file_exists; + get_fileinfo (args[*argn + 2], &ctx); if (args[*argn + 1][3]) bias = grub_strtosl (args[*argn + 1] + 3, 0, 0); if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0) - update_val ((file1exists && ! file_exists) - || (file1.mtimeset && file_info.mtimeset - && file1.mtime + bias > file_info.mtime)); + update_val ((file1exists && ! ctx.file_exists) + || (file1.mtimeset && ctx.file_info.mtimeset + && file1.mtime + bias > ctx.file_info.mtime), + &ctx); else - update_val ((! file1exists && file_exists) - || (file1.mtimeset && file_info.mtimeset - && file1.mtime + bias < file_info.mtime)); + update_val ((! file1exists && ctx.file_exists) + || (file1.mtimeset && ctx.file_info.mtimeset + && file1.mtime + bias < ctx.file_info.mtime), + &ctx); (*argn) += 3; continue; } @@ -308,52 +332,53 @@ test_parse (char **args, int *argn, int argc) /* File tests. */ if (grub_strcmp (args[*argn], "-d") == 0) { - get_fileinfo (args[*argn + 1]); - update_val (file_exists && file_info.dir); + get_fileinfo (args[*argn + 1], &ctx); + update_val (ctx.file_exists && ctx.file_info.dir, &ctx); (*argn) += 2; - return ret; + continue; } if (grub_strcmp (args[*argn], "-e") == 0) { - get_fileinfo (args[*argn + 1]); - update_val (file_exists); + get_fileinfo (args[*argn + 1], &ctx); + update_val (ctx.file_exists, &ctx); (*argn) += 2; - return ret; + continue; } if (grub_strcmp (args[*argn], "-f") == 0) { - get_fileinfo (args[*argn + 1]); + get_fileinfo (args[*argn + 1], &ctx); /* FIXME: check for other types. */ - update_val (file_exists && ! file_info.dir); + update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx); (*argn) += 2; - return ret; + continue; } if (grub_strcmp (args[*argn], "-s") == 0) { grub_file_t file; - file = grub_file_open (args[*argn + 1]); - update_val (file && (grub_file_size (file) != 0)); + file = grub_file_open (args[*argn + 1], GRUB_FILE_TYPE_GET_SIZE + | GRUB_FILE_TYPE_NO_DECOMPRESS); + update_val (file && (grub_file_size (file) != 0), &ctx); if (file) grub_file_close (file); grub_errno = GRUB_ERR_NONE; (*argn) += 2; - return ret; + continue; } /* String tests. */ if (grub_strcmp (args[*argn], "-n") == 0) { - update_val (args[*argn + 1][0]); + update_val (args[*argn + 1][0], &ctx); (*argn) += 2; continue; } if (grub_strcmp (args[*argn], "-z") == 0) { - update_val (! args[*argn + 1][0]); + update_val (! args[*argn + 1][0], &ctx); (*argn) += 2; continue; } @@ -365,42 +390,51 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn], ")") == 0) { (*argn)++; - return ret; + if (*depth > 0) + (*depth)--; + + return ctx.or || ctx.and; } /* Recursively invoke if parenthesis. */ if (grub_strcmp (args[*argn], "(") == 0) { (*argn)++; - update_val (test_parse (args, argn, argc)); + + if (++(*depth) > MAX_TEST_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded")); + depth--; + return ctx.or || ctx.and; + } + + update_val (test_parse (args, argn, argc, depth), &ctx); continue; } if (grub_strcmp (args[*argn], "!") == 0) { - invert = ! invert; + ctx.invert = ! ctx.invert; (*argn)++; continue; } if (grub_strcmp (args[*argn], "-a") == 0) { - /* If current value is 0 second value is to be discarded. */ - discard = ! ret; (*argn)++; continue; } if (grub_strcmp (args[*argn], "-o") == 0) { - /* If current value is 1 second value is to be discarded. */ - discard = ret; + ctx.or = ctx.or || ctx.and; + ctx.and = 1; (*argn)++; continue; } /* No test found. Interpret if as just a string. */ - update_val (args[*argn][0]); + update_val (args[*argn][0], &ctx); (*argn)++; } - return ret; + return ctx.or || ctx.and; } static grub_err_t @@ -408,12 +442,13 @@ grub_cmd_test (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { int argn = 0; + int depth = 0; if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0) argc--; - return test_parse (args, &argn, argc) ? GRUB_ERR_NONE - : grub_error (GRUB_ERR_TEST_FAILURE, "false"); + return test_parse (args, &argn, argc, &depth) ? GRUB_ERR_NONE + : grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } static grub_command_t cmd_1, cmd_2; @@ -422,8 +457,10 @@ GRUB_MOD_INIT(test) { cmd_1 = grub_register_command ("[", grub_cmd_test, N_("EXPRESSION ]"), N_("Evaluate an expression.")); + cmd_1->flags |= GRUB_COMMAND_FLAG_EXTRACTOR; cmd_2 = grub_register_command ("test", grub_cmd_test, N_("EXPRESSION"), N_("Evaluate an expression.")); + cmd_2->flags |= GRUB_COMMAND_FLAG_EXTRACTOR; } GRUB_MOD_FINI(test) diff --git a/grub-core/commands/testload.c b/grub-core/commands/testload.c new file mode 100644 index 000000000..76a8af94c --- /dev/null +++ b/grub-core/commands/testload.c @@ -0,0 +1,172 @@ +/* testload.c - load the same file in multiple ways */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Helper for grub_cmd_testload. */ +static grub_err_t +read_progress (grub_disk_addr_t sector __attribute__ ((unused)), + unsigned offset __attribute__ ((unused)), + unsigned len, + char *buf __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + for (; len >= GRUB_DISK_SECTOR_SIZE; len -= GRUB_DISK_SECTOR_SIZE) + grub_xputs ("."); + if (len) + grub_xputs ("."); + grub_refresh (); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_testload (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + char *buf; + grub_size_t size; + grub_off_t pos; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_TESTLOAD); + if (! file) + return grub_errno; + + size = grub_file_size (file) & ~(GRUB_DISK_SECTOR_SIZE - 1); + if (size == 0) + { + grub_file_close (file); + return GRUB_ERR_NONE; + } + + buf = grub_malloc (size); + if (! buf) + goto fail; + + grub_printf ("Reading %s sequentially", argv[0]); + file->read_hook = read_progress; + if (grub_file_read (file, buf, size) != (grub_ssize_t) size) + goto fail; + grub_printf (" Done.\n"); + + /* Read sequentially again. */ + grub_printf ("Reading %s sequentially again", argv[0]); + grub_file_seek (file, 0); + + for (pos = 0; pos < size;) + { + char sector[GRUB_DISK_SECTOR_SIZE]; + grub_size_t curlen = GRUB_DISK_SECTOR_SIZE; + + if (curlen > size - pos) + curlen = size - pos; + + if (grub_file_read (file, sector, curlen) + != (grub_ssize_t) curlen) + goto fail; + + if (grub_memcmp (sector, buf + pos, curlen) != 0) + { + grub_printf ("\nDiffers in %lld\n", (unsigned long long) pos); + goto fail; + } + pos += curlen; + } + grub_printf (" Done.\n"); + + /* Read backwards and compare. */ + grub_printf ("Reading %s backwards", argv[0]); + pos = size; + while (pos > 0) + { + char sector[GRUB_DISK_SECTOR_SIZE]; + + if (pos >= GRUB_DISK_SECTOR_SIZE) + pos -= GRUB_DISK_SECTOR_SIZE; + else + pos = 0; + + grub_file_seek (file, pos); + + if (grub_file_read (file, sector, GRUB_DISK_SECTOR_SIZE) + != GRUB_DISK_SECTOR_SIZE) + goto fail; + + if (grub_memcmp (sector, buf + pos, GRUB_DISK_SECTOR_SIZE) != 0) + { + int i; + + grub_printf ("\nDiffers in %lld\n", (unsigned long long) pos); + + for (i = 0; i < GRUB_DISK_SECTOR_SIZE; i++) + { + grub_printf ("%02x ", buf[pos + i]); + if ((i & 15) == 15) + grub_printf ("\n"); + } + + if (i) + grub_refresh (); + + goto fail; + } + } + grub_printf (" Done.\n"); + + return GRUB_ERR_NONE; + + fail: + + grub_file_close (file); + grub_free (buf); + + if (!grub_errno) + grub_error (GRUB_ERR_IO, "bad read"); + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(testload) +{ + cmd = + grub_register_command ("testload", grub_cmd_testload, + N_("FILE"), + N_("Load the same file in multiple ways.")); +} + +GRUB_MOD_FINI(testload) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/testspeed.c b/grub-core/commands/testspeed.c new file mode 100644 index 000000000..c13a9b8d8 --- /dev/null +++ b/grub-core/commands/testspeed.c @@ -0,0 +1,115 @@ +/* testspeed.c - Command to test file read speed */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define DEFAULT_BLOCK_SIZE 65536 + +static const struct grub_arg_option options[] = + { + {"size", 's', 0, N_("Specify size for each read operation"), 0, ARG_TYPE_INT}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +grub_cmd_testspeed (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + grub_uint64_t start; + grub_uint64_t end; + grub_ssize_t block_size; + grub_disk_addr_t total_size; + char *buffer; + grub_file_t file; + grub_uint64_t whole, fraction; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + + block_size = (state[0].set) ? + grub_strtoul (state[0].arg, 0, 0) : DEFAULT_BLOCK_SIZE; + + if (block_size <= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid block size")); + + buffer = grub_malloc (block_size); + if (buffer == NULL) + return grub_errno; + + file = grub_file_open (args[0], GRUB_FILE_TYPE_TESTLOAD); + if (file == NULL) + goto quit; + + total_size = 0; + start = grub_get_time_ms (); + while (1) + { + grub_ssize_t size = grub_file_read (file, buffer, block_size); + if (size <= 0) + break; + total_size += size; + } + end = grub_get_time_ms (); + grub_file_close (file); + + grub_printf_ (N_("File size: %s\n"), + grub_get_human_size (total_size, GRUB_HUMAN_SIZE_NORMAL)); + whole = grub_divmod64 (end - start, 1000, &fraction); + grub_printf_ (N_("Elapsed time: %d.%03d s \n"), + (unsigned) whole, + (unsigned) fraction); + + if (end != start) + { + grub_uint64_t speed = + grub_divmod64 (total_size * 100ULL * 1000ULL, end - start, 0); + + grub_printf_ (N_("Speed: %s \n"), + grub_get_human_size (speed, + GRUB_HUMAN_SIZE_SPEED)); + } + + quit: + grub_free (buffer); + + return grub_errno; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(testspeed) +{ + cmd = grub_register_extcmd ("testspeed", grub_cmd_testspeed, 0, N_("[-s SIZE] FILENAME"), + N_("Test file read speed."), + options); +} + +GRUB_MOD_FINI(testspeed) +{ + grub_unregister_extcmd (cmd); +} diff --git a/grub-core/commands/time.c b/grub-core/commands/time.c new file mode 100644 index 000000000..40d496e7b --- /dev/null +++ b/grub-core/commands/time.c @@ -0,0 +1,68 @@ +/* echo.c - Command to display a line of text */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +static grub_err_t +grub_cmd_time (grub_command_t ctxt __attribute__ ((unused)), + int argc, char **args) +{ + grub_command_t cmd; + grub_uint32_t start; + grub_uint32_t end; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("no command is specified")); + + cmd = grub_command_find (args[0]); + + if (!cmd) + return grub_error (GRUB_ERR_UNKNOWN_COMMAND, N_("can't find command `%s'"), + args[0]); + + start = grub_get_time_ms (); + (cmd->func) (cmd, argc - 1, &args[1]); + end = grub_get_time_ms (); + + grub_printf_ (N_("Elapsed time: %d.%03d seconds \n"), (end - start) / 1000, + (end - start) % 1000); + + return grub_errno; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(time) +{ + cmd = grub_register_command ("time", grub_cmd_time, + N_("COMMAND [ARGS]"), + N_("Measure time used by COMMAND")); +} + +GRUB_MOD_FINI(time) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c new file mode 100644 index 000000000..dde74ab83 --- /dev/null +++ b/grub-core/commands/tpm.c @@ -0,0 +1,125 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + * + * Core TPM support code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_tpm_verify_init (grub_file_t io, + enum grub_file_type type __attribute__ ((unused)), + void **context, enum grub_verify_flags *flags) +{ + *context = io->name; + *flags |= GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + + /* + * The loopback image is mapped as a disk allowing it to function like + * a block device. However, we measure files read from the block device + * not the device itself. For example, we don't measure block devices like + * hd0 disk directly. This process is crucial to prevent out-of-memory + * errors as loopback images are inherently large. + */ + if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_LOOPBACK) + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_tpm_verify_write (void *context, void *buf, grub_size_t size) +{ + grub_err_t status = grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context); + + if (status == GRUB_ERR_NONE) + return GRUB_ERR_NONE; + + grub_dprintf ("tpm", "Measuring buffer failed: %d\n", status); + return grub_is_tpm_fail_fatal () ? status : GRUB_ERR_NONE; +} + +static grub_err_t +grub_tpm_verify_string (char *str, enum grub_verify_string_type type) +{ + const char *prefix = NULL; + char *description; + grub_err_t status; + + switch (type) + { + case GRUB_VERIFY_KERNEL_CMDLINE: + prefix = "kernel_cmdline: "; + break; + case GRUB_VERIFY_MODULE_CMDLINE: + prefix = "module_cmdline: "; + break; + case GRUB_VERIFY_COMMAND: + prefix = "grub_cmd: "; + break; + } + description = grub_malloc (grub_strlen (str) + grub_strlen (prefix) + 1); + if (!description) + return grub_errno; + grub_memcpy (description, prefix, grub_strlen (prefix)); + grub_memcpy (description + grub_strlen (prefix), str, + grub_strlen (str) + 1); + status = + grub_tpm_measure ((unsigned char *) str, grub_strlen (str), + GRUB_STRING_PCR, description); + grub_free (description); + if (status == GRUB_ERR_NONE) + return GRUB_ERR_NONE; + + grub_dprintf ("tpm", "Measuring string %s failed: %d\n", str, status); + return grub_is_tpm_fail_fatal () ? status : GRUB_ERR_NONE; +} + +struct grub_file_verifier grub_tpm_verifier = { + .name = "tpm", + .init = grub_tpm_verify_init, + .write = grub_tpm_verify_write, + .verify_string = grub_tpm_verify_string, +}; + +GRUB_MOD_INIT (tpm) +{ + /* + * Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(), + * it does seem to call it late enough in the initialization sequence so + * that whatever discovered "device nodes" before this GRUB_MOD_INIT() is + * called, enables the ibmvtpm driver to see the device nodes. + */ + if (!grub_tpm_present()) + return; + grub_verifier_register (&grub_tpm_verifier); +} + +GRUB_MOD_FINI (tpm) +{ + if (!grub_tpm_present()) + return; + grub_verifier_unregister (&grub_tpm_verifier); +} diff --git a/grub-core/commands/tpm2_key_protector/args.c b/grub-core/commands/tpm2_key_protector/args.c new file mode 100644 index 000000000..48c39de01 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/args.c @@ -0,0 +1,127 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#include +#include +#include + +#include "tpm2_args.h" + +grub_err_t +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, + grub_uint8_t *pcr_count) +{ + char *current_pcr = value; + char *next_pcr; + const char *pcr_end; + grub_uint64_t pcr; + grub_uint8_t i; + + if (grub_strlen (value) == 0) + return GRUB_ERR_BAD_ARGUMENT; + + *pcr_count = 0; + for (i = 0; i < TPM_MAX_PCRS; i++) + { + next_pcr = grub_strchr (current_pcr, ','); + if (next_pcr == current_pcr) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("empty entry in PCR list")); + if (next_pcr != NULL) + *next_pcr = '\0'; + + pcr = grub_strtoul (current_pcr, &pcr_end, 10); + if (*current_pcr == '\0' || *pcr_end != '\0') + return grub_error (GRUB_ERR_BAD_NUMBER, N_("entry '%s' in PCR list is not a number"), current_pcr); + + if (pcr > TPM_MAX_PCRS - 1) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("entry %llu in PCR list is too large to be a PCR number, PCR numbers range from 0 to %u"), (unsigned long long)pcr, TPM_MAX_PCRS - 1); + + pcrs[i] = (grub_uint8_t) pcr; + ++(*pcr_count); + + if (next_pcr == NULL) + break; + + current_pcr = next_pcr + 1; + if (*current_pcr == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("trailing comma at the end of PCR list")); + } + + if (i == TPM_MAX_PCRS) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("too many PCRs in PCR list, the maximum number of PCRs is %u"), TPM_MAX_PCRS); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_asymmetric (const char *value, + grub_srk_type_t *srk_type) +{ + if (grub_strcasecmp (value, "ECC") == 0 || + grub_strcasecmp (value, "ECC_NIST_P256") == 0) + { + srk_type->type = TPM_ALG_ECC; + srk_type->detail.ecc_curve = TPM_ECC_NIST_P256; + } + else if (grub_strcasecmp (value, "RSA") == 0 || + grub_strcasecmp (value, "RSA2048") == 0) + { + srk_type->type = TPM_ALG_RSA; + srk_type->detail.rsa_bits = 2048; + } + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid asymmetric key type"), value); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank) +{ + if (grub_strcasecmp (value, "SHA1") == 0) + *bank = TPM_ALG_SHA1; + else if (grub_strcasecmp (value, "SHA256") == 0) + *bank = TPM_ALG_SHA256; + else if (grub_strcasecmp (value, "SHA384") == 0) + *bank = TPM_ALG_SHA384; + else if (grub_strcasecmp (value, "SHA512") == 0) + *bank = TPM_ALG_SHA512; + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid PCR bank"), value); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle) +{ + grub_uint64_t num; + const char *str_end; + + num = grub_strtoul (value, &str_end, 0); + if (*value == '\0' || *str_end != '\0') + return grub_error (GRUB_ERR_BAD_NUMBER, N_("TPM handle value '%s' is not a number"), value); + + if (num > GRUB_UINT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value %llu is too large to be a TPM handle, TPM handles are unsigned 32-bit integers"), (unsigned long long)num); + + *handle = (TPM_HANDLE_t) num; + + return GRUB_ERR_NONE; +} diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c new file mode 100644 index 000000000..857f3753f --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -0,0 +1,1489 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tpm2_args.h" +#include "tpm2.h" +#include "tpm2key.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef enum tpm2_protector_mode +{ + TPM2_PROTECTOR_MODE_UNSET, + TPM2_PROTECTOR_MODE_SRK, + TPM2_PROTECTOR_MODE_NV +} tpm2_protector_mode_t; + +typedef enum tpm2_protector_options +{ + OPTION_MODE, + OPTION_PCRS, + OPTION_BANK, + OPTION_TPM2KEY, + OPTION_KEYFILE, + OPTION_SRK, + OPTION_ASYMMETRIC, + OPTION_NVINDEX +} tpm2_protector_options_t; + +typedef struct tpm2_protector_context +{ + tpm2_protector_mode_t mode; + grub_uint8_t pcrs[TPM_MAX_PCRS]; + grub_uint8_t pcr_count; + grub_srk_type_t srk_type; + TPM_ALG_ID_t bank; + const char *tpm2key; + const char *keyfile; + TPM_HANDLE_t srk; + TPM_HANDLE_t nv; +} tpm2_protector_context_t; + +static const struct grub_arg_option tpm2_protector_init_cmd_options[] = + { + /* Options for all modes */ + { + .longarg = "mode", + .shortarg = 'm', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Unseal key using SRK ('srk') (default) or retrieve it from an NV " + "Index ('nv')."), + }, + { + .longarg = "pcrs", + .shortarg = 'p', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Comma-separated list of PCRs used to authorize key release " + "e.g., '7,11'. (default: 7)"), + }, + { + .longarg = "bank", + .shortarg = 'b', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Bank of PCRs used to authorize key release: " + "SHA1, SHA256, SHA384 or SHA512. (default: SHA256)"), + }, + /* SRK-mode options */ + { + .longarg = "tpm2key", + .shortarg = 'T', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, path to the key file in the TPM 2.0 Key File format " + "to unseal using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.tpm)."), + }, + { + .longarg = "keyfile", + .shortarg = 'k', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, path to the key file in the raw format to unseal " + "using the TPM (e.g., (hd0,gpt1)/boot/grub2/sealed.key). " + "(Mainly for backward compatibility. Please use '--tpm2key'.)"), + }, + { + .longarg = "srk", + .shortarg = 's', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, the SRK handle if the SRK is persistent."), + }, + { + .longarg = "asymmetric", + .shortarg = 'a', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("In SRK mode, the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)" + "(default: ECC)"), + }, + /* NV Index-mode options */ + { + .longarg = "nvindex", + .shortarg = 'n', + .flags = 0, + .arg = NULL, + .type = ARG_TYPE_STRING, + .doc = + N_("Required in NV Index mode, the NV handle to read which must " + "readily exist on the TPM and which contains the key."), + }, + /* End of list */ + {0, 0, 0, 0, 0, 0} + }; + +static grub_extcmd_t tpm2_protector_init_cmd; +static grub_extcmd_t tpm2_protector_clear_cmd; +static tpm2_protector_context_t tpm2_protector_ctx = {0}; + +static grub_command_t tpm2_dump_pcr_cmd; + +static grub_err_t +tpm2_protector_srk_read_file (const char *filepath, void **buffer, grub_size_t *buffer_size) +{ + grub_file_t file; + grub_off_t file_size; + void *read_buffer; + grub_off_t read_n; + grub_err_t err; + + /* + * Using GRUB_FILE_TYPE_SIGNATURE ensures we do not hash the keyfile into PCR9 + * otherwise we'll never be able to predict the value of PCR9 at unseal time + */ + file = grub_file_open (filepath, GRUB_FILE_TYPE_SIGNATURE); + if (file == NULL) + { + /* Push errno from grub_file_open() into the error message stack */ + grub_error_push(); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("could not open file: %s"), filepath); + goto error; + } + + file_size = grub_file_size (file); + if (file_size == 0) + { + err = grub_error (GRUB_ERR_OUT_OF_RANGE, N_("could not read file size: %s"), filepath); + goto error; + } + + read_buffer = grub_malloc (file_size); + if (read_buffer == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("could not allocate buffer for %s"), filepath); + goto error; + } + + read_n = grub_file_read (file, read_buffer, file_size); + if (read_n != file_size) + { + grub_free (read_buffer); + err = grub_error (GRUB_ERR_FILE_READ_ERROR, N_("could not retrieve file contents: %s"), filepath); + goto error; + } + + *buffer = read_buffer; + *buffer_size = file_size; + + err = GRUB_ERR_NONE; + + error: + if (file != NULL) + grub_file_close (file); + + return err; +} + +/* Check if the data is in TPM 2.0 Key File format */ +static bool +tpm2_protector_is_tpm2key (grub_uint8_t *buffer, grub_size_t buffer_size) +{ + /* id-sealedkey OID (2.23.133.10.1.5) in DER */ + const grub_uint8_t sealed_key_oid[] = {0x06, 0x06, 0x67, 0x81, 0x05, 0x0a}; + grub_size_t skip = 0; + + /* Need at least the first two bytes to check the tag and the length */ + if (buffer_size < 2) + return false; + + /* The first byte is always 0x30 (SEQUENCE). */ + if (buffer[0] != 0x30) + return false; + + /* + * Get the bytes of the length + * + * If the bit 8 of the second byte is 0, it is in the short form, so the second byte + * alone represents the length. Thus, the first two bytes are skipped. + * + * Otherwise, it is in the long form, and bits 1~7 indicate how many more bytes are in + * the length field, so we skip the first two bytes plus the bytes for the length. + */ + if ((buffer[1] & 0x80) == 0) + skip = 2; + else + skip = (buffer[1] & 0x7F) + 2; + + /* Make sure the buffer is large enough to contain id-sealedkey OID */ + if (buffer_size < skip + sizeof (sealed_key_oid)) + return false; + + /* Check id-sealedkey OID */ + if (grub_memcmp (buffer + skip, sealed_key_oid, sizeof (sealed_key_oid)) != 0) + return false; + + return true; +} + +static grub_err_t +tpm2_protector_unmarshal_raw (void *sealed_key, + grub_size_t sealed_key_size, + tpm2_sealed_key_t *sk) +{ + struct grub_tpm2_buffer buf; + + grub_tpm2_buffer_init (&buf); + if (sealed_key_size > buf.cap) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap); + + grub_memcpy (buf.data, sealed_key, sealed_key_size); + buf.size = sealed_key_size; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); + grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private); + + if (buf.error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM wire key file")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_unmarshal_tpm2key (void *sealed_key, + grub_size_t sealed_key_size, + tpm2key_policy_t *policy_seq, + tpm2key_authpolicy_t *authpol_seq, + grub_uint8_t *rsaparent, + grub_uint32_t *parent, + tpm2_sealed_key_t *sk) +{ + asn1_node tpm2key = NULL; + grub_uint8_t rsaparent_tmp; + grub_uint32_t parent_tmp; + void *sealed_pub = NULL; + grub_size_t sealed_pub_size; + void *sealed_priv = NULL; + grub_size_t sealed_priv_size; + struct grub_tpm2_buffer buf; + grub_err_t err; + + /* + * Start to parse the tpm2key file + * TPMKey ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + * secret [2] EXPLICIT OCTET STRING OPTIONAL, + * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + * description [4] EXPLICIT UTF8String OPTIONAL, + * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + * parent INTEGER, + * pubkey OCTET STRING, + * privkey OCTET STRING + * } + */ + err = grub_tpm2key_start_parsing (&tpm2key, sealed_key, sealed_key_size); + if (err != GRUB_ERR_NONE) + return err; + + /* + * Retrieve the policy sequence from 'policy' + * policy_seq will be NULL when 'policy' is not available + */ + err = grub_tpm2key_get_policy_seq (tpm2key, policy_seq); + if (err != GRUB_ERR_NONE) + goto error; + + /* + * Retrieve the authpolicy sequence from 'authPolicy' + * authpol_seq will be NULL when 'authPolicy' is not available + */ + err = grub_tpm2key_get_authpolicy_seq (tpm2key, authpol_seq); + if (err != GRUB_ERR_NONE) + goto error; + + /* Retrieve rsaParent */ + err = grub_tpm2key_get_rsaparent (tpm2key, &rsaparent_tmp); + if (err != GRUB_ERR_NONE) + goto error; + + *rsaparent = rsaparent_tmp; + + /* Retrieve the parent handle */ + err = grub_tpm2key_get_parent (tpm2key, &parent_tmp); + if (err != GRUB_ERR_NONE) + goto error; + + /* The parent handle should be either PERMANENT or PERSISTENT. */ + if (!TPM_HT_IS_PERMANENT (parent_tmp) && !TPM_HT_IS_PERSISTENT (parent_tmp)) + { + err = GRUB_ERR_OUT_OF_RANGE; + goto error; + } + + *parent = parent_tmp; + + /* Retrieve the public part of the sealed key */ + err = grub_tpm2key_get_pubkey (tpm2key, &sealed_pub, &sealed_pub_size); + if (err != GRUB_ERR_NONE) + goto error; + + /* Retrieve the private part of the sealed key */ + err = grub_tpm2key_get_privkey (tpm2key, &sealed_priv, &sealed_priv_size); + if (err != GRUB_ERR_NONE) + goto error; + + /* Unmarshal the sealed key */ + grub_tpm2_buffer_init (&buf); + if (sealed_pub_size + sealed_priv_size > buf.cap) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("sealed key larger than %llu bytes"), (unsigned long long)buf.cap); + goto error; + } + + grub_tpm2_buffer_pack (&buf, sealed_pub, sealed_pub_size); + grub_tpm2_buffer_pack (&buf, sealed_priv, sealed_priv_size); + + buf.offset = 0; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (&buf, &sk->public); + grub_Tss2_MU_TPM2B_PRIVATE_Unmarshal (&buf, &sk->private); + + if (buf.error != 0) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("malformed TPM 2.0 key file")); + goto error; + } + + err = GRUB_ERR_NONE; + + error: + /* End the parsing */ + grub_tpm2key_end_parsing (tpm2key); + grub_free (sealed_pub); + grub_free (sealed_priv); + + return err; +} + +/* Check if the SRK exists in the specified handle */ +static grub_err_t +tpm2_protector_srk_check (const TPM_HANDLE_t srk_handle) +{ + TPM_RC_t rc; + TPM2B_PUBLIC_t public; + + /* Find SRK */ + rc = grub_tpm2_readpublic (srk_handle, NULL, &public); + if (rc == TPM_RC_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to retrieve SRK from 0x%x (TPM2_ReadPublic: 0x%x)", srk_handle, rc); +} + +/* Get the SRK with the template */ +static grub_err_t +tpm2_protector_srk_get (const grub_srk_type_t srk_type, + const TPM_HANDLE_t parent, + TPM_HANDLE_t *srk_handle) +{ + TPM_RC_t rc; + TPMT_PUBLIC_PARMS_t parms = {0}; + TPMS_AUTH_COMMAND_t authCommand = {0}; + TPM2B_SENSITIVE_CREATE_t inSensitive = {0}; + TPM2B_PUBLIC_t inPublic = {0}; + TPM2B_DATA_t outsideInfo = {0}; + TPML_PCR_SELECTION_t creationPcr = {0}; + TPM2B_PUBLIC_t outPublic = {0}; + TPM2B_CREATION_DATA_t creationData = {0}; + TPM2B_DIGEST_t creationHash = {0}; + TPMT_TK_CREATION_t creationTicket = {0}; + TPM2B_NAME_t srkName = {0}; + TPM_HANDLE_t tmp_handle = 0; + + inPublic.publicArea.type = srk_type.type; + inPublic.publicArea.nameAlg = TPM_ALG_SHA256; + inPublic.publicArea.objectAttributes.restricted = 1; + inPublic.publicArea.objectAttributes.userWithAuth = 1; + inPublic.publicArea.objectAttributes.decrypt = 1; + inPublic.publicArea.objectAttributes.fixedTPM = 1; + inPublic.publicArea.objectAttributes.fixedParent = 1; + inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1; + inPublic.publicArea.objectAttributes.noDA = 1; + + if (srk_type.type == TPM_ALG_RSA) + { + inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.rsaDetail.keyBits = srk_type.detail.rsa_bits; + inPublic.publicArea.parameters.rsaDetail.exponent = 0; + } + else if (srk_type.type == TPM_ALG_ECC) + { + inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES; + inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128; + inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB; + inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL; + inPublic.publicArea.parameters.eccDetail.curveID = srk_type.detail.ecc_curve; + inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown SRK algorithm"); + + /* Test the parameters before SRK generation */ + parms.type = srk_type.type; + grub_memcpy (&parms.parameters, &inPublic.publicArea.parameters, + sizeof (TPMU_PUBLIC_PARMS_t)); + + rc = grub_tpm2_testparms (&parms, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unsupported SRK template (TPM2_TestParms: 0x%x)", rc); + + /* Create SRK */ + authCommand.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_createprimary (parent, &authCommand, &inSensitive, &inPublic, + &outsideInfo, &creationPcr, &tmp_handle, &outPublic, + &creationData, &creationHash, &creationTicket, + &srkName, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "could not create SRK (TPM2_CreatePrimary: 0x%x)", rc); + + *srk_handle = tmp_handle; + + return GRUB_ERR_NONE; +} + +/* + * Load the SRK from the persistent handle or create one with a given type of + * template, and then associate the sealed key with the SRK + * Return values: + * - GRUB_ERR_NONE: Everything is fine. + * - GRUB_ERR_BAD_ARGUMENT: The SRK doesn't match. Try another one. + * - Other: Something went wrong. + */ +static grub_err_t +tpm2_protector_srk_load (const grub_srk_type_t srk_type, + const tpm2_sealed_key_t *sealed_key, + const TPM_HANDLE_t parent, + TPM_HANDLE_t *sealed_handle, + TPM_HANDLE_t *srk_handle) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NAME_t name = {0}; + TPM_RC_t rc; + grub_err_t err; + + if (srk_handle == NULL) + return GRUB_ERR_BUG; + + if (*srk_handle != 0) + { + err = tpm2_protector_srk_check (*srk_handle); + if (err != GRUB_ERR_NONE) + return err; + } + else + { + err = tpm2_protector_srk_get (srk_type, parent, srk_handle); + if (err != GRUB_ERR_NONE) + return err; + } + + /* Load the sealed key and associate it with the SRK */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_load (*srk_handle, &authCmd, &sealed_key->private, &sealed_key->public, + sealed_handle, &name, NULL); + /* + * If TPM2_Load returns (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1), then it + * implies the wrong SRK is used. + */ + if (rc == (TPM_RC_INTEGRITY | TPM_RC_P | TPM_RC_1)) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "SRK not matched"); + goto error; + } + else if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to load sealed key (TPM2_Load: 0x%x)", rc); + goto error; + } + + return GRUB_ERR_NONE; + + error: + if (!TPM_HT_IS_PERSISTENT (*srk_handle)) + grub_tpm2_flushcontext (*srk_handle); + + return err; +} + +static const char * +srk_type_to_name (grub_srk_type_t srk_type) +{ + if (srk_type.type == TPM_ALG_ECC && srk_type.detail.ecc_curve == TPM_ECC_NIST_P256) + return "ECC_NIST_P256"; + else if (srk_type.type == TPM_ALG_RSA && srk_type.detail.rsa_bits == 2048) + return "RSA2048"; + + return "Unknown"; +} + +static grub_err_t +tpm2_protector_load_key (const tpm2_protector_context_t *ctx, + const tpm2_sealed_key_t *sealed_key, + const TPM_HANDLE_t parent_handle, + TPM_HANDLE_t *sealed_handle, + TPM_HANDLE_t *srk_handle) +{ + grub_err_t err; + int i; + grub_srk_type_t fallback_srks[] = { + { + .type = TPM_ALG_ECC, + .detail.ecc_curve = TPM_ECC_NIST_P256, + }, + { + .type = TPM_ALG_RSA, + .detail.rsa_bits = 2048, + }, + { + .type = TPM_ALG_ERROR, + } + }; + + /* Try the given persistent SRK if exists */ + if (*srk_handle != 0) + { + err = tpm2_protector_srk_load (ctx->srk_type, sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_printf ("Trying the specified SRK algorithm: %s\n", srk_type_to_name (ctx->srk_type)); + grub_errno = GRUB_ERR_NONE; + *srk_handle = 0; + } + + /* Try the specified algorithm for the SRK template */ + if (*srk_handle == 0) + { + err = tpm2_protector_srk_load (ctx->srk_type, sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + *srk_handle = 0; + } + + /* Try all the fallback SRK templates */ + for (i = 0; fallback_srks[i].type != TPM_ALG_ERROR; i++) + { + /* Skip the specified algorithm */ + if (fallback_srks[i].type == ctx->srk_type.type && + (fallback_srks[i].detail.rsa_bits == ctx->srk_type.detail.rsa_bits || + fallback_srks[i].detail.ecc_curve == ctx->srk_type.detail.ecc_curve)) + continue; + + grub_printf ("Trying fallback %s template\n", srk_type_to_name (fallback_srks[i])); + + *srk_handle = 0; + + err = tpm2_protector_srk_load (fallback_srks[i], sealed_key, + parent_handle, sealed_handle, + srk_handle); + if (err != GRUB_ERR_BAD_ARGUMENT) + return err; + + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + return err; +} + +static grub_err_t +tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffer *cmd_buf) +{ + TPM2B_DIGEST_t pcr_digest; + TPML_PCR_SELECTION_t pcr_sel; + TPM_RC_t rc; + + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &pcr_digest); + grub_Tss2_MU_TPML_PCR_SELECTION_Unmarshal (cmd_buf, &pcr_sel); + if (cmd_buf->error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to unmarshal commandPolicy for TPM2_PolicyPCR"); + + rc = grub_tpm2_policypcr (session, NULL, &pcr_digest, &pcr_sel, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to submit PCR policy (TPM2_PolicyPCR: 0x%x)", rc); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION_t session, struct grub_tpm2_buffer *cmd_buf) +{ + TPM2B_PUBLIC_t pubkey; + TPM2B_DIGEST_t policy_ref; + TPMT_SIGNATURE_t signature; + TPM2B_DIGEST_t pcr_policy; + TPM2B_DIGEST_t pcr_policy_hash; + TPMI_ALG_HASH_t sig_hash; + TPMT_TK_VERIFIED_t verification_ticket; + TPM_HANDLE_t pubkey_handle = 0; + TPM2B_NAME_t pubname; + TPM_RC_t rc; + grub_err_t err; + + grub_Tss2_MU_TPM2B_PUBLIC_Unmarshal (cmd_buf, &pubkey); + grub_Tss2_MU_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref); + grub_Tss2_MU_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature); + if (cmd_buf->error != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to unmarshal the buffer for TPM2_PolicyAuthorize"); + + /* Retrieve Policy Digest */ + rc = grub_tpm2_policygetdigest (session, NULL, &pcr_policy, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to get policy digest (TPM2_PolicyGetDigest: 0x%x).", rc); + + /* Calculate the digest of the polcy for VerifySignature */ + sig_hash = TPMT_SIGNATURE_get_hash_alg (&signature); + if (sig_hash == TPM_ALG_NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to get the hash algorithm of the signature"); + + rc = grub_tpm2_hash (NULL, (TPM2B_MAX_BUFFER_t *) &pcr_policy, sig_hash, + TPM_RH_NULL, &pcr_policy_hash, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to create PCR policy hash (TPM2_Hash: 0x%x)", rc); + + /* Load the public key */ + rc = grub_tpm2_loadexternal (NULL, NULL, &pubkey, TPM_RH_OWNER, &pubkey_handle, &pubname, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to load public key (TPM2_LoadExternal: 0x%x)", rc); + + /* Verify the signature against the public key and the policy digest */ + rc = grub_tpm2_verifysignature (pubkey_handle, NULL, &pcr_policy_hash, &signature, + &verification_ticket, NULL); + if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to verify signature (TPM2_VerifySignature: 0x%x)", rc); + goto error; + } + + /* Authorize the signed policy with the public key and the verification ticket */ + rc = grub_tpm2_policyauthorize (session, NULL, &pcr_policy, &policy_ref, &pubname, + &verification_ticket, NULL); + if (rc != TPM_RC_SUCCESS) + { + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to authorize PCR policy (TPM2_PolicyAuthorize: 0x%x)", rc); + goto error; + } + + err = GRUB_ERR_NONE; + + error: + grub_tpm2_flushcontext (pubkey_handle); + + return err; +} + +static grub_err_t +tpm2_protector_enforce_policy (tpm2key_policy_t policy, TPMI_SH_AUTH_SESSION_t session) +{ + struct grub_tpm2_buffer buf; + grub_err_t err; + + grub_tpm2_buffer_init (&buf); + if (policy->cmd_policy_len > buf.cap) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "CommandPolicy larger than TPM buffer"); + + grub_memcpy (buf.data, policy->cmd_policy, policy->cmd_policy_len); + buf.size = policy->cmd_policy_len; + + switch (policy->cmd_code) + { + case TPM_CC_PolicyPCR: + err = tpm2_protector_policypcr (session, &buf); + break; + case TPM_CC_PolicyAuthorize: + err = tpm2_protector_policyauthorize (session, &buf); + break; + default: + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown TPM Command: 0x%x", policy->cmd_code); + } + + return err; +} + +static grub_err_t +tpm2_protector_enforce_policy_seq (tpm2key_policy_t policy_seq, TPMI_SH_AUTH_SESSION_t session) +{ + tpm2key_policy_t policy; + grub_err_t err; + + FOR_LIST_ELEMENTS (policy, policy_seq) + { + err = tpm2_protector_enforce_policy (policy, session); + if (err != GRUB_ERR_NONE) + return err; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_simple_policy_seq (const tpm2_protector_context_t *ctx, + tpm2key_policy_t *policy_seq) +{ + tpm2key_policy_t policy = NULL; + struct grub_tpm2_buffer buf; + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = ctx->bank, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + grub_uint8_t i; + grub_err_t err; + + if (policy_seq == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + grub_tpm2_buffer_init (&buf); + + for (i = 0; i < ctx->pcr_count; i++) + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], ctx->pcrs[i]); + + grub_tpm2_buffer_pack_u16 (&buf, 0); + grub_Tss2_MU_TPML_PCR_SELECTION_Marshal (&buf, &pcr_sel); + + if (buf.error != 0) + return GRUB_ERR_BAD_ARGUMENT; + + policy = grub_malloc (sizeof(struct tpm2key_policy)); + if (policy == NULL) + { + err = GRUB_ERR_OUT_OF_MEMORY; + goto error; + } + policy->cmd_code = TPM_CC_PolicyPCR; + policy->cmd_policy = grub_malloc (buf.size); + if (policy->cmd_policy == NULL) + { + err = GRUB_ERR_OUT_OF_MEMORY; + goto error; + } + grub_memcpy (policy->cmd_policy, buf.data, buf.size); + policy->cmd_policy_len = buf.size; + + grub_list_push (GRUB_AS_LIST_P (policy_seq), GRUB_AS_LIST (policy)); + + return GRUB_ERR_NONE; + + error: + grub_free (policy); + + return err; +} + +static grub_err_t +tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, + grub_uint8_t **key, grub_size_t *key_size, bool *dump_pcr) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_SENSITIVE_DATA_t data; + TPM2B_NONCE_t nonceCaller = {0}; + TPMT_SYM_DEF_t symmetric = {0}; + TPMI_SH_AUTH_SESSION_t session; + grub_uint8_t *key_out; + TPM_RC_t rc; + grub_err_t err; + + *dump_pcr = false; + + /* Start Auth Session */ + nonceCaller.size = TPM_SHA256_DIGEST_SIZE; + symmetric.algorithm = TPM_ALG_NULL; + rc = grub_tpm2_startauthsession (TPM_RH_NULL, TPM_RH_NULL, NULL, &nonceCaller, NULL, + TPM_SE_POLICY, &symmetric, TPM_ALG_SHA256, + &session, NULL, NULL); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_DEVICE, "failed to start auth session (TPM2_StartAuthSession: 0x%x)", rc); + + /* Enforce the policy command sequence */ + err = tpm2_protector_enforce_policy_seq (policy_seq, session); + if (err != GRUB_ERR_NONE) + goto error; + + /* Unseal Sealed Key */ + authCmd.sessionHandle = session; + rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL); + if (rc != TPM_RC_SUCCESS) + { + /* + * Trigger PCR dump on policy fail + * TPM_RC_S (0x800) | TPM_RC_1 (0x100) | RC_FMT (0x80) | TPM_RC_POLICY_FAIL (0x1D) + */ + if (rc == 0x99D) + *dump_pcr = true; + + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key (TPM2_Unseal: 0x%x)", rc); + goto error; + } + + /* Epilogue */ + key_out = grub_malloc (data.size); + if (key_out == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory left to allocate unlock key buffer")); + goto error; + } + + grub_memcpy (key_out, data.buffer, data.size); + + *key = key_out; + *key_size = data.size; + + err = GRUB_ERR_NONE; + + error: + grub_tpm2_flushcontext (session); + + return err; +} + +#define TPM_PCR_STR_SIZE (sizeof (TPMU_HA_t) * 2 + 1) + +static grub_err_t +tpm2_protector_get_pcr_str (const TPM_ALG_ID_t algo, grub_uint32_t index, char *pcr_str, grub_uint16_t buf_size) +{ + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = algo, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + TPML_DIGEST_t digest = {0}; + grub_uint16_t i; + TPM_RC_t rc; + + if (buf_size < TPM_PCR_STR_SIZE) + { + grub_snprintf (pcr_str, buf_size, "insufficient buffer"); + return GRUB_ERR_OUT_OF_MEMORY; + } + + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], index); + + rc = grub_tpm2_pcr_read (NULL, &pcr_sel, NULL, NULL, &digest, NULL); + if (rc != TPM_RC_SUCCESS) + { + grub_snprintf (pcr_str, buf_size, "TPM2_PCR_Read: 0x%x", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Check the returned digest number and size */ + if (digest.count != 1 || digest.digests[0].size > sizeof (TPMU_HA_t)) + { + grub_snprintf (pcr_str, buf_size, "invalid digest"); + return GRUB_ERR_BAD_DEVICE; + } + + /* Print the digest to the buffer */ + for (i = 0; i < digest.digests[0].size; i++) + grub_snprintf (pcr_str + 2 * i, buf_size - 2 * i, "%02x", digest.digests[0].buffer[i]); + + return GRUB_ERR_NONE; +} + +static void +tpm2_protector_dump_pcr (const TPM_ALG_ID_t bank) +{ + const char *algo_name; + char pcr_str[TPM_PCR_STR_SIZE]; + grub_uint8_t i; + grub_err_t err; + + if (bank == TPM_ALG_SHA1) + algo_name = "sha1"; + else if (bank == TPM_ALG_SHA256) + algo_name = "sha256"; + else if (bank == TPM_ALG_SHA384) + algo_name = "sha384"; + else if (bank == TPM_ALG_SHA512) + algo_name = "sha512"; + else + algo_name = "other"; + + /* Try to fetch PCR 0 */ + err = tpm2_protector_get_pcr_str (bank, 0, pcr_str, sizeof (pcr_str)); + if (err != GRUB_ERR_NONE) + { + grub_printf ("Unsupported PCR bank [%s]: %s\n", algo_name, pcr_str); + return; + } + + grub_printf ("TPM PCR [%s]:\n", algo_name); + + grub_printf (" %02d: %s\n", 0, pcr_str); + for (i = 1; i < TPM_MAX_PCRS; i++) + { + tpm2_protector_get_pcr_str (bank, i, pcr_str, sizeof (pcr_str)); + grub_printf (" %02d: %s\n", i, pcr_str); + } +} + +static grub_err_t +tpm2_protector_key_from_buffer (const tpm2_protector_context_t *ctx, + void *buffer, grub_size_t buf_size, + grub_uint8_t **key, grub_size_t *key_size) +{ + tpm2_sealed_key_t sealed_key = {0}; + grub_uint8_t rsaparent = 0; + TPM_HANDLE_t parent_handle = 0; + TPM_HANDLE_t srk_handle = 0; + TPM_HANDLE_t sealed_handle = 0; + tpm2key_policy_t policy_seq = NULL; + tpm2key_authpolicy_t authpol = NULL; + tpm2key_authpolicy_t authpol_seq = NULL; + bool dump_pcr = false; + grub_err_t err; + + /* + * Retrieve sealed key, parent handle, policy sequence, and authpolicy + * sequence from the buffer + */ + if (tpm2_protector_is_tpm2key (buffer, buf_size) == true) + { + err = tpm2_protector_unmarshal_tpm2key (buffer, + buf_size, + &policy_seq, + &authpol_seq, + &rsaparent, + &parent_handle, + &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit1; + + if (rsaparent == 1) + { + tpm2_protector_context_t *ctx_w; + + /* Overwrite the SRK type as noted in the key */ + ctx_w = (tpm2_protector_context_t *)ctx; + ctx_w->srk_type.type = TPM_ALG_RSA; + ctx_w->srk_type.detail.rsa_bits = 2048; + } + } + else + { + parent_handle = TPM_RH_OWNER; + err = tpm2_protector_unmarshal_raw (buffer, buf_size, &sealed_key); + if (err != GRUB_ERR_NONE) + goto exit1; + } + + /* Set the SRK handle if it is specified with '--srk' or inside the key file */ + if (ctx->srk != 0) + srk_handle = ctx->srk; + else if (TPM_HT_IS_PERSISTENT (parent_handle)) + srk_handle = parent_handle; + + /* Load the sealed key into TPM and associate it with the SRK */ + err = tpm2_protector_load_key (ctx, &sealed_key, parent_handle, &sealed_handle, &srk_handle); + if (err != GRUB_ERR_NONE) + goto exit1; + + /* + * Set err to an error code to trigger the standalone policy sequence + * if there is no authpolicy sequence + */ + err = GRUB_ERR_READ_ERROR; + + /* Iterate the authpolicy sequence to find one that unseals the key */ + FOR_LIST_ELEMENTS (authpol, authpol_seq) + { + err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size, &dump_pcr); + if (err == GRUB_ERR_NONE) + break; + + /* + * Push the error message into the grub_error stack + * Note: The grub_error stack may overflow if there are too many policy + * sequences. Anyway, we still can keep the error messages from + * the first few policy sequences which are usually most likely to + * unseal the key. + */ + grub_error_push(); + } + + /* Give the standalone policy sequence a try */ + if (err != GRUB_ERR_NONE) + { + /* + * Create a basic policy sequence based on the given PCR selection if the + * key file doesn't provide one + */ + if (policy_seq == NULL) + { + err = tpm2_protector_simple_policy_seq (ctx, &policy_seq); + if (err != GRUB_ERR_NONE) + goto exit2; + } + + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); + } + + /* Pop error messages on success */ + if (err == GRUB_ERR_NONE) + while (grub_error_pop ()); + + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } + + exit2: + grub_tpm2_flushcontext (sealed_handle); + + if (!TPM_HT_IS_PERSISTENT (srk_handle)) + grub_tpm2_flushcontext (srk_handle); + + exit1: + grub_tpm2key_free_policy_seq (policy_seq); + grub_tpm2key_free_authpolicy_seq (authpol_seq); + return err; +} + +static grub_err_t +tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + const char *filepath; + void *file_bytes = NULL; + grub_size_t file_size = 0; + grub_err_t err; + + if (ctx->tpm2key != NULL) + filepath = ctx->tpm2key; + else if (ctx->keyfile != NULL) + filepath = ctx->keyfile; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("key file not specified")); + + err = tpm2_protector_srk_read_file (filepath, &file_bytes, &file_size); + if (err != GRUB_ERR_NONE) + return err; + + err = tpm2_protector_key_from_buffer (ctx, file_bytes, file_size, key, key_size); + + grub_free (file_bytes); + return err; +} + +static grub_err_t +tpm2_protector_load_persistent (const tpm2_protector_context_t *ctx, TPM_HANDLE_t sealed_handle, + grub_uint8_t **key, grub_size_t *key_size) +{ + tpm2key_policy_t policy_seq = NULL; + bool dump_pcr = false; + grub_err_t err; + + /* Create a basic policy sequence based on the given PCR selection */ + err = tpm2_protector_simple_policy_seq (ctx, &policy_seq); + if (err != GRUB_ERR_NONE) + goto exit; + + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); + + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } + + exit: + grub_tpm2_flushcontext (sealed_handle); + + grub_tpm2key_free_policy_seq (policy_seq); + + return err; +} + +static grub_err_t +tpm2_protector_key_from_nvindex (const tpm2_protector_context_t *ctx, TPM_HANDLE_t nvindex, + grub_uint8_t **key, grub_size_t *key_size) +{ + TPMS_AUTH_COMMAND_t authCmd = {0}; + TPM2B_NV_PUBLIC_t nv_public; + TPM2B_NAME_t nv_name; + grub_uint16_t data_size; + TPM2B_MAX_NV_BUFFER_t data; + TPM_RC_t rc; + + /* Get the data size in the NV index handle */ + rc = grub_tpm2_nv_readpublic (nvindex, NULL, &nv_public, &nv_name); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to retrieve info from 0x%x (TPM2_NV_ReadPublic: 0x%x)", nvindex, rc); + + data_size = nv_public.nvPublic.dataSize; + if (data_size > TPM_MAX_NV_BUFFER_SIZE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "insufficient data buffer"); + + /* Read the data from the NV index handle */ + authCmd.sessionHandle = TPM_RS_PW; + rc = grub_tpm2_nv_read (TPM_RH_OWNER, nvindex, &authCmd, data_size, 0, &data); + if (rc != TPM_RC_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to read data from 0x%x (TPM2_NV_Read: 0x%x)", nvindex, rc); + + return tpm2_protector_key_from_buffer (ctx, data.buffer, data_size, key, key_size); +} + +static grub_err_t +tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + grub_err_t err; + + if (TPM_HT_IS_PERSISTENT (ctx->nv) == true) + err = tpm2_protector_load_persistent (ctx, ctx->nv, key, key_size); + else if (TPM_HT_IS_NVINDEX (ctx->nv) == true) + err = tpm2_protector_key_from_nvindex (ctx, ctx->nv, key, key_size); + else + err = GRUB_ERR_BAD_ARGUMENT; + + return err; +} + +static grub_err_t +tpm2_protector_recover (const tpm2_protector_context_t *ctx, + grub_uint8_t **key, grub_size_t *key_size) +{ + switch (ctx->mode) + { + case TPM2_PROTECTOR_MODE_SRK: + return tpm2_protector_srk_recover (ctx, key, key_size); + case TPM2_PROTECTOR_MODE_NV: + return tpm2_protector_nv_recover (ctx, key, key_size); + default: + return GRUB_ERR_BAD_ARGUMENT; + } +} + +static grub_err_t +tpm2_protector_recover_key (grub_uint8_t **key, grub_size_t *key_size) +{ + /* Expect a call to tpm2_protector_init before anybody tries to use us */ + if (tpm2_protector_ctx.mode == TPM2_PROTECTOR_MODE_UNSET) + return grub_error (GRUB_ERR_INVALID_COMMAND, N_("cannot use TPM2 key protector without initializing it, call tpm2_protector_init first")); + + if (key == NULL || key_size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + return tpm2_protector_recover (&tpm2_protector_ctx, key, key_size); +} + +static grub_err_t +tpm2_protector_check_args (tpm2_protector_context_t *ctx) +{ + if (ctx->mode == TPM2_PROTECTOR_MODE_UNSET) + ctx->mode = TPM2_PROTECTOR_MODE_SRK; + + /* Checks for SRK mode */ + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && + (ctx->keyfile == NULL && ctx->tpm2key == NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, a key file must be specified: --tpm2key/-T or --keyfile/-k")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && + (ctx->keyfile != NULL && ctx->tpm2key != NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, please specify a key file with only --tpm2key/-T or --keyfile/-k")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_SRK && ctx->nv != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in SRK mode, an NV Index cannot be specified")); + + /* Checks for NV mode */ + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && ctx->nv == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("in NV Index mode, an NV Index must be specified: --nvindex or -n")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && + (ctx->tpm2key != NULL || ctx->keyfile != NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("a key file cannot be specified when using NV index mode")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && TPM_HT_IS_PERSISTENT (ctx->nv) == true && + (ctx->srk != 0 || ctx->srk_type.type != TPM_ALG_ERROR)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("an SRK cannot be specified when using NV index mode with a persistent handle")); + + if (ctx->mode == TPM2_PROTECTOR_MODE_NV && + (TPM_HT_IS_PERSISTENT (ctx->nv) == false && TPM_HT_IS_NVINDEX (ctx->nv) == false)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("an NV index must be either a persistent handle or an NV index handle when using NV index mode")); + + /* Defaults assignment */ + if (ctx->bank == TPM_ALG_ERROR) + ctx->bank = TPM_ALG_SHA256; + + if (ctx->pcr_count == 0) + { + ctx->pcrs[0] = 7; + ctx->pcr_count = 1; + } + + /* + * Set ECC_NIST_P256 as the default SRK when using SRK mode or NV mode with + * an NV index handle + */ + if (ctx->srk_type.type == TPM_ALG_ERROR && + (ctx->mode == TPM2_PROTECTOR_MODE_SRK || + (ctx->mode == TPM2_PROTECTOR_MODE_NV && TPM_HT_IS_NVINDEX (ctx->nv) == true))) + { + ctx->srk_type.type = TPM_ALG_ECC; + ctx->srk_type.detail.ecc_curve = TPM_ECC_NIST_P256; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_parse_file (const char *value, const char **file) +{ + if (grub_strlen (value) == 0) + return GRUB_ERR_BAD_ARGUMENT; + + *file = grub_strdup (value); + if (*file == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("no memory to duplicate file path")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_parse_mode (const char *value, tpm2_protector_mode_t *mode) +{ + if (grub_strcmp (value, "srk") == 0) + *mode = TPM2_PROTECTOR_MODE_SRK; + else if (grub_strcmp (value, "nv") == 0) + *mode = TPM2_PROTECTOR_MODE_NV; + else + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("value '%s' is not a valid TPM2 key protector mode"), value); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2_protector_init_cmd_handler (grub_extcmd_context_t ctxt, int argc, + char **args __attribute__ ((unused))) +{ + struct grub_arg_list *state = ctxt->state; + grub_err_t err; + + if (argc) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("the TPM2 key protector does not accept any non-option arguments (i.e., like -o and/or --option only)")); + + grub_free ((void *) tpm2_protector_ctx.keyfile); + grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx)); + + if (state[OPTION_MODE].set) /* mode */ + { + err = tpm2_protector_parse_mode (state[OPTION_MODE].arg, &tpm2_protector_ctx.mode); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_PCRS].set) /* pcrs */ + { + err = grub_tpm2_protector_parse_pcrs (state[OPTION_PCRS].arg, + tpm2_protector_ctx.pcrs, + &tpm2_protector_ctx.pcr_count); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_BANK].set) /* bank */ + { + err = grub_tpm2_protector_parse_bank (state[OPTION_BANK].arg, + &tpm2_protector_ctx.bank); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_TPM2KEY].set) /* tpm2key */ + { + err = tpm2_protector_parse_file (state[OPTION_TPM2KEY].arg, + &tpm2_protector_ctx.tpm2key); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_KEYFILE].set) /* keyfile */ + { + err = tpm2_protector_parse_file (state[OPTION_KEYFILE].arg, + &tpm2_protector_ctx.keyfile); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_SRK].set) /* srk */ + { + err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_SRK].arg, + &tpm2_protector_ctx.srk); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_ASYMMETRIC].set) /* asymmetric */ + { + err = grub_tpm2_protector_parse_asymmetric (state[OPTION_ASYMMETRIC].arg, + &tpm2_protector_ctx.srk_type); + if (err != GRUB_ERR_NONE) + return err; + } + + if (state[OPTION_NVINDEX].set) /* nvindex */ + { + err = grub_tpm2_protector_parse_tpm_handle (state[OPTION_NVINDEX].arg, + &tpm2_protector_ctx.nv); + if (err != GRUB_ERR_NONE) + return err; + } + + err = tpm2_protector_check_args (&tpm2_protector_ctx); + + /* This command only initializes the protector, so nothing else to do. */ + + return err; +} + +static grub_err_t +tpm2_protector_clear_cmd_handler (grub_extcmd_context_t ctxt __attribute__ ((unused)), + int argc, char **args __attribute__ ((unused))) +{ + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("tpm2_key_protector_clear accepts no arguments")); + + grub_free ((void *) tpm2_protector_ctx.keyfile); + grub_memset (&tpm2_protector_ctx, 0, sizeof (tpm2_protector_ctx)); + + return GRUB_ERR_NONE; +} + +static struct grub_key_protector tpm2_key_protector = + { + .name = "tpm2", + .recover_key = tpm2_protector_recover_key + }; + +static grub_err_t +tpm2_dump_pcr (grub_command_t cmd __attribute__((__unused__)), + int argc, char *argv[]) +{ + TPM_ALG_ID_t pcr_bank; + + if (argc == 0) + pcr_bank = TPM_ALG_SHA256; + else if (grub_strcmp (argv[0], "sha1") == 0) + pcr_bank = TPM_ALG_SHA1; + else if (grub_strcmp (argv[0], "sha256") == 0) + pcr_bank = TPM_ALG_SHA256; + else if (grub_strcmp (argv[0], "sha384") == 0) + pcr_bank = TPM_ALG_SHA384; + else if (grub_strcmp (argv[0], "sha512") == 0) + pcr_bank = TPM_ALG_SHA512; + else + { + grub_printf ("Unknown PCR bank\n"); + return GRUB_ERR_BAD_ARGUMENT; + } + + tpm2_protector_dump_pcr (pcr_bank); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT (tpm2_key_protector) +{ + tpm2_protector_init_cmd = + grub_register_extcmd ("tpm2_key_protector_init", + tpm2_protector_init_cmd_handler, 0, + N_("[-m mode] " + "[-p pcr_list] " + "[-b pcr_bank] " + "[-T tpm2_key_file_path] " + "[-k sealed_key_file_path] " + "[-s srk_handle] " + "[-a asymmetric_key_type] " + "[-n nv_index]"), + N_("Initialize the TPM2 key protector."), + tpm2_protector_init_cmd_options); + tpm2_protector_clear_cmd = + grub_register_extcmd ("tpm2_key_protector_clear", + tpm2_protector_clear_cmd_handler, 0, NULL, + N_("Clear the TPM2 key protector if previously initialized."), + NULL); + grub_key_protector_register (&tpm2_key_protector); + + tpm2_dump_pcr_cmd = + grub_register_command ("tpm2_dump_pcr", tpm2_dump_pcr, N_("Dump TPM2 PCRs"), + N_("Print all PCRs of the specified TPM 2.0 bank")); +} + +GRUB_MOD_FINI (tpm2_key_protector) +{ + grub_free ((void *) tpm2_protector_ctx.keyfile); + + grub_key_protector_unregister (&tpm2_key_protector); + grub_unregister_extcmd (tpm2_protector_clear_cmd); + grub_unregister_extcmd (tpm2_protector_init_cmd); + + grub_unregister_command (tpm2_dump_pcr_cmd); +} diff --git a/grub-core/commands/tpm2_key_protector/tpm2.h b/grub-core/commands/tpm2_key_protector/tpm2.h new file mode 100644 index 000000000..1c1d871b4 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2.h @@ -0,0 +1,36 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#ifndef GRUB_TPM2_TPM2_HEADER +#define GRUB_TPM2_TPM2_HEADER 1 + +#include +#include +#include + +/* Well-Known Windows SRK handle */ +#define TPM2_SRK_HANDLE 0x81000001 + +struct tpm2_sealed_key { + TPM2B_PUBLIC_t public; + TPM2B_PRIVATE_t private; +}; +typedef struct tpm2_sealed_key tpm2_sealed_key_t; + +#endif /* ! GRUB_TPM2_TPM2_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2_args.h b/grub-core/commands/tpm2_key_protector/tpm2_args.h new file mode 100644 index 000000000..bdbf9f373 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2_args.h @@ -0,0 +1,49 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#ifndef GRUB_TPM2_INTERNAL_ARGS_HEADER +#define GRUB_TPM2_INTERNAL_ARGS_HEADER 1 + +#include + +#include "tpm2.h" + +struct grub_srk_type +{ + TPMI_ALG_PUBLIC_t type; + union { + TPM_KEY_BITS_t rsa_bits; + TPM_ECC_CURVE_t ecc_curve; + } detail; +}; +typedef struct grub_srk_type grub_srk_type_t; + +extern grub_err_t +grub_tpm2_protector_parse_pcrs (char *value, grub_uint8_t *pcrs, grub_uint8_t *pcr_count); + +extern grub_err_t +grub_tpm2_protector_parse_asymmetric (const char *value, grub_srk_type_t *srk_type); + +extern grub_err_t +grub_tpm2_protector_parse_bank (const char *value, TPM_ALG_ID_t *bank); + +extern grub_err_t +grub_tpm2_protector_parse_tpm_handle (const char *value, TPM_HANDLE_t *handle); + +#endif /* ! GRUB_TPM2_INTERNAL_ARGS_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.asn b/grub-core/commands/tpm2_key_protector/tpm2key.asn new file mode 100644 index 000000000..e1fb51545 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.asn @@ -0,0 +1,49 @@ +-- +-- GRUB: GRand Unified Bootloader +-- Copyright (C) 2024 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 . +-- +-- This file describes TPM 2.0 Key File format for libtasn1. +-- To generate tpm2key_asn1_tab.c: asn1Parser tpm2key.asn +-- +TPM2KEY {} +DEFINITIONS IMPLICIT TAGS ::= + +BEGIN + +TPMPolicy ::= SEQUENCE { + CommandCode [0] EXPLICIT INTEGER, + CommandPolicy [1] EXPLICIT OCTET STRING +} + +TPMAuthPolicy ::= SEQUENCE { + Name [0] EXPLICIT UTF8String OPTIONAL, + Policy [1] EXPLICIT SEQUENCE OF TPMPolicy +} + +TPMKey ::= SEQUENCE { + type OBJECT IDENTIFIER, + emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + secret [2] EXPLICIT OCTET STRING OPTIONAL, + authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + description [4] EXPLICIT UTF8String OPTIONAL, + rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + parent INTEGER, + pubkey OCTET STRING, + privkey OCTET STRING +} + +END diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.c b/grub-core/commands/tpm2_key_protector/tpm2key.c new file mode 100644 index 000000000..3b6001d84 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.c @@ -0,0 +1,499 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 SUSE LLC + * Copyright (C) 2024 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 . + */ + +#include +#include +#include + +#include + +#include "tpm2key.h" + +extern asn1_static_node tpm2key_asn1_tab[]; +const char *sealed_key_oid = "2.23.133.10.1.5"; + +static int +asn1_allocate_and_read (asn1_node node, const char *name, void **content, grub_size_t *content_size) +{ + grub_uint8_t *tmpstr = NULL; + int tmpstr_size = 0; + int ret; + + if (content == NULL) + return ASN1_MEM_ERROR; + + ret = asn1_read_value (node, name, NULL, &tmpstr_size); + if (ret != ASN1_MEM_ERROR) + return ret; + + tmpstr = grub_malloc (tmpstr_size); + if (tmpstr == NULL) + return ASN1_MEM_ERROR; + + ret = asn1_read_value (node, name, tmpstr, &tmpstr_size); + if (ret != ASN1_SUCCESS) + return ret; + + *content = tmpstr; + *content_size = tmpstr_size; + + return ASN1_SUCCESS; +} + +static int +asn1_read_uint32 (asn1_node node, const char *name, grub_uint32_t *out) +{ + grub_uint32_t tmp = 0; + grub_uint8_t *ptr; + void *data = NULL; + grub_size_t data_size; + int ret; + + ret = asn1_allocate_and_read (node, name, &data, &data_size); + if (ret != ASN1_SUCCESS) + return ret; + + /* + * ASN.1 INTEGER is encoded in the following format: + * + * TAG LENGTH OCTECTS + * + * The integer TAG is 02 and LENGTH is the number of followed OCTECTS in + * big endian. For example: + * + * 0x1: 02 01 01 + * 0xabcd: 02 02 ab cd + * + * To decribe 0x1, it only takes 1 octect, so LENGTH is 0x01 and the + * octect is 0x01. On the other hand, 0xabcd requires 2 octects: 'ab" and + * 'cd', so LENGTH is 0x02. + * + * This function only expects a uint32 integer, so it rejects any integer + * containing more than 4 octects. + */ + if (data_size > 4) + { + ret = ASN1_MEM_ERROR; + goto error; + } + + /* Copy the octects into 'tmp' to make it a big-endian uint32 */ + ptr = (grub_uint8_t *) &tmp + (4 - data_size); + grub_memcpy (ptr, data, data_size); + + /* Convert the big-endian integer to host uint32 */ + tmp = grub_be_to_cpu32 (tmp); + + *out = tmp; + error: + if (data) + grub_free (data); + return ret; +} + +grub_err_t +grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size) +{ + asn1_node tpm2key; + asn1_node tpm2key_asn1 = NULL; + void *type_oid = NULL; + grub_size_t type_oid_size = 0; + void *empty_auth = NULL; + grub_size_t empty_auth_size = 0; + int tmp_size = 0; + int ret; + grub_err_t err; + + /* + * TPMKey ::= SEQUENCE { + * type OBJECT IDENTIFIER, + * emptyAuth [0] EXPLICIT BOOLEAN OPTIONAL, + * policy [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL, + * secret [2] EXPLICIT OCTET STRING OPTIONAL, + * authPolicy [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL, + * description [4] EXPLICIT UTF8String OPTIONAL, + * rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, + * parent INTEGER, + * pubkey OCTET STRING, + * privkey OCTET STRING + * } + */ + ret = asn1_array2tree (tpm2key_asn1_tab, &tpm2key_asn1, NULL); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to parse TPM2KEY ASN.1 array"); + + ret = asn1_create_element (tpm2key_asn1, "TPM2KEY.TPMKey", &tpm2key); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to create TPM2KEY.TPMKey"); + + ret = asn1_der_decoding (&tpm2key, data, size, NULL); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to decode TPM2KEY DER"); + + /* Check if 'type' is Sealed Key or not */ + ret = asn1_allocate_and_read (tpm2key, "type", &type_oid, &type_oid_size); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file"); + + if (grub_memcmp (sealed_key_oid, type_oid, type_oid_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a valid TPM2KEY file"); + goto error; + } + + /* 'emptyAuth' must be 'TRUE' since we don't support password authorization */ + ret = asn1_allocate_and_read (tpm2key, "emptyAuth", &empty_auth, &empty_auth_size); + if (ret != ASN1_SUCCESS || grub_strncmp ("TRUE", empty_auth, empty_auth_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "emptyAuth not TRUE"); + goto error; + } + + /* 'secret' should not be in a sealed key */ + ret = asn1_read_value (tpm2key, "secret", NULL, &tmp_size); + if (ret != ASN1_ELEMENT_NOT_FOUND) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, "\"secret\" not allowed for Sealed Key"); + goto error; + } + + *parsed_tpm2key = tpm2key; + + err = GRUB_ERR_NONE; + + error: + grub_free (type_oid); + grub_free (empty_auth); + + return err; +} + +void +grub_tpm2key_end_parsing (asn1_node tpm2key) +{ + asn1_delete_structure (&tpm2key); + tpm2key = NULL; +} + +grub_err_t +grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent) +{ + void *bool_str = NULL; + grub_size_t bool_str_size = 0; + int ret; + + if (rsaparent == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node"); + + ret = asn1_allocate_and_read (tpm2key, "rsaParent", &bool_str, &bool_str_size); + if (ret == ASN1_SUCCESS) + { + if (grub_strncmp ("TRUE", bool_str, bool_str_size) == 0) + *rsaparent = 1; + else + *rsaparent = 0; + } + else if (ret == ASN1_ELEMENT_NOT_FOUND) + *rsaparent = 0; + else + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve rsaParent"); + + grub_free (bool_str); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent) +{ + int ret; + + if (parent == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "NULL pointer detected"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid parent node"); + + ret = asn1_read_uint32 (tpm2key, "parent", parent); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve parent"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +tpm2key_get_octstring (asn1_node tpm2key, const char *name, void **data, grub_size_t *size) +{ + int ret; + + if (name == NULL || data == NULL || size == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid parameter(s)"); + + if (tpm2key == NULL) + return grub_error (GRUB_ERR_READ_ERROR, "invalid %s node", name); + + ret = asn1_allocate_and_read (tpm2key, name, data, size); + if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve %s", name); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size) +{ + return tpm2key_get_octstring (tpm2key, "pubkey", data, size); +} + +grub_err_t +grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size) +{ + return tpm2key_get_octstring (tpm2key, "privkey", data, size); +} + +/* + * The maximum and minimum number of elements for 'policy' and 'authPolicy' sequences + * + * Although there is no limit for the number of sequences elements, we set the upper + * bound to 99 to make it easier to implement the code. + * + * Any 'policy' or 'authPolicy' contains more than 99 commands/policies would become + * extremely complex to manage so it is impractical to support such use case. + */ +#define TPM2KEY_ELEMENTS_MAX 99 +#define TPM2KEY_ELEMENTS_MIN 1 + +/* + * The string to fetch 'Policy' from 'authPolicy': + * authPolicy.?XX.Policy + */ +#define AUTHPOLICY_POL_MAX_STR "authPolicy.?XX.Policy" +#define AUTHPOLICY_POL_MAX (sizeof (AUTHPOLICY_POL_MAX_STR)) + +/* + * Expected strings for CommandCode and CommandPolicy: + * policy.?XX.CommandCode + * policy.?XX.CommandPolicy + * authPolicy.?XX.Policy.?YY.CommandCode + * authPolicy.?XX.Policy.?YY.CommandPolicy + */ +#define CMD_CODE_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandCode" +#define CMD_POL_MAX_STR AUTHPOLICY_POL_MAX_STR".?YY.CommandPolicy" +#define CMD_CODE_MAX (sizeof (CMD_CODE_MAX_STR)) +#define CMD_POL_MAX (sizeof (CMD_POL_MAX_STR)) + +static int +tpm2key_get_policy_seq (asn1_node tpm2key, const char *prefix, + tpm2key_policy_t *policy_seq) +{ + tpm2key_policy_t tmp_seq = NULL; + tpm2key_policy_t policy = NULL; + int policy_n; + char cmd_code[CMD_CODE_MAX]; + char cmd_pol[CMD_POL_MAX]; + grub_size_t cmd_policy_len; + int i; + int ret; + + ret = asn1_number_of_elements (tpm2key, prefix, &policy_n); + if (ret != ASN1_SUCCESS) + return ret; + + /* + * Limit the number of policy commands to two digits (99) + * Although there is no upper bound for the number of policy commands, + * in practice, it takes one or two policy commands to unseal the key, + * so the 99 commands limit is more than enough. + */ + if (policy_n > TPM2KEY_ELEMENTS_MAX || policy_n < TPM2KEY_ELEMENTS_MIN) + return ASN1_VALUE_NOT_VALID; + + /* + * Iterate the policy commands backwards since grub_list_push() prepends + * the item into the list. + */ + for (i = policy_n; i >= 1; i--) { + policy = grub_zalloc (sizeof (struct tpm2key_policy)); + if (policy == NULL) + { + ret = ASN1_MEM_ALLOC_ERROR; + goto error; + } + grub_snprintf (cmd_code, CMD_CODE_MAX, "%s.?%d.CommandCode", prefix, i); + grub_snprintf (cmd_pol, CMD_POL_MAX, "%s.?%d.CommandPolicy", prefix, i); + + /* CommandCode [0] EXPLICIT INTEGER */ + ret = asn1_read_uint32 (tpm2key, cmd_code, &policy->cmd_code); + if (ret != ASN1_SUCCESS) + return ret; + + /* CommandPolicy [1] EXPLICIT OCTET STRING */ + ret = tpm2key_get_octstring (tpm2key, cmd_pol, &policy->cmd_policy, + &cmd_policy_len); + if (ret != ASN1_SUCCESS) + { + goto error; + } + else if (cmd_policy_len > GRUB_TPM2_BUFFER_CAPACITY) + { + /* + * CommandPolicy is the marshalled parameters for the TPM command so + * it should not be larger than the maximum TPM2 buffer. + */ + ret = ASN1_VALUE_NOT_VALID; + goto error; + } + policy->cmd_policy_len = (grub_uint16_t)cmd_policy_len; + + /* Prepend the policy command into the sequence */ + grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (policy)); + } + + *policy_seq = tmp_seq; + + return ASN1_SUCCESS; + + error: + if (policy != NULL) + { + grub_free (policy->cmd_policy); + grub_free (policy); + } + grub_tpm2key_free_policy_seq (tmp_seq); + + return ret; +} + +grub_err_t +grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq) +{ + int ret; + + ret = tpm2key_get_policy_seq (tpm2key, "policy", policy_seq); + if (ret == ASN1_ELEMENT_NOT_FOUND) + { + /* "policy" is optional, so it may not be available */ + *policy_seq = NULL; + return GRUB_ERR_NONE; + } + else if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy"); + + return GRUB_ERR_NONE; +} + +void +grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq) +{ + tpm2key_policy_t policy; + tpm2key_policy_t next; + + if (policy_seq == NULL) + return; + + FOR_LIST_ELEMENTS_SAFE (policy, next, policy_seq) + { + grub_free (policy->cmd_policy); + grub_free (policy); + } +} + +grub_err_t +grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq) +{ + tpm2key_authpolicy_t tmp_seq = NULL; + tpm2key_authpolicy_t authpol = NULL; + int authpol_n; + char authpol_pol[AUTHPOLICY_POL_MAX]; + int i; + int ret; + grub_err_t err; + + ret = asn1_number_of_elements (tpm2key, "authPolicy", &authpol_n); + if (ret == ASN1_ELEMENT_NOT_FOUND) + { + /* "authPolicy" is optional, so it may not be available */ + *authpol_seq = NULL; + return GRUB_ERR_NONE; + } + else if (ret != ASN1_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve authPolicy"); + + /* Limit the number of authPolicy elements to two digits (99) */ + if (authpol_n > TPM2KEY_ELEMENTS_MAX || authpol_n < TPM2KEY_ELEMENTS_MIN) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid number of authPolicy elements"); + + /* + * Iterate the authPolicy elements backwards since grub_list_push() prepends + * the item into the list. + */ + for (i = authpol_n; i >= 1; i--) { + authpol = grub_zalloc (sizeof (struct tpm2key_authpolicy)); + if (authpol == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate memory for authPolicy"); + goto error; + } + grub_snprintf (authpol_pol, AUTHPOLICY_POL_MAX, "authPolicy.?%d.Policy", i); + + ret = tpm2key_get_policy_seq (tpm2key, authpol_pol, &authpol->policy_seq); + if (ret != ASN1_SUCCESS) + { + err = grub_error (GRUB_ERR_READ_ERROR, "failed to retrieve policy from authPolicy"); + goto error; + } + + /* Prepend the authPolicy element into the sequence */ + grub_list_push (GRUB_AS_LIST_P (&tmp_seq), GRUB_AS_LIST (authpol)); + } + + *authpol_seq = tmp_seq; + + return GRUB_ERR_NONE; + + error: + if (authpol != NULL) + { + grub_tpm2key_free_policy_seq (authpol->policy_seq); + grub_free (authpol); + } + + grub_tpm2key_free_authpolicy_seq (tmp_seq); + + return err; +} + +void +grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq) +{ + tpm2key_authpolicy_t authpol; + tpm2key_authpolicy_t next; + + if (authpol_seq == NULL) + return; + + FOR_LIST_ELEMENTS_SAFE (authpol, next, authpol_seq) + { + grub_tpm2key_free_policy_seq (authpol->policy_seq); + grub_free (authpol); + } +} diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.h b/grub-core/commands/tpm2_key_protector/tpm2key.h new file mode 100644 index 000000000..f749d62c2 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key.h @@ -0,0 +1,87 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 SUSE LLC + * Copyright (C) 2024 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 . + */ + +#ifndef GRUB_TPM2_TPM2KEY_HEADER +#define GRUB_TPM2_TPM2KEY_HEADER 1 + +#include +#include + +/* + * TPMPolicy ::= SEQUENCE { + * CommandCode [0] EXPLICIT INTEGER, + * CommandPolicy [1] EXPLICIT OCTET STRING + * } + */ +struct tpm2key_policy { + struct tpm2key_policy *next; + struct tpm2key_policy **prev; + grub_uint32_t cmd_code; + void *cmd_policy; + grub_uint16_t cmd_policy_len; +}; +typedef struct tpm2key_policy *tpm2key_policy_t; + +/* + * TPMAuthPolicy ::= SEQUENCE { + * Name [0] EXPLICIT UTF8String OPTIONAL, + * Policy [1] EXPLICIT SEQUENCE OF TPMPolicy + * } + * + * Name is not a necessary part to unseal the key. Ignore it. + */ +struct tpm2key_authpolicy { + struct tpm2key_authpolicy *next; + struct tpm2key_authpolicy **prev; + /* char *name; */ + tpm2key_policy_t policy_seq; +}; +typedef struct tpm2key_authpolicy *tpm2key_authpolicy_t; + +extern grub_err_t +grub_tpm2key_start_parsing (asn1_node *parsed_tpm2key, void *data, grub_size_t size); + +extern void +grub_tpm2key_end_parsing (asn1_node tpm2key); + +extern grub_err_t +grub_tpm2key_get_rsaparent (asn1_node tpm2key, grub_uint8_t *rsaparent); + +extern grub_err_t +grub_tpm2key_get_parent (asn1_node tpm2key, grub_uint32_t *parent); + +extern grub_err_t +grub_tpm2key_get_pubkey (asn1_node tpm2key, void **data, grub_size_t *size); + +extern grub_err_t +grub_tpm2key_get_privkey (asn1_node tpm2key, void **data, grub_size_t *size); + +extern grub_err_t +grub_tpm2key_get_policy_seq (asn1_node tpm2key, tpm2key_policy_t *policy_seq); + +extern void +grub_tpm2key_free_policy_seq (tpm2key_policy_t policy_seq); + +extern grub_err_t +grub_tpm2key_get_authpolicy_seq (asn1_node tpm2key, tpm2key_authpolicy_t *authpol_seq); + +extern void +grub_tpm2key_free_authpolicy_seq (tpm2key_authpolicy_t authpol_seq); + +#endif /* GRUB_TPM2_TPM2KEY_HEADER */ diff --git a/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c new file mode 100644 index 000000000..bebe108a3 --- /dev/null +++ b/grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c @@ -0,0 +1,63 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 . + */ + +/* + * This file is generated by 'asn1Parser tpm2key.asn' and the '#include' + * headers are replaced with the ones in grub2. + * - 'grub/mm.h' for the definition of 'NULL' + * - 'libtasn1.h' for the definition of 'asn1_static_node' + */ + +#include +#include + +const asn1_static_node tpm2key_asn1_tab[] = { + { "TPM2KEY", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "TPMPolicy", 1610612741, NULL }, + { "CommandCode", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "CommandPolicy", 536879111, NULL }, + { NULL, 2056, "1"}, + { "TPMAuthPolicy", 1610612741, NULL }, + { "Name", 1610637346, NULL }, + { NULL, 2056, "0"}, + { "Policy", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "TPMPolicy"}, + { "TPMKey", 536870917, NULL }, + { "type", 1073741836, NULL }, + { "emptyAuth", 1610637316, NULL }, + { NULL, 2056, "0"}, + { "policy", 1610637323, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "TPMPolicy"}, + { "secret", 1610637319, NULL }, + { NULL, 2056, "2"}, + { "authPolicy", 1610637323, NULL }, + { NULL, 1073743880, "3"}, + { NULL, 2, "TPMAuthPolicy"}, + { "description", 1610637346, NULL }, + { NULL, 2056, "4"}, + { "rsaParent", 1610637316, NULL }, + { NULL, 2056, "5"}, + { "parent", 1073741827, NULL }, + { "pubkey", 1073741831, NULL }, + { "privkey", 7, NULL }, + { NULL, 0, NULL } +}; diff --git a/grub-core/commands/tr.c b/grub-core/commands/tr.c new file mode 100644 index 000000000..ef72841a2 --- /dev/null +++ b/grub-core/commands/tr.c @@ -0,0 +1,126 @@ +/* tr.c -- The tr command. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const struct grub_arg_option options[] = + { + { "set", 's', 0, N_("Set a variable to return value."), N_("VARNAME"), ARG_TYPE_STRING }, + { "upcase", 'U', 0, N_("Translate to upper case."), 0, 0 }, + { "downcase", 'D', 0, N_("Translate to lower case."), 0, 0 }, + { 0, 0, 0, 0, 0, 0 } + }; + +static const char *letters_lowercase = "abcdefghijklmnopqrstuvwxyz"; +static const char *letters_uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +static grub_err_t +grub_cmd_tr (grub_extcmd_context_t ctxt, int argc, char **args) +{ + char *var = 0; + const char *input = 0; + char *output = 0, *optr; + const char *s1 = 0; + const char *s2 = 0; + const char *iptr; + + /* Select the defaults from options. */ + if (ctxt->state[0].set) { + var = ctxt->state[0].arg; + input = grub_env_get (var); + } + + if (ctxt->state[1].set) { + s1 = letters_lowercase; + s2 = letters_uppercase; + } + + if (ctxt->state[2].set) { + s1 = letters_uppercase; + s2 = letters_lowercase; + } + + /* Check for arguments and update the defaults. */ + if (argc == 1) + input = args[0]; + + else if (argc == 2) { + s1 = args[0]; + s2 = args[1]; + + } else if (argc == 3) { + s1 = args[0]; + s2 = args[1]; + input = args[2]; + + } else if (argc > 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters"); + + if (!s1 || !s2 || !input) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing parameters"); + + if (grub_strlen (s1) != grub_strlen (s2)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "set sizes did not match"); + + /* Translate input into output buffer. */ + + output = grub_malloc (grub_strlen (input) + 1); + if (! output) + return grub_errno; + + optr = output; + for (iptr = input; *iptr; iptr++) + { + char *p = grub_strchr (s1, *iptr); + if (p) + *optr++ = s2[p - s1]; + else + *optr++ = *iptr; + } + *optr = '\0'; + + if (ctxt->state[0].set) + grub_env_set (var, output); + else + grub_printf ("%s\n", output); + + grub_free (output); + return GRUB_ERR_NONE; +} + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT(tr) +{ + cmd = grub_register_extcmd ("tr", grub_cmd_tr, 0, N_("[OPTIONS] [SET1] [SET2] [STRING]"), + N_("Translate SET1 characters to SET2 in STRING."), options); +} + +GRUB_MOD_FINI(tr) +{ + grub_unregister_extcmd (cmd); +} diff --git a/commands/true.c b/grub-core/commands/true.c similarity index 89% rename from commands/true.c rename to grub-core/commands/true.c index aa8125853..8cbba6583 100644 --- a/commands/true.c +++ b/grub-core/commands/true.c @@ -21,6 +21,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_err_t grub_cmd_true (struct grub_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), @@ -34,7 +36,7 @@ grub_cmd_false (struct grub_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char *argv[] __attribute__ ((unused))) { - return grub_error (GRUB_ERR_TEST_FAILURE, "false"); + return grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } static grub_command_t cmd_true, cmd_false; @@ -44,9 +46,11 @@ GRUB_MOD_INIT(true) { cmd_true = grub_register_command ("true", grub_cmd_true, + /* TRANSLATORS: it's a command description. */ 0, N_("Do nothing, successfully.")); cmd_false = grub_register_command ("false", grub_cmd_false, + /* TRANSLATORS: it's a command description. */ 0, N_("Do nothing, unsuccessfully.")); } diff --git a/commands/usbtest.c b/grub-core/commands/usbtest.c similarity index 78% rename from commands/usbtest.c rename to grub-core/commands/usbtest.c index b884a93f1..2c6d93fe6 100644 --- a/commands/usbtest.c +++ b/grub-core/commands/usbtest.c @@ -27,13 +27,15 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static const char *usb_classes[] = { - "", + "Unknown", "Audio", "Communication Interface", "HID", - "", + "Unknown", "Physical", "Image", "Printer", @@ -61,6 +63,11 @@ static const char *usb_devspeed[] = "High" }; +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + static grub_usb_err_t grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, char **string) @@ -83,24 +90,37 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, 0x06, (3 << 8) | index, langid, descstr.length, (char *) descstrp); - *string = grub_malloc (descstr.length / 2); + if (descstrp->length == 0) + { + grub_free (descstrp); + *string = grub_strdup (""); + if (! *string) + return GRUB_USB_ERR_INTERNAL; + return GRUB_USB_ERR_NONE; + } + + *string = grub_malloc (descstr.length * 2 + 1); if (! *string) { grub_free (descstrp); return GRUB_USB_ERR_INTERNAL; } - grub_utf16_to_utf8 ((grub_uint8_t *) *string, descstrp->str, descstrp->length / 2 - 1); - (*string)[descstr.length / 2 - 1] = '\0'; + *grub_utf16_to_utf8 ((grub_uint8_t *) *string, descstrp->str, + descstrp->length / 2 - 1) = 0; grub_free (descstrp); return GRUB_USB_ERR_NONE; } +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + static void usb_print_str (const char *description, grub_usb_device_t dev, int idx) { - char *name; + char *name = NULL; grub_usb_err_t err; /* XXX: LANGID */ @@ -118,7 +138,7 @@ usb_print_str (const char *description, grub_usb_device_t dev, int idx) } static int -usb_iterate (grub_usb_device_t dev) +usb_iterate (grub_usb_device_t dev, void *data __attribute__ ((unused))) { struct grub_usb_desc_device *descdev; int i; @@ -129,10 +149,10 @@ usb_iterate (grub_usb_device_t dev) usb_print_str ("Vendor", dev, descdev->strvendor); usb_print_str ("Serial", dev, descdev->strserial); - if (descdev->class > 0 && descdev->class <= 0x0E) - grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", - descdev->class, usb_classes[descdev->class], - descdev->subclass, descdev->protocol); + grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", + descdev->class, descdev->class < ARRAY_SIZE (usb_classes) + ? usb_classes[descdev->class] : "Unknown", + descdev->subclass, descdev->protocol); grub_printf ("USB version %d.%d, VendorID: 0x%02x, ProductID: 0x%02x, #conf: %d\n", descdev->usbrel >> 8, (descdev->usbrel >> 4) & 0x0F, descdev->vendorid, descdev->prodid, descdev->configcnt); @@ -155,10 +175,10 @@ usb_iterate (grub_usb_device_t dev) grub_printf ("Interface #%d: #Endpoints: %d ", i, interf->endpointcnt); - if (interf->class > 0 && interf->class <= 0x0E) - grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", - interf->class, usb_classes[interf->class], - interf->subclass, interf->protocol); + grub_printf ("Class: (0x%02x) %s, Subclass: 0x%02x, Protocol: 0x%02x\n", + interf->class, interf->class < ARRAY_SIZE (usb_classes) + ? usb_classes[interf->class] : "Unknown", + interf->subclass, interf->protocol); usb_print_str ("Interface", dev, interf->strif); @@ -185,8 +205,10 @@ grub_cmd_usbtest (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { + grub_usb_poll_devices (1); + grub_printf ("USB devices:\n\n"); - grub_usb_iterate (usb_iterate); + grub_usb_iterate (usb_iterate, NULL); return 0; } diff --git a/grub-core/commands/videoinfo.c b/grub-core/commands/videoinfo.c new file mode 100644 index 000000000..205ba78c9 --- /dev/null +++ b/grub-core/commands/videoinfo.c @@ -0,0 +1,267 @@ +/* videoinfo.c - command to list video modes. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct hook_ctx +{ + unsigned height, width, depth; + struct grub_video_mode_info *current_mode; +}; + +static int +hook (const struct grub_video_mode_info *info, void *hook_arg) +{ + struct hook_ctx *ctx = hook_arg; + + if (ctx->height && ctx->width && (info->width != ctx->width || info->height != ctx->height)) + return 0; + + if (ctx->depth && info->bpp != ctx->depth) + return 0; + + if (info->mode_number == GRUB_VIDEO_MODE_NUMBER_INVALID) + grub_printf (" "); + else + { + if (ctx->current_mode && info->mode_number == ctx->current_mode->mode_number) + grub_printf ("*"); + else + grub_printf (" "); + grub_printf (" 0x%03x ", info->mode_number); + } + grub_printf ("%4d x %4d x %2d (%4d) ", info->width, info->height, info->bpp, + info->pitch); + + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_PURE_TEXT) + grub_xputs (_("Text-only ")); + /* Show mask and position details for direct color modes. */ + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_RGB) + /* TRANSLATORS: "Direct color" is a mode when the color components + are written dirrectly into memory. */ + grub_printf_ (N_("Direct color, mask: %d/%d/%d/%d pos: %d/%d/%d/%d"), + info->red_mask_size, + info->green_mask_size, + info->blue_mask_size, + info->reserved_mask_size, + info->red_field_pos, + info->green_field_pos, + info->blue_field_pos, + info->reserved_field_pos); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) + /* TRANSLATORS: In "paletted color" mode you write the index of the color + in the palette. Synonyms include "packed pixel". */ + grub_xputs (_("Paletted ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_YUV) + grub_xputs (_("YUV ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_PLANAR) + /* TRANSLATORS: "Planar" is the video memory where you have to write + in several different banks "plans" to control the different color + components of the same pixel. */ + grub_xputs (_("Planar ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_HERCULES) + grub_xputs (_("Hercules ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_CGA) + grub_xputs (_("CGA ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_NONCHAIN4) + /* TRANSLATORS: Non-chain 4 is a 256-color planar + (unchained) video memory mode. */ + grub_xputs (_("Non-chain 4 ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) + grub_xputs (_("Monochrome ")); + if (info->mode_type & GRUB_VIDEO_MODE_TYPE_UNKNOWN) + grub_xputs (_("Unknown video mode ")); + + grub_xputs ("\n"); + + return 0; +} + +static void +print_edid (struct grub_video_edid_info *edid_info) +{ + unsigned int edid_width, edid_height; + + if (grub_video_edid_checksum (edid_info)) + { + grub_puts_ (N_(" EDID checksum invalid")); + grub_errno = GRUB_ERR_NONE; + return; + } + + grub_printf_ (N_(" EDID version: %u.%u\n"), + edid_info->version, edid_info->revision); + if (grub_video_edid_preferred_mode (edid_info, &edid_width, &edid_height) + == GRUB_ERR_NONE) + grub_printf_ (N_(" Preferred mode: %ux%u\n"), edid_width, edid_height); + else + { + grub_printf_ (N_(" No preferred mode available\n")); + grub_errno = GRUB_ERR_NONE; + } +} + +static grub_err_t +grub_cmd_videoinfo (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_video_adapter_t adapter; + grub_video_driver_id_t id; + struct hook_ctx ctx; + + ctx.height = ctx.width = ctx.depth = 0; + if (argc) + { + const char *ptr; + ptr = args[0]; + ctx.width = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + if (*ptr != 'x') + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid video mode specification `%s'"), + args[0]); + ptr++; + ctx.height = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + if (*ptr == 'x') + { + ptr++; + ctx.depth = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + return grub_errno; + } + } + +#ifdef GRUB_MACHINE_PCBIOS + if (grub_strcmp (cmd->name, "vbeinfo") == 0) + grub_dl_load ("vbe"); +#endif + + id = grub_video_get_driver_id (); + + grub_puts_ (N_("List of supported video modes:")); + grub_puts_ (N_("Legend: mask/position=red/green/blue/reserved")); + + FOR_VIDEO_ADAPTERS (adapter) + { + struct grub_video_mode_info info; + struct grub_video_edid_info edid_info; + + grub_printf_ (N_("Adapter `%s':\n"), adapter->name); + + if (!adapter->iterate) + { + grub_puts_ (N_(" No info available")); + continue; + } + + ctx.current_mode = NULL; + + if (adapter->id == id) + { + if (grub_video_get_info (&info) == GRUB_ERR_NONE) + ctx.current_mode = &info; + else + /* Don't worry about errors. */ + grub_errno = GRUB_ERR_NONE; + } + else if (id != GRUB_VIDEO_DRIVER_NONE) + { + grub_puts_ (N_(" A video driver is active, cannot initialize this driver until it is deactivated\n")); + continue; + } + else + { + if (adapter->init ()) + { + grub_puts_ (N_(" Failed to initialize video adapter")); + grub_errno = GRUB_ERR_NONE; + continue; + } + } + + if (adapter->print_adapter_specific_info) + adapter->print_adapter_specific_info (); + + adapter->iterate (hook, &ctx); + + if (adapter->get_edid && adapter->get_edid (&edid_info) == GRUB_ERR_NONE) + print_edid (&edid_info); + else + grub_errno = GRUB_ERR_NONE; + + ctx.current_mode = NULL; + + if (adapter->id != id) + { + if (adapter->fini ()) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + } + } + return GRUB_ERR_NONE; +} + +static grub_command_t cmd; +#ifdef GRUB_MACHINE_PCBIOS +static grub_command_t cmd_vbe; +#endif + +GRUB_MOD_INIT(videoinfo) +{ + cmd = grub_register_command ("videoinfo", grub_cmd_videoinfo, + /* TRANSLATORS: "x" has to be entered in, + like an identifier, so please don't + use better Unicode codepoints. */ + N_("[WxH[xD]]"), + N_("List available video modes. If " + "resolution is given show only modes" + " matching it.")); +#ifdef GRUB_MACHINE_PCBIOS + cmd_vbe = grub_register_command ("vbeinfo", grub_cmd_videoinfo, + /* TRANSLATORS: "x" has to be entered in, + like an identifier, so please don't + use better Unicode codepoints. */ + N_("[WxH[xD]]"), + N_("List available video modes. If " + "resolution is given show only modes" + " matching it.")); +#endif +} + +GRUB_MOD_FINI(videoinfo) +{ + grub_unregister_command (cmd); +#ifdef GRUB_MACHINE_PCBIOS + grub_unregister_command (cmd_vbe); +#endif +} + diff --git a/grub-core/commands/videotest.c b/grub-core/commands/videotest.c new file mode 100644 index 000000000..ac145afc2 --- /dev/null +++ b/grub-core/commands/videotest.c @@ -0,0 +1,241 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_videotest (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_err_t err; + grub_video_color_t color; + unsigned int x; + unsigned int y; + unsigned int width; + unsigned int height; + int i; + struct grub_video_render_target *text_layer; + grub_video_color_t palette[16]; + const char *mode = NULL; + +#ifdef GRUB_MACHINE_PCBIOS + if (grub_strcmp (cmd->name, "vbetest") == 0) + grub_dl_load ("vbe"); +#endif + mode = grub_env_get ("gfxmode"); + if (argc) + mode = args[0]; + if (!mode) + mode = "auto"; + + err = grub_video_set_mode (mode, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0); + if (err) + return err; + + grub_video_get_viewport (&x, &y, &width, &height); + + { + const char *str; + int texty; + grub_font_t sansbig; + grub_font_t sans; + grub_font_t sanssmall; + grub_font_t fixed; + struct grub_font_glyph *glyph; + + if (grub_video_create_render_target (&text_layer, width, height, + GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA) + || !text_layer) + goto fail; + + grub_video_set_active_render_target (text_layer); + + color = grub_video_map_rgb (0, 255, 255); + sansbig = grub_font_get ("Unknown Regular 16"); + sans = grub_font_get ("Unknown Regular 16"); + sanssmall = grub_font_get ("Unknown Regular 16"); + fixed = grub_font_get ("Fixed 20"); + if (! sansbig || ! sans || ! sanssmall || ! fixed) + return grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); + + glyph = grub_font_get_glyph (fixed, '*'); + grub_font_draw_glyph (glyph, color, 200 ,0); + + color = grub_video_map_rgb (255, 255, 255); + + texty = 32; + grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", + sans, color, 16, texty); + texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); + + texty += grub_font_get_ascent (fixed); + grub_font_draw_string ("The quick brown fox jumped over the lazy dog.", + fixed, color, 16, texty); + texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); + + /* To convert Unicode characters into UTF-8 for this test, the following + command is useful: + echo -ne '\x00\x00\x26\x3A' | iconv -f UTF-32BE -t UTF-8 | od -t x1 + This converts the Unicode character U+263A to UTF-8. */ + + /* Characters used: + Code point Description UTF-8 encoding + ----------- ------------------------------ -------------- + U+263A unfilled smiley face E2 98 BA + U+00A1 inverted exclamation point C2 A1 + U+00A3 British pound currency symbol C2 A3 + U+03C4 Greek tau CF 84 + U+00E4 lowercase letter a with umlaut C3 A4 + U+2124 set 'Z' symbol (integers) E2 84 A4 + U+2286 subset symbol E2 8A 86 + U+211D set 'R' symbol (real numbers) E2 84 9D */ + + str = + "Unicode test: happy\xE2\x98\xBA \xC2\xA3 5.00" + " \xC2\xA1\xCF\x84\xC3\xA4u! " + " \xE2\x84\xA4\xE2\x8A\x86\xE2\x84\x9D"; + color = grub_video_map_rgb (128, 128, 255); + + /* All characters in the string exist in the 'Fixed 20' (10x20) font. */ + texty += grub_font_get_ascent(fixed); + grub_font_draw_string (str, fixed, color, 16, texty); + texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed); + + texty += grub_font_get_ascent(sansbig); + grub_font_draw_string (str, sansbig, color, 16, texty); + texty += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig); + + texty += grub_font_get_ascent(sans); + grub_font_draw_string (str, sans, color, 16, texty); + texty += grub_font_get_descent (sans) + grub_font_get_leading (sans); + + texty += grub_font_get_ascent(sanssmall); + grub_font_draw_string (str, sanssmall, color, 16, texty); + texty += (grub_font_get_descent (sanssmall) + + grub_font_get_leading (sanssmall)); + + glyph = grub_font_get_glyph (fixed, '*'); + + for (i = 0; i < 16; i++) + { + color = grub_video_map_color (i); + palette[i] = color; + grub_font_draw_glyph (glyph, color, 16 + i * 16, 220); + } + } + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + for (i = 0; i < 5; i++) + { + + if (i == 0 || i == 1) + { + color = grub_video_map_rgb (0, 0, 0); + grub_video_fill_rect (color, 0, 0, width, height); + + color = grub_video_map_rgb (255, 0, 0); + grub_video_fill_rect (color, 0, 0, 100, 100); + + color = grub_video_map_rgb (0, 255, 0); + grub_video_fill_rect (color, 100, 0, 100, 100); + + color = grub_video_map_rgb (0, 0, 255); + grub_video_fill_rect (color, 200, 0, 100, 100); + + color = grub_video_map_rgb (0, 255, 255); + grub_video_fill_rect (color, 0, 100, 100, 100); + + color = grub_video_map_rgb (255, 0, 255); + grub_video_fill_rect (color, 100, 100, 100, 100); + + color = grub_video_map_rgb (255, 255, 0); + grub_video_fill_rect (color, 200, 100, 100, 100); + + grub_video_set_viewport (x + 150, y + 150, + width - 150 * 2, height - 150 * 2); + color = grub_video_map_rgb (77, 33, 77); + grub_video_fill_rect (color, 0, 0, width, height); + } + + color = grub_video_map_rgb (i, 33, 77); + grub_video_fill_rect (color, 0, 0, width, height); + grub_video_blit_render_target (text_layer, GRUB_VIDEO_BLIT_BLEND, 0, 0, + 0, 0, width, height); + grub_video_swap_buffers (); + } + + grub_getkey (); + + grub_video_delete_render_target (text_layer); + + grub_video_restore (); + + for (i = 0; i < 16; i++) + grub_printf("color %d: %08x\n", i, palette[i]); + + grub_errno = GRUB_ERR_NONE; + return grub_errno; + + fail: + grub_video_delete_render_target (text_layer); + grub_video_restore (); + return grub_errno; +} + +static grub_command_t cmd; +#ifdef GRUB_MACHINE_PCBIOS +static grub_command_t cmd_vbe; +#endif + +GRUB_MOD_INIT(videotest) +{ + cmd = grub_register_command ("videotest", grub_cmd_videotest, + /* TRANSLATORS: "x" has to be entered in, + like an identifier, so please don't + use better Unicode codepoints. */ + N_("[WxH]"), + /* TRANSLATORS: Here, on the other hand, it's + nicer to use unicode cross instead of x. */ + N_("Test video subsystem in mode WxH.")); +#ifdef GRUB_MACHINE_PCBIOS + cmd_vbe = grub_register_command ("vbetest", grub_cmd_videotest, + 0, N_("Test video subsystem.")); +#endif +} + +GRUB_MOD_FINI(videotest) +{ + grub_unregister_command (cmd); +#ifdef GRUB_MACHINE_PCBIOS + grub_unregister_command (cmd_vbe); +#endif +} diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c new file mode 100644 index 000000000..ed6586505 --- /dev/null +++ b/grub-core/commands/wildcard.c @@ -0,0 +1,652 @@ +/* wildcard.c - Wildcard character expansion for GRUB script. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static inline int isregexop (char ch); +static char ** merge (char **lhs, char **rhs); +static char *make_dir (const char *prefix, const char *start, const char *end); +static int make_regex (const char *regex_start, const char *regex_end, + regex_t *regexp); +static void split_path (const char *path, const char **suffix_end, const char **regex_end); +static char ** match_devices (const regex_t *regexp, int noparts); +static char ** match_files (const char *prefix, const char *suffix_start, + const char *suffix_end, const regex_t *regexp); + +static grub_err_t wildcard_expand (const char *s, char ***strs); + +struct grub_script_wildcard_translator grub_filename_translator = { + .expand = wildcard_expand, +}; + +static char ** +merge (char **dest, char **ps) +{ + int i; + int j; + char **p; + grub_size_t sz; + + if (! dest) + return ps; + + if (! ps) + return dest; + + for (i = 0; dest[i]; i++) + ; + for (j = 0; ps[j]; j++) + ; + + if (grub_add (i, j, &sz) || + grub_add (sz, 1, &sz) || + grub_mul (sz, sizeof (char *), &sz)) + return dest; + + p = grub_realloc (dest, sz); + if (! p) + { + grub_free (dest); + grub_free (ps); + return 0; + } + + dest = p; + for (j = 0; ps[j]; j++) + dest[i++] = ps[j]; + dest[i] = 0; + + grub_free (ps); + return dest; +} + +static inline int +isregexop (char ch) +{ + return grub_strchr ("*.\\|+{}[]?", ch) ? 1 : 0; +} + +static char * +make_dir (const char *prefix, const char *start, const char *end) +{ + char ch; + unsigned i; + unsigned n; + char *result; + + i = grub_strlen (prefix); + n = i + end - start; + + result = grub_malloc (n + 1); + if (! result) + return 0; + + grub_strcpy (result, prefix); + while (start < end && (ch = *start++)) + if (ch == '\\' && isregexop (*start)) + result[i++] = *start++; + else + result[i++] = ch; + + result[i] = '\0'; + return result; +} + +static int +make_regex (const char *start, const char *end, regex_t *regexp) +{ + char ch; + int i = 0; + unsigned len = end - start; + char *buffer; + grub_size_t sz; + + /* Worst case size is (len * 2 + 2 + 1). */ + if (grub_mul (len, 2, &sz) || + grub_add (sz, 3, &sz)) + return 1; + + buffer = grub_malloc (sz); + if (! buffer) + return 1; + + buffer[i++] = '^'; + while (start < end) + { + /* XXX Only * and ? expansion for now. */ + switch ((ch = *start++)) + { + case '\\': + buffer[i++] = ch; + if (*start != '\0') + buffer[i++] = *start++; + break; + + case '.': + case '(': + case ')': + case '@': + case '+': + case '|': + case '{': + case '}': + case '[': + case ']': + buffer[i++] = '\\'; + buffer[i++] = ch; + break; + + case '*': + buffer[i++] = '.'; + buffer[i++] = '*'; + break; + + case '?': + buffer[i++] = '.'; + break; + + default: + buffer[i++] = ch; + } + } + buffer[i++] = '$'; + buffer[i] = '\0'; + grub_dprintf ("expand", "Regexp is %s\n", buffer); + + if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK)) + { + grub_free (buffer); + return 1; + } + + grub_free (buffer); + return 0; +} + +/* Split `str' into two parts: (1) dirname that is regexop free (2) + dirname that has a regexop. */ +static void +split_path (const char *str, const char **noregexop, const char **regexop) +{ + char ch = 0; + int regex = 0; + + const char *end; + const char *split; /* points till the end of dirnaname that doesn't + need expansion. */ + + split = end = str; + while ((ch = *end)) + { + if (ch == '\\' && end[1]) + end++; + + else if (ch == '*' || ch == '?') + regex = 1; + + else if (ch == '/' && ! regex) + split = end + 1; /* forward to next regexop-free dirname */ + + else if (ch == '/' && regex) + break; /* stop at the first dirname with a regexop */ + + end++; + } + + *regexop = end; + if (! regex) + *noregexop = end; + else + *noregexop = split; +} + +/* Context for match_devices. */ +struct match_devices_ctx +{ + const regex_t *regexp; + int noparts; + int ndev; + char **devs; +}; + +/* Helper for match_devices. */ +static int +match_devices_iter (const char *name, void *data) +{ + struct match_devices_ctx *ctx = data; + char **t; + char *buffer; + grub_size_t sz; + + /* skip partitions if asked to. */ + if (ctx->noparts && grub_strchr (name, ',')) + return 0; + + buffer = grub_xasprintf ("(%s)", name); + if (! buffer) + return 1; + + grub_dprintf ("expand", "matching: %s\n", buffer); + if (regexec (ctx->regexp, buffer, 0, 0, 0)) + { + grub_dprintf ("expand", "not matched\n"); + fail: + grub_free (buffer); + return 0; + } + + if (grub_add (ctx->ndev, 2, &sz) || + grub_mul (sz, sizeof (char *), &sz)) + goto fail; + + t = grub_realloc (ctx->devs, sz); + if (! t) + { + grub_free (buffer); + return 1; + } + + ctx->devs = t; + ctx->devs[ctx->ndev++] = buffer; + ctx->devs[ctx->ndev] = 0; + return 0; +} + +static char ** +match_devices (const regex_t *regexp, int noparts) +{ + struct match_devices_ctx ctx = { + .regexp = regexp, + .noparts = noparts, + .ndev = 0, + .devs = 0 + }; + int i; + + if (grub_device_iterate (match_devices_iter, &ctx)) + goto fail; + + return ctx.devs; + + fail: + + for (i = 0; ctx.devs && ctx.devs[i]; i++) + grub_free (ctx.devs[i]); + + grub_free (ctx.devs); + + return 0; +} + +/* Context for match_files. */ +struct match_files_ctx +{ + const regex_t *regexp; + char **files; + unsigned nfile; + char *dir; +}; + +/* Helper for match_files. */ +static int +match_files_iter (const char *name, + const struct grub_dirhook_info *info __attribute__((unused)), + void *data) +{ + struct match_files_ctx *ctx = data; + char **t; + char *buffer; + grub_size_t sz; + + /* skip . and .. names */ + if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0) + return 0; + + grub_dprintf ("expand", "matching: %s in %s\n", name, ctx->dir); + if (regexec (ctx->regexp, name, 0, 0, 0)) + return 0; + + grub_dprintf ("expand", "matched\n"); + + buffer = grub_xasprintf ("%s%s", ctx->dir, name); + if (! buffer) + return 1; + + if (grub_add (ctx->nfile, 2, &sz) || + grub_mul (sz, sizeof (char *), &sz)) + goto fail; + + t = grub_realloc (ctx->files, sz); + if (!t) + { + fail: + grub_free (buffer); + return 1; + } + + ctx->files = t; + ctx->files[ctx->nfile++] = buffer; + ctx->files[ctx->nfile] = 0; + return 0; +} + +static char ** +match_files (const char *prefix, const char *suffix, const char *end, + const regex_t *regexp) +{ + struct match_files_ctx ctx = { + .regexp = regexp, + .nfile = 0, + .files = 0 + }; + int i; + const char *path; + char *device_name; + grub_fs_t fs; + grub_device_t dev; + + dev = 0; + device_name = 0; + grub_error_push (); + + ctx.dir = make_dir (prefix, suffix, end); + if (! ctx.dir) + goto fail; + + device_name = grub_file_get_device_name (ctx.dir); + dev = grub_device_open (device_name); + if (! dev) + goto fail; + + fs = grub_fs_probe (dev); + if (! fs) + goto fail; + + if (ctx.dir[0] == '(') + { + path = grub_strchr (ctx.dir, ')'); + if (!path) + goto fail; + path++; + } + else + path = ctx.dir; + + if (fs->fs_dir (dev, path, match_files_iter, &ctx)) + goto fail; + + grub_free (ctx.dir); + grub_device_close (dev); + grub_free (device_name); + grub_error_pop (); + return ctx.files; + + fail: + + grub_free (ctx.dir); + + for (i = 0; ctx.files && ctx.files[i]; i++) + grub_free (ctx.files[i]); + + grub_free (ctx.files); + + if (dev) + grub_device_close (dev); + + grub_free (device_name); + + grub_error_pop (); + return 0; +} + +/* Context for check_file. */ +struct check_file_ctx +{ + const char *basename; + int found; +}; + +/* Helper for check_file. */ +static int +check_file_iter (const char *name, const struct grub_dirhook_info *info, + void *data) +{ + struct check_file_ctx *ctx = data; + + if (ctx->basename[0] == 0 + || (info->case_insensitive ? grub_strcasecmp (name, ctx->basename) == 0 + : grub_strcmp (name, ctx->basename) == 0)) + { + ctx->found = 1; + return 1; + } + + return 0; +} + +static int +check_file (const char *dir, const char *basename) +{ + struct check_file_ctx ctx = { + .basename = basename, + .found = 0 + }; + grub_fs_t fs; + grub_device_t dev; + const char *device_name, *path; + + device_name = grub_file_get_device_name (dir); + dev = grub_device_open (device_name); + if (! dev) + goto fail; + + fs = grub_fs_probe (dev); + if (! fs) + goto fail; + + if (dir[0] == '(') + { + path = grub_strchr (dir, ')'); + if (!path) + goto fail; + path++; + } + else + path = dir; + + fs->fs_dir (dev, path[0] ? path : "/", check_file_iter, &ctx); + if (grub_errno == 0 && basename[0] == 0) + ctx.found = 1; + + fail: + grub_errno = 0; + + return ctx.found; +} + +static void +unescape (char *out, const char *in, const char *end) +{ + char *optr; + const char *iptr; + + for (optr = out, iptr = in; iptr < end;) + { + if (*iptr == '\\' && iptr + 1 < end) + { + *optr++ = iptr[1]; + iptr += 2; + continue; + } + if (*iptr == '\\') + break; + *optr++ = *iptr++; + } + *optr = 0; +} + +static grub_err_t +wildcard_expand (const char *s, char ***strs) +{ + const char *start; + const char *regexop; + const char *noregexop; + char **paths = 0; + int had_regexp = 0; + + unsigned i; + regex_t regexp; + + *strs = 0; + if (s[0] != '/' && s[0] != '(' && s[0] != '*') + return 0; + + start = s; + while (*start) + { + split_path (start, &noregexop, ®exop); + + if (noregexop == regexop) + { + grub_dprintf ("expand", "no expansion needed\n"); + if (paths == 0) + { + paths = grub_malloc (sizeof (char *) * 2); + if (!paths) + goto fail; + paths[0] = grub_malloc (regexop - start + 1); + if (!paths[0]) + goto fail; + unescape (paths[0], start, regexop); + paths[1] = 0; + } + else + { + int j = 0; + for (i = 0; paths[i]; i++) + { + char *o, *oend; + char *n; + char *p; + o = paths[i]; + oend = o + grub_strlen (o); + n = grub_malloc ((oend - o) + (regexop - start) + 1); + if (!n) + goto fail; + grub_memcpy (n, o, oend - o); + + unescape (n + (oend - o), start, regexop); + if (had_regexp) + p = grub_strrchr (n, '/'); + else + p = 0; + if (!p) + { + grub_free (o); + paths[j++] = n; + continue; + } + *p = 0; + if (!check_file (n, p + 1)) + { + grub_dprintf ("expand", "file <%s> in <%s> not found\n", + p + 1, n); + grub_free (o); + grub_free (n); + continue; + } + *p = '/'; + grub_free (o); + paths[j++] = n; + } + if (j == 0) + { + grub_free (paths); + paths = 0; + goto done; + } + paths[j] = 0; + } + grub_dprintf ("expand", "paths[0] = `%s'\n", paths[0]); + start = regexop; + continue; + } + + if (make_regex (noregexop, regexop, ®exp)) + goto fail; + + had_regexp = 1; + + if (paths == 0) + { + if (start == noregexop) /* device part has regexop */ + paths = match_devices (®exp, *start != '('); + + else /* device part explicit wo regexop */ + paths = match_files ("", start, noregexop, ®exp); + } + else + { + char **r = 0; + + for (i = 0; paths[i]; i++) + { + char **p; + + p = match_files (paths[i], start, noregexop, ®exp); + grub_free (paths[i]); + if (! p) + continue; + + r = merge (r, p); + if (! r) + goto fail; + } + grub_free (paths); + paths = r; + } + + regfree (®exp); + if (! paths) + goto done; + + start = regexop; + } + + done: + + *strs = paths; + return 0; + + fail: + + for (i = 0; paths && paths[i]; i++) + grub_free (paths[i]); + grub_free (paths); + regfree (®exp); + return grub_errno; +} diff --git a/grub-core/commands/xen/lsxen.c b/grub-core/commands/xen/lsxen.c new file mode 100644 index 000000000..97713102c --- /dev/null +++ b/grub-core/commands/xen/lsxen.c @@ -0,0 +1,90 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static int +hook (const char *dir, void *hook_data __attribute__ ((unused))) +{ + grub_printf ("%s\n", dir); + return 0; +} + +static grub_err_t +grub_cmd_lsxen (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + char *dir; + grub_err_t err; + char *buf; + + if (argc >= 1) + return grub_xenstore_dir (args[0], hook, NULL); + + buf = grub_xenstore_get_file ("domid", NULL); + if (!buf) + return grub_errno; + dir = grub_xasprintf ("/local/domain/%s", buf); + grub_free (buf); + err = grub_xenstore_dir (dir, hook, NULL); + grub_free (dir); + return err; +} + +static grub_err_t +grub_cmd_catxen (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + const char *dir = "domid"; + char *buf; + + if (argc >= 1) + dir = args[0]; + + buf = grub_xenstore_get_file (dir, NULL); + if (!buf) + return grub_errno; + grub_xputs (buf); + grub_xputs ("\n"); + grub_free (buf); + return GRUB_ERR_NONE; + +} + +static grub_command_t cmd_ls, cmd_cat; + +GRUB_MOD_INIT (lsxen) +{ + cmd_ls = grub_register_command ("xen_ls", grub_cmd_lsxen, N_("[DIR]"), + N_("List Xen storage.")); + cmd_cat = grub_register_command ("xen_cat", grub_cmd_catxen, N_("[DIR]"), + N_("List Xen storage.")); +} + +GRUB_MOD_FINI (lsxen) +{ + grub_unregister_command (cmd_ls); + grub_unregister_command (cmd_cat); +} diff --git a/commands/xnu_uuid.c b/grub-core/commands/xnu_uuid.c similarity index 77% rename from commands/xnu_uuid.c rename to grub-core/commands/xnu_uuid.c index 382d3196b..ae4b3a415 100644 --- a/commands/xnu_uuid.c +++ b/grub-core/commands/xnu_uuid.c @@ -34,6 +34,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + /* This prefix is used by xnu and boot-132 to hash together with volume serial. */ static grub_uint8_t hash_prefix[16] @@ -48,18 +50,29 @@ grub_cmd_xnu_uuid (grub_command_t cmd __attribute__ ((unused)), grub_uint8_t *xnu_uuid; char uuid_string[sizeof ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]; char *ptr; - grub_uint8_t ctx[GRUB_MD_MD5->contextsize]; + void *ctx; + int low = 0; if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "UUID required"); + if (argc > 1 && grub_strcmp (args[0], "-l") == 0) + { + low = 1; + argc--; + args++; + } + serial = grub_cpu_to_be64 (grub_strtoull (args[0], 0, 16)); - GRUB_MD_MD5->init (&ctx); - GRUB_MD_MD5->write (&ctx, hash_prefix, sizeof (hash_prefix)); - GRUB_MD_MD5->write (&ctx, &serial, sizeof (serial)); - GRUB_MD_MD5->final (&ctx); - xnu_uuid = GRUB_MD_MD5->read (&ctx); + ctx = grub_zalloc (GRUB_MD_MD5->contextsize); + if (!ctx) + return grub_errno; + GRUB_MD_MD5->init (ctx); + GRUB_MD_MD5->write (ctx, hash_prefix, sizeof (hash_prefix)); + GRUB_MD_MD5->write (ctx, &serial, sizeof (serial)); + GRUB_MD_MD5->final (ctx); + xnu_uuid = GRUB_MD_MD5->read (ctx); grub_snprintf (uuid_string, sizeof (uuid_string), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", @@ -73,13 +86,16 @@ grub_cmd_xnu_uuid (grub_command_t cmd __attribute__ ((unused)), (unsigned int) xnu_uuid[10], (unsigned int) xnu_uuid[11], (unsigned int) xnu_uuid[12], (unsigned int) xnu_uuid[13], (unsigned int) xnu_uuid[14], (unsigned int) xnu_uuid[15]); - for (ptr = uuid_string; *ptr; ptr++) - *ptr = grub_toupper (*ptr); + if (!low) + for (ptr = uuid_string; *ptr; ptr++) + *ptr = grub_toupper (*ptr); if (argc == 1) - grub_printf ("%s", uuid_string); + grub_printf ("%s\n", uuid_string); if (argc > 1) grub_env_set (args[1], uuid_string); + grub_free (ctx); + return GRUB_ERR_NONE; } @@ -89,9 +105,12 @@ static grub_command_t cmd; GRUB_MOD_INIT (xnu_uuid) { cmd = grub_register_command ("xnu_uuid", grub_cmd_xnu_uuid, - N_("GRUBUUID [VARNAME]"), + /* TRANSLATORS: GRUBUUID stands for "filesystem + UUID as used in GRUB". */ + N_("[-l] GRUBUUID [VARNAME]"), N_("Transform 64-bit UUID to format " - "suitable for XNU.")); + "suitable for XNU. If -l is given keep " + "it lowercase as done by blkid.")); } GRUB_MOD_FINI (xnu_uuid) diff --git a/grub-core/disk/AFSplitter.c b/grub-core/disk/AFSplitter.c new file mode 100644 index 000000000..249163ff0 --- /dev/null +++ b/grub-core/disk/AFSplitter.c @@ -0,0 +1,95 @@ +/* + * AFsplitter - Anti forensic information splitter + * Copyright 2004, Clemens Fruhwirth + * + * AFsplitter diffuses information over a large stripe of data, + * therefor supporting secure data destruction. + * + * This program is grub_free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the grub_free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the grub_free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv2+"); + +gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); + +static void +diffuse (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t size) +{ + grub_size_t i; + grub_uint32_t IV; /* host byte order independend hash IV */ + + grub_size_t fullblocks = size / hash->mdlen; + int padding = size % hash->mdlen; + grub_uint8_t final[GRUB_CRYPTO_MAX_MDLEN]; + grub_uint8_t temp[sizeof (IV) + GRUB_CRYPTO_MAX_MDLEN]; + + /* hash block the whole data set with different IVs to produce + * more than just a single data block + */ + for (i = 0; i < fullblocks; i++) + { + IV = grub_cpu_to_be32 (i); + grub_memcpy (temp, &IV, sizeof (IV)); + grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, hash->mdlen); + grub_crypto_hash (hash, dst + hash->mdlen * i, temp, + sizeof (IV) + hash->mdlen); + } + + if (padding) + { + IV = grub_cpu_to_be32 (i); + grub_memcpy (temp, &IV, sizeof (IV)); + grub_memcpy (temp + sizeof (IV), src + hash->mdlen * i, padding); + grub_crypto_hash (hash, final, temp, sizeof (IV) + padding); + grub_memcpy (dst + hash->mdlen * i, final, padding); + } +} + +/** + * Merges the splitted master key stored on disk into the original key + */ +gcry_err_code_t +AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, grub_uint8_t * dst, + grub_size_t blocksize, grub_size_t blocknumbers) +{ + grub_size_t i; + grub_uint8_t *bufblock; + + if (hash->mdlen > GRUB_CRYPTO_MAX_MDLEN || hash->mdlen == 0) + return GPG_ERR_INV_ARG; + + bufblock = grub_zalloc (blocksize); + if (bufblock == NULL) + return GPG_ERR_OUT_OF_MEMORY; + + grub_memset (bufblock, 0, blocksize); + for (i = 0; i < blocknumbers - 1; i++) + { + grub_crypto_xor (bufblock, src + (blocksize * i), bufblock, blocksize); + diffuse (hash, bufblock, bufblock, blocksize); + } + grub_crypto_xor (dst, src + (i * blocksize), bufblock, blocksize); + + grub_free (bufblock); + return GPG_ERR_NO_ERROR; +} diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c new file mode 100644 index 000000000..f3c968195 --- /dev/null +++ b/grub-core/disk/ahci.c @@ -0,0 +1,1161 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_ahci_cmd_head +{ + grub_uint32_t config; + grub_uint32_t transferred; + grub_uint64_t command_table_base; + grub_uint32_t unused[4]; +}; + +struct grub_ahci_prdt_entry +{ + grub_uint64_t data_base; + grub_uint32_t unused; + grub_uint32_t size; +}; + +struct grub_ahci_cmd_table +{ + grub_uint8_t cfis[0x40]; + grub_uint8_t command[0x10]; + grub_uint8_t reserved[0x30]; + struct grub_ahci_prdt_entry prdt[1]; +}; + +struct grub_ahci_hba_port +{ + grub_uint64_t command_list_base; + grub_uint64_t fis_base; + grub_uint32_t intstatus; + grub_uint32_t inten; + grub_uint32_t command; + grub_uint32_t unused1; + grub_uint32_t task_file_data; + grub_uint32_t sig; + grub_uint32_t status; + grub_uint32_t unused2; + grub_uint32_t sata_error; + grub_uint32_t sata_active; + grub_uint32_t command_issue; + grub_uint32_t unused3; + grub_uint32_t fbs; + grub_uint32_t unused4[15]; +}; + +enum grub_ahci_hba_port_command + { + GRUB_AHCI_HBA_PORT_CMD_ST = 0x01, + GRUB_AHCI_HBA_PORT_CMD_SPIN_UP = 0x02, + GRUB_AHCI_HBA_PORT_CMD_POWER_ON = 0x04, + GRUB_AHCI_HBA_PORT_CMD_FRE = 0x10, + GRUB_AHCI_HBA_PORT_CMD_CR = 0x8000, + GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000, + }; + +enum grub_ahci_hba_port_int_status + { + GRUB_AHCI_HBA_PORT_IS_IFS = (1UL << 27), + GRUB_AHCI_HBA_PORT_IS_HBDS = (1UL << 28), + GRUB_AHCI_HBA_PORT_IS_HBFS = (1UL << 29), + GRUB_AHCI_HBA_PORT_IS_TFES = (1UL << 30), + }; + +#define GRUB_AHCI_HBA_PORT_IS_FATAL_MASK ( \ + GRUB_AHCI_HBA_PORT_IS_IFS | \ + GRUB_AHCI_HBA_PORT_IS_HBDS | \ + GRUB_AHCI_HBA_PORT_IS_HBFS | \ + GRUB_AHCI_HBA_PORT_IS_TFES) + +struct grub_ahci_hba +{ + grub_uint32_t cap; + grub_uint32_t global_control; + grub_uint32_t intr_status; + grub_uint32_t ports_implemented; + grub_uint32_t unused1[6]; + grub_uint32_t bios_handoff; + grub_uint32_t unused2[53]; + struct grub_ahci_hba_port ports[32]; +}; + +struct grub_ahci_received_fis +{ + char raw[4096]; +}; + +enum + { + GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f + }; + +enum + { + GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001, + GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002, + GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000, + }; + +enum + { + GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1, + GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2, + GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8, + GRUB_AHCI_BIOS_HANDOFF_RWC = 8 + }; + + +struct grub_ahci_device +{ + struct grub_ahci_device *next; + struct grub_ahci_device **prev; + volatile struct grub_ahci_hba *hba; + int port; + int num; + struct grub_pci_dma_chunk *command_list_chunk; + volatile struct grub_ahci_cmd_head *command_list; + struct grub_pci_dma_chunk *command_table_chunk; + volatile struct grub_ahci_cmd_table *command_table; + struct grub_pci_dma_chunk *rfis; + int present; + int atapi; +}; + +static grub_err_t +grub_ahci_readwrite_real (struct grub_ahci_device *dev, + struct grub_disk_ata_pass_through_parms *parms, int reset); + + +enum + { + GRUB_AHCI_CONFIG_READ = 0, + GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f, + GRUB_AHCI_CONFIG_ATAPI = 0x20, + GRUB_AHCI_CONFIG_WRITE = 0x40, + GRUB_AHCI_CONFIG_PREFETCH = 0x80, + GRUB_AHCI_CONFIG_RESET = 0x100, + GRUB_AHCI_CONFIG_BIST = 0x200, + GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400, + GRUB_AHCI_CONFIG_PMP_MASK = 0xf000, + GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000, + }; +#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0 +#define GRUB_AHCI_CONFIG_PMP_SHIFT 12 +#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16 +#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000 + +#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000 + +static struct grub_ahci_device *grub_ahci_devices; +static int numdevs; + +static int +grub_ahci_pciinit (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + grub_pci_address_t addr; + grub_uint32_t class; + grub_uint32_t bar; + unsigned i, nports; + volatile struct grub_ahci_hba *hba; + + /* Read class. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + /* Check if this class ID matches that of a PCI IDE Controller. */ + if (class >> 8 != 0x010601) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5); + + bar = grub_pci_read (addr); + + if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK + | GRUB_PCI_ADDR_MEM_PREFETCH)) + != (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32)) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, grub_pci_read_word (addr) + | GRUB_PCI_COMMAND_MEM_ENABLED | GRUB_PCI_COMMAND_BUS_MASTER); + + hba = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK, + sizeof (*hba)); + grub_dprintf ("ahci", "dev: %x:%x.%x\n", dev.bus, dev.device, dev.function); + + grub_dprintf ("ahci", "tfd[0]: %x\n", + hba->ports[0].task_file_data); + grub_dprintf ("ahci", "cmd[0]: %x\n", + hba->ports[0].command); + grub_dprintf ("ahci", "st[0]: %x\n", + hba->ports[0].status); + grub_dprintf ("ahci", "err[0]: %x\n", + hba->ports[0].sata_error); + + grub_dprintf ("ahci", "tfd[1]: %x\n", + hba->ports[1].task_file_data); + grub_dprintf ("ahci", "cmd[1]: %x\n", + hba->ports[1].command); + grub_dprintf ("ahci", "st[1]: %x\n", + hba->ports[1].status); + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + hba->ports[1].sata_error = hba->ports[1].sata_error; + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + grub_dprintf ("ahci", "BH:%x\n", hba->bios_handoff); + + if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED)) + { + grub_uint64_t endtime; + + grub_dprintf ("ahci", "Requesting AHCI ownership\n"); + hba->bios_handoff = (hba->bios_handoff & ~GRUB_AHCI_BIOS_HANDOFF_RWC) + | GRUB_AHCI_BIOS_HANDOFF_OS_OWNED; + grub_dprintf ("ahci", "Waiting for BIOS to give up ownership\n"); + endtime = grub_get_time_ms () + 1000; + while ((hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED) + && grub_get_time_ms () < endtime); + if (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED) + { + grub_dprintf ("ahci", "Forcibly taking ownership\n"); + hba->bios_handoff = GRUB_AHCI_BIOS_HANDOFF_OS_OWNED; + hba->bios_handoff |= GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED; + } + else + grub_dprintf ("ahci", "AHCI ownership obtained\n"); + } + else + grub_dprintf ("ahci", "AHCI is already in OS mode\n"); + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + if (!(hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)) + grub_dprintf ("ahci", "AHCI is in compat mode. Switching\n"); + else + grub_dprintf ("ahci", "AHCI is in AHCI mode.\n"); + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + /* { + grub_uint64_t endtime; + hba->global_control |= 1; + endtime = grub_get_time_ms () + 1000; + while (hba->global_control & 1) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't reset AHCI\n"); + return 0; + } + }*/ + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + for (i = 0; i < 5; i++) + { + hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; + grub_millisleep (1); + if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN) + break; + } + if (i == 5) + { + grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n"); + return 0; + } + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + for (i = 0; i < 5; i++) + { + hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN; + grub_millisleep (1); + if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN) + break; + } + if (i == 5) + { + grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n"); + return 0; + } + + grub_dprintf ("ahci", "err[1]: %x\n", + hba->ports[1].sata_error); + + grub_dprintf ("ahci", "GLC:%x\n", hba->global_control); + + nports = (GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1; + + grub_dprintf ("ahci", "%d AHCI ports, PI = 0x%x\n", nports, + hba->ports_implemented); + + struct grub_ahci_device *adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1]; + struct grub_ahci_device *failed_adevs[GRUB_AHCI_HBA_CAP_NPORTS_MASK + 1]; + grub_uint32_t fr_running = 0; + + for (i = 0; i < nports; i++) + failed_adevs[i] = 0; + for (i = 0; i < nports; i++) + { + if (!(hba->ports_implemented & (1 << i))) + { + adevs[i] = 0; + continue; + } + + adevs[i] = grub_zalloc (sizeof (*adevs[i])); + if (!adevs[i]) + return 1; + + adevs[i]->hba = hba; + adevs[i]->port = i; + adevs[i]->present = 1; + adevs[i]->num = numdevs++; + } + + for (i = 0; i < nports; i++) + if (adevs[i]) + { + adevs[i]->hba->ports[adevs[i]->port].sata_error = adevs[i]->hba->ports[adevs[i]->port].sata_error; + grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + adevs[i]->command_list_chunk = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head) * 32); + if (!adevs[i]->command_list_chunk) + { + adevs[i] = 0; + continue; + } + + adevs[i]->command_table_chunk = grub_memalign_dma32 (1024, + sizeof (struct grub_ahci_cmd_table)); + if (!adevs[i]->command_table_chunk) + { + grub_dma_free (adevs[i]->command_list_chunk); + adevs[i] = 0; + continue; + } + + adevs[i]->command_list = grub_dma_get_virt (adevs[i]->command_list_chunk); + adevs[i]->command_table = grub_dma_get_virt (adevs[i]->command_table_chunk); + + grub_memset ((void *) adevs[i]->command_list, 0, + sizeof (struct grub_ahci_cmd_table)); + grub_memset ((void *) adevs[i]->command_table, 0, + sizeof (struct grub_ahci_cmd_head) * 32); + + adevs[i]->command_list->command_table_base + = grub_dma_get_phys (adevs[i]->command_table_chunk); + + grub_dprintf ("ahci", "found device ahci%d (port %d), command_table = %p, command_list = %p\n", + adevs[i]->num, adevs[i]->port, grub_dma_get_virt (adevs[i]->command_table_chunk), + grub_dma_get_virt (adevs[i]->command_list_chunk)); + + adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + } + + grub_uint64_t endtime; + endtime = grub_get_time_ms () + 1000; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + { + grub_dprintf ("ahci", "couldn't stop FR on port %d\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + + for (i = 0; i < nports; i++) + if (adevs[i]) + adevs[i]->hba->ports[adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + endtime = grub_get_time_ms () + 1000; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + { + grub_dprintf ("ahci", "couldn't stop CR on port %d\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + for (i = 0; i < nports; i++) + if (adevs[i]) + { + adevs[i]->hba->ports[adevs[i]->port].inten = 0; + adevs[i]->hba->ports[adevs[i]->port].intstatus = ~0; + // adevs[i]->hba->ports[adevs[i]->port].fbs = 0; + + grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + adevs[i]->rfis = grub_memalign_dma32 (4096, + sizeof (struct grub_ahci_received_fis)); + grub_memset ((char *) grub_dma_get_virt (adevs[i]->rfis), 0, + sizeof (struct grub_ahci_received_fis)); + grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_list_chunk), 0, + sizeof (struct grub_ahci_cmd_head)); + grub_memset ((char *) grub_dma_get_virt (adevs[i]->command_table_chunk), 0, + sizeof (struct grub_ahci_cmd_table)); + adevs[i]->hba->ports[adevs[i]->port].fis_base = grub_dma_get_phys (adevs[i]->rfis); + adevs[i]->hba->ports[adevs[i]->port].command_list_base + = grub_dma_get_phys (adevs[i]->command_list_chunk); + adevs[i]->hba->ports[adevs[i]->port].command_issue = 0; + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE; + } + + endtime = grub_get_time_ms () + 1000; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + { + grub_dprintf ("ahci", "couldn't start FR on port %d\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + + for (i = 0; i < nports; i++) + if (adevs[i]) + { + grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + fr_running |= (1 << i); + + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP; + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON; + adevs[i]->hba->ports[adevs[i]->port].command |= 1 << 28; + + grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + } + + /* 10ms should actually be enough. */ + endtime = grub_get_time_ms () + 100; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].status & 7) != 3) + { + grub_dprintf ("ahci", "couldn't detect device on port %d\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + + for (i = 0; i < nports; i++) + if (adevs[i]) + { + grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_POWER_ON; + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_SPIN_UP; + + grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + adevs[i]->hba->ports[adevs[i]->port].sata_error = ~0; + grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port, + (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - + (char *) adevs[i]->hba), + adevs[i]->hba->ports[adevs[i]->port].task_file_data, + adevs[i]->hba->ports[adevs[i]->port].command); + + grub_dprintf ("ahci", "port %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + } + + + for (i = 0; i < nports; i++) + if (adevs[i]) + { + grub_dprintf ("ahci", "port %d, offset: %x, tfd:%x, CMD: %x\n", adevs[i]->port, + (int) ((char *) &adevs[i]->hba->ports[adevs[i]->port].task_file_data - + (char *) adevs[i]->hba), + adevs[i]->hba->ports[adevs[i]->port].task_file_data, + adevs[i]->hba->ports[adevs[i]->port].command); + + grub_dprintf ("ahci", "port: %d, err: %x\n", adevs[i]->port, + adevs[i]->hba->ports[adevs[i]->port].sata_error); + + adevs[i]->hba->ports[adevs[i]->port].command + = (adevs[i]->hba->ports[adevs[i]->port].command & 0x0fffffff) | (1 << 28) + | GRUB_AHCI_HBA_PORT_CMD_SPIN_UP + | GRUB_AHCI_HBA_PORT_CMD_POWER_ON; + + /* struct grub_disk_ata_pass_through_parms parms2; + grub_memset (&parms2, 0, sizeof (parms2)); + parms2.taskfile.cmd = 8; + grub_ahci_readwrite_real (dev, &parms2, 1);*/ + } + + endtime = grub_get_time_ms () + 32000; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].task_file_data & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ))) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].task_file_data & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ))) + { + grub_dprintf ("ahci", "port %d is busy\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + + for (i = 0; i < nports; i++) + if (adevs[i]) + adevs[i]->hba->ports[adevs[i]->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST; + + endtime = grub_get_time_ms () + 1000; + + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + break; + if (i == nports) + break; + } + + for (i = 0; i < nports; i++) + if (adevs[i] && !(adevs[i]->hba->ports[adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + { + grub_dprintf ("ahci", "couldn't start CR on port %d\n", i); + failed_adevs[i] = adevs[i]; + adevs[i] = 0; + } + + grub_dprintf ("ahci", "cleaning up failed devs\n"); + + for (i = 0; i < nports; i++) + if (failed_adevs[i] && (fr_running & (1 << i))) + failed_adevs[i]->hba->ports[failed_adevs[i]->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + + endtime = grub_get_time_ms () + 1000; + while (grub_get_time_ms () < endtime) + { + for (i = 0; i < nports; i++) + if (failed_adevs[i] && (fr_running & (1 << i)) && (failed_adevs[i]->hba->ports[failed_adevs[i]->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + break; + if (i == nports) + break; + } + for (i = 0; i < nports; i++) + if (failed_adevs[i]) + { + grub_dma_free (failed_adevs[i]->command_list_chunk); + grub_dma_free (failed_adevs[i]->command_table_chunk); + grub_dma_free (failed_adevs[i]->rfis); + } + + for (i = 0; i < nports; i++) + if (adevs[i] && (adevs[i]->hba->ports[adevs[i]->port].sig >> 16) == 0xeb14) + adevs[i]->atapi = 1; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, grub_pci_read_word (addr) + | GRUB_PCI_COMMAND_BUS_MASTER); + + for (i = 0; i < nports; i++) + if (adevs[i]) + { + grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices), + GRUB_AS_LIST (adevs[i])); + } + + return 0; +} + +static grub_err_t +grub_ahci_initialize (void) +{ + grub_pci_iterate (grub_ahci_pciinit, NULL); + return grub_errno; +} + +static grub_err_t +grub_ahci_fini_hw (int noreturn __attribute__ ((unused))) +{ + struct grub_ahci_device *dev; + + for (dev = grub_ahci_devices; dev; dev = dev->next) + { + grub_uint64_t endtime; + + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop FR\n"); + break; + } + + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop CR\n"); + break; + } + grub_dma_free (dev->command_list_chunk); + grub_dma_free (dev->command_table_chunk); + grub_dma_free (dev->rfis); + dev->command_list_chunk = NULL; + dev->command_table_chunk = NULL; + dev->rfis = NULL; + } + return GRUB_ERR_NONE; +} + +static int +reinit_port (struct grub_ahci_device *dev) +{ + struct grub_pci_dma_chunk *command_list; + struct grub_pci_dma_chunk *command_table; + grub_uint64_t endtime; + + command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head)); + if (!command_list) + return 1; + + command_table = grub_memalign_dma32 (1024, + sizeof (struct grub_ahci_cmd_table)); + if (!command_table) + { + grub_dma_free (command_list); + return 1; + } + + grub_dprintf ("ahci", "found device ahci%d (port %d)\n", dev->num, dev->port); + + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop FR\n"); + goto out; + } + + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop CR\n"); + goto out; + } + + dev->hba->ports[dev->port].fbs = 2; + + dev->rfis = grub_memalign_dma32 (4096, + sizeof (struct grub_ahci_received_fis)); + grub_memset ((char *) grub_dma_get_virt (dev->rfis), 0, + sizeof (struct grub_ahci_received_fis)); + dev->hba->ports[dev->port].fis_base = grub_dma_get_phys (dev->rfis); + dev->hba->ports[dev->port].command_list_base + = grub_dma_get_phys (command_list); + dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE; + while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't start FR\n"); + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + goto out; + } + dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST; + while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't start CR\n"); + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_CR; + goto out_stop_fr; + } + + dev->hba->ports[dev->port].command + = (dev->hba->ports[dev->port].command & 0x0fffffff) | (1 << 28) | 2 | 4; + + dev->command_list_chunk = command_list; + dev->command_list = grub_dma_get_virt (command_list); + dev->command_table_chunk = command_table; + dev->command_table = grub_dma_get_virt (command_table); + dev->command_list->command_table_base + = grub_dma_get_phys (command_table); + + return 0; + out_stop_fr: + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop FR\n"); + break; + } + out: + grub_dma_free (command_list); + grub_dma_free (command_table); + grub_dma_free (dev->rfis); + return 1; +} + +static grub_err_t +grub_ahci_restore_hw (void) +{ + struct grub_ahci_device **pdev; + + for (pdev = &grub_ahci_devices; *pdev; pdev = &((*pdev)->next)) + if (reinit_port (*pdev)) + { + struct grub_ahci_device *odev; + odev = *pdev; + *pdev = (*pdev)->next; + grub_free (odev); + } + return GRUB_ERR_NONE; +} + + + + +static int +grub_ahci_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_ahci_device *dev; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + FOR_LIST_ELEMENTS(dev, grub_ahci_devices) + if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num, hook_data)) + return 1; + + return 0; +} + +#if 0 +static int +find_free_cmd_slot (struct grub_ahci_device *dev) +{ + int i; + for (i = 0; i < 32; i++) + { + if (dev->hda->ports[dev->port].command_issue & (1 << i)) + continue; + if (dev->hda->ports[dev->port].sata_active & (1 << i)) + continue; + return i; + } + return -1; +} +#endif + +enum + { + GRUB_AHCI_FIS_REG_H2D = 0x27 + }; + +static const int register_map[11] = { 3 /* Features */, + 12 /* Sectors */, + 4 /* LBA low */, + 5 /* LBA mid */, + 6 /* LBA high */, + 7 /* Device */, + 2 /* CMD register */, + 13 /* Sectors 48 */, + 8 /* LBA48 low */, + 9 /* LBA48 mid */, + 10 /* LBA48 high */ }; + +static grub_err_t +grub_ahci_reset_port (struct grub_ahci_device *dev, int force) +{ + grub_uint64_t endtime; + + dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error; + + if (force || (dev->hba->ports[dev->port].command_issue & 1) + || (dev->hba->ports[dev->port].task_file_data & 0x80)) + { + struct grub_disk_ata_pass_through_parms parms2; + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + dev->hba->ports[dev->port].command_issue = 0; + dev->command_list[0].config = 0; + dev->command_table[0].prdt[0].unused = 0; + dev->command_table[0].prdt[0].size = 0; + dev->command_table[0].prdt[0].data_base = 0; + + endtime = grub_get_time_ms () + 1000; + while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop CR"); + return grub_error (GRUB_ERR_IO, "couldn't stop CR"); + } + dev->hba->ports[dev->port].command |= 8; + while (dev->hba->ports[dev->port].command & 8) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't set CLO\n"); + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE; + return grub_error (GRUB_ERR_IO, "couldn't set CLO"); + } + + dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST; + while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR)) + if (grub_get_time_ms () > endtime) + { + grub_dprintf ("ahci", "couldn't stop CR"); + dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST; + return grub_error (GRUB_ERR_IO, "couldn't stop CR"); + } + dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error; + grub_memset (&parms2, 0, sizeof (parms2)); + parms2.taskfile.cmd = 8; + return grub_ahci_readwrite_real (dev, &parms2, 1); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ahci_readwrite_real (struct grub_ahci_device *dev, + struct grub_disk_ata_pass_through_parms *parms, int reset) +{ + struct grub_pci_dma_chunk *bufc; + grub_uint64_t endtime; + unsigned i; + grub_err_t err = GRUB_ERR_NONE; + + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + if (!reset) + grub_ahci_reset_port (dev, 0); + + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + dev->hba->ports[dev->port].task_file_data = 0; + dev->hba->ports[dev->port].command_issue = 0; + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error; + + grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n", + (unsigned long long) parms->size, + (unsigned long long) parms->cmdsize); + + if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16) + return grub_error (GRUB_ERR_BUG, "incorrect ATAPI command size"); + + if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH) + return grub_error (GRUB_ERR_BUG, "too big data buffer"); + + if (parms->size) + bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1)); + else + bufc = grub_memalign_dma32 (1024, 512); + + grub_dprintf ("ahci", "AHCI tfd = %x, CL=%p\n", + dev->hba->ports[dev->port].task_file_data, + dev->command_list); + /* FIXME: support port multipliers. */ + dev->command_list[0].config + = (5 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT) + // | GRUB_AHCI_CONFIG_CLEAR_R_OK + | (0 << GRUB_AHCI_CONFIG_PMP_SHIFT) + | ((parms->size ? 1 : 0) << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT) + | (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0) + | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ) + | (parms->taskfile.cmd == 8 ? (1 << 8) : 0); + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + dev->command_list[0].transferred = 0; + dev->command_list[0].command_table_base + = grub_dma_get_phys (dev->command_table_chunk); + + grub_memset ((char *) dev->command_list[0].unused, 0, + sizeof (dev->command_list[0].unused)); + + grub_memset ((char *) &dev->command_table[0], 0, + sizeof (dev->command_table[0])); + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + if (parms->cmdsize) + grub_memcpy ((char *) dev->command_table[0].command, parms->cmd, + parms->cmdsize); + + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D; + dev->command_table[0].cfis[1] = 0x80; + for (i = 0; i < sizeof (parms->taskfile.raw); i++) + dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; + + grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n", + dev->command_table[0].cfis[0], dev->command_table[0].cfis[1], + dev->command_table[0].cfis[2], dev->command_table[0].cfis[3], + dev->command_table[0].cfis[4], dev->command_table[0].cfis[5], + dev->command_table[0].cfis[6], dev->command_table[0].cfis[7]); + grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n", + dev->command_table[0].cfis[8], dev->command_table[0].cfis[9], + dev->command_table[0].cfis[10], dev->command_table[0].cfis[11], + dev->command_table[0].cfis[12], dev->command_table[0].cfis[13], + dev->command_table[0].cfis[14], dev->command_table[0].cfis[15]); + + dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc); + dev->command_table[0].prdt[0].unused = 0; + dev->command_table[0].prdt[0].size = (parms->size - 1); + + grub_dprintf ("ahci", "PRDT = %" PRIxGRUB_UINT64_T ", %x, %x (%" + PRIuGRUB_SIZE ")\n", + dev->command_table[0].prdt[0].data_base, + dev->command_table[0].prdt[0].unused, + dev->command_table[0].prdt[0].size, + (grub_size_t) ((char *) &dev->command_table[0].prdt[0] + - (char *) &dev->command_table[0])); + + if (parms->write) + grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size); + + grub_dprintf ("ahci", "AHCI command scheduled\n"); + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + grub_dprintf ("ahci", "AHCI inten = %x\n", + dev->hba->ports[dev->port].inten); + grub_dprintf ("ahci", "AHCI intstatus = %x\n", + dev->hba->ports[dev->port].intstatus); + + dev->hba->ports[dev->port].inten = 0xffffffff;//(1 << 2) | (1 << 5); + dev->hba->ports[dev->port].intstatus = 0xffffffff;//(1 << 2) | (1 << 5); + grub_dprintf ("ahci", "AHCI inten = %x\n", + dev->hba->ports[dev->port].inten); + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + dev->hba->ports[dev->port].sata_active = 1; + dev->hba->ports[dev->port].command_issue = 1; + grub_dprintf ("ahci", "AHCI sig = %x\n", dev->hba->ports[dev->port].sig); + grub_dprintf ("ahci", "AHCI tfd = %x\n", + dev->hba->ports[dev->port].task_file_data); + + endtime = grub_get_time_ms () + 20000; + while ((dev->hba->ports[dev->port].command_issue & 1)) + if (grub_get_time_ms () > endtime || + (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK)) + { + grub_dprintf ("ahci", "AHCI status <%x %x %x %x>\n", + dev->hba->ports[dev->port].command_issue, + dev->hba->ports[dev->port].sata_active, + dev->hba->ports[dev->port].intstatus, + dev->hba->ports[dev->port].task_file_data); + dev->hba->ports[dev->port].command_issue = 0; + if (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK) + err = grub_error (GRUB_ERR_IO, "AHCI transfer error"); + else + err = grub_error (GRUB_ERR_IO, "AHCI transfer timed out"); + if (!reset) + grub_ahci_reset_port (dev, 1); + break; + } + + grub_dprintf ("ahci", "AHCI command completed <%x %x %x %x %x, %x %x>\n", + dev->hba->ports[dev->port].command_issue, + dev->hba->ports[dev->port].intstatus, + dev->hba->ports[dev->port].task_file_data, + dev->command_list[0].transferred, + dev->hba->ports[dev->port].sata_error, + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x00], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x18]); + grub_dprintf ("ahci", + "last PIO FIS %08x %08x %08x %08x %08x %08x %08x %08x\n", + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x08], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x09], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0a], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0b], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0c], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0d], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0e], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0f]); + grub_dprintf ("ahci", + "last REG FIS %08x %08x %08x %08x %08x %08x %08x %08x\n", + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x10], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x11], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x12], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x13], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x14], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x15], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x16], + ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x17]); + + if (!parms->write) + grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size); + grub_dma_free (bufc); + + return err; +} + +static grub_err_t +grub_ahci_readwrite (grub_ata_t disk, + struct grub_disk_ata_pass_through_parms *parms, + int spinup __attribute__((__unused__))) +{ + return grub_ahci_readwrite_real (disk->data, parms, 0); +} + +static grub_err_t +grub_ahci_open (int id, int devnum, struct grub_ata *ata) +{ + struct grub_ahci_device *dev; + + if (id != GRUB_SCSI_SUBSYSTEM_AHCI) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device"); + + FOR_LIST_ELEMENTS(dev, grub_ahci_devices) + if (dev->num == devnum) + break; + + if (! dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device"); + + grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num); + + ata->data = dev; + ata->dma = 1; + ata->atapi = dev->atapi; + ata->maxbuffer = GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH; + ata->present = &dev->present; + + return GRUB_ERR_NONE; +} + +static struct grub_ata_dev grub_ahci_dev = + { + .iterate = grub_ahci_iterate, + .open = grub_ahci_open, + .readwrite = grub_ahci_readwrite, + }; + + + +static struct grub_preboot *fini_hnd; + +GRUB_MOD_INIT(ahci) +{ + grub_stop_disk_firmware (); + + /* AHCI initialization. */ + grub_ahci_initialize (); + + /* AHCI devices are handled by scsi.mod. */ + grub_ata_dev_register (&grub_ahci_dev); + + fini_hnd = grub_loader_register_preboot_hook (grub_ahci_fini_hw, + grub_ahci_restore_hw, + GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); +} + +GRUB_MOD_FINI(ahci) +{ + grub_ahci_fini_hw (0); + grub_loader_unregister_preboot_hook (fini_hnd); + + grub_ata_dev_unregister (&grub_ahci_dev); +} diff --git a/grub-core/disk/arc/arcdisk.c b/grub-core/disk/arc/arcdisk.c new file mode 100644 index 000000000..c94039a3d --- /dev/null +++ b/grub-core/disk/arc/arcdisk.c @@ -0,0 +1,325 @@ +/* ofdisk.c - Open Firmware disk access. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,2006,2007,2008,2009,2011 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 . + */ + +#include +#include +#include +#include +#include + +static grub_arc_fileno_t last_handle = 0; +static char *last_path = NULL; +static int handle_writable = 0; + +static int lnum = 0; + +struct arcdisk_hash_ent +{ + char *devpath; + int num; + struct arcdisk_hash_ent *next; +}; + +#define ARCDISK_HASH_SZ 8 +static struct arcdisk_hash_ent *arcdisk_hash[ARCDISK_HASH_SZ]; + +static int +arcdisk_hash_fn (const char *devpath) +{ + int hash = 0; + while (*devpath) + hash ^= *devpath++; + return (hash & (ARCDISK_HASH_SZ - 1)); +} + +static struct arcdisk_hash_ent * +arcdisk_hash_find (const char *devpath) +{ + struct arcdisk_hash_ent *p = arcdisk_hash[arcdisk_hash_fn (devpath)]; + + while (p) + { + if (!grub_strcmp (p->devpath, devpath)) + break; + p = p->next; + } + return p; +} + +static struct arcdisk_hash_ent * +arcdisk_hash_add (char *devpath) +{ + struct arcdisk_hash_ent *p; + struct arcdisk_hash_ent **head = &arcdisk_hash[arcdisk_hash_fn(devpath)]; + + p = grub_malloc (sizeof (*p)); + if (!p) + return NULL; + + p->devpath = devpath; + p->next = *head; + p->num = lnum++; + *head = p; + return p; +} + + +/* Context for grub_arcdisk_iterate. */ +struct grub_arcdisk_iterate_ctx +{ + grub_disk_dev_iterate_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_arcdisk_iterate. */ +static int +grub_arcdisk_iterate_iter (const char *name, + const struct grub_arc_component *comp, void *data) +{ + struct grub_arcdisk_iterate_ctx *ctx = data; + + if (!(comp->type == GRUB_ARC_COMPONENT_TYPE_DISK + || comp->type == GRUB_ARC_COMPONENT_TYPE_FLOPPY + || comp->type == GRUB_ARC_COMPONENT_TYPE_TAPE)) + return 0; + return ctx->hook (name, ctx->hook_data); +} + +static int +grub_arcdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_arcdisk_iterate_ctx ctx = { hook, hook_data }; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + return grub_arc_iterate_devs (grub_arcdisk_iterate_iter, &ctx, 1); +} + +#ifdef GRUB_CPU_MIPSEL +#define RAW_SUFFIX "partition(0)" +#else +#define RAW_SUFFIX "partition(10)" +#endif + +static grub_err_t +reopen (const char *name, int writable) +{ + grub_arc_fileno_t handle; + + if (last_path && grub_strcmp (last_path, name) == 0 + && (!writable || handle_writable)) + { + grub_dprintf ("arcdisk", "using already opened %s\n", name); + return GRUB_ERR_NONE; + } + if (last_path) + { + GRUB_ARC_FIRMWARE_VECTOR->close (last_handle); + grub_free (last_path); + last_path = NULL; + last_handle = 0; + handle_writable = 0; + } + if (GRUB_ARC_FIRMWARE_VECTOR->open (name, + writable ? GRUB_ARC_FILE_ACCESS_OPEN_RW + : GRUB_ARC_FILE_ACCESS_OPEN_RO, &handle)) + { + grub_dprintf ("arcdisk", "couldn't open %s\n", name); + return grub_error (GRUB_ERR_IO, "couldn't open %s", name); + } + handle_writable = writable; + last_path = grub_strdup (name); + if (!last_path) + return grub_errno; + last_handle = handle; + grub_dprintf ("arcdisk", "opened %s\n", name); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_arcdisk_open (const char *name, grub_disk_t disk) +{ + char *fullname; + grub_err_t err; + grub_arc_err_t r; + struct grub_arc_fileinfo info; + struct arcdisk_hash_ent *hash; + + if (grub_memcmp (name, "arc/", 4) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not arc device"); + fullname = grub_arc_alt_name_to_norm (name, RAW_SUFFIX); + disk->data = fullname; + grub_dprintf ("arcdisk", "opening %s\n", fullname); + + hash = arcdisk_hash_find (fullname); + if (!hash) + hash = arcdisk_hash_add (fullname); + if (!hash) + return grub_errno; + + err = reopen (fullname, 0); + if (err) + return err; + + r = GRUB_ARC_FIRMWARE_VECTOR->getfileinformation (last_handle, &info); + if (r) + { + grub_uint64_t res = 0; + int i; + + grub_dprintf ("arcdisk", "couldn't retrieve size: %ld\n", r); + for (i = 40; i >= 9; i--) + { + grub_uint64_t pos = res | (1ULL << i); + char buf[512]; + long unsigned count = 0; + grub_dprintf ("arcdisk", + "seek to 0x%" PRIxGRUB_UINT64_T "\n", pos); + if (GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0)) + continue; + if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf, + 0x200, &count)) + continue; + if (count == 0) + continue; + res |= (1ULL << i); + } + grub_dprintf ("arcdisk", + "determined disk size 0x%" PRIxGRUB_UINT64_T "\n", res); + disk->total_sectors = (res + 0x200) >> 9; + } + else + disk->total_sectors = (info.end >> 9); + + disk->id = hash->num; + return GRUB_ERR_NONE; +} + +static void +grub_arcdisk_close (grub_disk_t disk) +{ + grub_free (disk->data); +} + +static grub_err_t +grub_arcdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_err_t err; + grub_uint64_t pos = sector << 9; + unsigned long count; + grub_uint64_t totl = size << 9; + grub_arc_err_t r; + + err = reopen (disk->data, 0); + if (err) + return err; + r = GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); + if (r) + { + grub_dprintf ("arcdisk", "seek to 0x%" PRIxGRUB_UINT64_T " failed: %ld\n", + pos, r); + return grub_error (GRUB_ERR_IO, "couldn't seek"); + } + + while (totl) + { + if (GRUB_ARC_FIRMWARE_VECTOR->read (last_handle, buf, + totl, &count)) + return grub_error (GRUB_ERR_READ_ERROR, + N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + totl -= count; + buf += count; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_arcdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_err_t err; + grub_uint64_t pos = sector << 9; + unsigned long count; + grub_uint64_t totl = size << 9; + grub_arc_err_t r; + + err = reopen (disk->data, 1); + if (err) + return err; + r = GRUB_ARC_FIRMWARE_VECTOR->seek (last_handle, &pos, 0); + if (r) + { + grub_dprintf ("arcdisk", "seek to 0x%" PRIxGRUB_UINT64_T " failed: %ld\n", + pos, r); + return grub_error (GRUB_ERR_IO, "couldn't seek"); + } + + while (totl) + { + if (GRUB_ARC_FIRMWARE_VECTOR->write (last_handle, buf, + totl, &count)) + return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " + "to `%s'"), + (unsigned long long) sector, + disk->name); + totl -= count; + buf += count; + } + + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_arcdisk_dev = + { + .name = "arcdisk", + .id = GRUB_DISK_DEVICE_ARCDISK_ID, + .disk_iterate = grub_arcdisk_iterate, + .disk_open = grub_arcdisk_open, + .disk_close = grub_arcdisk_close, + .disk_read = grub_arcdisk_read, + .disk_write = grub_arcdisk_write, + .next = 0 + }; + +void +grub_arcdisk_init (void) +{ + grub_disk_dev_register (&grub_arcdisk_dev); +} + +void +grub_arcdisk_fini (void) +{ + if (last_path) + { + GRUB_ARC_FIRMWARE_VECTOR->close (last_handle); + grub_free (last_path); + last_path = NULL; + last_handle = 0; + } + + grub_disk_dev_unregister (&grub_arcdisk_dev); +} diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c new file mode 100644 index 000000000..a2433e29e --- /dev/null +++ b/grub-core/disk/ata.c @@ -0,0 +1,682 @@ +/* ata.c - ATA disk access. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_ata_dev_t grub_ata_dev_list; + +/* Byteorder has to be changed before strings can be read. */ +static void +grub_ata_strncpy (grub_uint16_t *dst16, grub_uint16_t *src16, grub_size_t len) +{ + unsigned int i; + + for (i = 0; i < len / 2; i++) + *(dst16++) = grub_swap_bytes16 (*(src16++)); + *dst16 = 0; +} + +static void +grub_ata_dumpinfo (struct grub_ata *dev, grub_uint16_t *info) +{ + grub_uint16_t text[21]; + + /* The device information was read, dump it for debugging. */ + grub_ata_strncpy (text, info + 10, 20); + grub_dprintf ("ata", "Serial: %s\n", (char *) text); + grub_ata_strncpy (text, info + 23, 8); + grub_dprintf ("ata", "Firmware: %s\n", (char *) text); + grub_ata_strncpy (text, info + 27, 40); + grub_dprintf ("ata", "Model: %s\n", (char *) text); + + if (! dev->atapi) + { + grub_dprintf ("ata", "Addressing: %d\n", dev->addr); + grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size); + grub_dprintf ("ata", "Sector size: %u\n", 1U << dev->log_sector_size); + } +} + +static grub_err_t +grub_atapi_identify (struct grub_ata *dev) +{ + struct grub_disk_ata_pass_through_parms parms; + grub_uint16_t *info; + grub_err_t err; + + info = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (! info) + return grub_errno; + + grub_memset (&parms, 0, sizeof (parms)); + parms.taskfile.disk = 0xE0; + parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE; + parms.size = GRUB_DISK_SECTOR_SIZE; + parms.buffer = info; + + err = dev->dev->readwrite (dev, &parms, *dev->present); + if (err) + { + *dev->present = 0; + return err; + } + + if (parms.size != GRUB_DISK_SECTOR_SIZE) + { + *dev->present = 0; + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "device cannot be identified"); + } + + dev->atapi = 1; + + grub_ata_dumpinfo (dev, info); + + grub_free (info); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ata_identify (struct grub_ata *dev) +{ + struct grub_disk_ata_pass_through_parms parms; + grub_uint64_t *info64; + grub_uint32_t *info32; + grub_uint16_t *info16; + grub_err_t err; + + if (dev->atapi) + return grub_atapi_identify (dev); + + info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (info64 == NULL) + return grub_errno; + info32 = (grub_uint32_t *) info64; + info16 = (grub_uint16_t *) info64; + + grub_memset (&parms, 0, sizeof (parms)); + parms.buffer = info16; + parms.size = GRUB_DISK_SECTOR_SIZE; + parms.taskfile.disk = 0xE0; + + parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE; + + err = dev->dev->readwrite (dev, &parms, *dev->present); + + if (err || parms.size != GRUB_DISK_SECTOR_SIZE) + { + grub_uint8_t sts = parms.taskfile.status; + grub_free (info16); + grub_errno = GRUB_ERR_NONE; + if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ + | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR + && (parms.taskfile.error & 0x04 /* ABRT */)) + /* Device without ATA IDENTIFY, try ATAPI. */ + return grub_atapi_identify (dev); + + else if (sts == 0x00) + { + *dev->present = 0; + /* No device, return error but don't print message. */ + return GRUB_ERR_UNKNOWN_DEVICE; + } + else + { + *dev->present = 0; + /* Other Error. */ + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "device cannot be identified"); + } + } + + /* Now it is certain that this is not an ATAPI device. */ + dev->atapi = 0; + + /* CHS is always supported. */ + dev->addr = GRUB_ATA_CHS; + + /* Check if LBA is supported. */ + if (info16[49] & grub_cpu_to_le16_compile_time ((1 << 9))) + { + /* Check if LBA48 is supported. */ + if (info16[83] & grub_cpu_to_le16_compile_time ((1 << 10))) + dev->addr = GRUB_ATA_LBA48; + else + dev->addr = GRUB_ATA_LBA; + } + + /* Determine the amount of sectors. */ + if (dev->addr != GRUB_ATA_LBA48) + dev->size = grub_le_to_cpu32 (info32[30]); + else + dev->size = grub_le_to_cpu64 (info64[25]); + + if (info16[106] & grub_cpu_to_le16_compile_time ((1 << 12))) + { + grub_uint32_t secsize; + secsize = grub_le_to_cpu32 (grub_get_unaligned32 (&info16[117])); + if (secsize & (secsize - 1) || !secsize + || secsize > 1048576) + secsize = 256; + dev->log_sector_size = grub_log2ull (secsize) + 1; + } + else + dev->log_sector_size = 9; + + /* Read CHS information. */ + dev->cylinders = grub_le_to_cpu16 (info16[1]); + dev->heads = grub_le_to_cpu16 (info16[3]); + dev->sectors_per_track = grub_le_to_cpu16 (info16[6]); + + grub_ata_dumpinfo (dev, info16); + + grub_free (info16); + + return 0; +} + +static grub_err_t +grub_ata_setaddress (struct grub_ata *dev, + struct grub_disk_ata_pass_through_parms *parms, + grub_disk_addr_t sector, + grub_size_t size, + grub_ata_addressing_t addressing) +{ + switch (addressing) + { + case GRUB_ATA_CHS: + { + unsigned int cylinder; + unsigned int head; + unsigned int sect; + + if (dev->sectors_per_track == 0 + || dev->heads == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "sector %" PRIxGRUB_UINT64_T " cannot be " + "addressed using CHS addressing", + sector); + + /* Calculate the sector, cylinder and head to use. */ + sect = ((grub_uint32_t) sector % dev->sectors_per_track) + 1; + cylinder = (((grub_uint32_t) sector / dev->sectors_per_track) + / dev->heads); + head = ((grub_uint32_t) sector / dev->sectors_per_track) % dev->heads; + + if (sect > dev->sectors_per_track + || cylinder > dev->cylinders + || head > dev->heads) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "sector %" PRIxGRUB_UINT64_T " cannot be " + "addressed using CHS addressing", + sector); + + parms->taskfile.disk = 0xE0 | head; + parms->taskfile.sectnum = sect; + parms->taskfile.cyllsb = cylinder & 0xFF; + parms->taskfile.cylmsb = cylinder >> 8; + + break; + } + + case GRUB_ATA_LBA: + if (size == 256) + size = 0; + parms->taskfile.disk = 0xE0 | ((sector >> 24) & 0x0F); + + parms->taskfile.sectors = size; + parms->taskfile.lba_low = sector & 0xFF; + parms->taskfile.lba_mid = (sector >> 8) & 0xFF; + parms->taskfile.lba_high = (sector >> 16) & 0xFF; + break; + + case GRUB_ATA_LBA48: + if (size == 65536) + size = 0; + + parms->taskfile.disk = 0xE0; + + /* Set "Previous". */ + parms->taskfile.sectors = size & 0xFF; + parms->taskfile.lba_low = sector & 0xFF; + parms->taskfile.lba_mid = (sector >> 8) & 0xFF; + parms->taskfile.lba_high = (sector >> 16) & 0xFF; + + /* Set "Current". */ + parms->taskfile.sectors48 = (size >> 8) & 0xFF; + parms->taskfile.lba48_low = (sector >> 24) & 0xFF; + parms->taskfile.lba48_mid = (sector >> 32) & 0xFF; + parms->taskfile.lba48_high = (sector >> 40) & 0xFF; + + break; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf, int rw) +{ + struct grub_ata *ata = disk->data; + + grub_ata_addressing_t addressing = ata->addr; + grub_size_t batch; + int cmd, cmd_write; + grub_size_t nsectors = 0; + + grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", + (unsigned long long) size, rw); + + if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0) + { + if (ata->dma) + { + cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT; + } + else + { + cmd = GRUB_ATA_CMD_READ_SECTORS_EXT; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT; + } + } + else + { + if (addressing == GRUB_ATA_LBA48) + addressing = GRUB_ATA_LBA; + if (ata->dma) + { + cmd = GRUB_ATA_CMD_READ_SECTORS_DMA; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA; + } + else + { + cmd = GRUB_ATA_CMD_READ_SECTORS; + cmd_write = GRUB_ATA_CMD_WRITE_SECTORS; + } + } + + if (addressing != GRUB_ATA_CHS) + batch = 256; + else + batch = 1; + + while (nsectors < size) + { + struct grub_disk_ata_pass_through_parms parms; + grub_err_t err; + + if (size - nsectors < batch) + batch = size - nsectors; + + grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch); + grub_memset (&parms, 0, sizeof (parms)); + grub_ata_setaddress (ata, &parms, sector, batch, addressing); + parms.taskfile.cmd = (! rw ? cmd : cmd_write); + parms.buffer = buf; + parms.size = batch << ata->log_sector_size; + parms.write = rw; + if (ata->dma) + parms.dma = 1; + + err = ata->dev->readwrite (ata, &parms, 0); + if (err) + return err; + if (parms.size != batch << ata->log_sector_size) + return grub_error (GRUB_ERR_READ_ERROR, "incomplete read"); + buf += batch << ata->log_sector_size; + sector += batch; + nsectors += batch; + } + + return GRUB_ERR_NONE; +} + + + +static inline void +grub_ata_real_close (struct grub_ata *ata) +{ + if (ata->dev->close) + ata->dev->close (ata); +} + +static struct grub_ata * +grub_ata_real_open (int id, int bus) +{ + struct grub_ata *ata; + grub_ata_dev_t p; + + ata = grub_zalloc (sizeof (*ata)); + if (!ata) + return NULL; + for (p = grub_ata_dev_list; p; p = p->next) + { + grub_err_t err; + if (p->open (id, bus, ata)) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + ata->dev = p; + /* Use the IDENTIFY DEVICE command to query the device. */ + err = grub_ata_identify (ata); + if (err) + { + if (!grub_errno) + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device"); + grub_free (ata); + return NULL; + } + return ata; + } + grub_free (ata); + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device"); + return NULL; +} + +/* Context for grub_ata_iterate. */ +struct grub_ata_iterate_ctx +{ + grub_disk_dev_iterate_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_ata_iterate. */ +static int +grub_ata_iterate_iter (int id, int bus, void *data) +{ + struct grub_ata_iterate_ctx *ctx = data; + struct grub_ata *ata; + int ret; + char devname[40]; + + ata = grub_ata_real_open (id, bus); + + if (!ata) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (ata->atapi) + { + grub_ata_real_close (ata); + return 0; + } + grub_snprintf (devname, sizeof (devname), + "%s%d", grub_scsi_names[id], bus); + ret = ctx->hook (devname, ctx->hook_data); + grub_ata_real_close (ata); + return ret; +} + +static int +grub_ata_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_ata_iterate_ctx ctx = { hook, hook_data }; + grub_ata_dev_t p; + + for (p = grub_ata_dev_list; p; p = p->next) + if (p->iterate && p->iterate (grub_ata_iterate_iter, &ctx, pull)) + return 1; + return 0; +} + +static grub_err_t +grub_ata_open (const char *name, grub_disk_t disk) +{ + unsigned id, bus; + struct grub_ata *ata; + + for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++) + if (grub_strncmp (grub_scsi_names[id], name, + grub_strlen (grub_scsi_names[id])) == 0 + && grub_isdigit (name[grub_strlen (grub_scsi_names[id])])) + break; + if (id == GRUB_SCSI_NUM_SUBSYSTEMS) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); + bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0); + ata = grub_ata_real_open (id, bus); + if (!ata) + return grub_errno; + + if (ata->atapi) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk"); + + disk->total_sectors = ata->size; + disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS)); + if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size))) + disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)); + + disk->log_sector_size = ata->log_sector_size; + + disk->id = grub_make_scsi_id (id, bus, 0); + + disk->data = ata; + + return 0; +} + +static void +grub_ata_close (grub_disk_t disk) +{ + struct grub_ata *ata = disk->data; + grub_ata_real_close (ata); +} + +static grub_err_t +grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + return grub_ata_readwrite (disk, sector, size, buf, 0); +} + +static grub_err_t +grub_ata_write (grub_disk_t disk, + grub_disk_addr_t sector, + grub_size_t size, + const char *buf) +{ + return grub_ata_readwrite (disk, sector, size, (char *) buf, 1); +} + +static struct grub_disk_dev grub_atadisk_dev = + { + .name = "ATA", + .id = GRUB_DISK_DEVICE_ATA_ID, + .disk_iterate = grub_ata_iterate, + .disk_open = grub_ata_open, + .disk_close = grub_ata_close, + .disk_read = grub_ata_read, + .disk_write = grub_ata_write, + .next = 0 + }; + + + +/* ATAPI code. */ + +static grub_err_t +grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf) +{ + struct grub_ata *dev = scsi->data; + struct grub_disk_ata_pass_through_parms parms; + grub_err_t err; + + grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size); + grub_memset (&parms, 0, sizeof (parms)); + + parms.taskfile.disk = 0; + parms.taskfile.features = 0; + parms.taskfile.atapi_ireason = 0; + parms.taskfile.atapi_cnthigh = size >> 8; + parms.taskfile.atapi_cntlow = size & 0xff; + parms.taskfile.cmd = GRUB_ATA_CMD_PACKET; + parms.cmd = cmd; + parms.cmdsize = cmdsize; + + parms.size = size; + parms.buffer = buf; + + err = dev->dev->readwrite (dev, &parms, 0); + if (err) + return err; + + if (parms.size != size) + return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)), + grub_size_t cmdsize __attribute__((unused)), + char *cmd __attribute__((unused)), + grub_size_t size __attribute__((unused)), + const char *buf __attribute__((unused))) +{ + // XXX: scsi.mod does not use write yet. + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented"); +} + +static grub_err_t +grub_atapi_open (int id, int bus, struct grub_scsi *scsi) +{ + struct grub_ata *ata; + + ata = grub_ata_real_open (id, bus); + if (!ata) + return grub_errno; + + if (! ata->atapi) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device"); + + scsi->data = ata; + scsi->luns = 1; + + return GRUB_ERR_NONE; +} + +/* Context for grub_atapi_iterate. */ +struct grub_atapi_iterate_ctx +{ + grub_scsi_dev_iterate_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_atapi_iterate. */ +static int +grub_atapi_iterate_iter (int id, int bus, void *data) +{ + struct grub_atapi_iterate_ctx *ctx = data; + struct grub_ata *ata; + int ret; + + ata = grub_ata_real_open (id, bus); + + if (!ata) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (!ata->atapi) + { + grub_ata_real_close (ata); + return 0; + } + ret = ctx->hook (id, bus, 1, ctx->hook_data); + grub_ata_real_close (ata); + return ret; +} + +static int +grub_atapi_iterate (grub_scsi_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_atapi_iterate_ctx ctx = { hook, hook_data }; + grub_ata_dev_t p; + + for (p = grub_ata_dev_list; p; p = p->next) + if (p->iterate && p->iterate (grub_atapi_iterate_iter, &ctx, pull)) + return 1; + return 0; +} + +static void +grub_atapi_close (grub_scsi_t disk) +{ + struct grub_ata *ata = disk->data; + grub_ata_real_close (ata); +} + + +void +grub_ata_dev_register (grub_ata_dev_t dev) +{ + dev->next = grub_ata_dev_list; + grub_ata_dev_list = dev; +} + +void +grub_ata_dev_unregister (grub_ata_dev_t dev) +{ + grub_ata_dev_t *p, q; + + for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next) + if (q == dev) + { + *p = q->next; + break; + } +} + +static struct grub_scsi_dev grub_atapi_dev = + { + .iterate = grub_atapi_iterate, + .open = grub_atapi_open, + .close = grub_atapi_close, + .read = grub_atapi_read, + .write = grub_atapi_write, + .next = 0 + }; + + + +GRUB_MOD_INIT(ata) +{ + grub_disk_dev_register (&grub_atadisk_dev); + + /* ATAPI devices are handled by scsi.mod. */ + grub_scsi_dev_register (&grub_atapi_dev); +} + +GRUB_MOD_FINI(ata) +{ + grub_scsi_dev_unregister (&grub_atapi_dev); + grub_disk_dev_unregister (&grub_atadisk_dev); +} diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c new file mode 100644 index 000000000..7065bcdcb --- /dev/null +++ b/grub-core/disk/cryptodisk.c @@ -0,0 +1,1916 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011,2019 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_UTIL +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_cryptodisk_dev_t grub_cryptodisk_list; + +enum + { + OPTION_UUID, + OPTION_ALL, + OPTION_BOOT, + OPTION_PASSWORD, + OPTION_KEYFILE, + OPTION_KEYFILE_OFFSET, + OPTION_KEYFILE_SIZE, + OPTION_HEADER, + OPTION_PROTECTOR + }; + +static const struct grub_arg_option options[] = + { + {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, + /* TRANSLATORS: It's still restricted to cryptodisks only. */ + {"all", 'a', 0, N_("Mount all."), 0, 0}, + {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0}, + {"password", 'p', 0, N_("Password to open volumes."), 0, ARG_TYPE_STRING}, + {"key-file", 'k', 0, N_("Key file"), 0, ARG_TYPE_STRING}, + {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT}, + {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, ARG_TYPE_INT}, + {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING}, + {"protector", 'P', GRUB_ARG_OPTION_REPEATABLE, + N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +struct cryptodisk_read_hook_ctx +{ + grub_file_t hdr_file; + grub_disk_addr_t part_start; +}; +typedef struct cryptodisk_read_hook_ctx *cryptodisk_read_hook_ctx_t; + +/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ +#define GF_POLYNOM 0x87 +static inline int GF_PER_SECTOR (const struct grub_cryptodisk *dev) +{ + return 1U << (dev->log_sector_size - GRUB_CRYPTODISK_GF_LOG_BYTES); +} + +static grub_cryptodisk_t cryptodisk_list = NULL; +static grub_uint8_t last_cryptodisk_id = 0; + +static void +gf_mul_x (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + unsigned j; + + for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[0] ^= GF_POLYNOM; +} + + +static void +gf_mul_x_be (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = (int) GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[GRUB_CRYPTODISK_GF_BYTES - 1] ^= GF_POLYNOM; +} + +static void +gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) +{ + unsigned i; + grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES]; + grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES); + grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES); + for (i = 0; i < GRUB_CRYPTODISK_GF_SIZE; i++) + { + if (((a[GRUB_CRYPTODISK_GF_BYTES - i / GRUB_CHAR_BIT - 1] >> (i % GRUB_CHAR_BIT))) & 1) + grub_crypto_xor (o, o, t, GRUB_CRYPTODISK_GF_BYTES); + gf_mul_x_be (t); + } +} + +static gcry_err_code_t +grub_crypto_pcbc_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[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; + grub_size_t blocksize; + if (!cipher->cipher->decrypt) + return GPG_ERR_NOT_SUPPORTED; + blocksize = cipher->cipher->blocksize; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) + return GPG_ERR_INV_ARG; + if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += blocksize, outptr += blocksize) + { + grub_memcpy (ivt, inptr, blocksize); + cipher->cipher->decrypt (cipher->ctx, outptr, inptr); + grub_crypto_xor (outptr, outptr, iv, blocksize); + grub_crypto_xor (iv, ivt, outptr, blocksize); + } + return GPG_ERR_NO_ERROR; +} + +static gcry_err_code_t +grub_crypto_pcbc_encrypt (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[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; + grub_size_t blocksize; + if (!cipher->cipher->encrypt) + return GPG_ERR_NOT_SUPPORTED; + blocksize = cipher->cipher->blocksize; + if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) + return GPG_ERR_INV_ARG; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) + return GPG_ERR_INV_ARG; + end = (grub_uint8_t *) in + size; + for (inptr = in, outptr = out; inptr < end; + inptr += blocksize, outptr += blocksize) + { + grub_memcpy (ivt, inptr, blocksize); + grub_crypto_xor (outptr, outptr, iv, blocksize); + cipher->cipher->encrypt (cipher->ctx, outptr, inptr); + grub_crypto_xor (iv, ivt, outptr, blocksize); + } + return GPG_ERR_NO_ERROR; +} + +struct lrw_sector +{ + grub_uint8_t low[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint8_t high[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint8_t low_byte, low_byte_c; +}; + +static void +generate_lrw_sector (struct lrw_sector *sec, + const struct grub_cryptodisk *dev, + const grub_uint8_t *iv) +{ + grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; + grub_uint16_t c; + int j; + grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES); + sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] + & (GF_PER_SECTOR (dev) - 1)); + sec->low_byte_c = (((GF_PER_SECTOR (dev) - 1) & ~sec->low_byte) + 1); + idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR (dev) - 1); + gf_mul_be (sec->low, dev->lrw_key, idx); + if (!sec->low_byte) + return; + + c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR (dev); + if (c & 0x100) + { + for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--) + { + idx[j]++; + if (idx[j] != 0) + break; + } + } + idx[GRUB_CRYPTODISK_GF_BYTES - 1] = c; + gf_mul_be (sec->high, dev->lrw_key, idx); +} + +static void __attribute__ ((unused)) +lrw_xor (const struct lrw_sector *sec, + const struct grub_cryptodisk *dev, + grub_uint8_t *b) +{ + unsigned i; + + for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; + i += GRUB_CRYPTODISK_GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES); + grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte, + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES); + if (!sec->low_byte) + return; + + for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; + i < (1U << dev->log_sector_size); i += GRUB_CRYPTODISK_GF_BYTES) + grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES); + grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, + b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, + dev->lrw_precalc, sec->low_byte * GRUB_CRYPTODISK_GF_BYTES); +} + +static gcry_err_code_t +grub_cryptodisk_endecrypt (struct grub_cryptodisk *dev, + grub_uint8_t * data, grub_size_t len, + grub_disk_addr_t sector, grub_size_t log_sector_size, + int do_encrypt) +{ + grub_size_t i; + gcry_err_code_t err; + + if (dev->cipher->cipher->blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) + return GPG_ERR_INV_ARG; + + /* The only mode without IV. */ + if (dev->mode == GRUB_CRYPTODISK_MODE_ECB && !dev->rekey) + return (do_encrypt ? grub_crypto_ecb_encrypt (dev->cipher, data, data, len) + : grub_crypto_ecb_decrypt (dev->cipher, data, data, len)); + + for (i = 0; i < len; i += ((grub_size_t) 1 << log_sector_size)) + { + grub_size_t sz = ((dev->cipher->cipher->blocksize + + sizeof (grub_uint32_t) - 1) + / sizeof (grub_uint32_t)); + grub_uint32_t iv[(GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE + 3) / 4]; + + if (dev->rekey) + { + grub_uint64_t zone = sector >> dev->rekey_shift; + if (zone != dev->last_rekey) + { + err = dev->rekey (dev, zone); + if (err) + return err; + dev->last_rekey = zone; + } + } + + grub_memset (iv, 0, sizeof (iv)); + switch (dev->mode_iv) + { + case GRUB_CRYPTODISK_MODE_IV_NULL: + break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH: + { + grub_uint64_t tmp; + void *ctx; + + ctx = grub_zalloc (dev->iv_hash->contextsize); + if (!ctx) + return GPG_ERR_OUT_OF_MEMORY; + + tmp = grub_cpu_to_le64 (sector << log_sector_size); + dev->iv_hash->init (ctx); + dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len); + dev->iv_hash->write (ctx, &tmp, sizeof (tmp)); + dev->iv_hash->final (ctx); + + grub_memcpy (iv, dev->iv_hash->read (ctx), sizeof (iv)); + grub_free (ctx); + } + break; + case GRUB_CRYPTODISK_MODE_IV_PLAIN64: + case GRUB_CRYPTODISK_MODE_IV_PLAIN: + /* + * The IV is a 32 or 64 bit value of the dm-crypt native sector + * number. If using 32 bit IV mode, zero out the most significant + * 32 bits. + */ + { + grub_uint64_t iv64; + + iv64 = grub_cpu_to_le64 (sector << (log_sector_size + - GRUB_CRYPTODISK_IV_LOG_SIZE)); + grub_set_unaligned64 (iv, iv64); + if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_PLAIN) + iv[1] = 0; + } + break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: + /* The IV is the 64 bit byte offset of the sector. */ + iv[1] = grub_cpu_to_le32 (sector >> (GRUB_TYPE_BITS (iv[1]) + - log_sector_size)); + iv[0] = grub_cpu_to_le32 ((sector << log_sector_size) + & GRUB_TYPE_U_MAX (iv[0])); + break; + case GRUB_CRYPTODISK_MODE_IV_BENBI: + { + grub_uint64_t num = (sector << dev->benbi_log) + 1; + iv[sz - 2] = grub_cpu_to_be32 (num >> GRUB_TYPE_BITS (iv[0])); + iv[sz - 1] = grub_cpu_to_be32 (num & GRUB_TYPE_U_MAX (iv[0])); + } + break; + case GRUB_CRYPTODISK_MODE_IV_ESSIV: + iv[0] = grub_cpu_to_le32 (sector & GRUB_TYPE_U_MAX (iv[0])); + err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + } + + switch (dev->mode) + { + case GRUB_CRYPTODISK_MODE_CBC: + if (do_encrypt) + err = grub_crypto_cbc_encrypt (dev->cipher, data + i, data + i, + ((grub_size_t) 1 << log_sector_size), iv); + else + err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, + ((grub_size_t) 1 << log_sector_size), iv); + if (err) + return err; + break; + + case GRUB_CRYPTODISK_MODE_PCBC: + if (do_encrypt) + err = grub_crypto_pcbc_encrypt (dev->cipher, data + i, data + i, + ((grub_size_t) 1 << log_sector_size), iv); + else + err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, + ((grub_size_t) 1 << log_sector_size), iv); + if (err) + return err; + break; + case GRUB_CRYPTODISK_MODE_XTS: + { + unsigned j; + err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, + dev->cipher->cipher->blocksize); + if (err) + return err; + + for (j = 0; j < (1U << log_sector_size); + j += dev->cipher->cipher->blocksize) + { + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + if (do_encrypt) + err = grub_crypto_ecb_encrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + else + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, iv, + dev->cipher->cipher->blocksize); + gf_mul_x ((grub_uint8_t *) iv); + } + } + break; + case GRUB_CRYPTODISK_MODE_LRW: + { + struct lrw_sector sec; + + generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv); + lrw_xor (&sec, dev, data + i); + + if (do_encrypt) + err = grub_crypto_ecb_encrypt (dev->cipher, data + i, + data + i, + (1U << log_sector_size)); + else + err = grub_crypto_ecb_decrypt (dev->cipher, data + i, + data + i, + (1U << log_sector_size)); + if (err) + return err; + lrw_xor (&sec, dev, data + i); + } + break; + case GRUB_CRYPTODISK_MODE_ECB: + if (do_encrypt) + err = grub_crypto_ecb_encrypt (dev->cipher, data + i, data + i, + (1U << log_sector_size)); + else + err = grub_crypto_ecb_decrypt (dev->cipher, data + i, data + i, + (1U << log_sector_size)); + if (err) + return err; + break; + default: + return GPG_ERR_NOT_IMPLEMENTED; + } + sector++; + } + return GPG_ERR_NO_ERROR; +} + +gcry_err_code_t +grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, + grub_uint8_t * data, grub_size_t len, + grub_disk_addr_t sector, grub_size_t log_sector_size) +{ + return grub_cryptodisk_endecrypt (dev, data, len, sector, log_sector_size, 0); +} + +grub_err_t +grub_cryptodisk_setcipher (grub_cryptodisk_t crypt, const char *ciphername, const char *ciphermode) +{ + const char *cipheriv = NULL; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; + grub_crypto_cipher_handle_t essiv_cipher = NULL; + const gcry_md_spec_t *essiv_hash = NULL; + const struct gcry_cipher_spec *ciph; + grub_cryptodisk_mode_t mode; + grub_cryptodisk_mode_iv_t mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64; + int benbi_log = 0; + grub_err_t ret = GRUB_ERR_NONE; + + ciph = grub_crypto_lookup_cipher_by_name (ciphername); + if (!ciph) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + ciphername); + goto err; + } + + /* Configure the cipher used for the bulk data. */ + cipher = grub_crypto_cipher_open (ciph); + if (!cipher) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s could not be initialized", + ciphername); + goto err; + } + + /* Configure the cipher mode. */ + if (grub_strcmp (ciphermode, "ecb") == 0) + { + mode = GRUB_CRYPTODISK_MODE_ECB; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; + cipheriv = NULL; + } + else if (grub_strcmp (ciphermode, "plain") == 0) + { + mode = GRUB_CRYPTODISK_MODE_CBC; + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; + cipheriv = NULL; + } + else if (grub_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0) + { + mode = GRUB_CRYPTODISK_MODE_CBC; + cipheriv = ciphermode + sizeof ("cbc-") - 1; + } + else if (grub_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0) + { + mode = GRUB_CRYPTODISK_MODE_PCBC; + cipheriv = ciphermode + sizeof ("pcbc-") - 1; + } + else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) + { + mode = GRUB_CRYPTODISK_MODE_XTS; + cipheriv = ciphermode + sizeof ("xts-") - 1; + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Secondary cipher %s isn't available", ciphername); + goto err; + } + if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported XTS block size: %" PRIuGRUB_SIZE, + cipher->cipher->blocksize); + goto err; + } + if (secondary_cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported XTS block size: %" PRIuGRUB_SIZE, + secondary_cipher->cipher->blocksize); + goto err; + } + } + else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) + { + mode = GRUB_CRYPTODISK_MODE_LRW; + cipheriv = ciphermode + sizeof ("lrw-") - 1; + if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported LRW block size: %" PRIuGRUB_SIZE, + cipher->cipher->blocksize); + goto err; + } + } + else + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", + ciphermode); + goto err; + } + + if (cipheriv == NULL) + ; + else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64; + else if (grub_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0) + mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; + else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) + { + if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1) + || cipher->cipher->blocksize == 0) + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported benbi blocksize: %" PRIuGRUB_SIZE, + cipher->cipher->blocksize); + /* FIXME should we return an error here? */ + for (benbi_log = 0; + (cipher->cipher->blocksize << benbi_log) < GRUB_DISK_SECTOR_SIZE; + benbi_log++); + mode_iv = GRUB_CRYPTODISK_MODE_IV_BENBI; + } + else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) + mode_iv = GRUB_CRYPTODISK_MODE_IV_NULL; + else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) + { + const char *hash_str = cipheriv + 6; + + mode_iv = GRUB_CRYPTODISK_MODE_IV_ESSIV; + + /* Configure the hash and cipher used for ESSIV. */ + essiv_hash = grub_crypto_lookup_md_by_name (hash_str); + if (!essiv_hash) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Couldn't load %s hash", hash_str); + goto err; + } + essiv_cipher = grub_crypto_cipher_open (ciph); + if (!essiv_cipher) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, + "Couldn't load %s cipher", ciphername); + goto err; + } + } + else + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown IV mode: %s", + cipheriv); + goto err; + } + + crypt->cipher = cipher; + crypt->benbi_log = benbi_log; + crypt->mode = mode; + crypt->mode_iv = mode_iv; + crypt->secondary_cipher = secondary_cipher; + crypt->essiv_cipher = essiv_cipher; + crypt->essiv_hash = essiv_hash; + +err: + if (ret) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (secondary_cipher); + } + return ret; +} + +gcry_err_code_t +grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize) +{ + gcry_err_code_t err; + int real_keysize; + + real_keysize = keysize; + if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) + real_keysize /= 2; + if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) + real_keysize -= dev->cipher->cipher->blocksize; + + /* Set the PBKDF2 output as the cipher key. */ + err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize); + if (err) + return err; + grub_memcpy (dev->key, key, keysize); + dev->keysize = keysize; + + /* Configure ESSIV if necessary. */ + if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_ESSIV) + { + grub_size_t essiv_keysize = dev->essiv_hash->mdlen; + grub_uint8_t hashed_key[GRUB_CRYPTO_MAX_MDLEN]; + if (essiv_keysize > GRUB_CRYPTO_MAX_MDLEN) + return GPG_ERR_INV_ARG; + + grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize); + err = grub_crypto_cipher_set_key (dev->essiv_cipher, + hashed_key, essiv_keysize); + if (err) + return err; + } + if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) + { + err = grub_crypto_cipher_set_key (dev->secondary_cipher, + key + real_keysize, + keysize / 2); + if (err) + return err; + } + + if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) + { + unsigned i; + grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; + + grub_free (dev->lrw_precalc); + grub_memcpy (dev->lrw_key, key + real_keysize, + dev->cipher->cipher->blocksize); + dev->lrw_precalc = grub_malloc ((1U << dev->log_sector_size)); + if (!dev->lrw_precalc) + return GPG_ERR_OUT_OF_MEMORY; + grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES); + for (i = 0; i < (1U << dev->log_sector_size); + i += GRUB_CRYPTODISK_GF_BYTES) + { + idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES; + gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); + } + } + return GPG_ERR_NO_ERROR; +} + +static int +grub_cryptodisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + grub_cryptodisk_t i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (i = cryptodisk_list; i != NULL; i = i->next) + { + char buf[30]; + grub_snprintf (buf, sizeof (buf), "crypto%lu", i->id); + if (hook (buf, hook_data)) + return 1; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cryptodisk_open (const char *name, grub_disk_t disk) +{ + grub_cryptodisk_t dev; + + if (grub_memcmp (name, "crypto", sizeof ("crypto") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + + if (grub_memcmp (name, "cryptouuid/", sizeof ("cryptouuid/") - 1) == 0) + { + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (grub_uuidcasecmp (name + sizeof ("cryptouuid/") - 1, dev->uuid, sizeof (dev->uuid)) == 0) + break; + } + else + { + unsigned long id = grub_strtoul (name + sizeof ("crypto") - 1, 0, 0); + if (grub_errno) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + /* Search for requested device in the list of CRYPTODISK devices. */ + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (dev->id == id) + break; + } + if (!dev) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); + +#ifdef GRUB_UTIL + if (dev->cheat) + { + grub_uint64_t cheat_dev_size; + unsigned int cheat_log_sector_size; + + if (!GRUB_UTIL_FD_IS_VALID (dev->cheat_fd)) + dev->cheat_fd = grub_util_fd_open (dev->cheat, GRUB_UTIL_FD_O_RDONLY); + if (!GRUB_UTIL_FD_IS_VALID (dev->cheat_fd)) + return grub_error (GRUB_ERR_IO, N_("cannot open `%s': %s"), + dev->cheat, grub_util_fd_strerror ()); + + /* Use the sector size and count of the cheat device. */ + cheat_dev_size = grub_util_get_fd_size (dev->cheat_fd, dev->cheat, &cheat_log_sector_size); + if (cheat_dev_size == -1) + { + const char *errmsg = grub_util_fd_strerror (); + grub_util_fd_close (dev->cheat_fd); + dev->cheat_fd = GRUB_UTIL_FD_INVALID; + return grub_error (GRUB_ERR_IO, N_("failed to query size of device `%s': %s"), + dev->cheat, errmsg); + } + + dev->log_sector_size = cheat_log_sector_size; + dev->total_sectors = cheat_dev_size >> cheat_log_sector_size; + } +#endif + + if (!dev->source_disk) + { + grub_dprintf ("cryptodisk", "Opening device %s\n", name); + /* Try to open the source disk and populate the requested disk. */ + dev->source_disk = grub_disk_open (dev->source); + if (!dev->source_disk) + return grub_errno; + } + + disk->data = dev; + disk->log_sector_size = dev->log_sector_size; + disk->total_sectors = dev->total_sectors; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; + disk->id = dev->id; + dev->ref++; + return GRUB_ERR_NONE; +} + +static void +grub_cryptodisk_close (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_dprintf ("cryptodisk", "Closing disk\n"); + + dev->ref--; + + if (dev->ref != 0) + return; +#ifdef GRUB_UTIL + if (dev->cheat) + { + grub_util_fd_close (dev->cheat_fd); + dev->cheat_fd = GRUB_UTIL_FD_INVALID; + } +#endif + grub_disk_close (dev->source_disk); + dev->source_disk = NULL; +} + +static grub_err_t +grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_err_t err; + gcry_err_code_t gcry_err; + +#ifdef GRUB_UTIL + if (dev->cheat) + { + int r; + r = grub_util_fd_seek (dev->cheat_fd, sector << disk->log_sector_size); + if (r) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + dev->cheat, grub_util_fd_strerror ()); + if (grub_util_fd_read (dev->cheat_fd, buf, size << disk->log_sector_size) + != (ssize_t) (size << disk->log_sector_size)) + return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"), + dev->cheat, grub_util_fd_strerror ()); + return GRUB_ERR_NONE; + } +#endif + + grub_dprintf ("cryptodisk", + "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" + PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", + size, sector, dev->offset_sectors); + + err = grub_disk_read (dev->source_disk, + grub_disk_from_native_sector (disk, sector + dev->offset_sectors), + 0, size << disk->log_sector_size, buf); + if (err) + { + grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err); + return err; + } + gcry_err = grub_cryptodisk_endecrypt (dev, (grub_uint8_t *) buf, + size << disk->log_sector_size, + sector, dev->log_sector_size, 0); + return grub_crypto_gcry_error (gcry_err); +} + +static grub_err_t +grub_cryptodisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + gcry_err_code_t gcry_err; + char *tmp; + grub_err_t err; + +#ifdef GRUB_UTIL + if (dev->cheat) + { + int r; + r = grub_util_fd_seek (dev->cheat_fd, sector << disk->log_sector_size); + if (r) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + dev->cheat, grub_util_fd_strerror ()); + if (grub_util_fd_write (dev->cheat_fd, buf, size << disk->log_sector_size) + != (ssize_t) (size << disk->log_sector_size)) + return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"), + dev->cheat, grub_util_fd_strerror ()); + return GRUB_ERR_NONE; + } +#endif + + tmp = grub_malloc (size << disk->log_sector_size); + if (!tmp) + return grub_errno; + grub_memcpy (tmp, buf, size << disk->log_sector_size); + + grub_dprintf ("cryptodisk", + "Writing %" PRIuGRUB_SIZE " sectors to sector 0x%" + PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", + size, sector, dev->offset_sectors); + + gcry_err = grub_cryptodisk_endecrypt (dev, (grub_uint8_t *) tmp, + size << disk->log_sector_size, + sector, disk->log_sector_size, 1); + if (gcry_err) + { + grub_free (tmp); + return grub_crypto_gcry_error (gcry_err); + } + + /* Since ->write was called so disk.mod is loaded but be paranoid */ + sector = sector + dev->offset_sectors; + if (grub_disk_write_weak) + err = grub_disk_write_weak (dev->source_disk, + grub_disk_from_native_sector (disk, sector), + 0, size << disk->log_sector_size, tmp); + else + err = grub_error (GRUB_ERR_BUG, "disk.mod not loaded"); + grub_free (tmp); + return err; +} + +#ifdef GRUB_UTIL +static grub_disk_memberlist_t +grub_cryptodisk_memberlist (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + grub_disk_memberlist_t list = NULL; + + list = grub_malloc (sizeof (*list)); + if (list) + { + list->disk = dev->source_disk; + list->next = NULL; + } + + return list; +} +#endif + +static void +cryptodisk_cleanup (void) +{ +#if 0 + grub_cryptodisk_t dev = cryptodisk_list; + grub_cryptodisk_t tmp; + + while (dev != NULL) + { + grub_free (dev->source); + grub_free (dev->cipher); + grub_free (dev->secondary_cipher); + grub_free (dev->essiv_cipher); + tmp = dev->next; + grub_free (dev); + dev = tmp; + } +#endif +} + +grub_err_t +grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source) +{ + newdev->source = grub_strdup (name); + if (!newdev->source) + return grub_errno; + + newdev->id = last_cryptodisk_id++; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->partition_start = grub_partition_get_start (source->partition); + newdev->next = cryptodisk_list; + cryptodisk_list = newdev; + + return GRUB_ERR_NONE; +} + +grub_cryptodisk_t +grub_cryptodisk_get_by_uuid (const char *uuid) +{ + grub_cryptodisk_t dev; + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (grub_uuidcasecmp (dev->uuid, uuid, sizeof (dev->uuid)) == 0) + return dev; + return NULL; +} + +grub_cryptodisk_t +grub_cryptodisk_get_by_source_disk (grub_disk_t disk) +{ + grub_cryptodisk_t dev; + for (dev = cryptodisk_list; dev != NULL; dev = dev->next) + if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) + if ((disk->partition && grub_partition_get_start (disk->partition) == dev->partition_start) || + (!disk->partition && dev->partition_start == 0)) + return dev; + return NULL; +} + +#ifdef GRUB_UTIL +grub_err_t +grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name, + grub_disk_t source, const char *cheat) +{ + newdev->cheat = grub_strdup (cheat); + newdev->source = grub_strdup (name); + if (!newdev->source || !newdev->cheat) + { + grub_free (newdev->source); + grub_free (newdev->cheat); + return grub_errno; + } + + newdev->cheat_fd = GRUB_UTIL_FD_INVALID; + newdev->source_id = source->id; + newdev->source_dev_id = source->dev->id; + newdev->partition_start = grub_partition_get_start (source->partition); + newdev->id = last_cryptodisk_id++; + newdev->next = cryptodisk_list; + cryptodisk_list = newdev; + + return GRUB_ERR_NONE; +} + +void +grub_util_cryptodisk_get_abstraction (grub_disk_t disk, + void (*cb) (const char *val, void *data), + void *data) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + + cb ("cryptodisk", data); + cb (dev->modname, data); + + if (dev->cipher) + cb (dev->cipher->cipher->modname, data); + if (dev->secondary_cipher) + cb (dev->secondary_cipher->cipher->modname, data); + if (dev->essiv_cipher) + cb (dev->essiv_cipher->cipher->modname, data); + if (dev->hash) + cb (dev->hash->modname, data); + if (dev->essiv_hash) + cb (dev->essiv_hash->modname, data); + if (dev->iv_hash) + cb (dev->iv_hash->modname, data); +} + +const char * +grub_util_cryptodisk_get_uuid (grub_disk_t disk) +{ + grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; + return dev->uuid; +} + +#endif + +static void +cryptodisk_close (grub_cryptodisk_t dev) +{ + grub_crypto_cipher_close (dev->cipher); + grub_crypto_cipher_close (dev->secondary_cipher); + grub_crypto_cipher_close (dev->essiv_cipher); + grub_free (dev); +} + +static grub_err_t +cryptodisk_read_hook (grub_disk_addr_t sector, unsigned offset, + unsigned length, char *buf, void *data) +{ + cryptodisk_read_hook_ctx_t ctx = data; + + if (ctx->hdr_file == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("header file not found")); + + if (grub_file_seek (ctx->hdr_file, + ((sector - ctx->part_start) * GRUB_DISK_SECTOR_SIZE) + offset) + == (grub_off_t) -1) + return grub_errno; + + if (grub_file_read (ctx->hdr_file, buf, length) != (grub_ssize_t) length) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("header file too small")); + return grub_errno; + } + + return GRUB_ERR_NONE; +} + +static grub_cryptodisk_t +grub_cryptodisk_scan_device_real (const char *name, + grub_disk_t source, + grub_cryptomount_args_t cargs) +{ + grub_err_t ret = GRUB_ERR_NONE; + grub_cryptodisk_t dev; + grub_cryptodisk_dev_t cr; + int i; + struct cryptodisk_read_hook_ctx read_hook_data = {0}; + int askpass = 0; + char *part = NULL; + + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + return dev; + + if (cargs->hdr_file != NULL) + { + /* + * Set read hook to read header from a file instead of the source disk. + * Disk read hooks are executed after the data has been read from the + * disk. This is okay, because the read hook is given the read buffer + * before its sent back to the caller. In this case, the hook can then + * overwrite the data read from the disk device with data from the + * header file sent in as the read hook data. This is transparent to the + * read caller. Since the callers of this function have just opened the + * source disk, there are no current read hooks, so there's no need to + * save/restore them nor consider if they should be called or not. + * + * This hook assumes that the header is at the start of the volume, which + * is not the case for some formats (eg. GELI). It also can only be used + * with formats where the detached header file can be written to the + * first blocks of the volume and the volume could still be unlocked. + * So the header file can not be formatted differently from the on-disk + * header. If these assumpts are not met, detached header file processing + * must be specially handled in the cryptodisk backend module. + * + * This hook needs only be set once and will be called potentially many + * times by a backend. This is fine because of the assumptions mentioned + * and the read hook reads from absolute offsets and is stateless. + */ + read_hook_data.part_start = grub_partition_get_start (source->partition); + read_hook_data.hdr_file = cargs->hdr_file; + source->read_hook = cryptodisk_read_hook; + source->read_hook_data = (void *) &read_hook_data; + } + + FOR_CRYPTODISK_DEVS (cr) + { + /* + * Loop through each cryptodisk backend that is registered (ie. loaded). + * If the scan returns NULL, then the backend being tested does not + * recognize the source disk, so move on to the next backend. + */ + dev = cr->scan (source, cargs); + if (grub_errno) + goto error_no_close; + if (!dev) + continue; + break; + } + + if (dev == NULL) + { + grub_error (GRUB_ERR_BAD_MODULE, + "no cryptodisk module can handle this device"); + goto error_no_close; + } + + if (cargs->protectors) + { + for (i = 0; cargs->protectors[i]; i++) + { + if (cargs->key_cache[i].invalid) + continue; + + if (cargs->key_cache[i].key == NULL) + { + ret = grub_key_protector_recover_key (cargs->protectors[i], + &cargs->key_cache[i].key, + &cargs->key_cache[i].key_len); + if (ret != GRUB_ERR_NONE) + { + if (grub_errno) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + grub_dprintf ("cryptodisk", + "failed to recover a key from key protector " + "%s, will not try it again for any other " + "disks, if any, during this invocation of " + "cryptomount\n", + cargs->protectors[i]); + + cargs->key_cache[i].invalid = 1; + continue; + } + } + + cargs->key_data = cargs->key_cache[i].key; + cargs->key_len = cargs->key_cache[i].key_len; + + ret = cr->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + { + /* Reset key data to trigger the passphrase prompt later */ + cargs->key_data = NULL; + cargs->key_len = 0; + + part = grub_partition_get_name (source->partition); + grub_dprintf ("cryptodisk", + "recovered a key from key protector %s but it " + "failed to unlock %s%s%s (%s)\n", + cargs->protectors[i], source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), dev->uuid); + grub_free (part); + continue; + } + else + { + ret = grub_cryptodisk_insert (dev, name, source); + if (ret != GRUB_ERR_NONE) + goto error; +#ifndef GRUB_UTIL + grub_cli_set_auth_needed (); +#endif + goto cleanup; + } + } + + part = grub_partition_get_name (source->partition); + grub_error (GRUB_ERR_ACCESS_DENIED, + N_("no key protector provided a usable key for %s%s%s (%s)"), + source->name, source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), dev->uuid); + grub_free (part); + } + + if (cargs->key_len) + { + ret = cr->recover_key (source, dev, cargs); + if (ret != GRUB_ERR_NONE) + goto error; + } + else + { + /* Get the passphrase from the user, if no key data. */ + unsigned long tries = 3; + const char *tries_env; + + /* + * Print the error from key protectors and clear grub_errno. + * + * Since '--protector' cannot coexist with '--password' and + * '--key-file', in case key protectors fail, only + * "cargs->key_len == 0" is expected, so cryptomount falls back + * here to request the passphrase. + * + * To avoid the error from key protectors stops the further code, + * print the error to notify the user why key protectors fail and + * clear grub_errno to have a fresh start. + */ + if (grub_errno != GRUB_ERR_NONE) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + askpass = 1; + cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs->key_data == NULL) + goto error_no_close; + + tries_env = grub_env_get ("cryptodisk_passphrase_tries"); + if (tries_env != NULL && tries_env[0] != '\0') + { + unsigned long tries_env_val; + const char *p; + + tries_env_val = grub_strtoul (tries_env, &p, 0); + if (*p == '\0' && tries_env_val != ~0UL) + tries = tries_env_val; + else + grub_printf_ (N_("Invalid cryptodisk_passphrase_tries value `%s'. Defaulting to %lu.\n"), + tries_env, + tries); + } + + for (; tries > 0; tries--) + { + part = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), + dev->uuid); + grub_free (part); + + if (!grub_password_get ((char *) cargs->key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error; + } + cargs->key_len = grub_strlen ((char *) cargs->key_data); + + ret = cr->recover_key (source, dev, cargs); + if (ret == GRUB_ERR_NONE) + break; + if (ret != GRUB_ERR_ACCESS_DENIED || tries == 1) + goto error; + grub_puts_ (N_("Invalid passphrase.")); + + /* + * Since recover_key() calls a function that returns grub_errno, + * a leftover error value from a previously rejected passphrase + * will trigger a phantom failure. We therefore clear it before + * trying a new passphrase. + */ + grub_errno = GRUB_ERR_NONE; + } + } + + ret = grub_cryptodisk_insert (dev, name, source); + if (ret != GRUB_ERR_NONE) + goto error; + + goto cleanup; + + error: + cryptodisk_close (dev); + error_no_close: + dev = NULL; + + cleanup: + if (cargs->hdr_file != NULL) + source->read_hook = NULL; + + if (askpass) + { + grub_memset (cargs->key_data, 0, cargs->key_len); + cargs->key_len = 0; + grub_free (cargs->key_data); + } + return dev; +} + +#ifdef GRUB_UTIL +#include +grub_err_t +grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat) +{ + grub_err_t err; + grub_cryptodisk_t dev; + grub_cryptodisk_dev_t cr; + grub_disk_t source; + struct grub_cryptomount_args cargs = {0}; + + /* Try to open disk. */ + source = grub_disk_open (sourcedev); + if (!source) + return grub_errno; + + dev = grub_cryptodisk_get_by_source_disk (source); + + if (dev) + { + grub_disk_close (source); + return GRUB_ERR_NONE; + } + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, &cargs); + if (grub_errno) + return grub_errno; + if (!dev) + continue; + + grub_util_info ("cheatmounted %s (%s) at %s", sourcedev, dev->modname, + cheat); + err = grub_cryptodisk_cheat_insert (dev, sourcedev, source, cheat); + grub_disk_close (source); + if (err) + grub_free (dev); + + return GRUB_ERR_NONE; + } + + grub_disk_close (source); + + return GRUB_ERR_NONE; +} +#endif + +static int +grub_cryptodisk_scan_device (const char *name, + void *data) +{ + int ret = 0; + grub_disk_t source; + grub_cryptodisk_t dev; + grub_cryptomount_args_t cargs = data; + grub_errno = GRUB_ERR_NONE; + + /* Try to open disk. */ + source = grub_disk_open (name); + if (!source) + { + grub_print_error (); + return 0; + } + + dev = grub_cryptodisk_scan_device_real (name, source, cargs); + if (dev) + { + ret = (cargs->search_uuid != NULL + && grub_uuidcasecmp (cargs->search_uuid, dev->uuid, sizeof (dev->uuid)) == 0); + goto cleanup; + } + + /* + * Do not print error when err is GRUB_ERR_BAD_MODULE to avoid many unhelpful + * error messages. + */ + if (grub_errno == GRUB_ERR_BAD_MODULE) + grub_error_pop (); + + if (cargs->search_uuid != NULL) + /* Push error onto stack to save for cryptomount. */ + grub_error_push (); + else + grub_print_error (); + + cleanup: + grub_disk_close (source); + return ret; +} + +static void +grub_cryptodisk_clear_key_cache (struct grub_cryptomount_args *cargs) +{ + int i; + + if (cargs->key_cache == NULL || cargs->protectors == NULL) + return; + + for (i = 0; cargs->protectors[i]; i++) + { + if (cargs->key_cache[i].key) + grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len); + grub_free (cargs->key_cache[i].key); + } + + grub_free (cargs->key_cache); +} + +static grub_err_t +grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + struct grub_cryptomount_args cargs = {0}; + + if (argc < 1 && !state[OPTION_ALL].set && !state[OPTION_BOOT].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + if (grub_cryptodisk_list == NULL) + return grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk modules loaded"); + + if (state[OPTION_PASSWORD].set && state[OPTION_PROTECTOR].set) /* password and key protector */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a password and a key protector cannot both be set"); + + if (state[OPTION_KEYFILE].set && state[OPTION_PROTECTOR].set) /* key file and key protector */ + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "a key file and a key protector cannot both be set"); + + if (state[OPTION_PASSWORD].set) /* password */ + { + cargs.key_data = (grub_uint8_t *) state[OPTION_PASSWORD].arg; + cargs.key_len = grub_strlen (state[OPTION_PASSWORD].arg); + } + + if (state[OPTION_KEYFILE].set) /* keyfile */ + { + const char *p = NULL; + grub_file_t keyfile; + unsigned long long keyfile_offset = 0, keyfile_size = 0; + + if (state[OPTION_KEYFILE_OFFSET].set) /* keyfile-offset */ + { + grub_errno = GRUB_ERR_NONE; + keyfile_offset = grub_strtoull (state[OPTION_KEYFILE_OFFSET].arg, &p, 0); + + if (state[OPTION_KEYFILE_OFFSET].arg[0] == '\0' || *p != '\0') + return grub_error (grub_errno, + N_("non-numeric or invalid keyfile offset `%s'"), + state[OPTION_KEYFILE_OFFSET].arg); + } + + if (state[OPTION_KEYFILE_SIZE].set) /* keyfile-size */ + { + grub_errno = GRUB_ERR_NONE; + keyfile_size = grub_strtoull (state[OPTION_KEYFILE_SIZE].arg, &p, 0); + + if (state[OPTION_KEYFILE_SIZE].arg[0] == '\0' || *p != '\0') + return grub_error (grub_errno, + N_("non-numeric or invalid keyfile size `%s'"), + state[OPTION_KEYFILE_SIZE].arg); + + if (keyfile_size == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("key file size is 0")); + + if (keyfile_size > GRUB_CRYPTODISK_MAX_KEYFILE_SIZE) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("key file size exceeds maximum (%d)"), + GRUB_CRYPTODISK_MAX_KEYFILE_SIZE); + } + + keyfile = grub_file_open (state[OPTION_KEYFILE].arg, + GRUB_FILE_TYPE_CRYPTODISK_ENCRYPTION_KEY); + if (keyfile == NULL) + return grub_errno; + + if (keyfile_offset > keyfile->size) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("Keyfile offset, %llu, is greater than " + "keyfile size, %llu"), + keyfile_offset, (unsigned long long) keyfile->size); + + if (grub_file_seek (keyfile, (grub_off_t) keyfile_offset) == (grub_off_t) -1) + return grub_errno; + + if (keyfile_size != 0) + { + if (keyfile_size > (keyfile->size - keyfile_offset)) + return grub_error (GRUB_ERR_FILE_READ_ERROR, + N_("keyfile is too small: requested %llu bytes," + " but the file only has %" PRIuGRUB_UINT64_T + " bytes left at offset %llu"), + keyfile_size, + (grub_off_t) (keyfile->size - keyfile_offset), + keyfile_offset); + + cargs.key_len = keyfile_size; + } + else + cargs.key_len = keyfile->size - keyfile_offset; + + cargs.key_data = grub_malloc (cargs.key_len); + if (cargs.key_data == NULL) + return GRUB_ERR_OUT_OF_MEMORY; + + if (grub_file_read (keyfile, cargs.key_data, cargs.key_len) != (grub_ssize_t) cargs.key_len) + return grub_error (GRUB_ERR_FILE_READ_ERROR, (N_("failed to read key file"))); + } + + if (state[OPTION_HEADER].set) /* header */ + { + if (state[OPTION_UUID].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("cannot use UUID lookup with detached header")); + + cargs.hdr_file = grub_file_open (state[OPTION_HEADER].arg, + GRUB_FILE_TYPE_CRYPTODISK_DETACHED_HEADER); + if (cargs.hdr_file == NULL) + return grub_errno; + } + + if (state[OPTION_PROTECTOR].set) /* key protector(s) */ + { + cargs.key_cache = grub_calloc (state[OPTION_PROTECTOR].set, sizeof (*cargs.key_cache)); + if (cargs.key_cache == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no memory for key protector key cache"); + cargs.protectors = state[OPTION_PROTECTOR].args; + } + + if (state[OPTION_UUID].set) /* uuid */ + { + int found_uuid; + grub_cryptodisk_t dev; + + dev = grub_cryptodisk_get_by_uuid (args[0]); + if (dev) + { + grub_cryptodisk_clear_key_cache (&cargs); + grub_dprintf ("cryptodisk", + "already mounted as crypto%lu\n", dev->id); + return GRUB_ERR_NONE; + } + + cargs.check_boot = state[OPTION_BOOT].set; + cargs.search_uuid = args[0]; + found_uuid = grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); + + if (found_uuid) + return GRUB_ERR_NONE; + else if (grub_errno == GRUB_ERR_NONE) + { + /* + * Try to pop the next error on the stack. If there is not one, then + * no device matched the given UUID. + */ + grub_error_pop (); + if (grub_errno == GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such cryptodisk found, perhaps a needed disk or cryptodisk module is not loaded"); + } + return grub_errno; + } + else if (state[OPTION_ALL].set || (argc == 0 && state[OPTION_BOOT].set)) /* -a|-b */ + { + cargs.check_boot = state[OPTION_BOOT].set; + grub_device_iterate (&grub_cryptodisk_scan_device, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); + return GRUB_ERR_NONE; + } + else + { + grub_disk_t disk; + grub_cryptodisk_t dev; + char *diskname; + char *disklast = NULL; + grub_size_t len; + + cargs.check_boot = state[OPTION_BOOT].set; + diskname = args[0]; + len = grub_strlen (diskname); + if (len && diskname[0] == '(' && diskname[len - 1] == ')') + { + disklast = &diskname[len - 1]; + *disklast = '\0'; + diskname++; + } + + disk = grub_disk_open (diskname); + if (!disk) + { + grub_cryptodisk_clear_key_cache (&cargs); + if (disklast) + *disklast = ')'; + return grub_errno; + } + + dev = grub_cryptodisk_get_by_source_disk (disk); + if (dev) + { + grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); + grub_disk_close (disk); + grub_cryptodisk_clear_key_cache (&cargs); + if (disklast) + *disklast = ')'; + return GRUB_ERR_NONE; + } + + dev = grub_cryptodisk_scan_device_real (diskname, disk, &cargs); + grub_cryptodisk_clear_key_cache (&cargs); + + grub_disk_close (disk); + if (disklast) + *disklast = ')'; + + return (dev == NULL) ? grub_errno : GRUB_ERR_NONE; + } +} + +static struct grub_disk_dev grub_cryptodisk_dev = { + .name = "cryptodisk", + .id = GRUB_DISK_DEVICE_CRYPTODISK_ID, + .disk_iterate = grub_cryptodisk_iterate, + .disk_open = grub_cryptodisk_open, + .disk_close = grub_cryptodisk_close, + .disk_read = grub_cryptodisk_read, + .disk_write = grub_cryptodisk_write, +#ifdef GRUB_UTIL + .disk_memberlist = grub_cryptodisk_memberlist, +#endif + .next = 0 +}; + +static char +hex (grub_uint8_t val) +{ + if (val < 10) + return '0' + val; + return 'a' + val - 10; +} + +/* Open a file named NAME and initialize FILE. */ +static char * +luks_script_get (grub_size_t *sz) +{ + grub_cryptodisk_t i; + grub_size_t size = 0, mul; + char *ptr, *ret; + + *sz = 0; + + for (i = cryptodisk_list; i != NULL; i = i->next) + if (grub_strcmp (i->modname, "luks") == 0 || + grub_strcmp (i->modname, "luks2") == 0) + { + /* + * Add space in the line for (in order) spaces, cipher mode, cipher IV + * mode, sector offset, sector size and the trailing newline. This is + * an upper bound on the size of this data. There are 15 extra bytes + * in an earlier version of this code that are unaccounted for. It is + * left in the calculations in case it is needed. At worst, its short- + * lived wasted space. + * + * 60 = 5 + 5 + 8 + 20 + 6 + 1 + 15 + */ + if (grub_add (size, grub_strlen (i->modname), &size) || + grub_add (size, sizeof ("_mount") + 60, &size) || + grub_add (size, grub_strlen (i->uuid), &size) || + grub_add (size, grub_strlen (i->cipher->cipher->name), &size) || + grub_mul (i->keysize, 2, &mul) || + grub_add (size, mul, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } + if (i->essiv_hash) + { + if (grub_add (size, grub_strlen (i->essiv_hash->name), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } + } + } + if (grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } + + ret = grub_malloc (size); + if (!ret) + return 0; + + ptr = ret; + + for (i = cryptodisk_list; i != NULL; i = i->next) + if (grub_strcmp (i->modname, "luks") == 0 || + grub_strcmp (i->modname, "luks2") == 0) + { + unsigned j; + const char *iptr; + ptr = grub_stpcpy (ptr, i->modname); + ptr = grub_stpcpy (ptr, "_mount "); + ptr = grub_stpcpy (ptr, i->uuid); + *ptr++ = ' '; + ptr += grub_snprintf (ptr, 21, "%" PRIxGRUB_OFFSET, i->offset_sectors); + *ptr++ = ' '; + ptr += grub_snprintf (ptr, 7, "%u", 1 << i->log_sector_size); + *ptr++ = ' '; + for (iptr = i->cipher->cipher->name; *iptr; iptr++) + *ptr++ = grub_tolower (*iptr); + switch (i->mode) + { + case GRUB_CRYPTODISK_MODE_ECB: + ptr = grub_stpcpy (ptr, "-ecb"); + break; + case GRUB_CRYPTODISK_MODE_CBC: + ptr = grub_stpcpy (ptr, "-cbc"); + break; + case GRUB_CRYPTODISK_MODE_PCBC: + ptr = grub_stpcpy (ptr, "-pcbc"); + break; + case GRUB_CRYPTODISK_MODE_XTS: + ptr = grub_stpcpy (ptr, "-xts"); + break; + case GRUB_CRYPTODISK_MODE_LRW: + ptr = grub_stpcpy (ptr, "-lrw"); + break; + } + + switch (i->mode_iv) + { + case GRUB_CRYPTODISK_MODE_IV_NULL: + ptr = grub_stpcpy (ptr, "-null"); + break; + case GRUB_CRYPTODISK_MODE_IV_PLAIN: + ptr = grub_stpcpy (ptr, "-plain"); + break; + case GRUB_CRYPTODISK_MODE_IV_PLAIN64: + ptr = grub_stpcpy (ptr, "-plain64"); + break; + case GRUB_CRYPTODISK_MODE_IV_BENBI: + ptr = grub_stpcpy (ptr, "-benbi"); + break; + case GRUB_CRYPTODISK_MODE_IV_ESSIV: + ptr = grub_stpcpy (ptr, "-essiv:"); + ptr = grub_stpcpy (ptr, i->essiv_hash->name); + break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH: + break; + } + *ptr++ = ' '; + for (j = 0; j < i->keysize; j++) + { + *ptr++ = hex (i->key[j] >> 4); + *ptr++ = hex (i->key[j] & 0xf); + } + *ptr++ = '\n'; + } + *ptr = '\0'; + *sz = ptr - ret; + return ret; +} + +#ifdef GRUB_MACHINE_EFI +grub_err_t +grub_cryptodisk_challenge_password (void) +{ + grub_cryptodisk_t cr_dev; + + for (cr_dev = cryptodisk_list; cr_dev != NULL; cr_dev = cr_dev->next) + { + grub_cryptodisk_dev_t cr; + grub_disk_t source = NULL; + grub_err_t ret = GRUB_ERR_NONE; + grub_cryptodisk_t dev = NULL; + char *part = NULL; + struct grub_cryptomount_args cargs = {0}; + + cargs.check_boot = 0; + cargs.search_uuid = cr_dev->uuid; + + source = grub_disk_open (cr_dev->source); + + if (source == NULL) + { + ret = grub_errno; + goto error_out; + } + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, &cargs); + if (grub_errno) + { + ret = grub_errno; + goto error_out; + } + if (dev == NULL) + continue; + break; + } + + if (dev == NULL) + { + ret = grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); + goto error_out; + } + + part = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN"), cr_dev->uuid); + grub_free (part); + + cargs.key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs.key_data == NULL) + { + ret = grub_errno; + goto error_out; + } + + if (!grub_password_get ((char *) cargs.key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error_out; + } + cargs.key_len = grub_strlen ((char *) cargs.key_data); + ret = cr->recover_key (source, dev, &cargs); + + error_out: + grub_disk_close (source); + if (dev != NULL) + cryptodisk_close (dev); + if (cargs.key_data) + { + grub_memset (cargs.key_data, 0, cargs.key_len); + grub_free (cargs.key_data); + } + + return ret; + } + + return GRUB_ERR_NONE; +} + +void +grub_cryptodisk_erasesecrets (void) +{ + grub_cryptodisk_t i; + grub_uint8_t *buf; + + buf = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN); + if (buf == NULL) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot allocate memory"); + + for (i = cryptodisk_list; i != NULL; i = i->next) + if (grub_cryptodisk_setkey (i, buf, i->keysize)) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot erase secrets for %s", i->source); + else + grub_printf ("Erased crypto secrets for %s\n", i->source); + /* + * Unfortunately, there is no way to "force unmount" a given disk, it may + * have mounted "child" disks as well, e.g., an LVM volume. So, this + * function MUST be called when there is no way back, e.g., when exiting. + * Otherwise, subsequent read calls for a cryptodisk will return garbage. + */ + + grub_free (buf); +} +#endif /* GRUB_MACHINE_EFI */ + +struct grub_procfs_entry luks_script = +{ + .name = "luks_script", + .get_contents = luks_script_get +}; + +static grub_extcmd_t cmd; + +GRUB_MOD_INIT (cryptodisk) +{ + grub_disk_dev_register (&grub_cryptodisk_dev); + cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, + N_("[ [-p password] | [-k keyfile" + " [-O keyoffset] [-S keysize] ] ] [-H file]" + " [-P protector [-P protector ...]]" + " "), + N_("Mount a crypto device."), options); + grub_procfs_register ("luks_script", &luks_script); +} + +GRUB_MOD_FINI (cryptodisk) +{ +#ifdef GRUB_MACHINE_EFI + grub_cryptodisk_erasesecrets (); +#endif + grub_disk_dev_unregister (&grub_cryptodisk_dev); + cryptodisk_cleanup (); + grub_unregister_extcmd (cmd); + grub_procfs_unregister (&luks_script); +} diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c new file mode 100644 index 000000000..3a26de60c --- /dev/null +++ b/grub-core/disk/diskfilter.c @@ -0,0 +1,1494 @@ +/* diskfilter.c - module to read RAID arrays. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef GRUB_UTIL +#include +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Linked list of DISKFILTER arrays. */ +static struct grub_diskfilter_vg *array_list; +grub_raid5_recover_func_t grub_raid5_recover_func; +grub_raid6_recover_func_t grub_raid6_recover_func; +grub_diskfilter_t grub_diskfilter_list; +static int inscnt = 0; +static int lv_num = 0; + +static struct grub_diskfilter_lv * +find_lv (const char *name); +static int is_lv_readable (struct grub_diskfilter_lv *lv, int easily); + + + +static grub_err_t +is_node_readable (const struct grub_diskfilter_node *node, int easily) +{ + /* Check whether we actually know the physical volume we want to + read from. */ + if (node->pv) + return !!(node->pv->disk); + if (node->lv) + return is_lv_readable (node->lv, easily); + return 0; +} + +static int +is_lv_readable (struct grub_diskfilter_lv *lv, int easily) +{ + unsigned i, j; + if (!lv) + return 0; + for (i = 0; i < lv->segment_count; i++) + { + int need = lv->segments[i].node_count, have = 0; + switch (lv->segments[i].type) + { + case GRUB_DISKFILTER_RAID6: + if (!easily) + need--; + /* Fallthrough. */ + case GRUB_DISKFILTER_RAID4: + case GRUB_DISKFILTER_RAID5: + if (!easily) + need--; + /* Fallthrough. */ + case GRUB_DISKFILTER_STRIPED: + break; + + case GRUB_DISKFILTER_MIRROR: + need = 1; + break; + + case GRUB_DISKFILTER_RAID10: + { + unsigned int n; + n = lv->segments[i].layout & 0xFF; + if (n == 1) + n = (lv->segments[i].layout >> 8) & 0xFF; + need = lv->segments[i].node_count - n + 1; + } + break; + } + for (j = 0; j < lv->segments[i].node_count; j++) + { + if (is_node_readable (lv->segments[i].nodes + j, easily)) + have++; + if (have >= need) + break; + } + if (have < need) + return 0; + } + + return 1; +} + +static grub_err_t +insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id, + struct grub_diskfilter_vg *array, + grub_disk_addr_t start_sector, + grub_diskfilter_t diskfilter __attribute__ ((unused))); + +static int +is_valid_diskfilter_name (const char *name) +{ + return (grub_memcmp (name, "md", sizeof ("md") - 1) == 0 + || grub_memcmp (name, "lvm/", sizeof ("lvm/") - 1) == 0 + || grub_memcmp (name, "lvmid/", sizeof ("lvmid/") - 1) == 0 + || grub_memcmp (name, "ldm/", sizeof ("ldm/") - 1) == 0); +} + +/* Helper for scan_disk. */ +static int +scan_disk_partition_iter (grub_disk_t disk, grub_partition_t p, void *data) +{ + const char *name = data; + struct grub_diskfilter_vg *arr; + grub_disk_addr_t start_sector; + struct grub_diskfilter_pv_id id; + grub_diskfilter_t diskfilter; + + grub_dprintf ("diskfilter", "Scanning for DISKFILTER devices on disk %s\n", + name); +#ifdef GRUB_UTIL + grub_util_info ("Scanning for DISKFILTER devices on disk %s", name); +#endif + + disk->partition = p; + + for (arr = array_list; arr != NULL; arr = arr->next) + { + struct grub_diskfilter_pv *m; + for (m = arr->pvs; m; m = m->next) + if (m->disk && m->disk->id == disk->id + && m->disk->dev->id == disk->dev->id + && m->part_start == grub_partition_get_start (disk->partition) + && m->part_size == grub_disk_native_sectors (disk)) + return 0; + } + + for (diskfilter = grub_diskfilter_list; diskfilter; diskfilter = diskfilter->next) + { +#ifdef GRUB_UTIL + grub_util_info ("Scanning for %s devices on disk %s", + diskfilter->name, name); +#endif + id.uuid = 0; + id.uuidlen = 0; + arr = diskfilter->detect (disk, &id, &start_sector); + if (arr && + (! insert_array (disk, &id, arr, start_sector, diskfilter))) + { + if (id.uuidlen) + grub_free (id.uuid); + return 0; + } + if (arr && id.uuidlen) + grub_free (id.uuid); + + /* This error usually means it's not diskfilter, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + } + + return 0; +} + +static int +scan_disk (const char *name, int accept_diskfilter) +{ + grub_disk_t disk; + static int scan_depth = 0; + + if (!accept_diskfilter && is_valid_diskfilter_name (name)) + return 0; + + if (scan_depth > 100) + return 0; + + scan_depth++; + disk = grub_disk_open (name); + if (!disk) + { + grub_errno = GRUB_ERR_NONE; + scan_depth--; + return 0; + } + scan_disk_partition_iter (disk, 0, (void *) name); + grub_partition_iterate (disk, scan_disk_partition_iter, (void *) name); + grub_disk_close (disk); + scan_depth--; + return 0; +} + +static int +scan_disk_hook (const char *name, void *data __attribute__ ((unused))) +{ + return scan_disk (name, 0); +} + +static void +scan_devices (const char *arname) +{ + grub_disk_dev_t p; + grub_disk_pull_t pull; + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_lv *lv = NULL; + int scan_depth; + int need_rescan; + + for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++) + { + /* look up the crytodisk devices first */ + for (p = grub_disk_dev_list; p; p = p->next) + if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID && p->disk_iterate) + { + if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) + return; + if (arname && is_lv_readable (find_lv (arname), 1)) + return; + break; + } + + /* check the devices other than crytodisk */ + for (p = grub_disk_dev_list; p; p = p->next) + if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID && p->disk_iterate) + { + if ((p->disk_iterate) (scan_disk_hook, NULL, pull)) + return; + if (arname && is_lv_readable (find_lv (arname), 1)) + return; + } + } + + scan_depth = 0; + need_rescan = 1; + while (need_rescan && scan_depth++ < 100) + { + need_rescan = 0; + for (vg = array_list; vg; vg = vg->next) + { + if (vg->lvs) + for (lv = vg->lvs; lv; lv = lv->next) + if (!lv->scanned && lv->fullname && lv->became_readable_at) + { + scan_disk (lv->fullname, 1); + lv->scanned = 1; + need_rescan = 1; + } + } + } + + if (need_rescan) + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "DISKFILTER scan depth exceeded"); +} + +static int +grub_diskfilter_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_diskfilter_vg *array; + int islcnt = 0; + + if (pull == GRUB_DISK_PULL_RESCAN) + { + islcnt = inscnt + 1; + scan_devices (NULL); + } + + if (pull != GRUB_DISK_PULL_NONE && pull != GRUB_DISK_PULL_RESCAN) + return 0; + + for (array = array_list; array; array = array->next) + { + struct grub_diskfilter_lv *lv; + if (array->lvs) + for (lv = array->lvs; lv; lv = lv->next) + if (lv->visible && lv->fullname && lv->became_readable_at >= islcnt) + { + if (hook (lv->fullname, hook_data)) + return 1; + } + } + + return 0; +} + +#ifdef GRUB_UTIL +static grub_disk_memberlist_t +grub_diskfilter_memberlist (grub_disk_t disk) +{ + struct grub_diskfilter_lv *lv = disk->data; + grub_disk_memberlist_t list = NULL, tmp; + struct grub_diskfilter_pv *pv; + grub_disk_pull_t pull; + grub_disk_dev_t p; + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_lv *lv2 = NULL; + struct grub_diskfilter_segment *seg; + unsigned int i, j; + + if (!lv->vg->pvs) + return NULL; + + pv = lv->vg->pvs; + while (pv && pv->disk) + pv = pv->next; + + for (pull = 0; pv && pull < GRUB_DISK_PULL_MAX; pull++) + for (p = grub_disk_dev_list; pv && p; p = p->next) + if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID + && p->disk_iterate) + { + (p->disk_iterate) (scan_disk_hook, NULL, pull); + while (pv && pv->disk) + pv = pv->next; + } + + for (vg = array_list; pv && vg; vg = vg->next) + { + if (vg->lvs) + for (lv2 = vg->lvs; pv && lv2; lv2 = lv2->next) + if (!lv2->scanned && lv2->fullname && lv2->became_readable_at) + { + scan_disk (lv2->fullname, 1); + lv2->scanned = 1; + while (pv && pv->disk) + pv = pv->next; + } + } + + for (i = 0, seg = lv->segments; i < lv->segment_count; i++, seg++) + for (j = 0; j < seg->node_count; ++j) + if (seg->nodes[j].pv != NULL) + { + pv = seg->nodes[j].pv; + + if (pv->disk == NULL) + { + /* + * TRANSLATORS: This message kicks in during the detection of + * which modules needs to be included in core image. This happens + * in the case of degraded RAID and means that autodetection may + * fail to include some of modules. It's an installation time + * message, not runtime message. + */ + grub_util_warn (_("Couldn't find physical volume `%s'." + " Some modules may be missing from core image."), + pv->name); + continue; + } + + for (tmp = list; tmp != NULL; tmp = tmp->next) + if (!grub_strcmp (tmp->disk->name, pv->disk->name)) + break; + if (tmp != NULL) + continue; + + tmp = grub_malloc (sizeof (*tmp)); + if (tmp == NULL) + goto fail; + tmp->disk = pv->disk; + tmp->next = list; + list = tmp; + } + + return list; + + fail: + while (list != NULL) + { + tmp = list; + list = list->next; + grub_free (tmp); + } + + return NULL; +} + +void +grub_diskfilter_get_partmap (grub_disk_t disk, + void (*cb) (const char *pm, void *data), + void *data) +{ + struct grub_diskfilter_lv *lv = disk->data; + struct grub_diskfilter_pv *pv; + + if (lv->vg->pvs) + for (pv = lv->vg->pvs; pv; pv = pv->next) + { + grub_size_t s; + if (!pv->disk) + { + /* TRANSLATORS: This message kicks in during the detection of + which modules needs to be included in core image. This happens + in the case of degraded RAID and means that autodetection may + fail to include some of modules. It's an installation time + message, not runtime message. */ + grub_util_warn (_("Couldn't find physical volume `%s'." + " Some modules may be missing from core image."), + pv->name); + continue; + } + for (s = 0; pv->partmaps[s]; s++) + cb (pv->partmaps[s], data); + } +} + +static const char * +grub_diskfilter_getname (struct grub_disk *disk) +{ + struct grub_diskfilter_lv *array = disk->data; + + return array->vg->driver->name; +} +#endif + +static inline char +hex2ascii (int c) +{ + if (c >= 10) + return 'a' + c - 10; + return c + '0'; +} + +static struct grub_diskfilter_lv * +find_lv (const char *name) +{ + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_lv *lv = NULL; + + for (vg = array_list; vg; vg = vg->next) + { + if (vg->lvs) + for (lv = vg->lvs; lv; lv = lv->next) + if (((lv->fullname && grub_strcmp (lv->fullname, name) == 0) + || (lv->idname && grub_strcmp (lv->idname, name) == 0)) + && is_lv_readable (lv, 0)) + return lv; + } + return NULL; +} + +static grub_err_t +grub_diskfilter_open (const char *name, grub_disk_t disk) +{ + struct grub_diskfilter_lv *lv; + + if (!is_valid_diskfilter_name (name)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown DISKFILTER device %s", + name); + + lv = find_lv (name); + + if (! lv) + { + scan_devices (name); + if (grub_errno) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + lv = find_lv (name); + } + + if (!lv) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown DISKFILTER device %s", + name); + + disk->id = lv->number; + disk->data = lv; + + disk->total_sectors = lv->size; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; + return 0; +} + +static void +grub_diskfilter_close (grub_disk_t disk __attribute ((unused))) +{ + return; +} + +static grub_err_t +read_lv (struct grub_diskfilter_lv *lv, grub_disk_addr_t sector, + grub_size_t size, char *buf); + +grub_err_t +grub_diskfilter_read_node (const struct grub_diskfilter_node *node, + grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + /* Check whether we actually know the physical volume we want to + read from. */ + if (node->pv) + { + if (node->pv->disk) + return grub_disk_read (node->pv->disk, sector + node->start + + node->pv->start_sector, + 0, size << GRUB_DISK_SECTOR_BITS, buf); + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + N_("physical volume %s not found"), node->pv->name); + + } + if (node->lv) + return read_lv (node->lv, sector + node->start, size, buf); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown node '%s'", node->name); +} + + +static grub_err_t +validate_segment (struct grub_diskfilter_segment *seg); + +static grub_err_t +validate_lv (struct grub_diskfilter_lv *lv) +{ + unsigned int i; + if (!lv) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown volume"); + + if (!lv->vg || lv->vg->extent_size == 0) + return grub_error (GRUB_ERR_READ_ERROR, "invalid volume"); + + for (i = 0; i < lv->segment_count; i++) + { + grub_err_t err; + err = validate_segment (&lv->segments[i]); + if (err) + return err; + } + return GRUB_ERR_NONE; +} + + +static grub_err_t +validate_node (const struct grub_diskfilter_node *node) +{ + /* Check whether we actually know the physical volume we want to + read from. */ + if (node->pv) + return GRUB_ERR_NONE; + if (node->lv) + return validate_lv (node->lv); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown node '%s'", node->name); +} + +static grub_err_t +validate_segment (struct grub_diskfilter_segment *seg) +{ + grub_err_t err; + + if (seg->stripe_size == 0 || seg->node_count == 0) + return grub_error(GRUB_ERR_BAD_FS, "invalid segment"); + + switch (seg->type) + { + case GRUB_DISKFILTER_RAID10: + { + grub_uint8_t near, far; + near = seg->layout & 0xFF; + far = (seg->layout >> 8) & 0xFF; + if ((seg->layout >> 16) == 0 && far == 0) + return grub_error(GRUB_ERR_BAD_FS, "invalid segment"); + if (near > seg->node_count) + return grub_error(GRUB_ERR_BAD_FS, "invalid segment"); + break; + } + + case GRUB_DISKFILTER_STRIPED: + case GRUB_DISKFILTER_MIRROR: + break; + + case GRUB_DISKFILTER_RAID4: + case GRUB_DISKFILTER_RAID5: + if (seg->node_count <= 1) + return grub_error(GRUB_ERR_BAD_FS, "invalid segment"); + break; + + case GRUB_DISKFILTER_RAID6: + if (seg->node_count <= 2) + return grub_error(GRUB_ERR_BAD_FS, "invalid segment"); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID level %d", seg->type); + } + + unsigned i; + for (i = 0; i < seg->node_count; i++) + { + err = validate_node (&seg->nodes[i]); + if (err) + return err; + } + return GRUB_ERR_NONE; + +} + +static grub_err_t +read_segment (struct grub_diskfilter_segment *seg, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_err_t err; + switch (seg->type) + { + case GRUB_DISKFILTER_STRIPED: + if (seg->node_count == 1) + return grub_diskfilter_read_node (&seg->nodes[0], + sector, size, buf); + /* Fallthrough. */ + case GRUB_DISKFILTER_MIRROR: + case GRUB_DISKFILTER_RAID10: + { + grub_disk_addr_t read_sector, far_ofs; + grub_uint64_t disknr, b, near, far, ofs; + unsigned int i, j; + + read_sector = grub_divmod64 (sector, seg->stripe_size, &b); + far = ofs = near = 1; + far_ofs = 0; + + if (seg->type == 1) + near = seg->node_count; + else if (seg->type == 10) + { + near = seg->layout & 0xFF; + far = (seg->layout >> 8) & 0xFF; + if (seg->layout >> 16) + { + ofs = far; + far_ofs = 1; + } + else + far_ofs = grub_divmod64 (seg->raid_member_size, + far * seg->stripe_size, 0); + + far_ofs *= seg->stripe_size; + } + + read_sector = grub_divmod64 (read_sector * near, + seg->node_count, + &disknr); + + ofs *= seg->stripe_size; + read_sector *= ofs; + + while (1) + { + grub_size_t read_size; + + read_size = seg->stripe_size - b; + if (read_size > size) + read_size = size; + + err = 0; + for (i = 0; i < near; i++) + { + unsigned int k; + + k = disknr; + err = 0; + for (j = 0; j < far; j++) + { + if (grub_errno == GRUB_ERR_READ_ERROR + || grub_errno == GRUB_ERR_UNKNOWN_DEVICE) + grub_errno = GRUB_ERR_NONE; + + err = grub_diskfilter_read_node (&seg->nodes[k], + read_sector + + j * far_ofs + b, + read_size, + buf); + if (! err) + break; + else if (err != GRUB_ERR_READ_ERROR + && err != GRUB_ERR_UNKNOWN_DEVICE) + return err; + k++; + if (k == seg->node_count) + k = 0; + } + + if (! err) + break; + + disknr++; + if (disknr == seg->node_count) + { + disknr = 0; + read_sector += ofs; + } + } + + if (err) + return err; + + buf += read_size << GRUB_DISK_SECTOR_BITS; + size -= read_size; + if (! size) + return GRUB_ERR_NONE; + + b = 0; + disknr += (near - i); + while (disknr >= seg->node_count) + { + disknr -= seg->node_count; + read_sector += ofs; + } + } + } + + case GRUB_DISKFILTER_RAID4: + case GRUB_DISKFILTER_RAID5: + case GRUB_DISKFILTER_RAID6: + { + grub_disk_addr_t read_sector; + grub_uint64_t b, p, n, disknr; + + /* n = 1 for level 4 and 5, 2 for level 6. */ + n = seg->type / 3; + + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, seg->stripe_size, &b); + read_sector = grub_divmod64 (read_sector, seg->node_count - n, + &disknr); + if (seg->type >= 5) + { + grub_divmod64 (read_sector, seg->node_count, &p); + + if (! (seg->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = seg->node_count - 1 - p; + + if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + } + else + { + grub_uint32_t q; + + q = p + (n - 1); + if (q >= seg->node_count) + q -= seg->node_count; + + if (disknr >= p) + disknr += n; + else if (disknr >= q) + disknr += q + 1; + } + + if (disknr >= seg->node_count) + disknr -= seg->node_count; + } + else + p = seg->node_count - n; + read_sector *= seg->stripe_size; + + while (1) + { + grub_size_t read_size; + int next_level; + + read_size = seg->stripe_size - b; + if (read_size > size) + read_size = size; + + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR + || grub_errno == GRUB_ERR_UNKNOWN_DEVICE) + grub_errno = GRUB_ERR_NONE; + + err = grub_diskfilter_read_node (&seg->nodes[disknr], + read_sector + b, + read_size, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR + && err != GRUB_ERR_UNKNOWN_DEVICE)) + return err; + + if (err) + { + grub_errno = GRUB_ERR_NONE; + if (seg->type == GRUB_DISKFILTER_RAID6) + { + err = ((grub_raid6_recover_func) ? + (*grub_raid6_recover_func) (seg, disknr, p, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + N_("module `%s' isn't loaded"), + "raid6rec")); + } + else + { + err = ((grub_raid5_recover_func) ? + (*grub_raid5_recover_func) (seg, disknr, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + N_("module `%s' isn't loaded"), + "raid5rec")); + } + + if (err) + return err; + } + + buf += read_size << GRUB_DISK_SECTOR_BITS; + size -= read_size; + sector += read_size; + if (! size) + break; + + b = 0; + disknr++; + + if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == seg->node_count) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= seg->node_count); + } + + if (next_level) + { + read_sector += seg->stripe_size; + + if (seg->type >= 5) + { + if (seg->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == seg->node_count - 1) ? 0 : p + 1; + else + p = (p == 0) ? seg->node_count - 1 : p - 1; + + if (seg->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr = p + n; + if (disknr >= seg->node_count) + disknr -= seg->node_count; + } + else + { + disknr -= seg->node_count; + if ((disknr >= p && disknr < p + n) + || (disknr + seg->node_count >= p + && disknr + seg->node_count < p + n)) + disknr = p + n; + if (disknr >= seg->node_count) + disknr -= seg->node_count; + } + } + else + disknr = 0; + } + } + } + return GRUB_ERR_NONE; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID level %d", seg->type); + } +} + +static grub_err_t +read_lv (struct grub_diskfilter_lv *lv, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + if (!lv) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "unknown volume"); + + while (size) + { + grub_err_t err = 0; + struct grub_diskfilter_vg *vg = lv->vg; + struct grub_diskfilter_segment *seg = lv->segments; + grub_uint64_t extent; + grub_uint64_t to_read; + + extent = grub_divmod64 (sector, vg->extent_size, NULL); + + /* Find the right segment. */ + { + unsigned int i; + for (i = 0; i < lv->segment_count; i++) + { + if ((seg->start_extent <= extent) + && ((seg->start_extent + seg->extent_count) > extent)) + break; + seg++; + } + if (i == lv->segment_count) + return grub_error (GRUB_ERR_READ_ERROR, "incorrect segment"); + } + to_read = ((seg->start_extent + seg->extent_count) + * vg->extent_size) - sector; + if (to_read > size) + to_read = size; + + err = read_segment (seg, sector - seg->start_extent * vg->extent_size, + to_read, buf); + if (err) + return err; + + size -= to_read; + sector += to_read; + buf += to_read << GRUB_DISK_SECTOR_BITS; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_diskfilter_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + return read_lv (disk->data, sector, size, buf); +} + +static grub_err_t +grub_diskfilter_write (grub_disk_t disk __attribute ((unused)), + grub_disk_addr_t sector __attribute ((unused)), + grub_size_t size __attribute ((unused)), + const char *buf __attribute ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "diskfilter writes are not supported"); +} + +struct grub_diskfilter_vg * +grub_diskfilter_get_vg_by_uuid (grub_size_t uuidlen, char *uuid) +{ + struct grub_diskfilter_vg *p; + + for (p = array_list; p != NULL; p = p->next) + if ((p->uuid_len == uuidlen) && + (! grub_memcmp (p->uuid, uuid, p->uuid_len))) + return p; + return NULL; +} + +grub_err_t +grub_diskfilter_vg_register (struct grub_diskfilter_vg *vg) +{ + struct grub_diskfilter_lv *lv, *p; + struct grub_diskfilter_vg *vgp; + unsigned i; + + grub_dprintf ("diskfilter", "Found array %s\n", vg->name); +#ifdef GRUB_UTIL + grub_util_info ("Found array %s", vg->name); +#endif + + for (lv = vg->lvs; lv; lv = lv->next) + { + /* RAID 1 and single-disk RAID 0 don't use a chunksize but code + assumes one so set one. */ + for (i = 0; i < lv->segment_count; i++) + { + if (lv->segments[i].type == 1) + lv->segments[i].stripe_size = 64; + if (lv->segments[i].type == GRUB_DISKFILTER_STRIPED + && lv->segments[i].node_count == 1 + && lv->segments[i].stripe_size == 0) + lv->segments[i].stripe_size = 64; + } + } + for (lv = vg->lvs; lv; lv = lv->next) + { + grub_err_t err; + + err = validate_lv(lv); + if (err) + return err; + lv->number = lv_num++; + + if (lv->fullname) + { + grub_size_t len; + int max_used_number = 0, need_new_name = 0; + len = grub_strlen (lv->fullname); + for (vgp = array_list; vgp; vgp = vgp->next) + for (p = vgp->lvs; p; p = p->next) + { + int cur_num; + char *num; + const char *end; + if (!p->fullname) + continue; + if (grub_strncmp (p->fullname, lv->fullname, len) != 0) + continue; + if (p->fullname[len] == 0) + { + need_new_name = 1; + continue; + } + num = p->fullname + len + 1; + if (!grub_isdigit (num[0])) + continue; + cur_num = grub_strtoul (num, &end, 10); + if (end[0]) + continue; + if (cur_num > max_used_number) + max_used_number = cur_num; + } + if (need_new_name) + { + char *tmp; + tmp = grub_xasprintf ("%s_%d", lv->fullname, max_used_number + 1); + if (!tmp) + return grub_errno; + grub_free (lv->fullname); + lv->fullname = tmp; + } + } + } + /* Add our new array to the list. */ + vg->next = array_list; + array_list = vg; + return GRUB_ERR_NONE; +} + +struct grub_diskfilter_vg * +grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb, + const char *name, grub_uint64_t disk_size, + grub_uint64_t stripe_size, + int layout, int level) +{ + struct grub_diskfilter_vg *array; + int i; + grub_size_t j, sz; + grub_uint64_t totsize; + struct grub_diskfilter_pv *pv; + grub_err_t err; + + /* We choose not to support more than the specified number of disks. */ + if (nmemb < 1 || nmemb > GRUB_MDRAID_MAX_DISKS) + { + grub_free (uuid); + return NULL; + } + + switch (level) + { + case 1: + totsize = disk_size; + break; + + case 10: + { + int n; + n = layout & 0xFF; + if (n == 1) + n = (layout >> 8) & 0xFF; + if (n == 0) + { + grub_free (uuid); + return NULL; + } + + totsize = grub_divmod64 (nmemb * disk_size, n, 0); + } + break; + + case 0: + case 4: + case 5: + case 6: + totsize = (nmemb - ((unsigned) level / 3U)) * disk_size; + break; + + default: + grub_free (uuid); + return NULL; + } + + array = grub_diskfilter_get_vg_by_uuid (uuidlen, uuid); + if (array) + { + if (array->lvs && array->lvs->size < totsize) + { + array->lvs->size = totsize; + if (array->lvs->segments) + array->lvs->segments->extent_count = totsize; + } + + if (array->lvs && array->lvs->segments + && array->lvs->segments->raid_member_size > disk_size) + array->lvs->segments->raid_member_size = disk_size; + + grub_free (uuid); + return array; + } + array = grub_zalloc (sizeof (*array)); + if (!array) + { + grub_free (uuid); + return NULL; + } + array->uuid = uuid; + array->uuid_len = uuidlen; + if (name) + { + /* Strip off the homehost if present. */ + char *colon = grub_strchr (name, ':'); + char *new_name = grub_xasprintf ("md/%s", + colon ? colon + 1 : name); + + if (! new_name) + goto fail; + + array->name = new_name; + } + + array->extent_size = 1; + array->lvs = grub_zalloc (sizeof (*array->lvs)); + if (!array->lvs) + goto fail; + array->lvs->segment_count = 1; + array->lvs->visible = 1; + if (array->name) + { + array->lvs->name = grub_strdup (array->name); + if (!array->lvs->name) + goto fail; + array->lvs->fullname = grub_strdup (array->name); + if (!array->lvs->fullname) + goto fail; + } + array->lvs->vg = array; + + if (grub_mul (uuidlen, 2, &sz) || + grub_add (sz, sizeof ("mduuid/"), &sz)) + goto fail; + + array->lvs->idname = grub_malloc (sz); + if (!array->lvs->idname) + goto fail; + + grub_memcpy (array->lvs->idname, "mduuid/", sizeof ("mduuid/") - 1); + for (j = 0; j < uuidlen; j++) + { + array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * j] + = hex2ascii (((unsigned char) uuid[j] >> 4)); + array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * j + 1] + = hex2ascii (((unsigned char) uuid[j] & 0xf)); + } + array->lvs->idname[sizeof ("mduuid/") - 1 + 2 * uuidlen] = '\0'; + + array->lvs->size = totsize; + + array->lvs->segments = grub_zalloc (sizeof (*array->lvs->segments)); + if (!array->lvs->segments) + goto fail; + array->lvs->segments->stripe_size = stripe_size; + array->lvs->segments->layout = layout; + array->lvs->segments->start_extent = 0; + array->lvs->segments->extent_count = totsize; + array->lvs->segments->type = level; + array->lvs->segments->node_count = nmemb; + array->lvs->segments->raid_member_size = disk_size; + array->lvs->segments->nodes + = grub_calloc (nmemb, sizeof (array->lvs->segments->nodes[0])); + if (array->lvs->segments->nodes == NULL) + goto fail; + + array->lvs->segments->stripe_size = stripe_size; + for (i = 0; i < nmemb; i++) + { + pv = grub_zalloc (sizeof (*pv)); + if (!pv) + goto fail; + pv->id.uuidlen = 0; + pv->id.id = i; + pv->next = array->pvs; + array->pvs = pv; + array->lvs->segments->nodes[i].pv = pv; + } + + err = grub_diskfilter_vg_register (array); + if (err) + goto fail; + + return array; + + fail: + if (array->lvs) + { + grub_free (array->lvs->name); + grub_free (array->lvs->fullname); + grub_free (array->lvs->idname); + if (array->lvs->segments) + { + grub_free (array->lvs->segments->nodes); + grub_free (array->lvs->segments); + } + grub_free (array->lvs); + } + while (array->pvs) + { + pv = array->pvs->next; + grub_free (array->pvs); + array->pvs = pv; + } + grub_free (array->name); + grub_free (array->uuid); + grub_free (array); + return NULL; +} + +static grub_err_t +insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id, + struct grub_diskfilter_vg *array, + grub_disk_addr_t start_sector, + grub_diskfilter_t diskfilter __attribute__ ((unused))) +{ + struct grub_diskfilter_pv *pv; + + grub_dprintf ("diskfilter", "Inserting %s (+%lld,%lld) into %s (%s)\n", disk->name, + (unsigned long long) grub_partition_get_start (disk->partition), + (unsigned long long) grub_disk_native_sectors (disk), + array->name, diskfilter->name); +#ifdef GRUB_UTIL + grub_util_info ("Inserting %s (+%" GRUB_HOST_PRIuLONG_LONG ",%" + GRUB_HOST_PRIuLONG_LONG ") into %s (%s)\n", disk->name, + (unsigned long long) grub_partition_get_start (disk->partition), + (unsigned long long) grub_disk_native_sectors (disk), + array->name, diskfilter->name); + array->driver = diskfilter; +#endif + + for (pv = array->pvs; pv; pv = pv->next) + if (id->uuidlen == pv->id.uuidlen + && id->uuidlen + ? (grub_memcmp (pv->id.uuid, id->uuid, id->uuidlen) == 0) + : (pv->id.id == id->id)) + { + struct grub_diskfilter_lv *lv; + /* FIXME: Check whether the update time of the superblocks are + the same. */ + if (pv->disk && grub_disk_native_sectors (disk) >= pv->part_size) + return GRUB_ERR_NONE; + pv->disk = grub_disk_open (disk->name); + if (!pv->disk) + return grub_errno; + /* This could happen to LVM on RAID, pv->disk points to the + raid device, we shouldn't change it. */ + pv->start_sector -= pv->part_start; + pv->part_start = grub_partition_get_start (disk->partition); + pv->part_size = grub_disk_native_sectors (disk); + +#ifdef GRUB_UTIL + { + grub_size_t s = 1; + grub_partition_t p; + for (p = disk->partition; p; p = p->parent) + s++; + pv->partmaps = xcalloc (s, sizeof (pv->partmaps[0])); + s = 0; + for (p = disk->partition; p; p = p->parent) + pv->partmaps[s++] = xstrdup (p->partmap->name); + pv->partmaps[s++] = 0; + } +#endif + if (start_sector != (grub_uint64_t)-1) + pv->start_sector = start_sector; + pv->start_sector += pv->part_start; + /* Add the device to the array. */ + for (lv = array->lvs; lv; lv = lv->next) + if (!lv->became_readable_at && lv->fullname && is_lv_readable (lv, 0)) + lv->became_readable_at = ++inscnt; + break; + } + + return 0; +} + +static void +free_array (void) +{ + while (array_list) + { + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_pv *pv; + struct grub_diskfilter_lv *lv; + + vg = array_list; + array_list = array_list->next; + + while ((pv = vg->pvs)) + { + vg->pvs = pv->next; + grub_free (pv->name); + if (pv->disk) + grub_disk_close (pv->disk); + if (pv->id.uuidlen) + grub_free (pv->id.uuid); +#ifdef GRUB_UTIL + grub_free (pv->partmaps); +#endif + grub_free (pv->internal_id); + grub_free (pv); + } + + while ((lv = vg->lvs)) + { + unsigned i; + vg->lvs = lv->next; + grub_free (lv->fullname); + grub_free (lv->name); + grub_free (lv->idname); + for (i = 0; i < lv->segment_count; i++) + grub_free (lv->segments[i].nodes); + grub_free (lv->segments); + grub_free (lv->internal_id); + grub_free (lv); + } + + grub_free (vg->uuid); + grub_free (vg->name); + grub_free (vg); + } + + array_list = 0; +} + +#ifdef GRUB_UTIL +struct grub_diskfilter_pv * +grub_diskfilter_get_pv_from_disk (grub_disk_t disk, + struct grub_diskfilter_vg **vg_out) +{ + struct grub_diskfilter_pv *pv; + struct grub_diskfilter_vg *vg; + + scan_disk (disk->name, 1); + for (vg = array_list; vg; vg = vg->next) + for (pv = vg->pvs; pv; pv = pv->next) + { + if (pv->disk && pv->disk->id == disk->id + && pv->disk->dev->id == disk->dev->id + && pv->part_start == grub_partition_get_start (disk->partition) + && pv->part_size == grub_disk_native_sectors (disk)) + { + if (vg_out) + *vg_out = vg; + return pv; + } + } + return NULL; +} +#endif + +static int +grub_diskfilter_check_pvs_encrypted (grub_disk_t disk, int *pvs_cnt) +{ + struct grub_diskfilter_lv *lv = disk->data; + struct grub_diskfilter_pv *pv; + + *pvs_cnt = 0; + + if (lv->vg->pvs) + for (pv = lv->vg->pvs; pv; pv = pv->next) + { + (*pvs_cnt)++; + + if (pv->disk == NULL) + { + /* Can be a partially activated VG, bail out. */ + return GRUB_ERR_TEST_FAILURE; + } + + if (pv->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID) + { + /* All backing devices must be cryptodisks, stop. */ + return GRUB_ERR_TEST_FAILURE; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_disk_t disk; + int check_pvs_res; + int namelen; + int pvs_cnt; + int opt_quiet = 0; + + if (argc == 2) + { + if (grub_strcmp (args[0], "--quiet") == 0) + { + opt_quiet = 1; + argc--; + args++; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized option: %s"), args[0]); + } + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected")); + + namelen = grub_strlen (args[0]); + if (namelen > 2 && (args[0][0] == '(') && (args[0][namelen - 1] == ')')) + args[0][namelen - 1] = 0; + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("invalid disk: %s"), + args[0]); + + if (!is_valid_diskfilter_name (&args[0][1])) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unrecognized disk: %s"), + &args[0][1]); + + disk = grub_disk_open (&args[0][1]); + if (disk == NULL) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such disk: %s"), + &args[0][1]); + + check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt); + grub_disk_close (disk); + if (!opt_quiet) + grub_printf ("%s is %sencrypted (%d pv%s examined)\n", &args[0][1], + (check_pvs_res == GRUB_ERR_NONE) ? "" : "un", + pvs_cnt, + (pvs_cnt > 1) ? "s" : ""); + + return check_pvs_res; +} + +static struct grub_disk_dev grub_diskfilter_dev = + { + .name = "diskfilter", + .id = GRUB_DISK_DEVICE_DISKFILTER_ID, + .disk_iterate = grub_diskfilter_iterate, + .disk_open = grub_diskfilter_open, + .disk_close = grub_diskfilter_close, + .disk_read = grub_diskfilter_read, + .disk_write = grub_diskfilter_write, +#ifdef GRUB_UTIL + .disk_memberlist = grub_diskfilter_memberlist, + .disk_raidname = grub_diskfilter_getname, +#endif + .next = 0 + }; + +static grub_command_t cmd; + + +GRUB_MOD_INIT(diskfilter) +{ + grub_disk_dev_register (&grub_diskfilter_dev); + cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck, + N_("[--quiet] DEVICE"), + N_("Check if a logical volume resides on encrypted disks.")); +} + +GRUB_MOD_FINI(diskfilter) +{ + grub_disk_dev_unregister (&grub_diskfilter_dev); + if (cmd != NULL) + grub_unregister_command (cmd); + free_array (); +} diff --git a/disk/dmraid_nvidia.c b/grub-core/disk/dmraid_nvidia.c similarity index 65% rename from disk/dmraid_nvidia.c rename to grub-core/disk/dmraid_nvidia.c index c4e3922cf..6372663e6 100644 --- a/disk/dmraid_nvidia.c +++ b/grub-core/disk/dmraid_nvidia.c @@ -22,7 +22,9 @@ #include #include #include -#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); #define NV_SIGNATURES 4 @@ -86,68 +88,97 @@ struct grub_nv_super char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */ grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ struct grub_nv_array array; /* Array information */ -} __attribute__ ((packed)); +} GRUB_PACKED; -static grub_err_t -grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +static struct grub_diskfilter_vg * +grub_dmraid_nv_detect (grub_disk_t disk, + struct grub_diskfilter_pv_id *id, + grub_disk_addr_t *start_sector) { grub_disk_addr_t sector; struct grub_nv_super sb; + int level; + grub_uint64_t disk_size; + grub_uint32_t capacity; + grub_uint8_t total_volumes; + char *uuid; if (disk->partition) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); - - sector = grub_disk_get_size (disk) - 2; + /* Skip partition. */ + return NULL; + sector = grub_disk_native_sectors (disk); + if (sector == GRUB_DISK_SIZE_UNKNOWN) + /* Not raid. */ + return NULL; + sector -= 2; if (grub_disk_read (disk, sector, 0, sizeof (sb), &sb)) - return grub_errno; + return NULL; if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + /* Not raid. */ + return NULL; if (sb.version != NV_VERSION) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unknown version: %d.%d", sb.version); + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unknown version: %d.%d", sb.version >> 8, sb.version & 0xFF); + return NULL; + } + + capacity = grub_le_to_cpu32 (sb.capacity); + total_volumes = sb.array.total_volumes; switch (sb.array.raid_level) { case NV_LEVEL_0: - array->level = 0; - array->disk_size = sb.capacity / sb.array.total_volumes; + level = 0; + if (total_volumes == 0) + /* Not RAID. */ + return NULL; + disk_size = capacity / total_volumes; break; case NV_LEVEL_1: - array->level = 1; - array->disk_size = sb.capacity; + level = 1; + disk_size = sb.capacity; break; case NV_LEVEL_5: - array->level = 5; - array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; - array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + level = 5; + if (total_volumes == 0 || total_volumes == 1) + /* Not RAID. */ + return NULL; + disk_size = capacity / (total_volumes - 1); break; default: - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported RAID level: %d", sb.array.raid_level); + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID level: %d", sb.array.raid_level); + return NULL; } - array->number = 0; - array->total_devs = sb.array.total_volumes; - array->chunk_size = sb.array.stripe_block_size; - array->index = sb.unit_number; - array->uuid_len = sizeof (sb.array.signature); - array->uuid = grub_malloc (sizeof (sb.array.signature)); - if (! array->uuid) - return grub_errno; + uuid = grub_malloc (sizeof (sb.array.signature)); + if (! uuid) + return NULL; - grub_memcpy (array->uuid, (char *) &sb.array.signature, + grub_memcpy (uuid, (char *) &sb.array.signature, sizeof (sb.array.signature)); - return 0; + id->uuidlen = 0; + id->id = sb.unit_number; + + *start_sector = 0; + + return grub_diskfilter_make_raid (sizeof (sb.array.signature), + uuid, sb.array.total_volumes, + "nv", disk_size, + sb.array.stripe_block_size, + GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC, + level); } -static struct grub_raid grub_dmraid_nv_dev = +static struct grub_diskfilter grub_dmraid_nv_dev = { .name = "dmraid_nv", .detect = grub_dmraid_nv_detect, @@ -156,10 +187,10 @@ static struct grub_raid grub_dmraid_nv_dev = GRUB_MOD_INIT(dm_nv) { - grub_raid_register (&grub_dmraid_nv_dev); + grub_diskfilter_register_front (&grub_dmraid_nv_dev); } GRUB_MOD_FINI(dm_nv) { - grub_raid_unregister (&grub_dmraid_nv_dev); + grub_diskfilter_unregister (&grub_dmraid_nv_dev); } diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c new file mode 100644 index 000000000..3b5ed5691 --- /dev/null +++ b/grub-core/disk/efi/efidisk.c @@ -0,0 +1,912 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct grub_efidisk_data +{ + grub_efi_handle_t handle; + grub_efi_device_path_t *device_path; + grub_efi_device_path_t *last_device_path; + grub_efi_block_io_t *block_io; + struct grub_efidisk_data *next; +}; + +/* GUID. */ +static grub_guid_t block_io_guid = GRUB_EFI_BLOCK_IO_GUID; + +static struct grub_efidisk_data *fd_devices; +static struct grub_efidisk_data *hd_devices; +static struct grub_efidisk_data *cd_devices; + +static struct grub_efidisk_data * +make_devices (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + struct grub_efidisk_data *devices = 0; + + /* Find handles which support the disk io interface. */ + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &block_io_guid, + 0, &num_handles); + if (! handles) + return 0; + + /* Make a linked list of devices. */ + for (handle = handles; num_handles--; handle++) + { + grub_efi_device_path_t *dp; + grub_efi_device_path_t *ldp; + struct grub_efidisk_data *d; + grub_efi_block_io_t *bio; + + dp = grub_efi_get_device_path (*handle); + if (! dp) + continue; + + ldp = grub_efi_find_last_device_path (dp); + if (! ldp) + /* This is empty. Why? */ + continue; + + bio = grub_efi_open_protocol (*handle, &block_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (! bio) + /* This should not happen... Why? */ + continue; + + /* iPXE adds stub Block IO protocol to loaded image device handle. It is + completely non-functional and simply returns an error for every method. + So attempt to detect and skip it. Magic number is literal "iPXE" and + check block size as well */ + /* FIXME: shoud we close it? We do not do it elsewhere */ + if (bio->media && bio->media->media_id == 0x69505845U && + bio->media->block_size == 1) + continue; + + d = grub_malloc (sizeof (*d)); + if (! d) + { + /* Uggh. */ + grub_free (handles); + while (devices) + { + d = devices->next; + grub_free (devices); + devices = d; + } + return 0; + } + + d->handle = *handle; + d->device_path = dp; + d->last_device_path = ldp; + d->block_io = bio; + d->next = devices; + devices = d; + } + + grub_free (handles); + + return devices; +} + +/* Find the parent device. */ +static struct grub_efidisk_data * +find_parent_device (struct grub_efidisk_data *devices, + struct grub_efidisk_data *d) +{ + grub_efi_device_path_t *dp, *ldp; + struct grub_efidisk_data *parent; + + dp = grub_efi_duplicate_device_path (d->device_path); + if (! dp) + return 0; + + ldp = grub_efi_find_last_device_path (dp); + if (! ldp) + return 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + for (parent = devices; parent; parent = parent->next) + { + /* Ignore itself. */ + if (parent == d) + continue; + + if (grub_efi_compare_device_paths (parent->device_path, dp) == 0) + break; + } + + grub_free (dp); + return parent; +} + +static int +is_child (struct grub_efidisk_data *child, + struct grub_efidisk_data *parent) +{ + grub_efi_device_path_t *dp, *ldp; + int ret; + + dp = grub_efi_duplicate_device_path (child->device_path); + if (! dp) + return 0; + + ldp = grub_efi_find_last_device_path (dp); + if (! ldp) + return 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ret = (grub_efi_compare_device_paths (dp, parent->device_path) == 0); + grub_free (dp); + return ret; +} + +#define FOR_CHILDREN(p, dev) for (p = dev; p; p = p->next) if (is_child (p, d)) + +/* Add a device into a list of devices in an ascending order. */ +static void +add_device (struct grub_efidisk_data **devices, struct grub_efidisk_data *d) +{ + struct grub_efidisk_data **p; + struct grub_efidisk_data *n; + + for (p = devices; *p; p = &((*p)->next)) + { + int ret; + + ret = grub_efi_compare_device_paths (grub_efi_find_last_device_path ((*p)->device_path), + grub_efi_find_last_device_path (d->device_path)); + if (ret == 0) + ret = grub_efi_compare_device_paths ((*p)->device_path, + d->device_path); + if (ret == 0) + return; + else if (ret > 0) + break; + } + + n = grub_malloc (sizeof (*n)); + if (! n) + return; + + grub_memcpy (n, d, sizeof (*n)); + n->next = (*p); + (*p) = n; +} + +/* Name the devices. */ +static void +name_devices (struct grub_efidisk_data *devices) +{ + struct grub_efidisk_data *d; + + /* First, identify devices by media device paths. */ + for (d = devices; d; d = d->next) + { + grub_efi_device_path_t *dp; + + dp = d->last_device_path; + if (! dp) + continue; + + if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE) + { + int is_hard_drive = 0; + + switch (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp)) + { + case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE: + is_hard_drive = 1; + /* Intentionally fall through. */ + case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE: + { + struct grub_efidisk_data *parent, *parent2; + + parent = find_parent_device (devices, d); + if (!parent) + { +#ifdef DEBUG_NAMES + grub_printf ("skipping orphaned partition: "); + grub_efi_print_device_path (d->device_path); +#endif + break; + } + parent2 = find_parent_device (devices, parent); + if (parent2) + { +#ifdef DEBUG_NAMES + grub_printf ("skipping subpartition: "); + grub_efi_print_device_path (d->device_path); +#endif + /* Mark itself as used. */ + d->last_device_path = 0; + break; + } + if (!parent->last_device_path) + { + d->last_device_path = 0; + break; + } + if (is_hard_drive) + { +#ifdef DEBUG_NAMES + grub_printf ("adding a hard drive by a partition: "); + grub_efi_print_device_path (parent->device_path); +#endif + add_device (&hd_devices, parent); + } + else + { +#ifdef DEBUG_NAMES + grub_printf ("adding a cdrom by a partition: "); + grub_efi_print_device_path (parent->device_path); +#endif + add_device (&cd_devices, parent); + } + + /* Mark the parent as used. */ + parent->last_device_path = 0; + } + /* Mark itself as used. */ + d->last_device_path = 0; + break; + + default: +#ifdef DEBUG_NAMES + grub_printf ("skipping other type: "); + grub_efi_print_device_path (d->device_path); +#endif + /* For now, ignore the others. */ + break; + } + } + else + { +#ifdef DEBUG_NAMES + grub_printf ("skipping non-media: "); + grub_efi_print_device_path (d->device_path); +#endif + } + } + + /* Let's see what can be added more. */ + for (d = devices; d; d = d->next) + { + grub_efi_device_path_t *dp; + grub_efi_block_io_media_t *m; + int is_floppy = 0; + + dp = d->last_device_path; + if (! dp) + continue; + + /* Ghosts proudly presented by Apple. */ + if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) + == GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE) + { + grub_efi_vendor_device_path_t *vendor = (grub_efi_vendor_device_path_t *) dp; + static const grub_guid_t apple = GRUB_EFI_VENDOR_APPLE_GUID; + + if (vendor->header.length == sizeof (*vendor) + && grub_memcmp (&vendor->vendor_guid, &apple, + sizeof (vendor->vendor_guid)) == 0 + && find_parent_device (devices, d)) + continue; + } + + m = d->block_io->media; + if (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_ACPI_DEVICE_PATH_TYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) + == GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE) + { + grub_efi_acpi_device_path_t *acpi + = (grub_efi_acpi_device_path_t *) dp; + /* Floppy EISA ID. */ + if (acpi->hid == 0x60441d0 || acpi->hid == 0x70041d0 + || acpi->hid == 0x70141d1) + is_floppy = 1; + } + if (is_floppy) + { +#ifdef DEBUG_NAMES + grub_printf ("adding a floppy: "); + grub_efi_print_device_path (d->device_path); +#endif + add_device (&fd_devices, d); + } + else if (m->read_only && m->block_size > GRUB_DISK_SECTOR_SIZE) + { + /* This check is too heuristic, but assume that this is a + CDROM drive. */ +#ifdef DEBUG_NAMES + grub_printf ("adding a cdrom by guessing: "); + grub_efi_print_device_path (d->device_path); +#endif + add_device (&cd_devices, d); + } + else + { + /* The default is a hard drive. */ +#ifdef DEBUG_NAMES + grub_printf ("adding a hard drive by guessing: "); + grub_efi_print_device_path (d->device_path); +#endif + add_device (&hd_devices, d); + } + } +} + +static void +free_devices (struct grub_efidisk_data *devices) +{ + struct grub_efidisk_data *p, *q; + + for (p = devices; p; p = q) + { + q = p->next; + grub_free (p); + } +} + +/* Enumerate all disks to name devices. */ +static void +enumerate_disks (void) +{ + struct grub_efidisk_data *devices; + + devices = make_devices (); + if (! devices) + return; + + name_devices (devices); + free_devices (devices); +} + +static int +grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_efidisk_data *d; + char buf[16]; + int count; + + switch (pull) + { + case GRUB_DISK_PULL_NONE: + for (d = hd_devices, count = 0; d; d = d->next, count++) + { + grub_snprintf (buf, sizeof (buf), "hd%d", count); + grub_dprintf ("efidisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + break; + case GRUB_DISK_PULL_REMOVABLE: + for (d = fd_devices, count = 0; d; d = d->next, count++) + { + grub_snprintf (buf, sizeof (buf), "fd%d", count); + grub_dprintf ("efidisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + + for (d = cd_devices, count = 0; d; d = d->next, count++) + { + grub_snprintf (buf, sizeof (buf), "cd%d", count); + grub_dprintf ("efidisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + break; + default: + return 0; + } + + return 0; +} + +static int +get_drive_number (const char *name) +{ + unsigned long drive; + + if ((name[0] != 'f' && name[0] != 'h' && name[0] != 'c') || name[1] != 'd') + goto fail; + + drive = grub_strtoul (name + 2, 0, 10); + if (grub_errno != GRUB_ERR_NONE) + goto fail; + + return (int) drive ; + + fail: + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a efidisk"); + return -1; +} + +static struct grub_efidisk_data * +get_device (struct grub_efidisk_data *devices, int num) +{ + struct grub_efidisk_data *d; + + for (d = devices; d && num; d = d->next, num--) + ; + + if (num == 0) + return d; + + return 0; +} + +static grub_err_t +grub_efidisk_open (const char *name, struct grub_disk *disk) +{ + int num; + struct grub_efidisk_data *d = 0; + grub_efi_block_io_media_t *m; + + grub_dprintf ("efidisk", "opening %s\n", name); + + num = get_drive_number (name); + if (num < 0) + return grub_errno; + + switch (name[0]) + { + case 'f': + d = get_device (fd_devices, num); + break; + case 'c': + d = get_device (cd_devices, num); + break; + case 'h': + d = get_device (hd_devices, num); + break; + default: + /* Never reach here. */ + break; + } + + if (! d) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); + + disk->id = ((num << GRUB_CHAR_BIT) | name[0]); + m = d->block_io->media; + /* FIXME: Probably it is better to store the block size in the disk, + and total sectors should be replaced with total blocks. */ + grub_dprintf ("efidisk", + "m = %p, last block = %llx, block size = %x, io align = %x\n", + m, (unsigned long long) m->last_block, m->block_size, + m->io_align); + + /* Ensure required buffer alignment is a power of two (or is zero). */ + if (m->io_align & (m->io_align - 1)) + return grub_error (GRUB_ERR_IO, "invalid buffer alignment %d", m->io_align); + + disk->total_sectors = m->last_block + 1; + /* Don't increase this value due to bug in some EFI. */ + disk->max_agglomerate = 0xa0000 >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS); + if (m->block_size & (m->block_size - 1) || !m->block_size) + return grub_error (GRUB_ERR_IO, "invalid sector size %d", + m->block_size); + disk->log_sector_size = grub_log2ull (m->block_size); + disk->data = d; + + grub_dprintf ("efidisk", "opening %s succeeded\n", name); + + return GRUB_ERR_NONE; +} + +static void +grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused))) +{ + /* EFI disks do not allocate extra memory, so nothing to do here. */ + grub_dprintf ("efidisk", "closing %s\n", disk->name); +} + +static grub_efi_status_t +grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector, + grub_size_t size, char *buf, int wr) +{ + struct grub_efidisk_data *d; + grub_efi_block_io_t *bio; + grub_efi_status_t status; + grub_size_t io_align, num_bytes; + char *aligned_buf; + + d = disk->data; + bio = d->block_io; + + /* + * If IoAlign is > 1, it should represent the required alignment. However, + * some UEFI implementations seem to report IoAlign=2 but require 2^IoAlign. + * Some implementation seem to require alignment despite not reporting any + * specific requirements. + * + * Make sure to use buffers which are at least aligned to block size. + */ + if (bio->media->io_align < bio->media->block_size) + io_align = bio->media->block_size; + else + io_align = bio->media->io_align; + + num_bytes = size << disk->log_sector_size; + + if ((grub_addr_t) buf & (io_align - 1)) + { + aligned_buf = grub_memalign (io_align, num_bytes); + if (! aligned_buf) + return GRUB_EFI_OUT_OF_RESOURCES; + if (wr) + grub_memcpy (aligned_buf, buf, num_bytes); + } + else + { + aligned_buf = buf; + } + + status = (wr ? bio->write_blocks : bio->read_blocks) (bio, bio->media->media_id, + (grub_efi_uint64_t) sector, + (grub_efi_uintn_t) num_bytes, + aligned_buf); + + if ((grub_addr_t) buf & (io_align - 1)) + { + if (!wr) + grub_memcpy (buf, aligned_buf, num_bytes); + grub_free (aligned_buf); + } + + return status; +} + +static grub_err_t +grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_efi_status_t status; + + grub_dprintf ("efidisk", + "reading 0x%lx sectors at the sector 0x%llx from %s\n", + (unsigned long) size, (unsigned long long) sector, disk->name); + + status = grub_efidisk_readwrite (disk, sector, size, buf, 0); + + if (status == GRUB_EFI_NO_MEDIA) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("no media in `%s'"), disk->name); + else if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_READ_ERROR, + N_("failure reading sector 0x%llx from `%s'"), + (unsigned long long) sector, + disk->name); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efidisk_write (struct grub_disk *disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_efi_status_t status; + + grub_dprintf ("efidisk", + "writing 0x%lx sectors at the sector 0x%llx to %s\n", + (unsigned long) size, (unsigned long long) sector, disk->name); + + status = grub_efidisk_readwrite (disk, sector, size, (char *) buf, 1); + + if (status == GRUB_EFI_NO_MEDIA) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("no media in `%s'"), disk->name); + else if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_WRITE_ERROR, + N_("failure writing sector 0x%llx to `%s'"), + (unsigned long long) sector, disk->name); + + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_efidisk_dev = + { + .name = "efidisk", + .id = GRUB_DISK_DEVICE_EFIDISK_ID, + .disk_iterate = grub_efidisk_iterate, + .disk_open = grub_efidisk_open, + .disk_close = grub_efidisk_close, + .disk_read = grub_efidisk_read, + .disk_write = grub_efidisk_write, + .next = 0 + }; + +void +grub_efidisk_fini (void) +{ + free_devices (fd_devices); + free_devices (hd_devices); + free_devices (cd_devices); + fd_devices = 0; + hd_devices = 0; + cd_devices = 0; + grub_disk_dev_unregister (&grub_efidisk_dev); +} + +void +grub_efidisk_init (void) +{ + grub_disk_firmware_fini = grub_efidisk_fini; + + enumerate_disks (); + grub_disk_dev_register (&grub_efidisk_dev); +} + +/* Some utility functions to map GRUB devices with EFI devices. */ +grub_efi_handle_t +grub_efidisk_get_device_handle (grub_disk_t disk) +{ + struct grub_efidisk_data *d; + char type; + + if (disk->dev->id != GRUB_DISK_DEVICE_EFIDISK_ID) + return 0; + + d = disk->data; + type = disk->name[0]; + + switch (type) + { + case 'f': + /* This is the simplest case. */ + return d->handle; + + case 'c': + /* FIXME: probably this is not correct. */ + return d->handle; + + case 'h': + /* If this is the whole disk, just return its own data. */ + if (! disk->partition) + return d->handle; + + /* Otherwise, we must query the corresponding device to the firmware. */ + { + struct grub_efidisk_data *devices; + grub_efi_handle_t handle = 0; + struct grub_efidisk_data *c; + + devices = make_devices (); + FOR_CHILDREN (c, devices) + { + grub_efi_hard_drive_device_path_t *hd; + + hd = (grub_efi_hard_drive_device_path_t *) c->last_device_path; + + if ((GRUB_EFI_DEVICE_PATH_TYPE (c->last_device_path) + == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE) + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (c->last_device_path) + == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE) + && (grub_partition_get_start (disk->partition) + == (hd->partition_start << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS))) + && (grub_partition_get_len (disk->partition) + == (hd->partition_size << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)))) + { + handle = c->handle; + break; + } + } + + free_devices (devices); + + if (handle != 0) + return handle; + } + break; + + default: + break; + } + + return 0; +} + +#define NEEDED_BUFLEN sizeof ("XdXXXXXXXXXX") +static inline int +get_diskname_from_path_real (const grub_efi_device_path_t *path, + struct grub_efidisk_data *head, + char *buf) +{ + int count = 0; + struct grub_efidisk_data *d; + for (d = head, count = 0; d; d = d->next, count++) + if (grub_efi_compare_device_paths (d->device_path, path) == 0) + { + grub_snprintf (buf, NEEDED_BUFLEN - 1, "d%d", count); + return 1; + } + return 0; +} + +static inline int +get_diskname_from_path (const grub_efi_device_path_t *path, + char *buf) +{ + if (get_diskname_from_path_real (path, hd_devices, buf + 1)) + { + buf[0] = 'h'; + return 1; + } + + if (get_diskname_from_path_real (path, fd_devices, buf + 1)) + { + buf[0] = 'f'; + return 1; + } + + if (get_diskname_from_path_real (path, cd_devices, buf + 1)) + { + buf[0] = 'c'; + return 1; + } + return 0; +} + +/* Context for grub_efidisk_get_device_name. */ +struct grub_efidisk_get_device_name_ctx +{ + char *partition_name; + grub_efi_hard_drive_device_path_t *hd; +}; + +/* Helper for grub_efidisk_get_device_name. + Find the identical partition. */ +static int +grub_efidisk_get_device_name_iter (grub_disk_t disk, + const grub_partition_t part, void *data) +{ + struct grub_efidisk_get_device_name_ctx *ctx = data; + + if (grub_partition_get_start (part) + == (ctx->hd->partition_start << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + && grub_partition_get_len (part) + == (ctx->hd->partition_size << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS))) + { + ctx->partition_name = grub_partition_get_name (part); + return 1; + } + + return 0; +} + +char * +grub_efidisk_get_device_name (grub_efi_handle_t *handle) +{ + grub_efi_device_path_t *dp, *ldp; + char device_name[NEEDED_BUFLEN]; + + dp = grub_efi_get_device_path (handle); + if (! dp) + return 0; + + ldp = grub_efi_find_last_device_path (dp); + if (! ldp) + return 0; + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE)) + { + struct grub_efidisk_get_device_name_ctx ctx; + char *dev_name; + grub_efi_device_path_t *dup_dp; + grub_disk_t parent = 0; + + /* It is necessary to duplicate the device path so that GRUB + can overwrite it. */ + dup_dp = grub_efi_duplicate_device_path (dp); + if (! dup_dp) + return 0; + + while (1) + { + grub_efi_device_path_t *dup_ldp; + dup_ldp = grub_efi_find_last_device_path (dup_dp); + if (! dup_ldp) + break; + + if (!(GRUB_EFI_DEVICE_PATH_TYPE (dup_ldp) == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE))) + break; + + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + + if (!get_diskname_from_path (dup_dp, device_name)) + { + grub_free (dup_dp); + return 0; + } + + parent = grub_disk_open (device_name); + grub_free (dup_dp); + + if (! parent) + return 0; + + /* Find a partition which matches the hard drive device path. */ + ctx.partition_name = NULL; + ctx.hd = (grub_efi_hard_drive_device_path_t *) ldp; + if (ctx.hd->partition_start == 0 + && (ctx.hd->partition_size << (parent->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + == grub_disk_native_sectors (parent)) + { + dev_name = grub_strdup (parent->name); + } + else + { + grub_partition_iterate (parent, grub_efidisk_get_device_name_iter, + &ctx); + + if (! ctx.partition_name) + { + /* No partition found. In most cases partition is embed in + the root path anyway, so this is not critical. + This happens only if partition is on partmap that GRUB + doesn't need to access root. + */ + grub_disk_close (parent); + return grub_strdup (device_name); + } + + dev_name = grub_xasprintf ("%s,%s", parent->name, + ctx.partition_name); + grub_free (ctx.partition_name); + } + grub_disk_close (parent); + + return dev_name; + } + /* This may be guessed device - floppy, cdrom or entire disk. */ + if (!get_diskname_from_path (dp, device_name)) + return 0; + return grub_strdup (device_name); +} diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c new file mode 100644 index 000000000..722910d64 --- /dev/null +++ b/grub-core/disk/geli.c @@ -0,0 +1,584 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011 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 . + */ + +/* This file is loosely based on FreeBSD geli implementation + (but no code was directly copied). FreeBSD geli is distributed under + following terms: */ +/*- + * Copyright (c) 2005-2006 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Dirty trick to solve circular dependency. */ +#ifdef GRUB_UTIL + +#include + +#undef GRUB_MD_SHA256 +#undef GRUB_MD_SHA512 + +static const gcry_md_spec_t * +grub_md_sha256_real (void) +{ + const gcry_md_spec_t *ret; + ret = grub_crypto_lookup_md_by_name ("sha256"); + if (!ret) + grub_util_error ("%s", _("Couldn't load sha256")); + return ret; +} + +static const gcry_md_spec_t * +grub_md_sha512_real (void) +{ + const gcry_md_spec_t *ret; + ret = grub_crypto_lookup_md_by_name ("sha512"); + if (!ret) + grub_util_error ("%s", _("Couldn't load sha512")); + return ret; +} + +#define GRUB_MD_SHA256 grub_md_sha256_real() +#define GRUB_MD_SHA512 grub_md_sha512_real() +#endif + +struct grub_geli_key +{ + grub_uint8_t iv_key[64]; + grub_uint8_t cipher_key[64]; + grub_uint8_t hmac[64]; +} GRUB_PACKED; + +struct grub_geli_phdr +{ + grub_uint8_t magic[16]; +#define GELI_MAGIC "GEOM::ELI" + grub_uint32_t version; + grub_uint32_t flags; + grub_uint16_t alg; + grub_uint16_t keylen; + grub_uint16_t unused3[5]; + grub_uint32_t sector_size; + grub_uint8_t keys_used; + grub_uint32_t niter; + grub_uint8_t salt[64]; + struct grub_geli_key keys[2]; +} GRUB_PACKED; + +enum + { + GRUB_GELI_FLAGS_ONETIME = 1, + GRUB_GELI_FLAGS_BOOT = 2, + }; + +/* FIXME: support version 0. */ +/* FIXME: support big-endian pre-version-4 volumes. */ +/* FIXME: support for keyfiles. */ +/* FIXME: support for HMAC. */ +const char *algorithms[] = { + [0x01] = "des", + [0x02] = "3des", + [0x03] = "blowfish", + [0x04] = "cast5", + /* FIXME: 0x05 is skipjack, but we don't have it. */ + [0x0b] = "aes", + /* FIXME: 0x10 is null. */ + [0x15] = "camellia128", + [0x16] = "aes" +}; + +static gcry_err_code_t +geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno) +{ + gcry_err_code_t gcry_err; + const struct { + char magic[4]; + grub_uint64_t zone; + } GRUB_PACKED tohash + = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) }; + GRUB_PROPERLY_ALIGNED_ARRAY (key, GRUB_CRYPTO_MAX_MDLEN); + + if (dev->hash->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return GPG_ERR_INV_ARG; + + grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n", + zoneno, dev->rekey_derived_size); + gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64, + &tohash, sizeof (tohash), key); + if (gcry_err) + return gcry_err; + + return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key, + dev->rekey_derived_size); +} + +static inline gcry_err_code_t +make_uuid (const struct grub_geli_phdr *header, + char *uuid) +{ + grub_uint8_t uuidbin[GRUB_CRYPTODISK_MAX_UUID_LENGTH]; + gcry_err_code_t err; + grub_uint8_t *iptr; + char *optr; + + if (2 * GRUB_MD_SHA256->mdlen + 1 > GRUB_CRYPTODISK_MAX_UUID_LENGTH) + return GPG_ERR_TOO_LARGE; + err = grub_crypto_hmac_buffer (GRUB_MD_SHA256, + header->salt, sizeof (header->salt), + "uuid", sizeof ("uuid") - 1, uuidbin); + if (err) + return err; + + optr = uuid; + for (iptr = uuidbin; iptr < &uuidbin[GRUB_MD_SHA256->mdlen]; iptr++) + { + grub_snprintf (optr, 3, "%02x", *iptr); + optr += 2; + } + *optr = 0; + return GPG_ERR_NO_ERROR; +} + +#ifdef GRUB_UTIL + +#include +#include + +char * +grub_util_get_geli_uuid (const char *dev) +{ + grub_util_fd_t fd; + grub_uint64_t s; + unsigned log_secsize; + grub_uint8_t hdr[512]; + struct grub_geli_phdr *header; + char *uuid; + gcry_err_code_t err; + + fd = grub_util_fd_open (dev, GRUB_UTIL_FD_O_RDONLY); + + if (!GRUB_UTIL_FD_IS_VALID (fd)) + return NULL; + + s = grub_util_get_fd_size (fd, dev, &log_secsize); + s >>= log_secsize; + if (grub_util_fd_seek (fd, (s << log_secsize) - 512) < 0) + grub_util_error ("%s", _("couldn't read ELI metadata")); + + uuid = xmalloc (GRUB_MD_SHA256->mdlen * 2 + 1); + if (grub_util_fd_read (fd, (void *) &hdr, 512) < 0) + grub_util_error ("%s", _("couldn't read ELI metadata")); + + grub_util_fd_close (fd); + + COMPILE_TIME_ASSERT (sizeof (header) <= 512); + header = (void *) &hdr; + + /* Look for GELI magic sequence. */ + if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) + || grub_le_to_cpu32 (header->version) > 7 + || grub_le_to_cpu32 (header->version) < 1) + grub_util_error ("%s", _("wrong ELI magic or version")); + + err = make_uuid ((void *) &hdr, uuid); + if (err) + { + grub_free (uuid); + return NULL; + } + + return uuid; +} +#endif + +static grub_cryptodisk_t +geli_scan (grub_disk_t disk, grub_cryptomount_args_t cargs) +{ + grub_cryptodisk_t newdev; + struct grub_geli_phdr header; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; + const struct gcry_cipher_spec *ciph; + const char *ciphername = NULL; + gcry_err_code_t gcry_err; + char uuid[GRUB_CRYPTODISK_MAX_UUID_LENGTH]; + grub_disk_addr_t sector; + grub_err_t err; + + /* Detached headers are not implemented yet */ + if (cargs->hdr_file != NULL) + return NULL; + + if (2 * GRUB_MD_SHA256->mdlen + 1 > GRUB_CRYPTODISK_MAX_UUID_LENGTH) + return NULL; + + sector = grub_disk_native_sectors (disk); + if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0) + return NULL; + + /* Read the GELI header. */ + err = grub_disk_read (disk, sector - 1, 0, sizeof (header), &header); + if (err) + return NULL; + + /* Look for GELI magic sequence. */ + if (grub_memcmp (header.magic, GELI_MAGIC, sizeof (GELI_MAGIC)) + || grub_le_to_cpu32 (header.version) > 7 + || grub_le_to_cpu32 (header.version) < 1) + { + grub_dprintf ("geli", "wrong magic %02x\n", header.magic[0]); + return NULL; + } + + if ((grub_le_to_cpu32 (header.sector_size) + & (grub_le_to_cpu32 (header.sector_size) - 1)) + || grub_le_to_cpu32 (header.sector_size) == 0) + { + grub_dprintf ("geli", "incorrect sector size %d\n", + grub_le_to_cpu32 (header.sector_size)); + return NULL; + } + + if (grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_ONETIME) + { + grub_dprintf ("geli", "skipping one-time volume\n"); + return NULL; + } + + if (cargs->check_boot && !(grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_BOOT)) + { + grub_dprintf ("geli", "not a boot volume\n"); + return NULL; + } + + gcry_err = make_uuid (&header, uuid); + if (gcry_err) + { + grub_crypto_gcry_error (gcry_err); + return NULL; + } + + if (cargs->search_uuid != NULL && grub_uuidcasecmp (cargs->search_uuid, uuid, sizeof (uuid)) != 0) + { + grub_dprintf ("geli", "%s != %s\n", uuid, cargs->search_uuid); + return NULL; + } + + if (grub_le_to_cpu16 (header.alg) >= ARRAY_SIZE (algorithms) + || algorithms[grub_le_to_cpu16 (header.alg)] == NULL) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown", + grub_le_to_cpu16 (header.alg)); + return NULL; + } + + ciphername = algorithms[grub_le_to_cpu16 (header.alg)]; + ciph = grub_crypto_lookup_cipher_by_name (ciphername); + if (!ciph) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", + ciphername); + return NULL; + } + + /* Configure the cipher used for the bulk data. */ + cipher = grub_crypto_cipher_open (ciph); + if (!cipher) + return NULL; + + if (grub_le_to_cpu16 (header.alg) == 0x16) + { + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) + { + grub_crypto_cipher_close (cipher); + return NULL; + } + + } + + if (grub_le_to_cpu16 (header.keylen) > 1024) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", + grub_le_to_cpu16 (header.keylen)); + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (secondary_cipher); + return NULL; + } + + newdev = grub_zalloc (sizeof (struct grub_cryptodisk)); + if (!newdev) + { + grub_crypto_cipher_close (cipher); + grub_crypto_cipher_close (secondary_cipher); + return NULL; + } + newdev->cipher = cipher; + newdev->secondary_cipher = secondary_cipher; + newdev->offset_sectors = 0; + newdev->source_disk = NULL; + newdev->benbi_log = 0; + if (grub_le_to_cpu16 (header.alg) == 0x16) + { + newdev->mode = GRUB_CRYPTODISK_MODE_XTS; + newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64; + } + else + { + newdev->mode = GRUB_CRYPTODISK_MODE_CBC; + newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH; + } + newdev->essiv_cipher = NULL; + newdev->essiv_hash = NULL; + newdev->hash = GRUB_MD_SHA512; + newdev->iv_hash = GRUB_MD_SHA256; + + newdev->log_sector_size = grub_log2ull (grub_le_to_cpu32 (header.sector_size)); + + if (grub_le_to_cpu32 (header.version) >= 5) + { + newdev->rekey = geli_rekey; + newdev->rekey_shift = 20; + } + + newdev->modname = "geli"; + + newdev->total_sectors = grub_disk_native_sectors (disk) - 1; + grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); + COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1); + return newdev; +} + +static grub_err_t +geli_recover_key (grub_disk_t source, grub_cryptodisk_t dev, grub_cryptomount_args_t cargs) +{ + grub_size_t keysize; + grub_uint8_t digest[GRUB_CRYPTO_MAX_MDLEN]; + grub_uint8_t geomkey[GRUB_CRYPTO_MAX_MDLEN]; + grub_uint8_t verify_key[GRUB_CRYPTO_MAX_MDLEN]; + grub_uint8_t zero[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; + grub_uint8_t geli_cipher_key[64]; + unsigned i; + gcry_err_code_t gcry_err; + struct grub_geli_phdr header; + grub_disk_addr_t sector; + grub_err_t err; + + if (cargs->key_data == NULL || cargs->key_len == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no key data"); + + if (dev->cipher->cipher->blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) + return grub_error (GRUB_ERR_BUG, "cipher block is too long"); + + if (dev->hash->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return grub_error (GRUB_ERR_BUG, "mdlen is too long"); + + sector = grub_disk_native_sectors (source); + if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0) + return grub_error (GRUB_ERR_BUG, "not a geli"); + + /* Read the GELI header. */ + err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header); + if (err) + return err; + + keysize = grub_le_to_cpu16 (header.keylen) / GRUB_CHAR_BIT; + grub_memset (zero, 0, sizeof (zero)); + + grub_puts_ (N_("Attempting to decrypt master key...")); + + /* Calculate the PBKDF2 of the user supplied passphrase. */ + if (grub_le_to_cpu32 (header.niter) != 0) + { + grub_uint8_t pbkdf_key[64]; + gcry_err = grub_crypto_pbkdf2 (dev->hash, cargs->key_data, + cargs->key_len, + header.salt, + sizeof (header.salt), + grub_le_to_cpu32 (header.niter), + pbkdf_key, sizeof (pbkdf_key)); + + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key, + sizeof (pbkdf_key), geomkey); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + else + { + struct grub_crypto_hmac_handle *hnd; + + hnd = grub_crypto_hmac_init (dev->hash, NULL, 0); + if (!hnd) + return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY); + + grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt)); + grub_crypto_hmac_write (hnd, cargs->key_data, cargs->key_len); + + gcry_err = grub_crypto_hmac_fini (hnd, geomkey); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + + gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, + dev->hash->mdlen, "\1", 1, digest); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey, + dev->hash->mdlen, "\0", 1, verify_key); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize); + + /* Try to recover master key from each active keyslot. */ + for (i = 0; i < ARRAY_SIZE (header.keys); i++) + { + struct grub_geli_key candidate_key; + grub_uint8_t key_hmac[GRUB_CRYPTO_MAX_MDLEN]; + + /* Check if keyslot is enabled. */ + if (! (header.keys_used & (1 << i))) + continue; + + grub_dprintf ("geli", "Trying keyslot %d\n", i); + + gcry_err = grub_crypto_cipher_set_key (dev->cipher, + digest, keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key, + &header.keys[i], + sizeof (candidate_key), + zero); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key, + dev->hash->mdlen, + &candidate_key, + (sizeof (candidate_key) + - sizeof (candidate_key.hmac)), + key_hmac); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + + if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0) + continue; + grub_printf_ (N_("Slot %d opened\n"), i); + + if (grub_le_to_cpu32 (header.version) >= 7) + { + /* GELI >=7 uses the cipher_key */ + grub_memcpy (geli_cipher_key, candidate_key.cipher_key, + sizeof (candidate_key.cipher_key)); + } + else + { + /* GELI <=6 uses the iv_key */ + grub_memcpy (geli_cipher_key, candidate_key.iv_key, + sizeof (candidate_key.iv_key)); + } + + /* Set the master key. */ + if (!dev->rekey) + { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header.alg) == 0x16) + real_keysize *= 2; + gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, + real_keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + else + { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header.alg) == 0x16) + real_keysize *= 2; + + grub_memcpy (dev->rekey_key, geli_cipher_key, + sizeof (geli_cipher_key)); + dev->rekey_derived_size = real_keysize; + dev->last_rekey = -1; + COMPILE_TIME_ASSERT (sizeof (dev->rekey_key) + >= sizeof (geli_cipher_key)); + } + + dev->iv_prefix_len = sizeof (candidate_key.iv_key); + grub_memcpy (dev->iv_prefix, candidate_key.iv_key, + sizeof (candidate_key.iv_key)); + + COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key)); + + return GRUB_ERR_NONE; + } + + return GRUB_ACCESS_DENIED; +} + +struct grub_cryptodisk_dev geli_crypto = { + .scan = geli_scan, + .recover_key = geli_recover_key +}; + +GRUB_MOD_INIT (geli) +{ + grub_cryptodisk_dev_register (&geli_crypto); +} + +GRUB_MOD_FINI (geli) +{ + grub_cryptodisk_dev_unregister (&geli_crypto); +} diff --git a/disk/host.c b/grub-core/disk/host.c similarity index 83% rename from disk/host.c rename to grub-core/disk/host.c index c4f3e7150..f34529f86 100644 --- a/disk/host.c +++ b/grub-core/disk/host.c @@ -20,16 +20,24 @@ /* When using the disk, make a reference to this module. Otherwise the user will end up with a useless module :-). */ +#include +#include + #include #include #include +#include int grub_disk_host_i_want_a_reference; static int -grub_host_iterate (int (*hook) (const char *name)) +grub_host_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) { - if (hook ("host")) + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + if (hook ("host", hook_data)) return 1; return 0; } @@ -41,9 +49,8 @@ grub_host_open (const char *name, grub_disk_t disk) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a host disk"); disk->total_sectors = 0; - disk->id = (unsigned long) "host"; + disk->id = 0; - disk->has_partitions = 0; disk->data = 0; return GRUB_ERR_NONE; @@ -77,11 +84,11 @@ static struct grub_disk_dev grub_host_dev = /* The only important line in this file :-) */ .name = "host", .id = GRUB_DISK_DEVICE_HOST_ID, - .iterate = grub_host_iterate, - .open = grub_host_open, - .close = grub_host_close, - .read = grub_host_read, - .write = grub_host_write, + .disk_iterate = grub_host_iterate, + .disk_open = grub_host_open, + .disk_close = grub_host_close, + .disk_read = grub_host_read, + .disk_write = grub_host_write, .next = 0 }; diff --git a/grub-core/disk/i386/pc/biosdisk.c b/grub-core/disk/i386/pc/biosdisk.c new file mode 100644 index 000000000..1d6788950 --- /dev/null +++ b/grub-core/disk/i386/pc/biosdisk.c @@ -0,0 +1,687 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static int cd_drive = 0; +static int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap); + +static int grub_biosdisk_get_num_floppies (void) +{ + struct grub_bios_int_registers regs; + int drive; + + /* reset the disk system first */ + regs.eax = 0; + regs.edx = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + grub_bios_interrupt (0x13, ®s); + + for (drive = 0; drive < 2; drive++) + { + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY; + regs.edx = drive; + + /* call GET DISK TYPE */ + regs.eax = 0x1500; + grub_bios_interrupt (0x13, ®s); + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + break; + + /* check if this drive exists */ + if (!(regs.eax & 0x300)) + break; + } + + return drive; +} + +/* + * 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. + */ + +static int +grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap) +{ + struct grub_bios_int_registers regs; + regs.eax = ah << 8; + /* compute the address of disk_address_packet */ + regs.ds = (((grub_addr_t) dap) & 0xffff0000) >> 4; + regs.esi = (((grub_addr_t) dap) & 0xffff); + regs.edx = drive; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + grub_bios_interrupt (0x13, ®s); + return (regs.eax >> 8) & 0xff; +} + +/* + * 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. + */ +static int +grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff, + int soff, int nsec, int segment) +{ + int ret, i; + + /* Try 3 times. */ + for (i = 0; i < 3; i++) + { + struct grub_bios_int_registers regs; + + /* set up CHS information */ + /* set %ch to low eight bits of cylinder */ + regs.ecx = (coff << 8) & 0xff00; + /* set bits 6-7 of %cl to high two bits of cylinder */ + regs.ecx |= (coff >> 2) & 0xc0; + /* set bits 0-5 of %cl to sector */ + regs.ecx |= soff & 0x3f; + + /* set %dh to head and %dl to drive */ + regs.edx = (drive & 0xff) | ((hoff << 8) & 0xff00); + /* set %ah to AH */ + regs.eax = (ah << 8) & 0xff00; + /* set %al to NSEC */ + regs.eax |= nsec & 0xff; + + regs.ebx = 0; + regs.es = segment; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + grub_bios_interrupt (0x13, ®s); + /* check if successful */ + if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY)) + return 0; + + /* save return value */ + ret = regs.eax >> 8; + + /* if fail, reset the disk system */ + regs.eax = 0; + regs.edx = (drive & 0xff); + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x13, ®s); + } + return ret; +} + +/* + * Check if LBA is supported for DRIVE. If it is supported, then return + * the major version of extensions, otherwise zero. + */ +static int +grub_biosdisk_check_int13_extensions (int drive) +{ + struct grub_bios_int_registers regs; + + regs.edx = drive & 0xff; + regs.eax = 0x4100; + regs.ebx = 0x55aa; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x13, ®s); + + if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY) + return 0; + + if ((regs.ebx & 0xffff) != 0xaa55) + return 0; + + /* check if AH=0x42 is supported */ + if (!(regs.ecx & 1)) + return 0; + + return (regs.eax >> 8) & 0xff; +} + +/* + * Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an + * error occurs, then return non-zero, otherwise zero. + */ +static int +grub_biosdisk_get_diskinfo_standard (int drive, + unsigned long *cylinders, + unsigned long *heads, + unsigned long *sectors) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x0800; + regs.edx = drive & 0xff; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x13, ®s); + + /* Check if unsuccessful. Ignore return value if carry isn't set to + workaround some buggy BIOSes. */ + if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0)) + return (regs.eax & 0xff00) >> 8; + + /* bogus BIOSes may not return an error number */ + /* 0 sectors means no disk */ + if (!(regs.ecx & 0x3f)) + /* XXX 0x60 is one of the unused error numbers */ + return 0x60; + + /* the number of heads is counted from zero */ + *heads = ((regs.edx >> 8) & 0xff) + 1; + *cylinders = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x0300)) + 1; + *sectors = regs.ecx & 0x3f; + return 0; +} + +static int +grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax) +{ + struct grub_bios_int_registers regs; + + regs.eax = ax; + + /* compute the address of drive parameters */ + regs.esi = ((grub_addr_t) drp) & 0xf; + regs.ds = ((grub_addr_t) drp) >> 4; + regs.edx = drive & 0xff; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x13, ®s); + + /* Check if unsuccessful. Ignore return value if carry isn't set to + workaround some buggy BIOSes. */ + if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0)) + return (regs.eax & 0xff00) >> 8; + + return 0; +} + +/* + * Return the cdrom information of DRIVE in CDRP. If an error occurs, + * then return non-zero, otherwise zero. + */ +static int +grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp) +{ + return grub_biosdisk_get_diskinfo_real (drive, cdrp, 0x4b01); +} + +/* + * Return the geometry of DRIVE in a drive parameters, DRP. If an error + * occurs, then return non-zero, otherwise zero. + */ +static int +grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp) +{ + return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800); +} + +static int +grub_biosdisk_get_drive (const char *name) +{ + unsigned long drive; + + if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive) + return cd_drive; + + if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd') + goto fail; + + drive = grub_strtoul (name + 2, 0, 10); + if (grub_errno != GRUB_ERR_NONE) + goto fail; + + if (name[0] == 'h') + drive += 0x80; + + return (int) drive ; + + fail: + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk"); + return -1; +} + +static int +grub_biosdisk_call_hook (grub_disk_dev_iterate_hook_t hook, void *hook_data, + int drive) +{ + char name[10]; + + if (cd_drive && drive == cd_drive) + return hook ("cd", hook_data); + + grub_snprintf (name, sizeof (name), + (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80)); + return hook (name, hook_data); +} + +static int +grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + int num_floppies; + int drive; + + /* For hard disks, attempt to read the MBR. */ + switch (pull) + { + case GRUB_DISK_PULL_NONE: + for (drive = 0x80; drive < 0x90; drive++) + { + if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1, + GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0) + { + grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive); + break; + } + + if (grub_biosdisk_call_hook (hook, hook_data, drive)) + return 1; + } + return 0; + + case GRUB_DISK_PULL_REMOVABLE: + if (cd_drive) + { + if (grub_biosdisk_call_hook (hook, hook_data, cd_drive)) + return 1; + } + + /* For floppy disks, we can get the number safely. */ + num_floppies = grub_biosdisk_get_num_floppies (); + for (drive = 0; drive < num_floppies; drive++) + if (grub_biosdisk_call_hook (hook, hook_data, drive)) + return 1; + return 0; + default: + return 0; + } + return 0; +} + +static grub_err_t +grub_biosdisk_open (const char *name, grub_disk_t disk) +{ + grub_uint64_t total_sectors = 0; + int drive; + struct grub_biosdisk_data *data; + + drive = grub_biosdisk_get_drive (name); + if (drive < 0) + return grub_errno; + + disk->id = drive; + + data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data)); + if (! data) + return grub_errno; + + data->drive = drive; + + if ((cd_drive) && (drive == cd_drive)) + { + data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; + data->sectors = 8; + disk->log_sector_size = 11; + /* TODO: get the correct size. */ + total_sectors = GRUB_DISK_SIZE_UNKNOWN; + } + else + { + /* HDD */ + int version; + + disk->log_sector_size = 9; + + version = grub_biosdisk_check_int13_extensions (drive); + if (version) + { + struct grub_biosdisk_drp *drp + = (struct grub_biosdisk_drp *) grub_absolute_pointer (GRUB_MEMORY_MACHINE_SCRATCH_ADDR); + + /* Clear out the DRP. */ + grub_memset (drp, 0, sizeof (*drp)); + drp->size = sizeof (*drp); + if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp)) + { + data->flags = GRUB_BIOSDISK_FLAG_LBA; + + if (drp->total_sectors) + total_sectors = drp->total_sectors; + else + /* Some buggy BIOSes doesn't return the total sectors + correctly but returns zero. So if it is zero, compute + it by C/H/S returned by the LBA BIOS call. */ + total_sectors = ((grub_uint64_t) drp->cylinders) + * drp->heads * drp->sectors; + if (drp->bytes_per_sector + && !(drp->bytes_per_sector & (drp->bytes_per_sector - 1)) + && drp->bytes_per_sector >= 512 + && drp->bytes_per_sector <= 16384) + disk->log_sector_size = grub_log2ull (drp->bytes_per_sector); + } + } + } + + if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM)) + { + if (grub_biosdisk_get_diskinfo_standard (drive, + &data->cylinders, + &data->heads, + &data->sectors) != 0) + { + if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA)) + { + data->sectors = 63; + data->heads = 255; + data->cylinders + = grub_divmod64 (total_sectors + + data->heads * data->sectors - 1, + data->heads * data->sectors, 0); + } + else + { + grub_free (data); + return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name); + } + } + + if (data->sectors == 0) + data->sectors = 63; + if (data->heads == 0) + data->heads = 255; + + if (! total_sectors) + total_sectors = ((grub_uint64_t) data->cylinders) + * data->heads * data->sectors; + } + + disk->total_sectors = total_sectors; + /* Limit the max to 0x7f because of Phoenix EDD. */ + disk->max_agglomerate = 0x7f >> GRUB_DISK_CACHE_BITS; + COMPILE_TIME_ASSERT ((0x7f >> GRUB_DISK_CACHE_BITS + << (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS)) + + sizeof (struct grub_biosdisk_dap) + < GRUB_MEMORY_MACHINE_SCRATCH_SIZE); + + disk->data = data; + + return GRUB_ERR_NONE; +} + +static void +grub_biosdisk_close (grub_disk_t disk) +{ + grub_free (disk->data); +} + +/* For readability. */ +#define GRUB_BIOSDISK_READ 0 +#define GRUB_BIOSDISK_WRITE 1 + +#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3 + +static grub_err_t +grub_biosdisk_rw (int cmd, grub_disk_t disk, + grub_disk_addr_t sector, grub_size_t size, + unsigned segment) +{ + struct grub_biosdisk_data *data = disk->data; + + /* VirtualBox fails with sectors above 2T on CDs. + Since even BD-ROMS are never that big anyway, return error. */ + if ((data->flags & GRUB_BIOSDISK_FLAG_CDROM) + && (sector >> 32)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of disk `%s'"), + disk->name); + + if (data->flags & GRUB_BIOSDISK_FLAG_LBA) + { + struct grub_biosdisk_dap *dap; + + dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR + + (GRUB_DISK_MAX_LBA_SECTORS + << disk->log_sector_size)); + dap->length = sizeof (*dap); + dap->reserved = 0; + dap->blocks = size; + dap->buffer = segment << 16; /* The format SEGMENT:ADDRESS. */ + dap->block = sector; + + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) + { + int i; + + if (cmd) + return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write to CD-ROM")); + + for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++) + if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap)) + break; + + if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT) + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + } + else + if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap)) + { + /* Fall back to the CHS mode. */ + data->flags &= ~GRUB_BIOSDISK_FLAG_LBA; + disk->total_sectors = data->cylinders * data->heads * data->sectors; + return grub_biosdisk_rw (cmd, disk, sector, size, segment); + } + } + else + { + unsigned coff, hoff, soff; + unsigned head; + + /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with + the traditional CHS access. */ + if (sector > + 1024 /* cylinders */ * + 256 /* heads */ * + 63 /* spt */) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of disk `%s'"), + disk->name); + + soff = ((grub_uint32_t) sector) % data->sectors + 1; + head = ((grub_uint32_t) sector) / data->sectors; + hoff = head % data->heads; + coff = head / data->heads; + + if (coff >= data->cylinders) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of disk `%s'"), + disk->name); + + if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive, + coff, hoff, soff, size, segment)) + { + switch (cmd) + { + case GRUB_BIOSDISK_READ: + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + case GRUB_BIOSDISK_WRITE: + return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " + "to `%s'"), + (unsigned long long) sector, + disk->name); + } + } + } + + return GRUB_ERR_NONE; +} + +/* Return the number of sectors which can be read safely at a time. */ +static grub_size_t +get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector) +{ + grub_size_t size; + grub_uint64_t offset; + struct grub_biosdisk_data *data = disk->data; + grub_uint32_t sectors = data->sectors; + + if (data->flags & GRUB_BIOSDISK_FLAG_LBA) + sectors = GRUB_DISK_MAX_LBA_SECTORS; + + /* OFFSET = SECTOR % SECTORS */ + grub_divmod64 (sector, sectors, &offset); + + size = sectors - offset; + + return size; +} + +static grub_err_t +grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + while (size) + { + grub_size_t len; + + len = get_safe_sectors (disk, sector); + + if (len > size) + len = size; + + if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len, + GRUB_MEMORY_MACHINE_SCRATCH_SEG)) + return grub_errno; + + grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, + len << disk->log_sector_size); + + buf += len << disk->log_sector_size; + sector += len; + size -= len; + } + + return grub_errno; +} + +static grub_err_t +grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + struct grub_biosdisk_data *data = disk->data; + + if (data->flags & GRUB_BIOSDISK_FLAG_CDROM) + return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM")); + + while (size) + { + grub_size_t len; + + len = get_safe_sectors (disk, sector); + if (len > size) + len = size; + + grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf, + len << disk->log_sector_size); + + if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len, + GRUB_MEMORY_MACHINE_SCRATCH_SEG)) + return grub_errno; + + buf += len << disk->log_sector_size; + sector += len; + size -= len; + } + + return grub_errno; +} + +static struct grub_disk_dev grub_biosdisk_dev = + { + .name = "biosdisk", + .id = GRUB_DISK_DEVICE_BIOSDISK_ID, + .disk_iterate = grub_biosdisk_iterate, + .disk_open = grub_biosdisk_open, + .disk_close = grub_biosdisk_close, + .disk_read = grub_biosdisk_read, + .disk_write = grub_biosdisk_write, + .next = 0 + }; + +static void +grub_disk_biosdisk_fini (void) +{ + grub_disk_dev_unregister (&grub_biosdisk_dev); +} + +GRUB_MOD_INIT(biosdisk) +{ + struct grub_biosdisk_cdrp *cdrp + = (struct grub_biosdisk_cdrp *) grub_absolute_pointer (GRUB_MEMORY_MACHINE_SCRATCH_ADDR); + grub_uint8_t boot_drive; + + if (grub_disk_firmware_is_tainted) + { + grub_puts_ (N_("Native disk drivers are in use. " + "Refusing to use firmware disk interface.")); + return; + } + grub_disk_firmware_fini = grub_disk_biosdisk_fini; + + grub_memset (cdrp, 0, sizeof (*cdrp)); + cdrp->size = sizeof (*cdrp); + cdrp->media_type = 0xFF; + boot_drive = (grub_boot_device >> 24); + if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive, cdrp)) + && ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK) + == GRUB_BIOSDISK_CDTYPE_NO_EMUL)) + cd_drive = cdrp->drive_no; + /* Since diskboot.S rejects devices over 0x90 it must be a CD booted with + cdboot.S + */ + if (boot_drive >= 0x90) + cd_drive = boot_drive; + + grub_disk_dev_register (&grub_biosdisk_dev); +} + +GRUB_MOD_FINI(biosdisk) +{ + grub_disk_biosdisk_fini (); +} diff --git a/disk/ieee1275/nand.c b/grub-core/disk/ieee1275/nand.c similarity index 77% rename from disk/ieee1275/nand.c rename to grub-core/disk/ieee1275/nand.c index df2ee81f3..bcf3a06f4 100644 --- a/disk/ieee1275/nand.c +++ b/grub-core/disk/ieee1275/nand.c @@ -22,6 +22,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct grub_nand_data { @@ -30,21 +33,32 @@ struct grub_nand_data }; static int -grub_nand_iterate (int (*hook) (const char *name)) +grub_nand_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) { - auto int dev_iterate (struct grub_ieee1275_devalias *alias); - int dev_iterate (struct grub_ieee1275_devalias *alias) - { - if (! grub_strcmp (alias->name, "nand")) - { - hook (alias->name); - return 1; - } + static int have_nand = -1; - return 0; + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + if (have_nand == -1) + { + struct grub_ieee1275_devalias alias; + + have_nand = 0; + FOR_IEEE1275_DEVALIASES(alias) + if (grub_strcmp (alias.name, "nand") == 0) + { + have_nand = 1; + break; + } + grub_ieee1275_devalias_free (&alias); } - return grub_devalias_iterate (dev_iterate); + if (have_nand) + return hook ("nand", hook_data); + + return 0; } static grub_err_t @@ -56,6 +70,7 @@ grub_nand_open (const char *name, grub_disk_t disk) { grub_ieee1275_ihandle_t dev_ihandle = 0; struct grub_nand_data *data = 0; + const char *devname; struct size_args { struct grub_ieee1275_common_hdr common; @@ -66,14 +81,18 @@ grub_nand_open (const char *name, grub_disk_t disk) grub_ieee1275_cell_t size2; } args; - if (! grub_strstr (name, "nand")) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a NAND device"); + if (grub_memcmp (name, "nand/", sizeof ("nand/") - 1) == 0) + devname = name + sizeof ("nand/") - 1; + else if (grub_strcmp (name, "nand") == 0) + devname = name; + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a NAND device"); data = grub_malloc (sizeof (*data)); if (! data) goto fail; - grub_ieee1275_open (name, &dev_ihandle); + grub_ieee1275_open (devname, &dev_ihandle); if (! dev_ihandle) { grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); @@ -94,6 +113,11 @@ grub_nand_open (const char *name, grub_disk_t disk) } data->block_size = (args.size1 >> GRUB_DISK_SECTOR_BITS); + if (!data->block_size) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "invalid block size"); + goto fail; + } INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 3); args.method = (grub_ieee1275_cell_t) "size"; @@ -113,7 +137,6 @@ grub_nand_open (const char *name, grub_disk_t disk) disk->id = dev_ihandle; - disk->has_partitions = 0; disk->data = data; return 0; @@ -172,7 +195,10 @@ grub_nand_read (grub_disk_t disk, grub_disk_addr_t sector, args.result = 1; if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.result)) - return grub_error (GRUB_ERR_READ_ERROR, "read error"); + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); ofs = 0; size -= len; @@ -189,18 +215,19 @@ grub_nand_write (grub_disk_t disk __attribute ((unused)), grub_size_t size __attribute ((unused)), const char *buf __attribute ((unused))) { - return GRUB_ERR_NOT_IMPLEMENTED_YET; + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "nand write is not supported"); } static struct grub_disk_dev grub_nand_dev = { .name = "nand", .id = GRUB_DISK_DEVICE_NAND_ID, - .iterate = grub_nand_iterate, - .open = grub_nand_open, - .close = grub_nand_close, - .read = grub_nand_read, - .write = grub_nand_write, + .disk_iterate = grub_nand_iterate, + .disk_open = grub_nand_open, + .disk_close = grub_nand_close, + .disk_read = grub_nand_read, + .disk_write = grub_nand_write, .next = 0 }; diff --git a/grub-core/disk/ieee1275/obdisk.c b/grub-core/disk/ieee1275/obdisk.c new file mode 100644 index 000000000..fcc39e0a2 --- /dev/null +++ b/grub-core/disk/ieee1275/obdisk.c @@ -0,0 +1,1109 @@ +/* obdisk.c - Open Boot disk access. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IEEE1275_DEV "ieee1275/" +#define IEEE1275_DISK_ALIAS "/disk@" + +struct disk_dev +{ + struct disk_dev *next; + struct disk_dev **prev; + char *name; + char *raw_name; + char *grub_devpath; + char *grub_alias_devpath; + grub_ieee1275_ihandle_t ihandle; + grub_uint32_t block_size; + grub_uint64_t num_blocks; + unsigned int log_sector_size; + grub_uint32_t opened; + grub_uint32_t valid; + grub_uint32_t boot_dev; +}; + +struct parent_dev +{ + struct parent_dev *next; + struct parent_dev **prev; + char *name; + char *type; + grub_ieee1275_ihandle_t ihandle; + grub_uint32_t address_cells; +}; + +static struct grub_scsi_test_unit_ready tur = +{ + .opcode = grub_scsi_cmd_test_unit_ready, + .lun = 0, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + .control = 0, +}; + +static int disks_enumerated; +static struct disk_dev *disk_devs; +static struct parent_dev *parent_devs; + +static const char *block_blacklist[] = { + /* Requires additional work in grub before being able to be used. */ + "/iscsi-hba", + /* This block device should never be used by grub. */ + "/reboot-memory@0", + 0 +}; + +#define STRCMP(a, b) ((a) && (b) && (grub_strcmp (a, b) == 0)) + +static char * +strip_ob_partition (char *path) +{ + char *sptr; + + sptr = grub_strstr (path, ":"); + + if (sptr != NULL) + *sptr = '\0'; + + return path; +} + +static void +escape_commas (const char *src, char *dest) +{ + const char *iptr; + + for (iptr = src; *iptr; ) + { + if (*iptr == ',') + *dest++ ='\\'; + + *dest++ = *iptr++; + } + + *dest = '\0'; +} + +static int +count_commas (const char *src) +{ + int count = 0; + + for ( ; *src; src++) + if (*src == ',') + count++; + + return count; +} + + +static char * +decode_grub_devname (const char *name) +{ + char *devpath; + char *p, c; + grub_size_t sz; + + if (grub_add (grub_strlen (name), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device name")); + return NULL; + } + + devpath = grub_malloc (sz); + if (devpath == NULL) + return NULL; + + /* Un-escape commas. */ + p = devpath; + while ((c = *name++) != '\0') + { + if (c == '\\' && *name == ',') + { + *p++ = ','; + name++; + } + else + *p++ = c; + } + + *p++ = '\0'; + + return devpath; +} + +static char * +encode_grub_devname (const char *path) +{ + char *encoding, *optr; + grub_size_t sz; + + if (path == NULL) + return NULL; + + if (grub_add (sizeof (IEEE1275_DEV) + 1, count_commas (path), &sz) || + grub_add (sz, grub_strlen (path), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining encoding size")); + grub_print_error (); + return NULL; + } + + encoding = grub_malloc (sz); + + if (encoding == NULL) + { + grub_print_error (); + return NULL; + } + + optr = grub_stpcpy (encoding, IEEE1275_DEV); + escape_commas (path, optr); + return encoding; +} + +static char * +get_parent_devname (const char *devname) +{ + char *parent, *pptr; + + parent = grub_strdup (devname); + + if (parent == NULL) + { + grub_print_error (); + return NULL; + } + + pptr = grub_strstr (parent, IEEE1275_DISK_ALIAS); + + if (pptr != NULL) + *pptr = '\0'; + + return parent; +} + +static void +free_parent_dev (struct parent_dev *parent) +{ + if (parent != NULL) + { + grub_free (parent->name); + grub_free (parent->type); + grub_free (parent); + } +} + +static struct parent_dev * +init_parent (const char *parent) +{ + struct parent_dev *op; + + op = grub_zalloc (sizeof (struct parent_dev)); + + if (op == NULL) + { + grub_print_error (); + return NULL; + } + + op->name = grub_strdup (parent); + op->type = grub_malloc (IEEE1275_MAX_PROP_LEN); + + if ((op->name == NULL) || (op->type == NULL)) + { + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + return op; +} + +static struct parent_dev * +open_new_parent (const char *parent) +{ + struct parent_dev *op = init_parent(parent); + grub_ieee1275_ihandle_t ihandle; + grub_ieee1275_phandle_t phandle; + grub_uint32_t address_cells = 2; + + if (op == NULL) + return NULL; + + grub_ieee1275_open (parent, &ihandle); + + if (ihandle == 0) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to open %s", parent); + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + if (grub_ieee1275_instance_to_package (ihandle, &phandle)) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to get parent %s", parent); + grub_print_error (); + free_parent_dev (op); + return NULL; + } + + /* + * IEEE Std 1275-1994 page 110: A missing "address-cells" property + * signifies that the number of address cells is two. So ignore on error. + */ + if (grub_ieee1275_get_integer_property (phandle, "#address-cells", + &address_cells, + sizeof (address_cells), 0) != 0) + address_cells = 2; + + grub_ieee1275_get_property (phandle, "device_type", op->type, + IEEE1275_MAX_PROP_LEN, NULL); + + op->ihandle = ihandle; + op->address_cells = address_cells; + return op; +} + +static struct parent_dev * +open_parent (const char *parent) +{ + struct parent_dev *op; + + op = grub_named_list_find (GRUB_AS_NAMED_LIST (parent_devs), parent); + + if (op == NULL) + { + op = open_new_parent (parent); + + if (op != NULL) + grub_list_push (GRUB_AS_LIST_P (&parent_devs), GRUB_AS_LIST (op)); + } + + return op; +} + +static void +display_parents (void) +{ + struct parent_dev *parent; + + grub_printf ("-------------------- PARENTS --------------------\n"); + + FOR_LIST_ELEMENTS (parent, parent_devs) + { + grub_printf ("name: %s\n", parent->name); + grub_printf ("type: %s\n", parent->type); + grub_printf ("address_cells %x\n", parent->address_cells); + } + + grub_printf ("-------------------------------------------------\n"); +} + +static char * +canonicalise_4cell_ua (grub_ieee1275_ihandle_t ihandle, char *unit_address) +{ + grub_uint32_t phy_lo, phy_hi, lun_lo, lun_hi; + int valid_phy = 0; + grub_size_t size; + char *canon = NULL; + + valid_phy = grub_ieee1275_decode_unit4 (ihandle, unit_address, + grub_strlen (unit_address), &phy_lo, + &phy_hi, &lun_lo, &lun_hi); + + if ((valid_phy == 0) && (phy_hi != 0xffffffff)) + canon = grub_ieee1275_encode_uint4 (ihandle, phy_lo, phy_hi, + lun_lo, lun_hi, &size); + + return canon; +} + +static char * +canonicalise_disk (const char *devname) +{ + char *canon, *parent; + struct parent_dev *op; + + canon = grub_ieee1275_canonicalise_devname (devname); + + if (canon == NULL) + { + /* This should not happen. */ + grub_error (GRUB_ERR_BAD_DEVICE, "canonicalise devname failed"); + grub_print_error (); + return NULL; + } + + /* Don't try to open the parent of a virtual device. */ + if (grub_strstr (canon, "virtual-devices")) + return canon; + + parent = get_parent_devname (canon); + + if (parent == NULL) + return NULL; + + op = open_parent (parent); + + /* + * Devices with 4 address cells can have many different types of addressing + * (phy, wwn, and target lun). Use the parents encode-unit / decode-unit + * to find the true canonical name. + */ + if ((op) && (op->address_cells == 4)) + { + char *unit_address, *real_unit_address, *real_canon; + grub_size_t real_unit_str_len; + + unit_address = grub_strstr (canon, IEEE1275_DISK_ALIAS); + unit_address += grub_strlen (IEEE1275_DISK_ALIAS); + + if (unit_address == NULL) + { + /* + * This should not be possible, but return the canonical name for + * the non-disk block device. + */ + grub_free (parent); + return (canon); + } + + real_unit_address = canonicalise_4cell_ua (op->ihandle, unit_address); + + if (real_unit_address == NULL) + { + /* + * This is not an error, since this function could be called with a devalias + * containing a drive that isn't installed in the system. + */ + grub_free (parent); + return NULL; + } + + real_unit_str_len = grub_strlen (op->name) + sizeof (IEEE1275_DISK_ALIAS) + + grub_strlen (real_unit_address); + if (grub_add (grub_strlen (op->name), sizeof (IEEE1275_DISK_ALIAS), &real_unit_str_len) || + grub_add (real_unit_str_len, grub_strlen (real_unit_address), &real_unit_str_len)) + { + grub_free (parent); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of canonical name")); + grub_print_error (); + return NULL; + } + + real_canon = grub_malloc (real_unit_str_len); + if (real_canon == NULL) + { + grub_free (parent); + grub_print_error (); + return NULL; + } + + grub_snprintf (real_canon, real_unit_str_len, "%s/disk@%s", + op->name, real_unit_address); + + grub_free (canon); + canon = real_canon; + } + + grub_free (parent); + return (canon); +} + +static struct disk_dev * +add_canon_disk (const char *cname) +{ + grub_size_t sz; + struct disk_dev *dev; + + dev = grub_zalloc (sizeof (struct disk_dev)); + + if (dev == NULL) + goto failed; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_RAW_DEVNAMES)) + { + /* + * Append :nolabel to the end of all SPARC disks. + * nolabel is mutually exclusive with all other + * arguments and allows a client program to open + * the entire (raw) disk. Any disk label is ignored. + */ + if (grub_add (grub_strlen (cname), sizeof (":nolabel"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while appending :nolabel to end of canonical name"); + goto failed; + } + + dev->raw_name = grub_malloc (sz); + + if (dev->raw_name == NULL) + goto failed; + + grub_snprintf (dev->raw_name, sz, "%s:nolabel", cname); + } + + /* + * Don't use grub_ieee1275_encode_devname here, the devpath in grub.cfg doesn't + * understand device aliases, which the layer above sometimes sends us. + */ + dev->grub_devpath = encode_grub_devname(cname); + + if (dev->grub_devpath == NULL) + goto failed; + + dev->name = grub_strdup (cname); + + if (dev->name == NULL) + goto failed; + + dev->valid = 1; + grub_list_push (GRUB_AS_LIST_P (&disk_devs), GRUB_AS_LIST (dev)); + return dev; + + failed: + grub_print_error (); + + if (dev != NULL) + { + grub_free (dev->name); + grub_free (dev->grub_devpath); + grub_free (dev->raw_name); + } + + grub_free (dev); + return NULL; +} + +static grub_err_t +add_disk (const char *path) +{ + grub_err_t ret = GRUB_ERR_NONE; + struct disk_dev *dev; + char *canon; + + canon = canonicalise_disk (path); + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + if ((canon != NULL) && (dev == NULL)) + { + struct disk_dev *ob_device; + + ob_device = add_canon_disk (canon); + + if (ob_device == NULL) + ret = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure to add disk"); + } + else if (dev != NULL) + dev->valid = 1; + + grub_free (canon); + return (ret); +} + +static grub_err_t +grub_obdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *dest) +{ + grub_err_t ret = GRUB_ERR_NONE; + struct disk_dev *dev; + unsigned long long pos; + grub_ssize_t result = 0; + + if (disk->data == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, "invalid disk data"); + + dev = (struct disk_dev *)disk->data; + pos = sector << disk->log_sector_size; + grub_ieee1275_seek (dev->ihandle, pos, &result); + + if (result < 0) + { + dev->opened = 0; + return grub_error (GRUB_ERR_READ_ERROR, "seek error, can't seek block %llu", + (long long) sector); + } + + grub_ieee1275_read (dev->ihandle, dest, size << disk->log_sector_size, + &result); + + if (result != (grub_ssize_t) (size << disk->log_sector_size)) + { + dev->opened = 0; + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + } + return ret; +} + +static void +grub_obdisk_close (grub_disk_t disk) +{ + grub_memset (disk, 0, sizeof (*disk)); +} + +static void +scan_usb_disk (const char *parent) +{ + struct parent_dev *op; + grub_ssize_t result; + + op = open_parent (parent); + + if (op == NULL) + { + grub_error (GRUB_ERR_BAD_DEVICE, "unable to open %s", parent); + grub_print_error (); + return; + } + + if ((grub_ieee1275_set_address (op->ihandle, 0, 0) == 0) && + (grub_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && + (result == 0)) + { + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@0", parent); + add_disk (buf); + grub_free (buf); + } +} + +static void +scan_nvme_disk (const char *path) +{ + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@1", path); + add_disk (buf); + grub_free (buf); +} + +static void +scan_sparc_sas_2cell (struct parent_dev *op) +{ + grub_ssize_t result; + grub_uint8_t tgt; + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + for (tgt = 0; tgt < 0xf; tgt++) + { + + if ((grub_ieee1275_set_address(op->ihandle, tgt, 0) == 0) && + (grub_ieee1275_no_data_command (op->ihandle, &tur, &result) == 0) && + (result == 0)) + { + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%" + PRIxGRUB_UINT32_T, op->name, tgt); + + add_disk (buf); + } + } +} + +static void +scan_sparc_sas_4cell (struct parent_dev *op) +{ + grub_uint16_t exp; + grub_uint8_t phy; + char *buf; + + buf = grub_malloc (IEEE1275_MAX_PATH_LEN); + + if (buf == NULL) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "disk scan failure"); + grub_print_error (); + return; + } + + /* + * Cycle thru the potential for dual ported SAS disks + * behind a SAS expander. + */ + for (exp = 0; exp <= 0x100; exp+=0x100) + + /* The current limit is 32 disks on a phy. */ + for (phy = 0; phy < 0x20; phy++) + { + char *canon = NULL; + + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "p%" PRIxGRUB_UINT32_T ",0", + exp | phy); + + canon = canonicalise_4cell_ua (op->ihandle, buf); + + if (canon != NULL) + { + grub_snprintf (buf, IEEE1275_MAX_PATH_LEN, "%s/disk@%s", + op->name, canon); + + add_disk (buf); + grub_free (canon); + } + } + + grub_free (buf); +} + +static void +scan_sparc_sas_disk (const char *parent) +{ + struct parent_dev *op; + + op = open_parent (parent); + + if ((op != NULL) && (op->address_cells == 4)) + scan_sparc_sas_4cell (op); + else if ((op != NULL) && (op->address_cells == 2)) + scan_sparc_sas_2cell (op); +} + +static void +iterate_devtree (const struct grub_ieee1275_devalias *alias) +{ + struct grub_ieee1275_devalias child; + + if ((grub_strcmp (alias->type, "scsi-2") == 0) || + (grub_strcmp (alias->type, "scsi-sas") == 0)) + return scan_sparc_sas_disk (alias->path); + + else if (grub_strcmp (alias->type, "nvme") == 0) + return scan_nvme_disk (alias->path); + + else if (grub_strcmp (alias->type, "scsi-usb") == 0) + return scan_usb_disk (alias->path); + + else if (grub_strcmp (alias->type, "block") == 0) + { + const char **bl = block_blacklist; + + while (*bl != NULL) + { + if (grub_strstr (alias->path, *bl)) + return; + bl++; + } + + add_disk (alias->path); + return; + } + + FOR_IEEE1275_DEVCHILDREN (alias->path, child) + iterate_devtree (&child); +} + +static void +enumerate_disks (void) +{ + struct grub_ieee1275_devalias alias; + + FOR_IEEE1275_DEVCHILDREN("/", alias) + iterate_devtree (&alias); +} + +static grub_err_t +add_bootpath (void) +{ + struct disk_dev *ob_device; + grub_err_t ret = GRUB_ERR_NONE; + char *dev, *alias; + char *type; + + dev = grub_ieee1275_get_boot_dev (); + + if (dev == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure adding boot device"); + + type = grub_ieee1275_get_device_type (dev); + + if (type == NULL) + { + grub_free (dev); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure adding boot device"); + } + + alias = NULL; + + if (grub_strcmp (type, "network") != 0) + { + dev = strip_ob_partition (dev); + ob_device = add_canon_disk (dev); + + if (ob_device == NULL) + ret = grub_error (GRUB_ERR_OUT_OF_MEMORY, "failure adding boot device"); + + ob_device->valid = 1; + + alias = grub_ieee1275_get_devname (dev); + + if (alias && grub_strcmp (alias, dev) != 0) + ob_device->grub_alias_devpath = grub_ieee1275_encode_devname (dev); + + ob_device->boot_dev = 1; + } + + grub_free (type); + grub_free (dev); + grub_free (alias); + return ret; +} + +static void +enumerate_aliases (void) +{ + struct grub_ieee1275_devalias alias; + + /* + * Some block device aliases are not in canonical form + * + * For example: + * + * disk3 /pci@301/pci@1/scsi@0/disk@p3 + * disk2 /pci@301/pci@1/scsi@0/disk@p2 + * disk1 /pci@301/pci@1/scsi@0/disk@p1 + * disk /pci@301/pci@1/scsi@0/disk@p0 + * disk0 /pci@301/pci@1/scsi@0/disk@p0 + * + * None of these devices are in canonical form. + * + * Also, just because there is a devalias, doesn't mean there is a disk + * at that location. And a valid boot block device doesn't have to have + * a devalias at all. + * + * At this point, all valid disks have been found in the system + * and devaliases that point to canonical names are stored in the + * disk_devs list already. + */ + FOR_IEEE1275_DEVALIASES (alias) + { + struct disk_dev *dev; + char *canon; + + if (grub_strcmp (alias.type, "block") != 0) + continue; + + canon = canonicalise_disk (alias.name); + + if (canon == NULL) + /* This is not an error, a devalias could point to a nonexistent disk. */ + continue; + + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + if (dev != NULL) + { + /* + * If more than one alias points to the same device, + * remove the previous one unless it is the boot dev, + * since the upper level will use the first one. The reason + * all the others are redone is in the case of hot-plugging + * a disk. If the boot disk gets hot-plugged, it will come + * thru here with a different name without the boot_dev flag + * set. + */ + if ((dev->boot_dev) && (dev->grub_alias_devpath)) + continue; + + grub_free (dev->grub_alias_devpath); + dev->grub_alias_devpath = grub_ieee1275_encode_devname (alias.path); + } + grub_free (canon); + } +} + +static void +display_disks (void) +{ + struct disk_dev *dev; + + grub_printf ("--------------------- DISKS ---------------------\n"); + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + grub_printf ("name: %s\n", dev->name); + grub_printf ("grub_devpath: %s\n", dev->grub_devpath); + grub_printf ("grub_alias_devpath: %s\n", dev->grub_alias_devpath); + grub_printf ("valid: %s\n", (dev->valid) ? "yes" : "no"); + grub_printf ("boot_dev: %s\n", (dev->boot_dev) ? "yes" : "no"); + grub_printf ("opened: %s\n", (dev->ihandle) ? "yes" : "no"); + grub_printf ("block size: %" PRIuGRUB_UINT32_T "\n", + dev->block_size); + grub_printf ("num blocks: %" PRIuGRUB_UINT64_T "\n", + dev->num_blocks); + grub_printf ("log sector size: %" PRIuGRUB_UINT32_T "\n", + dev->log_sector_size); + grub_printf ("\n"); + } + + grub_printf ("-------------------------------------------------\n"); +} + +static void +display_stats (void) +{ + const char *debug = grub_env_get ("debug"); + + if (debug == NULL) + return; + + if (grub_strword (debug, "all") || grub_strword (debug, "obdisk")) + { + display_parents (); + display_disks (); + } +} + +static void +invalidate_all_disks (void) +{ + struct disk_dev *dev = NULL; + + if (disks_enumerated != 0) + FOR_LIST_ELEMENTS (dev, disk_devs) + dev->valid = 0; +} + +static struct disk_dev * +find_legacy_grub_devpath (const char *name) +{ + struct disk_dev *dev = NULL; + char *canon, *devpath = NULL; + + devpath = decode_grub_devname (name + sizeof ("ieee1275")); + canon = canonicalise_disk (devpath); + + if (canon != NULL) + dev = grub_named_list_find (GRUB_AS_NAMED_LIST (disk_devs), canon); + + grub_free (devpath); + grub_free (canon); + return dev; +} + +static void +enumerate_devices (void) +{ + invalidate_all_disks (); + enumerate_disks (); + enumerate_aliases (); + disks_enumerated = 1; + display_stats (); +} + +static struct disk_dev * +find_grub_devpath_real (const char *name) +{ + struct disk_dev *dev = NULL; + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if ((STRCMP (dev->grub_devpath, name)) + || (STRCMP (dev->grub_alias_devpath, name))) + break; + } + + return dev; +} + +static struct disk_dev * +find_grub_devpath (const char *name) +{ + struct disk_dev *dev = NULL; + int enumerated; + + do { + enumerated = disks_enumerated; + dev = find_grub_devpath_real (name); + + if (dev != NULL) + break; + + dev = find_legacy_grub_devpath (name); + + if (dev != NULL) + break; + + enumerate_devices (); + } while (enumerated == 0); + + return dev; +} + +static int +grub_obdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct disk_dev *dev; + const char *name; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + enumerate_devices (); + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if (dev->valid == 1) + { + if (dev->grub_alias_devpath) + name = dev->grub_alias_devpath; + else + name = dev->grub_devpath; + + if (hook (name, hook_data)) + return 1; + } + } + + return 0; +} + +static grub_err_t +grub_obdisk_open (const char *name, grub_disk_t disk) +{ + grub_ieee1275_ihandle_t ihandle = 0; + struct disk_dev *dev = NULL; + + if (grub_strncmp (name, IEEE1275_DEV, sizeof (IEEE1275_DEV) - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not IEEE1275 device"); + + dev = find_grub_devpath (name); + + if (dev == NULL) + { + grub_printf ("UNKNOWN DEVICE: %s\n", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "%s", name); + } + + if (dev->opened == 0) + { + if (dev->raw_name != NULL) + grub_ieee1275_open (dev->raw_name, &ihandle); + else + grub_ieee1275_open (dev->name, &ihandle); + + if (ihandle == 0) + { + grub_printf ("Can't open device %s\n", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device %s", name); + } + + dev->block_size = grub_ieee1275_get_block_size (ihandle); + dev->num_blocks = grub_ieee1275_num_blocks (ihandle); + + if (dev->num_blocks == 0) + dev->num_blocks = grub_ieee1275_num_blocks64 (ihandle); + + if (dev->num_blocks == 0) + dev->num_blocks = GRUB_DISK_SIZE_UNKNOWN; + + if (dev->block_size != 0) + dev->log_sector_size = grub_log2ull (dev->block_size); + else + dev->log_sector_size = 9; + + dev->ihandle = ihandle; + dev->opened = 1; + } + + disk->total_sectors = dev->num_blocks; + disk->id = dev->ihandle; + disk->data = dev; + disk->log_sector_size = dev->log_sector_size; + return GRUB_ERR_NONE; +} + + +static struct grub_disk_dev grub_obdisk_dev = + { + .name = "obdisk", + .id = GRUB_DISK_DEVICE_OBDISK_ID, + .disk_iterate = grub_obdisk_iterate, + .disk_open = grub_obdisk_open, + .disk_close = grub_obdisk_close, + .disk_read = grub_obdisk_read, + }; + +void +grub_obdisk_init (void) +{ + grub_disk_firmware_fini = grub_obdisk_fini; + add_bootpath (); + grub_disk_dev_register (&grub_obdisk_dev); +} + +void +grub_obdisk_fini (void) +{ + struct disk_dev *dev; + + FOR_LIST_ELEMENTS (dev, disk_devs) + { + if (dev->opened != 0) + grub_ieee1275_close (dev->ihandle); + } + + grub_disk_dev_unregister (&grub_obdisk_dev); +} diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c new file mode 100644 index 000000000..dbc0f1aba --- /dev/null +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -0,0 +1,783 @@ +/* ofdisk.c - Open Firmware disk access. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static char *last_devpath; +static grub_ieee1275_ihandle_t last_ihandle; + +struct ofdisk_hash_ent +{ + char *devpath; + char *open_path; + char *grub_devpath; + int is_boot; + int is_removable; + int block_size_fails; + /* Pointer to shortest available name on nodes representing canonical names, + otherwise NULL. */ + const char *shortest; + const char *grub_shortest; + struct ofdisk_hash_ent *next; +}; + +static grub_err_t +grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, + struct ofdisk_hash_ent *op); + +#define OFDISK_HASH_SZ 8 +static struct ofdisk_hash_ent *ofdisk_hash[OFDISK_HASH_SZ]; + +static int +ofdisk_hash_fn (const char *devpath) +{ + int hash = 0; + while (*devpath) + hash ^= *devpath++; + return (hash & (OFDISK_HASH_SZ - 1)); +} + +static struct ofdisk_hash_ent * +ofdisk_hash_find (const char *devpath) +{ + struct ofdisk_hash_ent *p = ofdisk_hash[ofdisk_hash_fn(devpath)]; + + while (p) + { + if (!grub_strcmp (p->devpath, devpath)) + break; + p = p->next; + } + return p; +} + +static struct ofdisk_hash_ent * +ofdisk_hash_add_real (char *devpath) +{ + struct ofdisk_hash_ent *p; + struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; + const char *iptr; + char *optr; + grub_size_t sz; + + p = grub_zalloc (sizeof (*p)); + if (!p) + return NULL; + + p->devpath = devpath; + + if (grub_mul (grub_strlen (p->devpath), 2, &sz) || + grub_add (sz, sizeof ("ieee1275/"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + + p->grub_devpath = grub_malloc (sz); + + if (!p->grub_devpath) + { + grub_free (p); + return NULL; + } + + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0)) + { + if (grub_add (grub_strlen (p->devpath), 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of an open path")); + return NULL; + } + + p->open_path = grub_malloc (sz); + if (!p->open_path) + { + grub_free (p->grub_devpath); + grub_free (p); + return NULL; + } + optr = grub_stpcpy (p->open_path, p->devpath); + *optr++ = ':'; + *optr++ = '0'; + *optr = '\0'; + } + else + p->open_path = p->devpath; + + optr = grub_stpcpy (p->grub_devpath, "ieee1275/"); + for (iptr = p->devpath; *iptr; ) + { + if (*iptr == ',') + *optr++ = '\\'; + *optr++ = *iptr++; + } + *optr = 0; + + p->next = *head; + *head = p; + return p; +} + +static int +check_string_removable (const char *str) +{ + const char *ptr = grub_strrchr (str, '/'); + + if (ptr) + ptr++; + else + ptr = str; + return (grub_strncmp (ptr, "cdrom", 5) == 0 || grub_strncmp (ptr, "fd", 2) == 0); +} + +static struct ofdisk_hash_ent * +ofdisk_hash_add (char *devpath, char *curcan) +{ + struct ofdisk_hash_ent *p, *pcan; + + p = ofdisk_hash_add_real (devpath); + + grub_dprintf ("disk", "devpath = %s, canonical = %s\n", devpath, curcan); + + if (!curcan) + { + p->shortest = p->devpath; + p->grub_shortest = p->grub_devpath; + if (check_string_removable (devpath)) + p->is_removable = 1; + return p; + } + + pcan = ofdisk_hash_find (curcan); + if (!pcan) + pcan = ofdisk_hash_add_real (curcan); + else + grub_free (curcan); + + if (check_string_removable (devpath) || check_string_removable (curcan)) + pcan->is_removable = 1; + + if (!pcan) + grub_errno = GRUB_ERR_NONE; + else + { + if (!pcan->shortest + || grub_strlen (pcan->shortest) > grub_strlen (devpath)) + { + pcan->shortest = p->devpath; + pcan->grub_shortest = p->grub_devpath; + } + } + + return p; +} + +static void +dev_iterate_real (const char *name, const char *path) +{ + struct ofdisk_hash_ent *op; + + grub_dprintf ("disk", "disk name = %s, path = %s\n", name, + path); + + op = ofdisk_hash_find (path); + if (!op) + { + char *name_dup = grub_strdup (name); + char *can = grub_strdup (path); + if (!name_dup || !can) + { + grub_errno = GRUB_ERR_NONE; + grub_free (name_dup); + grub_free (can); + return; + } + op = ofdisk_hash_add (name_dup, can); + } + return; +} + +static void +dev_iterate (const struct grub_ieee1275_devalias *alias) +{ + if (grub_strcmp (alias->type, "vscsi") == 0) + { + static grub_ieee1275_ihandle_t ihandle; + struct set_color_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t nentries; + grub_ieee1275_cell_t table; + } + args; + char *buf, *bufptr; + unsigned i; + grub_size_t sz; + + if (grub_ieee1275_open (alias->path, &ihandle)) + return; + + /* This method doesn't need memory allocation for the table. Open + firmware takes care of all memory management and the result table + stays in memory and is never freed. */ + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 3); + args.method = (grub_ieee1275_cell_t) "vscsi-report-luns"; + args.ihandle = ihandle; + args.table = 0; + args.nentries = 0; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1 || args.catch_result) + { + grub_ieee1275_close (ihandle); + return; + } + + if (grub_add (grub_strlen (alias->path), 32, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for vscsi"); + grub_ieee1275_close (ihandle); + return; + } + + buf = grub_malloc (sz); + if (!buf) + { + grub_ieee1275_close (ihandle); + return; + } + bufptr = grub_stpcpy (buf, alias->path); + + for (i = 0; i < args.nentries; i++) + { + grub_uint64_t *ptr; + + ptr = *(grub_uint64_t **) (args.table + 4 + 8 * i); + while (*ptr) + { + grub_snprintf (bufptr, 32, "/disk@%" PRIxGRUB_UINT64_T, *ptr++); + dev_iterate_real (buf, buf); + } + } + grub_ieee1275_close (ihandle); + grub_free (buf); + return; + } + else if (grub_strcmp (alias->type, "sas_ioa") == 0) + { + /* The method returns the number of disks and a table where + * each ID is 64-bit long. Example of sas paths: + * /pci@80000002000001f/pci1014,034A@0/sas/disk@c05db70800 + * /pci@80000002000001f/pci1014,034A@0/sas/disk@a05db70800 + * /pci@80000002000001f/pci1014,034A@0/sas/disk@805db70800 */ + + struct sas_children + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t max; + grub_ieee1275_cell_t table; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t nentries; + } + args; + char *buf, *bufptr; + unsigned i; + grub_uint64_t *table; + grub_uint16_t table_size; + grub_ieee1275_ihandle_t ihandle; + grub_size_t sz; + + if (grub_add (grub_strlen (alias->path), sizeof ("/disk@7766554433221100"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for sas_ioa"); + return; + } + + buf = grub_malloc (sz); + if (!buf) + return; + bufptr = grub_stpcpy (buf, alias->path); + + /* Power machines documentation specify 672 as maximum SAS disks in + one system. Using a slightly larger value to be safe. */ + table_size = 768; + table = grub_calloc (table_size, sizeof (grub_uint64_t)); + + if (!table) + { + grub_free (buf); + return; + } + + if (grub_ieee1275_open (alias->path, &ihandle)) + { + grub_free (buf); + grub_free (table); + return; + } + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 2); + args.method = (grub_ieee1275_cell_t) "get-sas-children"; + args.ihandle = ihandle; + args.max = table_size; + args.table = (grub_ieee1275_cell_t) table; + args.catch_result = 0; + args.nentries = 0; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + { + grub_ieee1275_close (ihandle); + grub_free (table); + grub_free (buf); + return; + } + + for (i = 0; i < args.nentries; i++) + { + grub_snprintf (bufptr, sizeof ("/disk@7766554433221100"), + "/disk@%" PRIxGRUB_UINT64_T, table[i]); + dev_iterate_real (buf, buf); + } + + grub_ieee1275_close (ihandle); + grub_free (table); + grub_free (buf); + } + + if (!grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS) + && grub_strcmp (alias->type, "block") == 0) + { + dev_iterate_real (alias->path, alias->path); + return; + } + + { + struct grub_ieee1275_devalias child; + + FOR_IEEE1275_DEVCHILDREN(alias->path, child) + dev_iterate (&child); + } +} + +static void +scan (void) +{ + struct grub_ieee1275_devalias alias; + FOR_IEEE1275_DEVALIASES(alias) + { + if (grub_strcmp (alias.type, "block") != 0) + continue; + dev_iterate_real (alias.name, alias.path); + } + + FOR_IEEE1275_DEVCHILDREN("/", alias) + dev_iterate (&alias); +} + +static int +grub_ofdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + unsigned i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + scan (); + + for (i = 0; i < ARRAY_SIZE (ofdisk_hash); i++) + { + static struct ofdisk_hash_ent *ent; + for (ent = ofdisk_hash[i]; ent; ent = ent->next) + { + if (!ent->shortest) + continue; + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY)) + { + grub_ieee1275_phandle_t dev; + char tmp[8]; + + if (grub_ieee1275_finddevice (ent->devpath, &dev)) + { + grub_dprintf ("disk", "finddevice (%s) failed\n", + ent->devpath); + continue; + } + + if (grub_ieee1275_get_property (dev, "iconname", tmp, + sizeof tmp, 0)) + { + grub_dprintf ("disk", "get iconname failed\n"); + continue; + } + + if (grub_strcmp (tmp, "sdmmc") != 0) + { + grub_dprintf ("disk", "device is not an SD card\n"); + continue; + } + } + + if (!ent->is_boot && ent->is_removable) + continue; + + if (hook (ent->grub_shortest, hook_data)) + return 1; + } + } + return 0; +} + +static char * +compute_dev_path (const char *name) +{ + char *devpath; + char *p, c; + grub_size_t sz; + + if (grub_add (grub_strlen (name), 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + + devpath = grub_malloc (sz); + if (!devpath) + return NULL; + + /* Un-escape commas. */ + p = devpath; + while ((c = *name++) != '\0') + { + if (c == '\\' && *name == ',') + { + *p++ = ','; + name++; + } + else + *p++ = c; + } + + *p++ = '\0'; + + return devpath; +} + +static grub_err_t +grub_ofdisk_open (const char *name, grub_disk_t disk) +{ + grub_ieee1275_phandle_t dev; + char *devpath; + /* XXX: This should be large enough for any possible case. */ + char prop[64]; + grub_ssize_t actual; + grub_uint32_t block_size = 0; + grub_err_t err; + + if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "not IEEE1275 device"); + devpath = compute_dev_path (name + sizeof ("ieee1275/") - 1); + if (! devpath) + return grub_errno; + + grub_dprintf ("disk", "Opening `%s'.\n", devpath); + + if (grub_ieee1275_finddevice (devpath, &dev)) + { + grub_free (devpath); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "can't read device properties"); + } + + if (grub_ieee1275_get_property (dev, "device_type", prop, sizeof (prop), + &actual)) + { + grub_free (devpath); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read the device type"); + } + + if (grub_strcmp (prop, "block")) + { + grub_free (devpath); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device"); + } + + /* XXX: There is no property to read the number of blocks. There + should be a property `#blocks', but it is not there. Perhaps it + is possible to use seek for this. */ + disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + + { + struct ofdisk_hash_ent *op; + op = ofdisk_hash_find (devpath); + if (!op) + op = ofdisk_hash_add (devpath, NULL); + if (!op) + { + grub_free (devpath); + return grub_errno; + } + disk->id = (unsigned long) op; + disk->data = op->open_path; + + err = grub_ofdisk_get_block_size (devpath, &block_size, op); + if (err) + { + grub_free (devpath); + return err; + } + if (block_size != 0) + disk->log_sector_size = grub_log2ull (block_size); + else + disk->log_sector_size = 9; + } + + grub_free (devpath); + return 0; +} + +static void +grub_ofdisk_close (grub_disk_t disk) +{ + if (disk->data == last_devpath) + { + if (last_ihandle) + grub_ieee1275_close (last_ihandle); + last_ihandle = 0; + last_devpath = NULL; + } + disk->data = 0; +} + +static grub_err_t +grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector) +{ + grub_ssize_t status; + unsigned long long pos; + + if (disk->data != last_devpath) + { + if (last_ihandle) + grub_ieee1275_close (last_ihandle); + last_ihandle = 0; + last_devpath = NULL; + + grub_ieee1275_open (disk->data, &last_ihandle); + if (! last_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + last_devpath = disk->data; + } + + pos = sector << disk->log_sector_size; + + grub_ieee1275_seek (last_ihandle, pos, &status); + if (status < 0) + return grub_error (GRUB_ERR_READ_ERROR, + "seek error, can't seek block %llu", + (long long) sector); + return 0; +} + +static grub_err_t +grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_err_t err; + grub_ssize_t actual; + err = grub_ofdisk_prepare (disk, sector); + if (err) + return err; + grub_ieee1275_read (last_ihandle, buf, size << disk->log_sector_size, + &actual); + if (actual != (grub_ssize_t) (size << disk->log_sector_size)) + return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " + "from `%s'"), + (unsigned long long) sector, + disk->name); + + return 0; +} + +static grub_err_t +grub_ofdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_err_t err; + grub_ssize_t actual; + err = grub_ofdisk_prepare (disk, sector); + if (err) + return err; + grub_ieee1275_write (last_ihandle, buf, size << disk->log_sector_size, + &actual); + if (actual != (grub_ssize_t) (size << disk->log_sector_size)) + return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " + "to `%s'"), + (unsigned long long) sector, + disk->name); + + return 0; +} + +static struct grub_disk_dev grub_ofdisk_dev = + { + .name = "ofdisk", + .id = GRUB_DISK_DEVICE_OFDISK_ID, + .disk_iterate = grub_ofdisk_iterate, + .disk_open = grub_ofdisk_open, + .disk_close = grub_ofdisk_close, + .disk_read = grub_ofdisk_read, + .disk_write = grub_ofdisk_write, + .next = 0 + }; + +static void +insert_bootpath (void) +{ + char *bootpath; + grub_ssize_t bootpath_size; + char *type; + grub_size_t sz; + + if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", + &bootpath_size) + || bootpath_size <= 0) + { + /* Should never happen. */ + grub_printf ("/chosen/bootpath property missing!\n"); + return; + } + + if (grub_add (bootpath_size, 64, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining bootpath size")); + return; + } + + bootpath = (char *) grub_malloc (sz); + if (! bootpath) + { + grub_print_error (); + return; + } + grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath, + (grub_size_t) bootpath_size + 1, 0); + bootpath[bootpath_size] = '\0'; + + /* Transform an OF device path to a GRUB path. */ + + type = grub_ieee1275_get_device_type (bootpath); + if (!(type && grub_strcmp (type, "network") == 0)) + { + struct ofdisk_hash_ent *op; + char *device = grub_ieee1275_get_devname (bootpath); + op = ofdisk_hash_add (device, NULL); + op->is_boot = 1; + } + grub_free (type); + grub_free (bootpath); +} + +void +grub_ofdisk_fini (void) +{ + if (last_ihandle) + grub_ieee1275_close (last_ihandle); + last_ihandle = 0; + last_devpath = NULL; + + grub_disk_dev_unregister (&grub_ofdisk_dev); +} + +void +grub_ofdisk_init (void) +{ + grub_disk_firmware_fini = grub_ofdisk_fini; + + insert_bootpath (); + + grub_disk_dev_register (&grub_ofdisk_dev); +} + +static grub_err_t +grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, + struct ofdisk_hash_ent *op) +{ + struct size_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t result; + grub_ieee1275_cell_t size1; + grub_ieee1275_cell_t size2; + } args_ieee1275; + + if (last_ihandle) + grub_ieee1275_close (last_ihandle); + + last_ihandle = 0; + last_devpath = NULL; + + grub_ieee1275_open (device, &last_ihandle); + if (! last_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + + *block_size = 0; + + if (op->block_size_fails >= 2) + return GRUB_ERR_NONE; + + INIT_IEEE1275_COMMON (&args_ieee1275.common, "call-method", 2, 2); + args_ieee1275.method = (grub_ieee1275_cell_t) "block-size"; + args_ieee1275.ihandle = last_ihandle; + args_ieee1275.result = 1; + + if (IEEE1275_CALL_ENTRY_FN (&args_ieee1275) == -1) + { + grub_dprintf ("disk", "can't get block size: failed call-method\n"); + op->block_size_fails++; + } + else if (args_ieee1275.result) + { + grub_dprintf ("disk", "can't get block size: %lld\n", + (long long) args_ieee1275.result); + op->block_size_fails++; + } + else if (args_ieee1275.size1 + && !(args_ieee1275.size1 & (args_ieee1275.size1 - 1)) + && args_ieee1275.size1 >= 512 && args_ieee1275.size1 <= 16384) + { + op->block_size_fails = 0; + *block_size = args_ieee1275.size1; + } + + return 0; +} diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c new file mode 100644 index 000000000..0d146c1c0 --- /dev/null +++ b/grub-core/disk/key_protector.c @@ -0,0 +1,73 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_key_protector *grub_key_protectors = NULL; + +grub_err_t +grub_key_protector_register (struct grub_key_protector *protector) +{ + if (protector == NULL || protector->name == NULL || protector->name[0] == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector for registration"); + + if (grub_key_protectors != NULL && + grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), protector->name) != NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Key protector '%s' already registered", protector->name); + + grub_list_push (GRUB_AS_LIST_P (&grub_key_protectors), GRUB_AS_LIST (protector)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_key_protector_unregister (struct grub_key_protector *protector) +{ + if (protector == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector for unregistration"); + + grub_list_remove (GRUB_AS_LIST (protector)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_key_protector_recover_key (const char *protector, grub_uint8_t **key, + grub_size_t *key_size) +{ + struct grub_key_protector *kp = NULL; + + if (grub_key_protectors == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "No key protector registered"); + + if (protector == NULL || protector[0] == '\0') + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid key protector"); + + kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors), protector); + if (kp == NULL) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "Key protector '%s' not found", protector); + + return kp->recover_key (key, key_size); +} diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c new file mode 100644 index 000000000..0f4f22aaa --- /dev/null +++ b/grub-core/disk/ldm.c @@ -0,0 +1,1123 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_UTIL +#include +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define LDM_GUID_STRLEN 64 +#define LDM_NAME_STRLEN 32 + +typedef grub_uint8_t *grub_ldm_id_t; + +enum { STRIPE = 1, SPANNED = 2, RAID5 = 3 }; + +#define LDM_LABEL_SECTOR 6 +struct grub_ldm_vblk { + char magic[4]; + grub_uint8_t unused1[12]; + grub_uint16_t update_status; + grub_uint8_t flags; + grub_uint8_t type; + grub_uint32_t unused2; + grub_uint8_t dynamic[104]; +} GRUB_PACKED; +#define LDM_VBLK_MAGIC "VBLK" + +enum + { + STATUS_CONSISTENT = 0, + STATUS_STILL_ACTIVE = 1, + STATUS_NOT_ACTIVE_YET = 2 + }; + +enum + { + ENTRY_COMPONENT = 0x32, + ENTRY_PARTITION = 0x33, + ENTRY_DISK = 0x34, + ENTRY_VOLUME = 0x51, + }; + +struct grub_ldm_label +{ + char magic[8]; + grub_uint32_t unused1; + grub_uint16_t ver_major; + grub_uint16_t ver_minor; + grub_uint8_t unused2[32]; + char disk_guid[LDM_GUID_STRLEN]; + char host_guid[LDM_GUID_STRLEN]; + char group_guid[LDM_GUID_STRLEN]; + char group_name[LDM_NAME_STRLEN]; + grub_uint8_t unused3[11]; + grub_uint64_t pv_start; + grub_uint64_t pv_size; + grub_uint64_t config_start; + grub_uint64_t config_size; +} GRUB_PACKED; + + +#define LDM_MAGIC "PRIVHEAD" + + + +static inline grub_uint64_t +read_int (grub_uint8_t *in, grub_size_t s) +{ + grub_uint8_t *ptr2; + grub_uint64_t ret; + ret = 0; + for (ptr2 = in; ptr2 < in + s; ptr2++) + { + ret <<= 8; + ret |= *ptr2; + } + return ret; +} + +static int +check_ldm_partition (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p, void *data) +{ + int *has_ldm = data; + + if (p->number >= 4) + return 1; + if (p->msdostype == GRUB_PC_PARTITION_TYPE_LDM) + { + *has_ldm = 1; + return 1; + } + return 0; +} + +static int +msdos_has_ldm_partition (grub_disk_t dsk) +{ + grub_err_t err; + int has_ldm = 0; + + err = grub_partition_msdos_iterate (dsk, check_ldm_partition, &has_ldm); + if (err) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + + return has_ldm; +} + +static const grub_guid_t ldm_type = GRUB_GPT_PARTITION_TYPE_LDM; + +/* Helper for gpt_ldm_sector. */ +static int +gpt_ldm_sector_iter (grub_disk_t disk, const grub_partition_t p, void *data) +{ + grub_disk_addr_t *sector = data; + struct grub_gpt_partentry gptdata; + grub_partition_t p2; + + p2 = disk->partition; + disk->partition = p->parent; + if (grub_disk_read (disk, p->offset, p->index, + sizeof (gptdata), &gptdata)) + { + disk->partition = p2; + return 0; + } + disk->partition = p2; + + if (! grub_memcmp (&gptdata.type, &ldm_type, 16)) + { + *sector = p->start + p->len - 1; + return 1; + } + return 0; +} + +static grub_disk_addr_t +gpt_ldm_sector (grub_disk_t dsk) +{ + grub_disk_addr_t sector = 0; + grub_err_t err; + + err = grub_gpt_partition_map_iterate (dsk, gpt_ldm_sector_iter, §or); + if (err) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + return sector; +} + +static void +free_pv (struct grub_diskfilter_pv *pv) +{ + if (pv == NULL) + return; + + grub_free (pv->internal_id); + grub_free (pv->id.uuid); + grub_free (pv); +} + +static void +free_lv (struct grub_diskfilter_lv *lv) +{ + if (lv == NULL) + return; + + grub_free (lv->internal_id); + grub_free (lv->name); + grub_free (lv->fullname); + if (lv->segments) + { + unsigned int i; + for (i = 0; i < lv->segment_count; i++) + grub_free (lv->segments[i].nodes); + grub_free (lv->segments); + } + grub_free (lv); +} + +static struct grub_diskfilter_vg * +make_vg (grub_disk_t disk, + const struct grub_ldm_label *label) +{ + grub_disk_addr_t startsec, endsec, cursec; + struct grub_diskfilter_vg *vg; + grub_err_t err; + + /* First time we see this volume group. We've to create the + whole volume group structure. */ + vg = grub_malloc (sizeof (*vg)); + if (! vg) + return NULL; + vg->extent_size = 1; + vg->name = grub_malloc (LDM_NAME_STRLEN + 1); + vg->uuid = grub_malloc (LDM_GUID_STRLEN + 1); + if (! vg->uuid || !vg->name) + goto fail1; + + grub_memcpy (vg->uuid, label->group_guid, LDM_GUID_STRLEN); + grub_memcpy (vg->name, label->group_name, LDM_NAME_STRLEN); + vg->name[LDM_NAME_STRLEN] = 0; + vg->uuid[LDM_GUID_STRLEN] = 0; + vg->uuid_len = grub_strlen (vg->uuid); + + vg->lvs = NULL; + vg->pvs = NULL; + + startsec = grub_be_to_cpu64 (label->config_start); + endsec = startsec + grub_be_to_cpu64 (label->config_size); + + /* First find disks. */ + for (cursec = startsec + 0x12; cursec < endsec; cursec++) + { + struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE + / sizeof (struct grub_ldm_vblk)]; + unsigned i; + grub_size_t sz; + err = grub_disk_read (disk, cursec, 0, + sizeof(vblk), &vblk); + if (err) + goto fail2; + + for (i = 0; i < ARRAY_SIZE (vblk); i++) + { + struct grub_diskfilter_pv *pv; + grub_uint8_t *ptr; + if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, + sizeof (vblk[i].magic)) != 0) + continue; + if (grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_CONSISTENT + && grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_STILL_ACTIVE) + continue; + if (vblk[i].type != ENTRY_DISK) + continue; + pv = grub_zalloc (sizeof (*pv)); + if (!pv) + goto fail2; + + pv->disk = 0; + ptr = vblk[i].dynamic; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + grub_free (pv); + goto fail2; + } + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (pv); + goto fail2; + } + + pv->internal_id = grub_malloc (sz); + if (!pv->internal_id) + { + free_pv (pv); + goto fail2; + } + grub_memcpy (pv->internal_id, ptr, (grub_size_t) ptr[0] + 1); + pv->internal_id[(grub_size_t) ptr[0] + 1] = 0; + + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_pv (pv); + goto fail2; + } + /* ptr = name. */ + ptr += *ptr + 1; + if (ptr + *ptr + 1 + >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_pv (pv); + goto fail2; + } + pv->id.uuidlen = *ptr; + + if (grub_add (pv->id.uuidlen, 1, &sz)) + { + free_pv (pv); + goto fail2; + } + + pv->id.uuid = grub_malloc (sz); + if (pv->id.uuid == NULL) + { + free_pv (pv); + goto fail2; + } + grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); + pv->id.uuid[pv->id.uuidlen] = 0; + + pv->next = vg->pvs; + vg->pvs = pv; + } + } + + /* Then find LVs. */ + for (cursec = startsec + 0x12; cursec < endsec; cursec++) + { + struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE + / sizeof (struct grub_ldm_vblk)]; + unsigned i; + grub_size_t sz; + err = grub_disk_read (disk, cursec, 0, + sizeof(vblk), &vblk); + if (err) + goto fail2; + + for (i = 0; i < ARRAY_SIZE (vblk); i++) + { + struct grub_diskfilter_lv *lv; + grub_uint8_t *ptr; + if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, + sizeof (vblk[i].magic)) != 0) + continue; + if (grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_CONSISTENT + && grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_STILL_ACTIVE) + continue; + if (vblk[i].type != ENTRY_VOLUME) + continue; + lv = grub_zalloc (sizeof (*lv)); + if (!lv) + goto fail2; + + lv->vg = vg; + lv->segment_count = 1; + lv->segment_alloc = 1; + lv->visible = 1; + lv->segments = grub_zalloc (sizeof (*lv->segments)); + if (!lv->segments) + { + free_lv (lv); + goto fail2; + } + lv->segments->start_extent = 0; + lv->segments->type = GRUB_DISKFILTER_MIRROR; + lv->segments->node_count = 0; + lv->segments->node_alloc = 8; + lv->segments->nodes = grub_calloc (lv->segments->node_alloc, + sizeof (*lv->segments->nodes)); + if (!lv->segments->nodes) + { + free_lv (lv); + goto fail2; + } + ptr = vblk[i].dynamic; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + if (grub_add (ptr[0], 2, &sz)) + { + free_lv (lv); + goto fail2; + } + lv->internal_id = grub_malloc (sz); + if (!lv->internal_id) + { + free_lv (lv); + goto fail2; + } + grub_memcpy (lv->internal_id, ptr, ptr[0] + 1); + lv->internal_id[ptr[0] + 1] = 0; + + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + if (grub_add (*ptr, 1, &sz)) + { + free_lv (lv); + goto fail2; + } + lv->name = grub_malloc (sz); + if (!lv->name) + { + free_lv (lv); + goto fail2; + } + grub_memcpy (lv->name, ptr + 1, *ptr); + lv->name[*ptr] = 0; + lv->fullname = grub_xasprintf ("ldm/%s/%s", + vg->uuid, lv->name); + if (!lv->fullname) + { + free_lv (lv); + goto fail2; + } + ptr += *ptr + 1; + if (ptr + *ptr + 1 + >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + /* ptr = volume type. */ + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + /* ptr = flags. */ + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + + /* Skip state, type, unknown, volume number, zeros, flags. */ + ptr += 14 + 1 + 1 + 1 + 3 + 1; + /* ptr = number of children. */ + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + + /* Skip 2 more fields. */ + ptr += 8 + 8; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) + || ptr + *ptr + 1>= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (lv); + goto fail2; + } + lv->size = read_int (ptr + 1, *ptr); + lv->segments->extent_count = lv->size; + + lv->next = vg->lvs; + vg->lvs = lv; + } + } + + /* Now the components. */ + for (cursec = startsec + 0x12; cursec < endsec; cursec++) + { + struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE + / sizeof (struct grub_ldm_vblk)]; + unsigned i; + grub_size_t sz; + err = grub_disk_read (disk, cursec, 0, + sizeof(vblk), &vblk); + if (err) + goto fail2; + + for (i = 0; i < ARRAY_SIZE (vblk); i++) + { + struct grub_diskfilter_lv *comp; + struct grub_diskfilter_lv *lv; + grub_uint8_t type; + + grub_uint8_t *ptr; + if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, + sizeof (vblk[i].magic)) != 0) + continue; + if (grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_CONSISTENT + && grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_STILL_ACTIVE) + continue; + if (vblk[i].type != ENTRY_COMPONENT) + continue; + comp = grub_zalloc (sizeof (*comp)); + if (!comp) + goto fail2; + comp->visible = 0; + comp->name = 0; + comp->fullname = 0; + + ptr = vblk[i].dynamic; + if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + if (grub_add (ptr[0], 2, &sz)) + { + free_lv (comp); + goto fail2; + } + comp->internal_id = grub_malloc (sz); + if (!comp->internal_id) + { + free_lv (comp); + goto fail2; + } + grub_memcpy (comp->internal_id, ptr, ptr[0] + 1); + comp->internal_id[ptr[0] + 1] = 0; + + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + /* ptr = name. */ + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + /* ptr = state. */ + ptr += *ptr + 1; + type = *ptr++; + /* skip zeros. */ + ptr += 4; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + + /* ptr = number of children. */ + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + ptr += 8 + 8; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + for (lv = vg->lvs; lv; lv = lv->next) + { + if (lv->internal_id[0] == ptr[0] + && grub_memcmp (lv->internal_id + 1, ptr + 1, ptr[0]) == 0) + break; + } + if (!lv) + { + free_lv (comp); + continue; + } + comp->size = lv->size; + if (type == SPANNED) + { + comp->segment_alloc = 8; + comp->segment_count = 0; + comp->segments = grub_calloc (comp->segment_alloc, + sizeof (*comp->segments)); + if (!comp->segments) + { + free_lv (comp); + goto fail2; + } + } + else + { + comp->segment_alloc = 1; + comp->segment_count = 1; + comp->segments = grub_malloc (sizeof (*comp->segments)); + if (!comp->segments) + { + free_lv (comp); + goto fail2; + } + comp->segments->start_extent = 0; + comp->segments->extent_count = lv->size; + comp->segments->layout = 0; + if (type == STRIPE) + comp->segments->type = GRUB_DISKFILTER_STRIPED; + else if (type == RAID5) + { + comp->segments->type = GRUB_DISKFILTER_RAID5; + comp->segments->layout = GRUB_RAID_LAYOUT_SYMMETRIC_MASK; + } + else + { + free_lv (comp); + goto fail2; + } + ptr += *ptr + 1; + ptr++; + if (!(vblk[i].flags & 0x10)) + { + free_lv (comp); + goto fail2; + } + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) + || ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + comp->segments->stripe_size = read_int (ptr + 1, *ptr); + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + free_lv (comp); + goto fail2; + } + comp->segments->node_count = read_int (ptr + 1, *ptr); + comp->segments->node_alloc = comp->segments->node_count; + comp->segments->nodes = grub_calloc (comp->segments->node_alloc, + sizeof (*comp->segments->nodes)); + if (comp->segments->nodes == NULL) + { + free_lv (comp); + goto fail2; + } + } + + if (lv->segments->node_alloc == lv->segments->node_count) + { + void *t; + + if (grub_mul (lv->segments->node_alloc, 2, &lv->segments->node_alloc) || + grub_mul (lv->segments->node_alloc, sizeof (*lv->segments->nodes), &sz)) + { + free_lv (comp); + goto fail2; + } + + t = grub_realloc (lv->segments->nodes, sz); + if (!t) + { + free_lv (comp); + goto fail2; + } + lv->segments->nodes = t; + } + lv->segments->nodes[lv->segments->node_count].pv = 0; + lv->segments->nodes[lv->segments->node_count].start = 0; + lv->segments->nodes[lv->segments->node_count++].lv = comp; + comp->next = vg->lvs; + vg->lvs = comp; + } + } + /* Partitions. */ + for (cursec = startsec + 0x12; cursec < endsec; cursec++) + { + struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE + / sizeof (struct grub_ldm_vblk)]; + unsigned i; + err = grub_disk_read (disk, cursec, 0, + sizeof(vblk), &vblk); + if (err) + goto fail2; + + for (i = 0; i < ARRAY_SIZE (vblk); i++) + { + struct grub_diskfilter_lv *comp; + struct grub_diskfilter_node part; + grub_disk_addr_t start, size; + + grub_uint8_t *ptr; + part.name = 0; + if (grub_memcmp (vblk[i].magic, LDM_VBLK_MAGIC, + sizeof (vblk[i].magic)) != 0) + continue; + if (grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_CONSISTENT + && grub_be_to_cpu16 (vblk[i].update_status) + != STATUS_STILL_ACTIVE) + continue; + if (vblk[i].type != ENTRY_PARTITION) + continue; + part.lv = 0; + part.pv = 0; + + ptr = vblk[i].dynamic; + if (ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + /* ID */ + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + /* ptr = name. */ + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + + /* skip zeros and logcommit id. */ + ptr += 4 + 8; + if (ptr + 16 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + part.start = read_int (ptr, 8); + start = read_int (ptr + 8, 8); + ptr += 16; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) + || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + size = read_int (ptr + 1, *ptr); + ptr += *ptr + 1; + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) + || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + + for (comp = vg->lvs; comp; comp = comp->next) + if (comp->internal_id[0] == ptr[0] + && grub_memcmp (ptr + 1, comp->internal_id + 1, + comp->internal_id[0]) == 0) + goto out; + continue; + out: + if (ptr >= vblk[i].dynamic + sizeof (vblk[i].dynamic) + || ptr + *ptr + 1 >= vblk[i].dynamic + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + ptr += *ptr + 1; + struct grub_diskfilter_pv *pv; + for (pv = vg->pvs; pv; pv = pv->next) + if (pv->internal_id[0] == ptr[0] + && grub_memcmp (pv->internal_id + 1, ptr + 1, ptr[0]) == 0) + part.pv = pv; + + if (comp->segment_alloc == 1) + { + unsigned node_index; + ptr += *ptr + 1; + if (ptr + *ptr + 1 >= vblk[i].dynamic + + sizeof (vblk[i].dynamic)) + { + goto fail2; + } + node_index = read_int (ptr + 1, *ptr); + if (node_index < comp->segments->node_count) + comp->segments->nodes[node_index] = part; + } + else + { + if (comp->segment_alloc == comp->segment_count) + { + void *t; + grub_size_t sz; + + if (grub_mul (comp->segment_alloc, 2, &comp->segment_alloc) || + grub_mul (comp->segment_alloc, sizeof (*comp->segments), &sz)) + goto fail2; + + t = grub_realloc (comp->segments, sz); + if (!t) + goto fail2; + comp->segments = t; + } + comp->segments[comp->segment_count].start_extent = start; + comp->segments[comp->segment_count].extent_count = size; + comp->segments[comp->segment_count].type = GRUB_DISKFILTER_STRIPED; + comp->segments[comp->segment_count].node_count = 1; + comp->segments[comp->segment_count].node_alloc = 1; + comp->segments[comp->segment_count].nodes + = grub_malloc (sizeof (*comp->segments[comp->segment_count].nodes)); + if (!comp->segments[comp->segment_count].nodes) + { + grub_free (comp->segments); + goto fail2; + } + comp->segments[comp->segment_count].nodes[0] = part; + comp->segment_count++; + } + } + } + if (grub_diskfilter_vg_register (vg)) + goto fail2; + return vg; + fail2: + { + struct grub_diskfilter_lv *lv, *next_lv; + struct grub_diskfilter_pv *pv, *next_pv; + for (lv = vg->lvs; lv; lv = next_lv) + { + next_lv = lv->next; + free_lv (lv); + + } + for (pv = vg->pvs; pv; pv = next_pv) + { + next_pv = pv->next; + free_pv (pv); + } + } + fail1: + grub_free (vg->uuid); + grub_free (vg->name); + grub_free (vg); + return NULL; +} + +static struct grub_diskfilter_vg * +grub_ldm_detect (grub_disk_t disk, + struct grub_diskfilter_pv_id *id, + grub_disk_addr_t *start_sector) +{ + grub_err_t err; + struct grub_ldm_label label; + struct grub_diskfilter_vg *vg; + +#ifdef GRUB_UTIL + grub_util_info ("scanning %s for LDM", disk->name); +#endif + + { + int i; + int has_ldm = msdos_has_ldm_partition (disk); + for (i = 0; i < 3; i++) + { + grub_disk_addr_t sector = LDM_LABEL_SECTOR; + switch (i) + { + case 0: + if (!has_ldm) + continue; + sector = LDM_LABEL_SECTOR; + break; + case 1: + /* LDM is never inside a partition. */ + if (!has_ldm || disk->partition) + continue; + sector = grub_disk_native_sectors (disk); + if (sector == GRUB_DISK_SIZE_UNKNOWN) + continue; + sector--; + break; + /* FIXME: try the third copy. */ + case 2: + sector = gpt_ldm_sector (disk); + if (!sector) + continue; + break; + } + err = grub_disk_read (disk, sector, 0, + sizeof(label), &label); + if (err) + return NULL; + if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0 + && grub_be_to_cpu16 (label.ver_major) == 0x02 + && grub_be_to_cpu16 (label.ver_minor) >= 0x0b + && grub_be_to_cpu16 (label.ver_minor) <= 0x0c) + break; + } + + /* Return if we didn't find a label. */ + if (i == 3) + { +#ifdef GRUB_UTIL + grub_util_info ("no LDM signature found"); +#endif + return NULL; + } + } + + id->uuid = grub_malloc (LDM_GUID_STRLEN + 1); + if (!id->uuid) + return NULL; + grub_memcpy (id->uuid, label.disk_guid, LDM_GUID_STRLEN); + id->uuid[LDM_GUID_STRLEN] = 0; + id->uuidlen = grub_strlen ((char *) id->uuid); + *start_sector = grub_be_to_cpu64 (label.pv_start); + + { + grub_size_t s; + for (s = 0; s < LDM_GUID_STRLEN && label.group_guid[s]; s++); + vg = grub_diskfilter_get_vg_by_uuid (s, label.group_guid); + if (! vg) + vg = make_vg (disk, &label); + } + + if (!vg) + { + grub_free (id->uuid); + return NULL; + } + return vg; +} + +#ifdef GRUB_UTIL + +char * +grub_util_get_ldm (grub_disk_t disk, grub_disk_addr_t start) +{ + struct grub_diskfilter_pv *pv = NULL; + struct grub_diskfilter_vg *vg = NULL; + struct grub_diskfilter_lv *res = 0, *lv, *res_lv = 0; + + pv = grub_diskfilter_get_pv_from_disk (disk, &vg); + + if (!pv) + return NULL; + + for (lv = vg->lvs; lv; lv = lv->next) + if (lv->segment_count == 1 && lv->segments->node_count == 1 + && lv->segments->type == GRUB_DISKFILTER_STRIPED + && lv->segments->nodes->pv == pv + && lv->segments->nodes->start + pv->start_sector == start) + { + res_lv = lv; + break; + } + if (!res_lv) + return NULL; + for (lv = vg->lvs; lv; lv = lv->next) + if (lv->segment_count == 1 && lv->segments->node_count == 1 + && lv->segments->type == GRUB_DISKFILTER_MIRROR + && lv->segments->nodes->lv == res_lv) + { + res = lv; + break; + } + if (res && res->fullname) + return grub_strdup (res->fullname); + return NULL; +} + +int +grub_util_is_ldm (grub_disk_t disk) +{ + int i; + int has_ldm = msdos_has_ldm_partition (disk); + for (i = 0; i < 3; i++) + { + grub_disk_addr_t sector = LDM_LABEL_SECTOR; + grub_err_t err; + struct grub_ldm_label label; + + switch (i) + { + case 0: + if (!has_ldm) + continue; + sector = LDM_LABEL_SECTOR; + break; + case 1: + /* LDM is never inside a partition. */ + if (!has_ldm || disk->partition) + continue; + sector = grub_disk_native_sectors (disk); + if (sector == GRUB_DISK_SIZE_UNKNOWN) + continue; + sector--; + break; + /* FIXME: try the third copy. */ + case 2: + sector = gpt_ldm_sector (disk); + if (!sector) + continue; + break; + } + err = grub_disk_read (disk, sector, 0, sizeof(label), &label); + if (err) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + /* This check is more relaxed on purpose. */ + if (grub_memcmp (label.magic, LDM_MAGIC, sizeof (label.magic)) == 0) + return 1; + } + + return 0; +} + +grub_err_t +grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors, + unsigned int max_nsectors, + grub_embed_type_t embed_type, + grub_disk_addr_t **sectors) +{ + struct grub_diskfilter_pv *pv = NULL; + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_lv *lv; + unsigned i; + + if (embed_type != GRUB_EMBED_PCBIOS) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "LDM currently supports only PC-BIOS embedding"); + if (disk->partition) + return grub_error (GRUB_ERR_BUG, "disk isn't LDM"); + pv = grub_diskfilter_get_pv_from_disk (disk, &vg); + if (!pv) + return grub_error (GRUB_ERR_BUG, "disk isn't LDM"); + for (lv = vg->lvs; lv; lv = lv->next) + { + struct grub_diskfilter_lv *comp; + + if (!lv->visible || !lv->fullname) + continue; + + if (lv->segment_count != 1) + continue; + if (lv->segments->type != GRUB_DISKFILTER_MIRROR + || lv->segments->node_count != 1 + || lv->segments->start_extent != 0 + || lv->segments->extent_count != lv->size) + continue; + + comp = lv->segments->nodes->lv; + if (!comp) + continue; + + if (comp->segment_count != 1 || comp->size != lv->size) + continue; + if (comp->segments->type != GRUB_DISKFILTER_STRIPED + || comp->segments->node_count != 1 + || comp->segments->start_extent != 0 + || comp->segments->extent_count != lv->size) + continue; + + /* How to implement proper check is to be discussed. */ +#if 1 + if (1) + continue; +#else + if (grub_strcmp (lv->name, "Volume5") != 0) + continue; +#endif + if (lv->size < *nsectors) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + /* TRANSLATORS: it's a partition for embedding, + not a partition embed into something. GRUB + install tools put core.img into a place + usable for bootloaders (called generically + "embedding zone") and this operation is + called "embedding". */ + N_("your LDM Embedding Partition is too small;" + " embedding won't be possible")); + *nsectors = lv->size; + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; + *sectors = grub_calloc (*nsectors, sizeof (**sectors)); + if (!*sectors) + return grub_errno; + for (i = 0; i < *nsectors; i++) + (*sectors)[i] = (lv->segments->nodes->start + + comp->segments->nodes->start + + comp->segments->nodes->pv->start_sector + i); + return GRUB_ERR_NONE; + } + + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + /* TRANSLATORS: it's a partition for embedding, + not a partition embed into something. */ + N_("this LDM has no Embedding Partition;" + " embedding won't be possible")); +} +#endif + +static struct grub_diskfilter grub_ldm_dev = { + .name = "ldm", + .detect = grub_ldm_detect, + .next = 0 +}; + +GRUB_MOD_INIT (ldm) +{ + grub_diskfilter_register_back (&grub_ldm_dev); +} + +GRUB_MOD_FINI (ldm) +{ + grub_diskfilter_unregister (&grub_ldm_dev); +} diff --git a/disk/loopback.c b/grub-core/disk/loopback.c similarity index 61% rename from disk/loopback.c rename to grub-core/disk/loopback.c index a8b7cf5d7..2bea4e922 100644 --- a/disk/loopback.c +++ b/grub-core/disk/loopback.c @@ -24,21 +24,28 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct grub_loopback { char *devname; - char *filename; - int has_partitions; + grub_file_t file; struct grub_loopback *next; + unsigned long id; + grub_uint64_t refcnt; }; static struct grub_loopback *loopback_list; +static unsigned long last_id = 0; static const struct grub_arg_option options[] = { - {"delete", 'd', 0, N_("Delete the loopback device entry."), 0, 0}, - {"partitions", 'p', 0, N_("Simulate a hard drive with partitions."), 0, 0}, + /* TRANSLATORS: The disk is simply removed from the list of available ones, + not wiped, avoid to scare user. */ + {"delete", 'd', 0, N_("Delete the specified loopback drive."), 0, 0}, + {"decompress", 'D', 0, N_("Transparently decompress backing file."), 0, 0}, {0, 0, 0, 0, 0, 0} }; @@ -59,11 +66,13 @@ delete_loopback (const char *name) if (! dev) return grub_error (GRUB_ERR_BAD_DEVICE, "device not found"); + if (dev->refcnt > 0) + return grub_error (GRUB_ERR_STILL_REFERENCED, "device still referenced"); /* Remove the device from the list. */ *prev = dev->next; grub_free (dev->devname); - grub_free (dev->filename); + grub_file_close (dev->file); grub_free (dev); return 0; @@ -71,87 +80,75 @@ delete_loopback (const char *name) /* The command to add and remove loopback devices. */ static grub_err_t -grub_cmd_loopback (grub_extcmd_t cmd, int argc, char **args) +grub_cmd_loopback (grub_extcmd_context_t ctxt, int argc, char **args) { - struct grub_arg_list *state = state = cmd->state; + struct grub_arg_list *state = ctxt->state; grub_file_t file; + enum grub_file_type type = GRUB_FILE_TYPE_LOOPBACK; struct grub_loopback *newdev; + grub_err_t ret; if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); /* Check if `-d' was used. */ if (state[0].set) - return delete_loopback (args[0]); + return delete_loopback (args[0]); + + if (!state[1].set) + type |= GRUB_FILE_TYPE_NO_DECOMPRESS; if (argc < 2) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); - file = grub_file_open (args[1]); - if (! file) - return grub_errno; - - /* Close the file, the only reason for opening it is validation. */ - grub_file_close (file); - - /* First try to replace the old device. */ + /* Check that a device with requested name does not already exist. */ for (newdev = loopback_list; newdev; newdev = newdev->next) if (grub_strcmp (newdev->devname, args[0]) == 0) - break; + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name already exists"); - if (newdev) - { - char *newname = grub_strdup (args[1]); - if (! newname) - return grub_errno; - - grub_free (newdev->filename); - newdev->filename = newname; - - /* Set has_partitions when `--partitions' was used. */ - newdev->has_partitions = state[1].set; - - return 0; - } + file = grub_file_open (args[1], type); + if (! file) + return grub_errno; /* Unable to replace it, make a new entry. */ newdev = grub_malloc (sizeof (struct grub_loopback)); if (! newdev) - return grub_errno; + goto fail; newdev->devname = grub_strdup (args[0]); if (! newdev->devname) { grub_free (newdev); - return grub_errno; + goto fail; } - newdev->filename = grub_strdup (args[1]); - if (! newdev->filename) - { - grub_free (newdev->devname); - grub_free (newdev); - return grub_errno; - } - - /* Set has_partitions when `--partitions' was used. */ - newdev->has_partitions = state[1].set; + newdev->file = file; + newdev->id = last_id++; + newdev->refcnt = 0; /* Add the new entry to the list. */ newdev->next = loopback_list; loopback_list = newdev; return 0; + +fail: + ret = grub_errno; + grub_file_close (file); + return ret; } static int -grub_loopback_iterate (int (*hook) (const char *name)) +grub_loopback_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) { struct grub_loopback *d; + if (pull != GRUB_DISK_PULL_NONE) + return 0; for (d = loopback_list; d; d = d->next) { - if (hook (d->devname)) + if (hook (d->devname, hook_data)) return 1; } return 0; @@ -160,7 +157,6 @@ grub_loopback_iterate (int (*hook) (const char *name)) static grub_err_t grub_loopback_open (const char *name, grub_disk_t disk) { - grub_file_t file; struct grub_loopback *dev; for (dev = loopback_list; dev; dev = dev->next) @@ -170,17 +166,22 @@ grub_loopback_open (const char *name, grub_disk_t disk) if (! dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - file = grub_file_open (dev->filename); - if (! file) - return grub_errno; + if (grub_add (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count overflow"); /* Use the filesize for the disk size, round up to a complete sector. */ - disk->total_sectors = ((file->size + GRUB_DISK_SECTOR_SIZE - 1) - / GRUB_DISK_SECTOR_SIZE); - disk->id = (unsigned long) dev; + if (dev->file->size != GRUB_FILE_SIZE_UNKNOWN) + disk->total_sectors = ((dev->file->size + GRUB_DISK_SECTOR_SIZE - 1) + / GRUB_DISK_SECTOR_SIZE); + else + disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + /* Avoid reading more than 512M. */ + disk->max_agglomerate = 1 << (29 - GRUB_DISK_SECTOR_BITS + - GRUB_DISK_CACHE_BITS); - disk->has_partitions = dev->has_partitions; - disk->data = file; + disk->id = dev->id; + + disk->data = dev; return 0; } @@ -188,16 +189,17 @@ grub_loopback_open (const char *name, grub_disk_t disk) static void grub_loopback_close (grub_disk_t disk) { - grub_file_t file = (grub_file_t) disk->data; + struct grub_loopback *dev = disk->data; - grub_file_close (file); + if (grub_sub (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count underflow"); } static grub_err_t grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { - grub_file_t file = (grub_file_t) disk->data; + grub_file_t file = ((struct grub_loopback *) disk->data)->file; grub_off_t pos; grub_file_seek (file, sector << GRUB_DISK_SECTOR_BITS); @@ -225,33 +227,35 @@ grub_loopback_write (grub_disk_t disk __attribute ((unused)), grub_size_t size __attribute ((unused)), const char *buf __attribute ((unused))) { - return GRUB_ERR_NOT_IMPLEMENTED_YET; + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "loopback write is not supported"); } static struct grub_disk_dev grub_loopback_dev = { .name = "loopback", .id = GRUB_DISK_DEVICE_LOOPBACK_ID, - .iterate = grub_loopback_iterate, - .open = grub_loopback_open, - .close = grub_loopback_close, - .read = grub_loopback_read, - .write = grub_loopback_write, + .disk_iterate = grub_loopback_iterate, + .disk_open = grub_loopback_open, + .disk_close = grub_loopback_close, + .disk_read = grub_loopback_read, + .disk_write = grub_loopback_write, .next = 0 }; static grub_extcmd_t cmd; -GRUB_MOD_INIT(loop) +GRUB_MOD_INIT(loopback) { - cmd = grub_register_extcmd ("loopback", grub_cmd_loopback, - GRUB_COMMAND_FLAG_BOTH, - N_("[-d|-p] DEVICENAME FILE."), - N_("Make a device of a file."), options); + cmd = grub_register_extcmd ("loopback", grub_cmd_loopback, 0, + N_("[-d] [-D] DEVICENAME FILE."), + /* TRANSLATORS: The file itself is not destroyed + or transformed into drive. */ + N_("Make a virtual drive from a file."), options); grub_disk_dev_register (&grub_loopback_dev); } -GRUB_MOD_FINI(loop) +GRUB_MOD_FINI(loopback) { grub_unregister_extcmd (cmd); grub_disk_dev_unregister (&grub_loopback_dev); diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c new file mode 100644 index 000000000..9e1e6a5d9 --- /dev/null +++ b/grub-core/disk/luks.c @@ -0,0 +1,301 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007,2010,2011,2019 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define LUKS_KEY_ENABLED 0x00AC71F3 + +/* On disk LUKS header */ +struct grub_luks_phdr +{ + grub_uint8_t magic[6]; +#define LUKS_MAGIC "LUKS\xBA\xBE" + grub_uint16_t version; + char cipherName[32]; + char cipherMode[32]; + char hashSpec[32]; + grub_uint32_t payloadOffset; + grub_uint32_t keyBytes; + grub_uint8_t mkDigest[20]; + grub_uint8_t mkDigestSalt[32]; + grub_uint32_t mkDigestIterations; + char uuid[40]; + struct + { + grub_uint32_t active; + grub_uint32_t passwordIterations; + grub_uint8_t passwordSalt[32]; + grub_uint32_t keyMaterialOffset; + grub_uint32_t stripes; + } keyblock[8]; +} GRUB_PACKED; + +typedef struct grub_luks_phdr *grub_luks_phdr_t; + +gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); + +static grub_cryptodisk_t +luks_scan (grub_disk_t disk, grub_cryptomount_args_t cargs) +{ + grub_cryptodisk_t newdev; + struct grub_luks_phdr header; + char ciphername[sizeof (header.cipherName) + 1]; + char ciphermode[sizeof (header.cipherMode) + 1]; + char hashspec[sizeof (header.hashSpec) + 1]; + grub_err_t err; + + if (cargs->check_boot) + return NULL; + + /* Read the LUKS header. */ + err = grub_disk_read (disk, 0, 0, sizeof (header), &header); + if (err) + { + if (err == GRUB_ERR_OUT_OF_RANGE) + grub_errno = GRUB_ERR_NONE; + return NULL; + } + + /* Look for LUKS magic sequence. */ + if (grub_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) + || grub_be_to_cpu16 (header.version) != 1) + return NULL; + + if (cargs->search_uuid != NULL && grub_uuidcasecmp (cargs->search_uuid, header.uuid, sizeof (header.uuid)) != 0) + { + grub_dprintf ("luks", "%s != %s\n", header.uuid, cargs->search_uuid); + return NULL; + } + + /* Make sure that strings are null terminated. */ + grub_memcpy (ciphername, header.cipherName, sizeof (header.cipherName)); + ciphername[sizeof (header.cipherName)] = 0; + grub_memcpy (ciphermode, header.cipherMode, sizeof (header.cipherMode)); + ciphermode[sizeof (header.cipherMode)] = 0; + grub_memcpy (hashspec, header.hashSpec, sizeof (header.hashSpec)); + hashspec[sizeof (header.hashSpec)] = 0; + + newdev = grub_zalloc (sizeof (struct grub_cryptodisk)); + if (!newdev) + return NULL; + newdev->offset_sectors = grub_be_to_cpu32 (header.payloadOffset); + newdev->source_disk = NULL; + newdev->log_sector_size = GRUB_LUKS1_LOG_SECTOR_SIZE; + newdev->total_sectors = grub_disk_native_sectors (disk) - newdev->offset_sectors; + grub_memcpy (newdev->uuid, header.uuid, sizeof (header.uuid)); + newdev->modname = "luks"; + + /* Configure the hash used for the AF splitter and HMAC. */ + newdev->hash = grub_crypto_lookup_md_by_name (hashspec); + if (!newdev->hash) + { + grub_free (newdev); + grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + hashspec); + return NULL; + } + + err = grub_cryptodisk_setcipher (newdev, ciphername, ciphermode); + if (err) + { + grub_free (newdev); + return NULL; + } + + COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= sizeof (header.uuid)); + return newdev; +} + +static grub_err_t +luks_recover_key (grub_disk_t source, + grub_cryptodisk_t dev, + grub_cryptomount_args_t cargs) +{ + struct grub_luks_phdr header; + grub_size_t keysize; + grub_uint8_t *split_key = NULL; + grub_uint8_t candidate_digest[sizeof (header.mkDigest)]; + unsigned i; + grub_size_t length; + grub_err_t err; + grub_size_t max_stripes = 1; + + if (cargs->key_data == NULL || cargs->key_len == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no key data"); + + err = grub_disk_read (source, 0, 0, sizeof (header), &header); + if (err) + return err; + + grub_puts_ (N_("Attempting to decrypt master key...")); + keysize = grub_be_to_cpu32 (header.keyBytes); + if (keysize > GRUB_CRYPTODISK_MAX_KEYLEN) + return grub_error (GRUB_ERR_BAD_FS, "key is too long"); + + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + if (grub_be_to_cpu32 (header.keyblock[i].active) == LUKS_KEY_ENABLED + && grub_be_to_cpu32 (header.keyblock[i].stripes) > max_stripes) + max_stripes = grub_be_to_cpu32 (header.keyblock[i].stripes); + + split_key = grub_calloc (keysize, max_stripes); + if (!split_key) + return grub_errno; + + /* Try to recover master key from each active keyslot. */ + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + { + gcry_err_code_t gcry_err; + grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; + grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN]; + + /* Check if keyslot is enabled. */ + if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) + continue; + + grub_dprintf ("luks", "Trying keyslot %d\n", i); + + /* Calculate the PBKDF2 of the user supplied passphrase. */ + gcry_err = grub_crypto_pbkdf2 (dev->hash, cargs->key_data, + cargs->key_len, + header.keyblock[i].passwordSalt, + sizeof (header.keyblock[i].passwordSalt), + grub_be_to_cpu32 (header.keyblock[i]. + passwordIterations), + digest, keysize); + + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "PBKDF2 done\n"); + + gcry_err = grub_cryptodisk_setkey (dev, digest, keysize); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + length = (keysize * grub_be_to_cpu32 (header.keyblock[i].stripes)); + + /* Read and decrypt the key material from the disk. */ + err = grub_disk_read (source, + grub_be_to_cpu32 (header.keyblock + [i].keyMaterialOffset), 0, + length, split_key); + if (err) + { + grub_free (split_key); + return err; + } + + gcry_err = grub_cryptodisk_decrypt (dev, split_key, length, 0, + GRUB_LUKS1_LOG_SECTOR_SIZE); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + /* Merge the decrypted key material to get the candidate master key. */ + gcry_err = AF_merge (dev->hash, split_key, candidate_key, keysize, + grub_be_to_cpu32 (header.keyblock[i].stripes)); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "candidate key recovered\n"); + + /* Calculate the PBKDF2 of the candidate master key. */ + gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key, + grub_be_to_cpu32 (header.keyBytes), + header.mkDigestSalt, + sizeof (header.mkDigestSalt), + grub_be_to_cpu32 + (header.mkDigestIterations), + candidate_digest, + sizeof (candidate_digest)); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + /* Compare the calculated PBKDF2 to the digest stored + in the header to see if it's correct. */ + if (grub_memcmp (candidate_digest, header.mkDigest, + sizeof (header.mkDigest)) != 0) + { + grub_dprintf ("luks", "bad digest\n"); + continue; + } + + /* TRANSLATORS: It's a cryptographic key slot: one element of an array + where each element is either empty or holds a key. */ + grub_printf_ (N_("Slot %d opened\n"), i); + + /* Set the master key. */ + gcry_err = grub_cryptodisk_setkey (dev, candidate_key, keysize); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + grub_free (split_key); + + return GRUB_ERR_NONE; + } + + grub_free (split_key); + return GRUB_ACCESS_DENIED; +} + +struct grub_cryptodisk_dev luks_crypto = { + .scan = luks_scan, + .recover_key = luks_recover_key +}; + +GRUB_MOD_INIT (luks) +{ + COMPILE_TIME_ASSERT (sizeof (((struct grub_luks_phdr *) 0)->uuid) + < GRUB_CRYPTODISK_MAX_UUID_LENGTH); + grub_cryptodisk_dev_register (&luks_crypto); +} + +GRUB_MOD_FINI (luks) +{ + grub_cryptodisk_dev_unregister (&luks_crypto); +} diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c new file mode 100644 index 000000000..8036d76ff --- /dev/null +++ b/grub-core/disk/luks2.c @@ -0,0 +1,808 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define LUKS_MAGIC_1ST "LUKS\xBA\xBE" +#define LUKS_MAGIC_2ND "SKUL\xBA\xBE" + +enum grub_luks2_kdf_type +{ + LUKS2_KDF_TYPE_ARGON2I, + LUKS2_KDF_TYPE_PBKDF2 +}; +typedef enum grub_luks2_kdf_type grub_luks2_kdf_type_t; + +/* On disk LUKS header */ +struct grub_luks2_header +{ + char magic[6]; + grub_uint16_t version; + grub_uint64_t hdr_size; + grub_uint64_t seqid; + char label[48]; + char csum_alg[32]; + grub_uint8_t salt[64]; + char uuid[40]; + char subsystem[48]; + grub_uint64_t hdr_offset; + char _padding[184]; + grub_uint8_t csum[64]; + char _padding4096[7*512]; +} GRUB_PACKED; +typedef struct grub_luks2_header grub_luks2_header_t; + +struct grub_luks2_keyslot +{ + /* The integer key to the associative array of keyslots. */ + grub_uint64_t idx; + grub_int64_t key_size; + grub_int64_t priority; + struct + { + const char *encryption; + grub_uint64_t offset; + grub_uint64_t size; + grub_int64_t key_size; + } area; + struct + { + const char *hash; + grub_int64_t stripes; + } af; + struct + { + grub_luks2_kdf_type_t type; + const char *salt; + union + { + struct + { + grub_int64_t time; + grub_int64_t memory; + grub_int64_t cpus; + } argon2i; + struct + { + const char *hash; + grub_int64_t iterations; + } pbkdf2; + } u; + } kdf; +}; +typedef struct grub_luks2_keyslot grub_luks2_keyslot_t; + +struct grub_luks2_segment +{ + grub_uint64_t idx; + grub_uint64_t offset; + const char *size; + const char *encryption; + grub_int64_t sector_size; +}; +typedef struct grub_luks2_segment grub_luks2_segment_t; + +struct grub_luks2_digest +{ + grub_uint64_t idx; + /* Both keyslots and segments are interpreted as bitfields here */ + grub_uint64_t keyslots; + grub_uint64_t segments; + const char *salt; + const char *digest; + const char *hash; + grub_int64_t iterations; +}; +typedef struct grub_luks2_digest grub_luks2_digest_t; + +gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, grub_uint8_t * src, + grub_uint8_t * dst, grub_size_t blocksize, + grub_size_t blocknumbers); + +static grub_err_t +luks2_parse_keyslot (grub_luks2_keyslot_t *out, const grub_json_t *keyslot) +{ + grub_json_t area, af, kdf; + const char *type; + + if (grub_json_getstring (&type, keyslot, "type")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid keyslot"); + else if (grub_strcmp (type, "luks2")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported keyslot type %s", type); + else if (grub_json_getint64 (&out->key_size, keyslot, "key_size")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing keyslot information"); + if (grub_json_getint64 (&out->priority, keyslot, "priority")) + out->priority = 1; + + if (grub_json_getvalue (&area, keyslot, "area") || + grub_json_getstring (&type, &area, "type")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid key area"); + else if (grub_strcmp (type, "raw")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported key area type: %s", type); + else if (grub_json_getuint64 (&out->area.offset, &area, "offset") || + grub_json_getuint64 (&out->area.size, &area, "size") || + grub_json_getstring (&out->area.encryption, &area, "encryption") || + grub_json_getint64 (&out->area.key_size, &area, "key_size")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing key area information"); + + if (grub_json_getvalue (&kdf, keyslot, "kdf") || + grub_json_getstring (&type, &kdf, "type") || + grub_json_getstring (&out->kdf.salt, &kdf, "salt")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing or invalid KDF"); + else if (!grub_strcmp (type, "argon2i") || !grub_strcmp (type, "argon2id")) + { + out->kdf.type = LUKS2_KDF_TYPE_ARGON2I; + if (grub_json_getint64 (&out->kdf.u.argon2i.time, &kdf, "time") || + grub_json_getint64 (&out->kdf.u.argon2i.memory, &kdf, "memory") || + grub_json_getint64 (&out->kdf.u.argon2i.cpus, &kdf, "cpus")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing Argon2i parameters"); + } + else if (!grub_strcmp (type, "pbkdf2")) + { + out->kdf.type = LUKS2_KDF_TYPE_PBKDF2; + if (grub_json_getstring (&out->kdf.u.pbkdf2.hash, &kdf, "hash") || + grub_json_getint64 (&out->kdf.u.pbkdf2.iterations, &kdf, "iterations")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing PBKDF2 parameters"); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported KDF type %s", type); + + if (grub_json_getvalue (&af, keyslot, "af") || + grub_json_getstring (&type, &af, "type")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing or invalid area"); + if (grub_strcmp (type, "luks1")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported AF type %s", type); + if (grub_json_getint64 (&out->af.stripes, &af, "stripes") || + grub_json_getstring (&out->af.hash, &af, "hash")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing AF parameters"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +luks2_parse_segment (grub_luks2_segment_t *out, const grub_json_t *segment) +{ + const char *type; + + if (grub_json_getstring (&type, segment, "type")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid segment type"); + else if (grub_strcmp (type, "crypt")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported segment type %s", type); + + if (grub_json_getuint64 (&out->offset, segment, "offset") || + grub_json_getstring (&out->size, segment, "size") || + grub_json_getstring (&out->encryption, segment, "encryption") || + grub_json_getint64 (&out->sector_size, segment, "sector_size")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing segment parameters"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +luks2_parse_digest (grub_luks2_digest_t *out, const grub_json_t *digest) +{ + grub_json_t segments, keyslots, o; + grub_size_t i, size; + grub_uint64_t bit; + const char *type; + + if (grub_json_getstring (&type, digest, "type")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest type"); + else if (grub_strcmp (type, "pbkdf2")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported digest type %s", type); + + if (grub_json_getvalue (&segments, digest, "segments") || + grub_json_getvalue (&keyslots, digest, "keyslots") || + grub_json_getstring (&out->salt, digest, "salt") || + grub_json_getstring (&out->digest, digest, "digest") || + grub_json_getstring (&out->hash, digest, "hash") || + grub_json_getint64 (&out->iterations, digest, "iterations")) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Missing digest parameters"); + + if (grub_json_getsize (&size, &segments)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Digest references no segments"); + + out->segments = 0; + for (i = 0; i < size; i++) + { + if (grub_json_getchild (&o, &segments, i) || + grub_json_getuint64 (&bit, &o, NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid segment"); + out->segments |= (1 << bit); + } + + if (grub_json_getsize (&size, &keyslots)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Digest references no keyslots"); + + out->keyslots = 0; + for (i = 0; i < size; i++) + { + if (grub_json_getchild (&o, &keyslots, i) || + grub_json_getuint64 (&bit, &o, NULL)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot"); + out->keyslots |= (1 << bit); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +luks2_get_keyslot (grub_luks2_keyslot_t *k, grub_luks2_digest_t *d, grub_luks2_segment_t *s, + const grub_json_t *root, grub_size_t keyslot_json_idx) +{ + grub_json_t keyslots, keyslot, digests, digest, segments, segment; + grub_size_t json_idx, size; + + /* Get nth keyslot */ + if (grub_json_getvalue (&keyslots, root, "keyslots") || + grub_json_getchild (&keyslot, &keyslots, keyslot_json_idx) || + grub_json_getuint64 (&k->idx, &keyslot, NULL) || + grub_json_getchild (&keyslot, &keyslot, 0) || + luks2_parse_keyslot (k, &keyslot)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse keyslot index %" PRIuGRUB_SIZE, keyslot_json_idx); + + /* Get digest that matches the keyslot. */ + if (grub_json_getvalue (&digests, root, "digests") || + grub_json_getsize (&size, &digests)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get digests"); + for (json_idx = 0; json_idx < size; json_idx++) + { + if (grub_json_getchild (&digest, &digests, json_idx) || + grub_json_getuint64 (&d->idx, &digest, NULL) || + grub_json_getchild (&digest, &digest, 0) || + luks2_parse_digest (d, &digest)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse digest index %" PRIuGRUB_SIZE, json_idx); + + if ((d->keyslots & (1 << k->idx))) + break; + } + if (json_idx == size) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "No digest for keyslot \"%" PRIuGRUB_UINT64_T "\"", k->idx); + + /* Get segment that matches the digest. */ + if (grub_json_getvalue (&segments, root, "segments") || + grub_json_getsize (&size, &segments)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get segments"); + for (json_idx = 0; json_idx < size; json_idx++) + { + if (grub_json_getchild (&segment, &segments, json_idx) || + grub_json_getuint64 (&s->idx, &segment, NULL) || + grub_json_getchild (&segment, &segment, 0) || + luks2_parse_segment (s, &segment)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not parse segment index %" PRIuGRUB_SIZE, json_idx); + + if ((d->segments & (1 << s->idx))) + break; + } + if (json_idx == size) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "No segment for digest \"%" PRIuGRUB_UINT64_T "\"", d->idx); + + return GRUB_ERR_NONE; +} + +/* Determine whether to use primary or secondary header */ +static grub_err_t +luks2_read_header (grub_disk_t disk, grub_luks2_header_t *outhdr) +{ + grub_luks2_header_t primary, secondary, *header = &primary; + grub_err_t ret; + + /* Read the primary LUKS header. */ + ret = grub_disk_read (disk, 0, 0, sizeof (primary), &primary); + if (ret) + return ret; + + /* Look for LUKS magic sequence. */ + if (grub_memcmp (primary.magic, LUKS_MAGIC_1ST, sizeof (primary.magic)) || + grub_be_to_cpu16 (primary.version) != 2) + return GRUB_ERR_BAD_SIGNATURE; + + /* Read the secondary header. */ + ret = grub_disk_read (disk, 0, grub_be_to_cpu64 (primary.hdr_size), sizeof (secondary), &secondary); + if (ret) + return ret; + + /* Look for LUKS magic sequence. */ + if (grub_memcmp (secondary.magic, LUKS_MAGIC_2ND, sizeof (secondary.magic)) || + grub_be_to_cpu16 (secondary.version) != 2) + return GRUB_ERR_BAD_SIGNATURE; + + if (grub_be_to_cpu64 (primary.seqid) < grub_be_to_cpu64 (secondary.seqid)) + header = &secondary; + grub_memcpy (outhdr, header, sizeof (*header)); + + return GRUB_ERR_NONE; +} + +static grub_cryptodisk_t +luks2_scan (grub_disk_t disk, grub_cryptomount_args_t cargs) +{ + grub_cryptodisk_t cryptodisk; + grub_luks2_header_t header; + + if (cargs->check_boot) + return NULL; + + if (luks2_read_header (disk, &header)) + { + grub_errno = GRUB_ERR_NONE; + return NULL; + } + + if (cargs->search_uuid != NULL && grub_uuidcasecmp (cargs->search_uuid, header.uuid, sizeof (header.uuid)) != 0) + { + grub_dprintf ("luks2", "%s != %s\n", header.uuid, cargs->search_uuid); + return NULL; + } + + cryptodisk = grub_zalloc (sizeof (*cryptodisk)); + if (!cryptodisk) + return NULL; + + COMPILE_TIME_ASSERT (sizeof (cryptodisk->uuid) >= sizeof (header.uuid)); + grub_memcpy (cryptodisk->uuid, header.uuid, sizeof (header.uuid)); + + cryptodisk->modname = "luks2"; + return cryptodisk; +} + +static grub_err_t +luks2_base64_decode (const char *in, grub_size_t inlen, grub_uint8_t *decoded, idx_t *decodedlen) +{ + grub_size_t unescaped_len = 0; + char *unescaped = NULL; + bool successful; + + if (grub_json_unescape (&unescaped, &unescaped_len, in, inlen) != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("could not unescape Base64 string")); + + successful = base64_decode (unescaped, unescaped_len, (char *) decoded, decodedlen); + grub_free (unescaped); + if (!successful) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("could not decode Base64 string")); + + return GRUB_ERR_NONE; +} + +static grub_err_t +luks2_verify_key (grub_luks2_digest_t *d, grub_uint8_t *candidate_key, + grub_size_t candidate_key_len) +{ + grub_uint8_t candidate_digest[GRUB_CRYPTODISK_MAX_KEYLEN]; + grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN], salt[GRUB_CRYPTODISK_MAX_KEYLEN]; + idx_t saltlen = sizeof (salt), digestlen = sizeof (digest); + const gcry_md_spec_t *hash; + gcry_err_code_t gcry_ret; + + /* Decode both digest and salt */ + if (luks2_base64_decode (d->digest, grub_strlen (d->digest), + digest, &digestlen) != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest"); + if (luks2_base64_decode (d->salt, grub_strlen (d->salt), + salt, &saltlen) != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid digest salt"); + + /* Configure the hash used for the digest. */ + hash = grub_crypto_lookup_md_by_name (d->hash); + if (!hash) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", d->hash); + + /* Calculate the candidate key's digest */ + gcry_ret = grub_crypto_pbkdf2 (hash, + candidate_key, candidate_key_len, + salt, saltlen, + d->iterations, + candidate_digest, digestlen); + if (gcry_ret) + return grub_crypto_gcry_error (gcry_ret); + + if (grub_memcmp (candidate_digest, digest, digestlen) != 0) + return grub_error (GRUB_ERR_ACCESS_DENIED, "Mismatching digests"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +luks2_decrypt_key (grub_uint8_t *out_key, + grub_disk_t source, grub_cryptodisk_t crypt, + grub_luks2_keyslot_t *k, + const grub_uint8_t *passphrase, grub_size_t passphraselen) +{ + grub_uint8_t area_key[GRUB_CRYPTODISK_MAX_KEYLEN]; + grub_uint8_t salt[GRUB_CRYPTODISK_MAX_KEYLEN]; + grub_uint8_t *split_key = NULL; + idx_t saltlen = sizeof (salt); + char cipher[32], *p; + const gcry_md_spec_t *hash; + gcry_err_code_t gcry_ret; + grub_err_t ret; + + if (luks2_base64_decode (k->kdf.salt, grub_strlen (k->kdf.salt), + salt, &saltlen) != GRUB_ERR_NONE) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid keyslot salt"); + goto err; + } + + /* Calculate the binary area key of the user supplied passphrase. */ + switch (k->kdf.type) + { + case LUKS2_KDF_TYPE_ARGON2I: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Argon2 not supported"); + goto err; + case LUKS2_KDF_TYPE_PBKDF2: + hash = grub_crypto_lookup_md_by_name (k->kdf.u.pbkdf2.hash); + if (!hash) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + k->kdf.u.pbkdf2.hash); + goto err; + } + + gcry_ret = grub_crypto_pbkdf2 (hash, (grub_uint8_t *) passphrase, + passphraselen, + salt, saltlen, + k->kdf.u.pbkdf2.iterations, + area_key, k->area.key_size); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + break; + } + + /* Set up disk encryption parameters for the key area */ + grub_strncpy (cipher, k->area.encryption, sizeof (cipher)); + p = grub_memchr (cipher, '-', grub_strlen (cipher)); + if (!p) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid encryption"); + *p = '\0'; + + ret = grub_cryptodisk_setcipher (crypt, cipher, p + 1); + if (ret) + return ret; + + gcry_ret = grub_cryptodisk_setkey (crypt, area_key, k->area.key_size); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + /* Read and decrypt the binary key area with the area key. */ + split_key = grub_malloc (k->area.size); + if (!split_key) + { + ret = grub_errno; + goto err; + } + + grub_errno = GRUB_ERR_NONE; + ret = grub_disk_read (source, 0, k->area.offset, k->area.size, split_key); + if (ret) + { + grub_error (GRUB_ERR_IO, "Read error: %s\n", grub_errmsg); + goto err; + } + + /* + * The key slots area is always encrypted in 512-byte sectors, + * regardless of encrypted data sector size. + */ + gcry_ret = grub_cryptodisk_decrypt (crypt, split_key, k->area.size, 0, + GRUB_LUKS1_LOG_SECTOR_SIZE); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + /* Configure the hash used for anti-forensic merging. */ + hash = grub_crypto_lookup_md_by_name (k->af.hash); + if (!hash) + { + ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", + k->af.hash); + goto err; + } + + /* Merge the decrypted key material to get the candidate master key. */ + gcry_ret = AF_merge (hash, split_key, out_key, k->key_size, k->af.stripes); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + grub_dprintf ("luks2", "Candidate key recovered\n"); + + err: + grub_free (split_key); + return ret; +} + +static grub_err_t +luks2_recover_key (grub_disk_t source, + grub_cryptodisk_t crypt, + grub_cryptomount_args_t cargs) +{ + grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; + char cipher[32], *json_header = NULL, *ptr; + grub_size_t candidate_key_len = 0, json_idx, size; + grub_luks2_header_t header; + grub_luks2_keyslot_t keyslot; + grub_luks2_digest_t digest; + grub_luks2_segment_t segment; + gcry_err_code_t gcry_ret; + grub_json_t *json = NULL, keyslots; + grub_err_t ret; + grub_size_t sz; + + if (cargs->key_data == NULL || cargs->key_len == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no key data"); + + ret = luks2_read_header (source, &header); + if (ret) + return ret; + + if (grub_sub (grub_be_to_cpu64 (header.hdr_size), sizeof (header), &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while calculating json header size"); + + json_header = grub_zalloc (sz); + if (!json_header) + return GRUB_ERR_OUT_OF_MEMORY; + + /* Read the JSON area. */ + ret = grub_disk_read (source, 0, grub_be_to_cpu64 (header.hdr_offset) + sizeof (header), + grub_be_to_cpu64 (header.hdr_size) - sizeof (header), json_header); + if (ret) + goto err; + + ptr = grub_memchr (json_header, 0, grub_be_to_cpu64 (header.hdr_size) - sizeof (header)); + if (!ptr) + goto err; + + ret = grub_json_parse (&json, json_header, grub_be_to_cpu64 (header.hdr_size)); + if (ret) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid LUKS2 JSON header"); + goto err; + } + + if (grub_json_getvalue (&keyslots, json, "keyslots") || + grub_json_getsize (&size, &keyslots)) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Could not get keyslots"); + goto err; + } + + if (grub_disk_native_sectors (source) == GRUB_DISK_SIZE_UNKNOWN) + { + /* FIXME: Allow use of source disk, and maybe cause errors in read. */ + grub_dprintf ("luks2", "Source disk %s has an unknown size, " + "conservatively returning error\n", source->name); + ret = grub_error (GRUB_ERR_BUG, "Unknown size of luks2 source device"); + goto err; + } + + /* Try all keyslot */ + for (json_idx = 0; json_idx < size; json_idx++) + { + char indexstr[21]; /* log10(2^64) ~ 20, plus NUL character. */ + typeof (source->total_sectors) max_crypt_sectors = 0; + + grub_errno = GRUB_ERR_NONE; + ret = luks2_get_keyslot (&keyslot, &digest, &segment, json, json_idx); + if (ret) + { + /* + * luks2_get_keyslot() can fail for a variety of reasons that do not + * necessarily mean the next keyslot should not be tried (e.g. a new + * kdf type). So always try the next slot. + */ + grub_dprintf ("luks2", "Failed to get keyslot %" PRIuGRUB_UINT64_T "\n", keyslot.idx); + continue; + } + if (grub_errno != GRUB_ERR_NONE) + grub_dprintf ("luks2", "Ignoring unhandled error %d from luks2_get_keyslot\n", grub_errno); + + if (keyslot.priority == 0) + { + grub_dprintf ("luks2", "Ignoring keyslot \"%" PRIuGRUB_UINT64_T "\" due to priority\n", keyslot.idx); + continue; + } + + grub_dprintf ("luks2", "Trying keyslot \"%" PRIuGRUB_UINT64_T "\"\n", keyslot.idx); + + /* Sector size should be one of 512, 1024, 2048, or 4096. */ + if (!(segment.sector_size == 512 || segment.sector_size == 1024 || + segment.sector_size == 2048 || segment.sector_size == 4096)) + { + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" sector" + " size %" PRIuGRUB_UINT64_T " is not one of" + " 512, 1024, 2048, or 4096\n", + segment.idx, segment.sector_size); + continue; + } + + /* Set up disk according to keyslot's segment. */ + crypt->offset_sectors = grub_divmod64 (segment.offset, segment.sector_size, NULL); + crypt->log_sector_size = grub_log2ull (segment.sector_size); + /* Set to the source disk/partition size, which is the maximum we allow. */ + max_crypt_sectors = grub_disk_native_sectors (source); + max_crypt_sectors = grub_convert_sector (max_crypt_sectors, GRUB_DISK_SECTOR_BITS, + crypt->log_sector_size); + + if (max_crypt_sectors < crypt->offset_sectors) + { + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" has offset" + " %" PRIuGRUB_UINT64_T " which is greater than" + " source disk size %" PRIuGRUB_UINT64_T "," + " skipping\n", segment.idx, crypt->offset_sectors, + max_crypt_sectors); + continue; + } + + if (grub_strcmp (segment.size, "dynamic") == 0) + crypt->total_sectors = max_crypt_sectors - crypt->offset_sectors; + else + { + grub_errno = GRUB_ERR_NONE; + + /* Convert segment.size to sectors, rounding up to nearest sector */ + crypt->total_sectors = grub_strtoull (segment.size, NULL, 10); + + if (grub_errno == GRUB_ERR_NONE) + { + crypt->total_sectors = ALIGN_UP (crypt->total_sectors, + 1 << crypt->log_sector_size); + crypt->total_sectors >>= crypt->log_sector_size; + } + else if (grub_errno == GRUB_ERR_BAD_NUMBER) + { + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" size" + " \"%s\" is not a parsable number," + " skipping keyslot\n", + segment.idx, segment.size); + continue; + } + else if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + { + /* + * There was an overflow in parsing segment.size, so disk must + * be very large or the string is incorrect. + * + * TODO: Allow reading of at least up max_crypt_sectors. Really, + * its very unlikely one would be booting from such a large drive + * anyway. Use another smaller LUKS2 boot device. + */ + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" size" + " %s overflowed 64-bit unsigned integer," + " skipping keyslot\n", segment.idx, segment.size); + continue; + } + } + + if (crypt->total_sectors == 0) + { + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" has zero" + " sectors, skipping\n", segment.idx); + continue; + } + else if (max_crypt_sectors < (crypt->offset_sectors + crypt->total_sectors)) + { + grub_dprintf ("luks2", "Segment \"%" PRIuGRUB_UINT64_T "\" has last" + " data position greater than source disk size," + " the end of the crypto device will be" + " inaccessible\n", segment.idx); + + /* Allow decryption up to the end of the source disk. */ + crypt->total_sectors = max_crypt_sectors - crypt->offset_sectors; + } + + ret = luks2_decrypt_key (candidate_key, source, crypt, &keyslot, + cargs->key_data, cargs->key_len); + if (ret) + { + grub_dprintf ("luks2", "Decryption with keyslot \"%" PRIuGRUB_UINT64_T "\" failed: %s\n", + keyslot.idx, grub_errmsg); + continue; + } + + ret = luks2_verify_key (&digest, candidate_key, keyslot.key_size); + if (ret) + { + grub_dprintf ("luks2", "Could not open keyslot \"%" PRIuGRUB_UINT64_T "\": %s\n", + keyslot.idx, grub_errmsg); + continue; + } + + grub_snprintf (indexstr, sizeof (indexstr) - 1, "%" PRIuGRUB_UINT64_T, keyslot.idx); + /* + * TRANSLATORS: It's a cryptographic key slot: one element of an array + * where each element is either empty or holds a key. + */ + grub_printf_ (N_("Slot \"%s\" opened\n"), indexstr); + + candidate_key_len = keyslot.key_size; + break; + } + if (candidate_key_len == 0) + { + ret = grub_error (GRUB_ERR_ACCESS_DENIED, "Invalid passphrase"); + goto err; + } + + /* Set up disk cipher. */ + grub_strncpy (cipher, segment.encryption, sizeof (cipher)); + ptr = grub_memchr (cipher, '-', grub_strlen (cipher)); + if (!ptr) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid encryption"); + *ptr = '\0'; + + ret = grub_cryptodisk_setcipher (crypt, cipher, ptr + 1); + if (ret) + goto err; + + /* Set the master key. */ + gcry_ret = grub_cryptodisk_setkey (crypt, candidate_key, candidate_key_len); + if (gcry_ret) + { + ret = grub_crypto_gcry_error (gcry_ret); + goto err; + } + + err: + grub_free (json_header); + grub_json_free (json); + return ret; +} + +static struct grub_cryptodisk_dev luks2_crypto = { + .scan = luks2_scan, + .recover_key = luks2_recover_key +}; + +GRUB_MOD_INIT (luks2) +{ + grub_cryptodisk_dev_register (&luks2_crypto); +} + +GRUB_MOD_FINI (luks2) +{ + grub_cryptodisk_dev_unregister (&luks2_crypto); +} diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c new file mode 100644 index 000000000..af6a8e93c --- /dev/null +++ b/grub-core/disk/lvm.c @@ -0,0 +1,1098 @@ +/* lvm.c - module to read Logical Volumes. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008,2009,2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_UTIL +#include +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct ignored_feature_lv +{ + struct grub_diskfilter_lv *lv; + char *origin; + struct ignored_feature_lv *next; +}; + + +/* Go the string STR and return the number after STR. *P will point + at the number. In case STR is not found, *P will be NULL and the + return value will be 0. */ +static grub_uint64_t +grub_lvm_getvalue (const char ** const p, const char *str) +{ + *p = grub_strstr (*p, str); + if (! *p) + return 0; + *p += grub_strlen (str); + return grub_strtoull (*p, p, 10); +} + +#if 0 +static int +grub_lvm_checkvalue (char **p, char *str, char *tmpl) +{ + int tmpllen = grub_strlen (tmpl); + *p = grub_strstr (*p, str); + if (! *p) + return 0; + *p += grub_strlen (str); + if (**p != '"') + return 0; + return (grub_memcmp (*p + 1, tmpl, tmpllen) == 0 && (*p)[tmpllen + 1] == '"'); +} +#endif + +static int +grub_lvm_check_flag (const char *p, const char *str, const char *flag) +{ + grub_size_t len_str = grub_strlen (str), len_flag = grub_strlen (flag); + while (1) + { + const char *q; + p = grub_strstr (p, str); + if (! p) + return 0; + p += len_str; + if (grub_memcmp (p, " = [", sizeof (" = [") - 1) != 0) + continue; + q = p + sizeof (" = [") - 1; + while (1) + { + while (grub_isspace (*q)) + q++; + if (*q != '"') + return 0; + q++; + if (grub_memcmp (q, flag, len_flag) == 0 && q[len_flag] == '"') + return 1; + while (*q != '"') + q++; + q++; + if (*q == ']') + return 0; + q++; + } + } +} + +static void +grub_lvm_free_ignored_feature_lvs (struct ignored_feature_lv *ignored_feature_lvs) +{ + struct ignored_feature_lv *ignored_feature; + + while ((ignored_feature = ignored_feature_lvs)) + { + ignored_feature_lvs = ignored_feature_lvs->next; + + if (ignored_feature->lv) + { + unsigned int i; + + for (i = 0; i < ignored_feature->lv->segment_count; ++i) + if (ignored_feature->lv->segments) + grub_free (ignored_feature->lv->segments[i].nodes); + grub_free (ignored_feature->lv->segments); + grub_free (ignored_feature->lv->fullname); + grub_free (ignored_feature->lv->idname); + grub_free (ignored_feature->lv->name); + } + grub_free (ignored_feature->lv); + grub_free (ignored_feature->origin); + grub_free (ignored_feature); + } +} + +static struct grub_diskfilter_vg * +grub_lvm_detect (grub_disk_t disk, + struct grub_diskfilter_pv_id *id, + grub_disk_addr_t *start_sector) +{ + grub_err_t err; + grub_uint64_t mda_offset, mda_size; + grub_size_t ptr; + char buf[GRUB_LVM_LABEL_SIZE]; + char vg_id[GRUB_LVM_ID_STRLEN+1]; + char pv_id[GRUB_LVM_ID_STRLEN+1]; + char *metadatabuf, *mda_end, *vgname; + const char *p, *q; + struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf; + struct grub_lvm_pv_header *pvh; + struct grub_lvm_disk_locn *dlocn; + struct grub_lvm_mda_header *mdah; + struct grub_lvm_raw_locn *rlocn; + unsigned int i, j; + grub_size_t vgname_len; + struct grub_diskfilter_vg *vg; + struct grub_diskfilter_pv *pv; + + /* Search for label. */ + for (i = 0; i < GRUB_LVM_LABEL_SCAN_SECTORS; i++) + { + err = grub_disk_read (disk, i, 0, sizeof(buf), buf); + if (err) + goto fail; + + if ((! grub_strncmp ((char *)lh->id, GRUB_LVM_LABEL_ID, + sizeof (lh->id))) + && (! grub_strncmp ((char *)lh->type, GRUB_LVM_LVM2_LABEL, + sizeof (lh->type)))) + break; + } + + /* Return if we didn't find a label. */ + if (i == GRUB_LVM_LABEL_SCAN_SECTORS) + { +#ifdef GRUB_UTIL + grub_util_info ("no LVM signature found"); +#endif + goto fail; + } + + /* + * We read a grub_lvm_pv_header and then 2 grub_lvm_disk_locns that + * immediately follow the PV header. Make sure we have space for both. + */ + if (grub_le_to_cpu32 (lh->offset_xl) >= + GRUB_LVM_LABEL_SIZE - sizeof (struct grub_lvm_pv_header) - + 2 * sizeof (struct grub_lvm_disk_locn)) + { +#ifdef GRUB_UTIL + grub_util_info ("LVM PV header/disk locations are beyond the end of the block"); +#endif + goto fail; + } + + pvh = (struct grub_lvm_pv_header *) (buf + grub_le_to_cpu32(lh->offset_xl)); + + for (i = 0, j = 0; i < GRUB_LVM_ID_LEN; i++) + { + pv_id[j++] = pvh->pv_uuid[i]; + if ((i != 1) && (i != 29) && (i % 4 == 1)) + pv_id[j++] = '-'; + } + pv_id[j] = '\0'; + + dlocn = pvh->disk_areas_xl; + + dlocn++; + /* Is it possible to have multiple data/metadata areas? I haven't + seen devices that have it. */ + if (dlocn->offset) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "we don't support multiple LVM data areas"); + +#ifdef GRUB_UTIL + grub_util_info ("we don't support multiple LVM data areas"); +#endif + goto fail; + } + + dlocn++; + mda_offset = grub_le_to_cpu64 (dlocn->offset); + mda_size = grub_le_to_cpu64 (dlocn->size); + + /* It's possible to have multiple copies of metadata areas, we just use the + first one. */ + + /* Allocate buffer space for the circular worst-case scenario. */ + metadatabuf = grub_calloc (2, mda_size); + if (! metadatabuf) + goto fail; + + err = grub_disk_read (disk, 0, mda_offset, mda_size, metadatabuf); + if (err) + goto fail2; + + mdah = (struct grub_lvm_mda_header *) metadatabuf; + if ((grub_strncmp ((char *)mdah->magic, GRUB_LVM_FMTT_MAGIC, + sizeof (mdah->magic))) + || (grub_le_to_cpu32 (mdah->version) != GRUB_LVM_FMTT_VERSION)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unknown LVM metadata header"); +#ifdef GRUB_UTIL + grub_util_info ("unknown LVM metadata header"); +#endif + goto fail2; + } + + rlocn = mdah->raw_locns; + if (grub_le_to_cpu64 (rlocn->offset) >= grub_le_to_cpu64 (mda_size)) + { +#ifdef GRUB_UTIL + grub_util_info ("metadata offset is beyond end of metadata area"); +#endif + goto fail2; + } + + if (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) > + grub_le_to_cpu64 (mdah->size)) + { + if (2 * mda_size < GRUB_LVM_MDA_HEADER_SIZE || + (grub_le_to_cpu64 (rlocn->offset) + grub_le_to_cpu64 (rlocn->size) - + grub_le_to_cpu64 (mdah->size) > mda_size - GRUB_LVM_MDA_HEADER_SIZE)) + { +#ifdef GRUB_UTIL + grub_util_info ("cannot copy metadata wrap in circular buffer"); +#endif + goto fail2; + } + + /* Metadata is circular. Copy the wrap in place. */ + grub_memcpy (metadatabuf + mda_size, + metadatabuf + GRUB_LVM_MDA_HEADER_SIZE, + grub_le_to_cpu64 (rlocn->offset) + + grub_le_to_cpu64 (rlocn->size) - + grub_le_to_cpu64 (mdah->size)); + } + + if (grub_add ((grub_size_t)metadatabuf, + (grub_size_t)grub_le_to_cpu64 (rlocn->offset), + &ptr)) + { + error_parsing_metadata: +#ifdef GRUB_UTIL + grub_util_info ("error parsing metadata"); +#endif + goto fail2; + } + + p = q = (char *)ptr; + + if (grub_add (ptr, (grub_size_t) grub_le_to_cpu64 (rlocn->size), &ptr)) + goto error_parsing_metadata; + + mda_end = (char *)ptr; + + while (*q != ' ' && q < mda_end) + q++; + + if (q == mda_end) + goto error_parsing_metadata; + + vgname_len = q - p; + vgname = grub_malloc (vgname_len + 1); + if (!vgname) + goto fail2; + + grub_memcpy (vgname, p, vgname_len); + vgname[vgname_len] = '\0'; + + p = grub_strstr (q, "id = \""); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("couldn't find ID"); +#endif + goto fail3; + } + p += sizeof ("id = \"") - 1; + grub_memcpy (vg_id, p, GRUB_LVM_ID_STRLEN); + vg_id[GRUB_LVM_ID_STRLEN] = '\0'; + + vg = grub_diskfilter_get_vg_by_uuid (GRUB_LVM_ID_STRLEN, vg_id); + + if (! vg) + { + struct ignored_feature_lv *ignored_feature_lvs = NULL; + + /* First time we see this volume group. We've to create the + whole volume group structure. */ + vg = grub_malloc (sizeof (*vg)); + if (! vg) + goto fail3; + vg->name = vgname; + vg->uuid = grub_malloc (GRUB_LVM_ID_STRLEN); + if (! vg->uuid) + goto fail3; + grub_memcpy (vg->uuid, vg_id, GRUB_LVM_ID_STRLEN); + vg->uuid_len = GRUB_LVM_ID_STRLEN; + + vg->extent_size = grub_lvm_getvalue (&p, "extent_size = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown extent size"); +#endif + goto fail4; + } + + vg->lvs = NULL; + vg->pvs = NULL; + + p = grub_strstr (p, "physical_volumes {"); + if (p) + { + p += sizeof ("physical_volumes {") - 1; + + /* Add all the pvs to the volume group. */ + while (1) + { + grub_ssize_t s; + while (grub_isspace (*p) && p < mda_end) + p++; + + if (p == mda_end) + goto fail4; + + if (*p == '}') + break; + + pv = grub_zalloc (sizeof (*pv)); + if (pv == NULL) + goto fail4; + q = p; + while (*q != ' ' && q < mda_end) + q++; + + if (q == mda_end) + goto pvs_fail_noname; + + s = q - p; + pv->name = grub_malloc (s + 1); + if (pv->name == NULL) + goto pvs_fail_noname; + grub_memcpy (pv->name, p, s); + pv->name[s] = '\0'; + + p = grub_strstr (p, "id = \""); + if (p == NULL) + goto pvs_fail; + p += sizeof("id = \"") - 1; + + pv->id.uuid = grub_malloc (GRUB_LVM_ID_STRLEN); + if (!pv->id.uuid) + goto pvs_fail; + grub_memcpy (pv->id.uuid, p, GRUB_LVM_ID_STRLEN); + pv->id.uuidlen = GRUB_LVM_ID_STRLEN; + + pv->start_sector = grub_lvm_getvalue (&p, "pe_start = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown pe_start"); +#endif + goto pvs_fail; + } + + p = grub_strchr (p, '}'); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("error parsing pe_start"); +#endif + goto pvs_fail; + } + p++; + + pv->disk = NULL; + pv->next = vg->pvs; + vg->pvs = pv; + + continue; + pvs_fail: + grub_free (pv->name); + pvs_fail_noname: + grub_free (pv); + goto fail4; + } + } + else + goto fail4; + + p = grub_strstr (p, "logical_volumes {"); + if (p) + { + p += sizeof ("logical_volumes {") - 1; + + /* And add all the lvs to the volume group. */ + while (1) + { + grub_ssize_t s; + int skip_lv = 0; + struct grub_diskfilter_lv *lv; + struct grub_diskfilter_segment *seg; + int is_pvmove; + + while (grub_isspace (*p) && p < mda_end) + p++; + + if (p == mda_end) + goto fail4; + + if (*p == '}') + break; + + lv = grub_zalloc (sizeof (*lv)); + if (lv == NULL) + goto fail4; + + q = p; + while (*q != ' ' && q < mda_end) + q++; + + if (q == mda_end) + goto lvs_fail; + + s = q - p; + lv->name = grub_strndup (p, s); + if (!lv->name) + goto lvs_fail; + + { + const char *iptr; + char *optr; + + /* + * This is kind of hard to read with our safe (but rather + * baroque) math primatives, but it boils down to: + * + * sz0 = vgname_len * 2 + 1 + + * s * 2 + 1 + + * sizeof ("lvm/") - 1; + */ + grub_size_t sz0 = vgname_len, sz1 = s; + + if (grub_mul (sz0, 2, &sz0) || + grub_add (sz0, 1, &sz0) || + grub_mul (sz1, 2, &sz1) || + grub_add (sz1, 1, &sz1) || + grub_add (sz0, sz1, &sz0) || + grub_add (sz0, sizeof ("lvm/") - 1, &sz0)) + goto lvs_fail; + + lv->fullname = grub_malloc (sz0); + if (!lv->fullname) + goto lvs_fail; + + grub_memcpy (lv->fullname, "lvm/", sizeof ("lvm/") - 1); + optr = lv->fullname + sizeof ("lvm/") - 1; + for (iptr = vgname; iptr < vgname + vgname_len; iptr++) + { + *optr++ = *iptr; + if (*iptr == '-') + *optr++ = '-'; + } + *optr++ = '-'; + for (iptr = p; iptr < p + s; iptr++) + { + *optr++ = *iptr; + if (*iptr == '-') + *optr++ = '-'; + } + *optr++ = 0; + lv->idname = grub_malloc (sizeof ("lvmid/") + + 2 * GRUB_LVM_ID_STRLEN + 1); + if (!lv->idname) + goto lvs_fail; + grub_memcpy (lv->idname, "lvmid/", + sizeof ("lvmid/") - 1); + grub_memcpy (lv->idname + sizeof ("lvmid/") - 1, + vg_id, GRUB_LVM_ID_STRLEN); + lv->idname[sizeof ("lvmid/") - 1 + GRUB_LVM_ID_STRLEN] = '/'; + + p = grub_strstr (q, "id = \""); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("couldn't find ID"); +#endif + goto lvs_fail; + } + p += sizeof ("id = \"") - 1; + grub_memcpy (lv->idname + sizeof ("lvmid/") - 1 + + GRUB_LVM_ID_STRLEN + 1, + p, GRUB_LVM_ID_STRLEN); + lv->idname[sizeof ("lvmid/") - 1 + 2 * GRUB_LVM_ID_STRLEN + 1] = '\0'; + } + + lv->size = 0; + + lv->visible = grub_lvm_check_flag (p, "status", "VISIBLE"); + is_pvmove = grub_lvm_check_flag (p, "status", "PVMOVE"); + + lv->segment_count = grub_lvm_getvalue (&p, "segment_count = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown segment_count"); +#endif + goto lvs_fail; + } + lv->segments = grub_calloc (lv->segment_count, sizeof (*seg)); + if (lv->segments == NULL) + goto lvs_fail; + seg = lv->segments; + + for (i = 0; i < lv->segment_count; i++) + { + + p = grub_strstr (p, "segment"); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown segment"); +#endif + goto lvs_segment_fail; + } + + seg->start_extent = grub_lvm_getvalue (&p, "start_extent = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown start_extent"); +#endif + goto lvs_segment_fail; + } + seg->extent_count = grub_lvm_getvalue (&p, "extent_count = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown extent_count"); +#endif + goto lvs_segment_fail; + } + + p = grub_strstr (p, "type = \""); + if (p == NULL) + goto lvs_segment_fail; + p += sizeof("type = \"") - 1; + + lv->size += seg->extent_count * vg->extent_size; + + if (grub_memcmp (p, "striped\"", + sizeof ("striped\"") - 1) == 0) + { + struct grub_diskfilter_node *stripe; + + seg->type = GRUB_DISKFILTER_STRIPED; + seg->node_count = grub_lvm_getvalue (&p, "stripe_count = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown stripe_count"); +#endif + goto lvs_segment_fail; + } + + if (seg->node_count != 1) + { + seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown stripe_size"); +#endif + goto lvs_segment_fail; + } + } + + seg->nodes = grub_calloc (seg->node_count, + sizeof (*stripe)); + if (seg->nodes == NULL) + goto lvs_segment_fail; + stripe = seg->nodes; + + p = grub_strstr (p, "stripes = ["); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown stripes"); +#endif + goto lvs_segment_fail2; + } + p += sizeof("stripes = [") - 1; + + for (j = 0; j < seg->node_count; j++) + { + p = grub_strchr (p, '"'); + if (p == NULL) + goto lvs_segment_fail2; + q = ++p; + while (q < mda_end && *q != '"') + q++; + + if (q == mda_end) + goto lvs_segment_fail2; + + s = q - p; + + stripe->name = grub_malloc (s + 1); + if (stripe->name == NULL) + goto lvs_segment_fail2; + + grub_memcpy (stripe->name, p, s); + stripe->name[s] = '\0'; + + p = q + 1; + + stripe->start = grub_lvm_getvalue (&p, ",") + * vg->extent_size; + if (p == NULL) + { + grub_free (stripe->name); + goto lvs_segment_fail2; + } + + stripe++; + } + } + else if (grub_memcmp (p, "mirror\"", sizeof ("mirror\"") - 1) + == 0) + { + seg->type = GRUB_DISKFILTER_MIRROR; + seg->node_count = grub_lvm_getvalue (&p, "mirror_count = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown mirror_count"); +#endif + goto lvs_segment_fail; + } + + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; + + p = grub_strstr (p, "mirrors = ["); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown mirrors"); +#endif + goto lvs_segment_fail2; + } + p += sizeof("mirrors = [") - 1; + + for (j = 0; j < seg->node_count; j++) + { + char *lvname; + + p = grub_strchr (p, '"'); + if (p == NULL) + goto lvs_segment_fail2; + q = ++p; + while (q < mda_end && *q != '"') + q++; + + if (q == mda_end) + goto lvs_segment_fail2; + + s = q - p; + + lvname = grub_malloc (s + 1); + if (lvname == NULL) + goto lvs_segment_fail2; + + grub_memcpy (lvname, p, s); + lvname[s] = '\0'; + seg->nodes[j].name = lvname; + p = q + 1; + } + /* Only first (original) is ok with in progress pvmove. */ + if (is_pvmove) + seg->node_count = 1; + } + else if (grub_memcmp (p, "raid", sizeof ("raid") - 1) == 0 + && ((p[sizeof ("raid") - 1] >= '4' + && p[sizeof ("raid") - 1] <= '6') + || p[sizeof ("raid") - 1] == '1') + && p[sizeof ("raidX") - 1] == '"') + { + switch (p[sizeof ("raid") - 1]) + { + case '1': + seg->type = GRUB_DISKFILTER_MIRROR; + break; + case '4': + seg->type = GRUB_DISKFILTER_RAID4; + seg->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; + break; + case '5': + seg->type = GRUB_DISKFILTER_RAID5; + seg->layout = GRUB_RAID_LAYOUT_LEFT_SYMMETRIC; + break; + case '6': + seg->type = GRUB_DISKFILTER_RAID6; + seg->layout = (GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC + | GRUB_RAID_LAYOUT_MUL_FROM_POS); + break; + } + seg->node_count = grub_lvm_getvalue (&p, "device_count = "); + + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown device_count"); +#endif + goto lvs_segment_fail; + } + + if (seg->type != GRUB_DISKFILTER_MIRROR) + { + seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = "); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown stripe_size"); +#endif + goto lvs_segment_fail; + } + } + + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; + + p = grub_strstr (p, "raids = ["); + if (p == NULL) + { +#ifdef GRUB_UTIL + grub_util_info ("unknown raids"); +#endif + goto lvs_segment_fail2; + } + p += sizeof("raids = [") - 1; + + for (j = 0; j < seg->node_count; j++) + { + char *lvname; + + p = grub_strchr (p, '"'); + p = p ? grub_strchr (p + 1, '"') : 0; + p = p ? grub_strchr (p + 1, '"') : 0; + if (p == NULL) + goto lvs_segment_fail2; + q = ++p; + while (*q != '"') + q++; + + s = q - p; + + lvname = grub_malloc (s + 1); + if (lvname == NULL) + goto lvs_segment_fail2; + + grub_memcpy (lvname, p, s); + lvname[s] = '\0'; + seg->nodes[j].name = lvname; + p = q + 1; + } + if (seg->type == GRUB_DISKFILTER_RAID4) + { + char *tmp; + tmp = seg->nodes[0].name; + grub_memmove (seg->nodes, seg->nodes + 1, + sizeof (seg->nodes[0]) + * (seg->node_count - 1)); + seg->nodes[seg->node_count - 1].name = tmp; + } + } + /* + * Cache and integrity LVs have extra parts that + * we can ignore for our read-only access. + */ + else if (grub_strncmp (p, "cache\"", sizeof ("cache\"") - 1) == 0 || + grub_strncmp (p, "cache+CACHE_USES_CACHEVOL\"", sizeof ("cache+CACHE_USES_CACHEVOL\"") - 1) == 0 || + grub_strncmp (p, "integrity\"", sizeof ("integrity\"") - 1) == 0) + { + struct ignored_feature_lv *ignored_feature = NULL; + + char *p2, *p3; + grub_size_t sz; +#ifdef GRUB_UTIL + p2 = grub_strchr (p, '"'); + if (p2) + *p2 = '\0'; + grub_util_info ("Ignoring extra metadata type '%s' for %s", p, lv->name); + if (p2) + *p2 ='"'; +#endif + + ignored_feature = grub_zalloc (sizeof (*ignored_feature)); + if (!ignored_feature) + goto ignored_feature_lv_fail; + ignored_feature->lv = grub_zalloc (sizeof (*ignored_feature->lv)); + if (!ignored_feature->lv) + goto ignored_feature_lv_fail; + grub_memcpy (ignored_feature->lv, lv, sizeof (*ignored_feature->lv)); + + if (lv->fullname) + { + ignored_feature->lv->fullname = grub_strdup (lv->fullname); + if (!ignored_feature->lv->fullname) + goto ignored_feature_lv_fail; + } + if (lv->idname) + { + ignored_feature->lv->idname = grub_strdup (lv->idname); + if (!ignored_feature->lv->idname) + goto ignored_feature_lv_fail; + } + if (lv->name) + { + ignored_feature->lv->name = grub_strdup (lv->name); + if (!ignored_feature->lv->name) + goto ignored_feature_lv_fail; + } + + skip_lv = 1; + + + p2 = grub_strstr (p, "origin = \""); + if (!p2) + goto ignored_feature_lv_fail; + + p2 = grub_strchr (p2, '"'); + if (!p2) + goto ignored_feature_lv_fail; + + p3 = ++p2; + if (p3 == mda_end) + goto ignored_feature_lv_fail; + p3 = grub_strchr (p3, '"'); + if (!p3) + goto ignored_feature_lv_fail; + + sz = p3 - p2; + + ignored_feature->origin = grub_malloc (sz + 1); + if (!ignored_feature->origin) + goto ignored_feature_lv_fail; + grub_memcpy (ignored_feature->origin, p2, sz); + ignored_feature->origin[sz] = '\0'; + + ignored_feature->next = ignored_feature_lvs; + ignored_feature_lvs = ignored_feature; + break; + + ignored_feature_lv_fail: + if (ignored_feature) + { + grub_free (ignored_feature->origin); + if (ignored_feature->lv) + { + grub_free (ignored_feature->lv->fullname); + grub_free (ignored_feature->lv->idname); + grub_free (ignored_feature->lv->name); + } + grub_free (ignored_feature->lv); + grub_free (ignored_feature); + } + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); + goto fail4; + } + else + { +#ifdef GRUB_UTIL + char *p2; + p2 = grub_strchr (p, '"'); + if (p2) + *p2 = '\0'; + grub_util_info ("unknown LVM type %s", p); + if (p2) + *p2 ='"'; +#endif + /* Found a non-supported type, give up and move on. */ + skip_lv = 1; + break; + } + + seg++; + + continue; + lvs_segment_fail2: + grub_free (seg->nodes); + lvs_segment_fail: + goto fail4; + } + + if (p != NULL) + p = grub_strchr (p, '}'); + if (p == NULL) + goto lvs_fail; + p += 3; + + if (skip_lv) + { + grub_free (lv->name); + grub_free (lv); + continue; + } + + lv->vg = vg; + lv->next = vg->lvs; + vg->lvs = lv; + + continue; + lvs_fail: + grub_free (lv->name); + grub_free (lv); + goto fail4; + } + } + + + { + struct ignored_feature_lv *ignored_feature; + + for (ignored_feature = ignored_feature_lvs; ignored_feature; ignored_feature = ignored_feature->next) + { + struct grub_diskfilter_lv *lv; + + for (lv = vg->lvs; lv; lv = lv->next) + if (grub_strcmp (lv->name, ignored_feature->origin) == 0) + break; + if (lv) + { + ignored_feature->lv->segments = grub_calloc (lv->segment_count, sizeof (*lv->segments)); + if (!ignored_feature->lv->segments) + { + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); + goto fail4; + } + grub_memcpy (ignored_feature->lv->segments, lv->segments, lv->segment_count * sizeof (*lv->segments)); + + for (i = 0; i < lv->segment_count; ++i) + { + struct grub_diskfilter_node *nodes = lv->segments[i].nodes; + grub_size_t node_count = lv->segments[i].node_count; + + ignored_feature->lv->segments[i].nodes = grub_calloc (node_count, sizeof (*nodes)); + if (!ignored_feature->lv->segments[i].nodes) + { + for (j = 0; j < i; ++j) + grub_free (ignored_feature->lv->segments[j].nodes); + grub_free (ignored_feature->lv->segments); + ignored_feature->lv->segments = NULL; + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); + goto fail4; + } + grub_memcpy (ignored_feature->lv->segments[i].nodes, nodes, node_count * sizeof (*nodes)); + } + + if (ignored_feature->lv->segments) + { + ignored_feature->lv->segment_count = lv->segment_count; + ignored_feature->lv->vg = vg; + ignored_feature->lv->next = vg->lvs; + vg->lvs = ignored_feature->lv; + ignored_feature->lv = NULL; + } + } + else + { +#ifdef GRUB_UTIL + grub_util_info ("Couldn't find LVM part of ignored feature on %s", ignored_feature->origin); +#endif + } + } + } + + /* Match LVs. Must be done after cache and integrity are found. */ + { + struct grub_diskfilter_lv *lv1; + struct grub_diskfilter_lv *lv2; + + for (lv1 = vg->lvs; lv1; lv1 = lv1->next) + for (i = 0; i < lv1->segment_count; i++) + for (j = 0; j < lv1->segments[i].node_count; j++) + { + if (vg->pvs) + for (pv = vg->pvs; pv; pv = pv->next) + { + if (! grub_strcmp (pv->name, + lv1->segments[i].nodes[j].name)) + { + lv1->segments[i].nodes[j].pv = pv; + break; + } + } + if (lv1->segments[i].nodes[j].pv == NULL) + for (lv2 = vg->lvs; lv2; lv2 = lv2->next) + { + if (lv1 == lv2) + continue; + if (grub_strcmp (lv2->name, + lv1->segments[i].nodes[j].name) == 0) + lv1->segments[i].nodes[j].lv = lv2; + } + } + } + + grub_lvm_free_ignored_feature_lvs (ignored_feature_lvs); + if (grub_diskfilter_vg_register (vg)) + goto fail4; + } + else + { + grub_free (vgname); + } + + id->uuid = grub_malloc (GRUB_LVM_ID_STRLEN); + if (!id->uuid) + goto fail4; + grub_memcpy (id->uuid, pv_id, GRUB_LVM_ID_STRLEN); + id->uuidlen = GRUB_LVM_ID_STRLEN; + grub_free (metadatabuf); + *start_sector = -1; + return vg; + + /* Failure path. */ + fail4: + grub_free (vg); + fail3: + grub_free (vgname); + + fail2: + grub_free (metadatabuf); + fail: + return NULL; +} + + + +static struct grub_diskfilter grub_lvm_dev = { + .name = "lvm", + .detect = grub_lvm_detect, + .next = 0 +}; + +GRUB_MOD_INIT (lvm) +{ + grub_diskfilter_register_back (&grub_lvm_dev); +} + +GRUB_MOD_FINI (lvm) +{ + grub_diskfilter_unregister (&grub_lvm_dev); +} diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c new file mode 100644 index 000000000..dd5d440a3 --- /dev/null +++ b/grub-core/disk/mdraid1x_linux.c @@ -0,0 +1,311 @@ +/* mdraid_linux.c - module to handle Linux Software RAID. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define SB_MAGIC 0xa92b4efc + +/* + * The version-1 superblock : + * All numeric fields are little-endian. + * + * Total size: 256 bytes plus 2 per device. + * 1K allows 384 devices. + */ + +struct grub_raid_super_1x +{ + /* Constant array information - 128 bytes. */ + grub_uint32_t magic; /* MD_SB_MAGIC: 0xa92b4efc - little endian. */ + grub_uint32_t major_version; /* 1. */ + grub_uint32_t feature_map; /* Bit 0 set if 'bitmap_offset' is meaningful. */ + grub_uint32_t pad0; /* Always set to 0 when writing. */ + + grub_uint8_t set_uuid[16]; /* User-space generated. */ + char set_name[32]; /* Set and interpreted by user-space. */ + + grub_uint64_t ctime; /* Lo 40 bits are seconds, top 24 are microseconds or 0. */ + grub_uint32_t level; /* -4 (multipath), -1 (linear), 0,1,4,5. */ + grub_uint32_t layout; /* only for raid5 and raid10 currently. */ + grub_uint64_t size; /* Used size of component devices, in 512byte sectors. */ + + grub_uint32_t chunksize; /* In 512byte sectors. */ + grub_uint32_t raid_disks; + grub_uint32_t bitmap_offset; /* Sectors after start of superblock that bitmap starts + * NOTE: signed, so bitmap can be before superblock + * only meaningful of feature_map[0] is set. + */ + + /* These are only valid with feature bit '4'. */ + grub_uint32_t new_level; /* New level we are reshaping to. */ + grub_uint64_t reshape_position; /* Next address in array-space for reshape. */ + grub_uint32_t delta_disks; /* Change in number of raid_disks. */ + grub_uint32_t new_layout; /* New layout. */ + grub_uint32_t new_chunk; /* New chunk size (512byte sectors). */ + grub_uint8_t pad1[128 - 124]; /* Set to 0 when written. */ + + /* Constant this-device information - 64 bytes. */ + grub_uint64_t data_offset; /* Sector start of data, often 0. */ + grub_uint64_t data_size; /* Sectors in this device that can be used for data. */ + grub_uint64_t super_offset; /* Sector start of this superblock. */ + grub_uint64_t recovery_offset; /* Sectors before this offset (from data_offset) have been recovered. */ + grub_uint32_t dev_number; /* Permanent identifier of this device - not role in raid. */ + grub_uint32_t cnt_corrected_read; /* Number of read errors that were corrected by re-writing. */ + grub_uint8_t device_uuid[16]; /* User-space setable, ignored by kernel. */ + grub_uint8_t devflags; /* Per-device flags. Only one defined... */ + grub_uint8_t pad2[64 - 57]; /* Set to 0 when writing. */ + + /* Array state information - 64 bytes. */ + grub_uint64_t utime; /* 40 bits second, 24 btes microseconds. */ + grub_uint64_t events; /* Incremented when superblock updated. */ + grub_uint64_t resync_offset; /* Data before this offset (from data_offset) known to be in sync. */ + grub_uint32_t sb_csum; /* Checksum upto devs[max_dev]. */ + grub_uint32_t max_dev; /* Size of devs[] array to consider. */ + grub_uint8_t pad3[64 - 32]; /* Set to 0 when writing. */ + + /* Device state information. Indexed by dev_number. + * 2 bytes per device. + * Note there are no per-device state flags. State information is rolled + * into the 'roles' value. If a device is spare or faulty, then it doesn't + * have a meaningful role. + */ + grub_uint16_t dev_roles[0]; /* Role in array, or 0xffff for a spare, or 0xfffe for faulty. */ +}; +/* Could be GRUB_PACKED, but since all members in this struct + are already appropriately aligned, we can omit this and avoid suboptimal + assembly in some cases. */ + +#define WriteMostly1 1 /* Mask for writemostly flag in above devflags. */ + +#define GRUB_MD_SECTOR_SHIFT 9 /* Follow Linux kernel v6.8. */ +#define GRUB_MD_SECTOR_SIZE (1 << GRUB_MD_SECTOR_SHIFT) + +static struct grub_diskfilter_vg * +grub_mdraid_detect (grub_disk_t disk, + struct grub_diskfilter_pv_id *id, + grub_disk_addr_t *start_sector) +{ + grub_uint64_t size; + grub_uint8_t minor_version; + + size = grub_disk_native_sectors (disk); + + /* Check for an 1.x superblock. + * It's always aligned to a 4K boundary + * and depending on the minor version it can be: + * 0: At least 8K, but less than 12K, from end of device + * 1: At start of device + * 2: 4K from start of device. + */ + + for (minor_version = 0; minor_version < 3; ++minor_version) + { + grub_disk_addr_t sector = 0; + struct grub_raid_super_1x sb; + grub_uint16_t role; + grub_uint32_t level; + struct grub_diskfilter_vg *array; + char *uuid; + grub_uint64_t sb_sz, data_end, sb_end; + + if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) + continue; + + switch (minor_version) + { + case 0: + sector = (size - 8 * 2) & ~(4 * 2 - 1); + break; + case 1: + sector = 0; + break; + case 2: + sector = 4 * 2; + break; + } + + if (grub_disk_read (disk, sector, 0, sizeof (struct grub_raid_super_1x), + &sb)) + return NULL; + + if (sb.magic != grub_cpu_to_le32_compile_time (SB_MAGIC) + || grub_le_to_cpu64 (sb.super_offset) != sector) + continue; + + /* + * The first check follows the Linux kernel's data_size + * validation from v6.8-rc5. + */ + if (grub_le_to_cpu64 (sb.data_size) < 10 || + grub_le_to_cpu64 (sb.raid_disks) > GRUB_MDRAID_MAX_DISKS) + { + grub_dprintf ("mdraid1x", "Corrupted superblock\n"); + return NULL; + } + + /* + * Total size of superblock: 256 bytes plus 2 bytes per device + * in the array. + */ + sb_sz = sizeof (struct grub_raid_super_1x) + grub_le_to_cpu64 (sb.raid_disks) * 2; + + if (grub_add (grub_le_to_cpu64 (sb.super_offset), + (ALIGN_UP(sb_sz, GRUB_MD_SECTOR_SIZE) >> GRUB_MD_SECTOR_SHIFT), &sb_end)) + { + grub_dprintf ("mdraid1x", "Invalid superblock end.\n"); + return NULL; + } + + if (grub_add (grub_le_to_cpu64 (sb.data_offset), + grub_le_to_cpu64 (sb.data_size), &data_end)) + { + grub_dprintf ("mdraid1x", "Invalid data end.\n"); + return NULL; + } + + /* In minor versions 1 and 2, superblock is positioned before data. */ + if (minor_version) + { + if (grub_le_to_cpu64 (sb.data_offset) < sb_end) + { + grub_dprintf ("mdraid1x", + "The superblock either overlaps with the data " + "or is behind it.\n"); + return NULL; + } + + if (data_end > size) + { + grub_dprintf ("mdraid1x", + "The data region ends at %" PRIuGRUB_UINT64_T ", " + "past the end of the disk (%" PRIuGRUB_UINT64_T ")\n", + data_end, size); + return NULL; + } + } + else + { + /* In minor version 0, superblock is at the end of the device. */ + if (grub_le_to_cpu64 (sb.super_offset) < data_end) + { + grub_dprintf ("mdraid1x", + "The data either overlaps with the superblock " + "or is behind it.\n"); + return NULL; + } + + if (sb_end > size) + { + grub_dprintf ("mdraid1x", + "The superblock region ends at " + "%" PRIuGRUB_UINT64_T ", past the end of " + "the disk (%" PRIuGRUB_UINT64_T ")\n", + sb_end, size); + return NULL; + } + } + + if (sb.major_version != grub_cpu_to_le32_compile_time (1)) + /* Unsupported version. */ + return NULL; + + level = grub_le_to_cpu32 (sb.level); + + /* Multipath. */ + if ((int) level == -4) + level = 1; + + if (level != 0 && level != 1 && level != 4 && + level != 5 && level != 6 && level != 10) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.level); + return NULL; + } + + if (grub_le_to_cpu32 (sb.dev_number) >= + grub_le_to_cpu32 (sb.max_dev)) + /* Spares aren't implemented. */ + return NULL; + + if (grub_disk_read (disk, sector, + (char *) (sb.dev_roles + grub_le_to_cpu32 (sb.dev_number)) + - (char *) &sb, + sizeof (role), &role)) + return NULL; + + if (grub_le_to_cpu16 (role) + >= grub_le_to_cpu32 (sb.raid_disks)) + /* Spares aren't implemented. */ + return NULL; + + id->uuidlen = 0; + id->id = grub_le_to_cpu16 (role); + + uuid = grub_malloc (16); + if (!uuid) + return NULL; + + grub_memcpy (uuid, sb.set_uuid, 16); + + *start_sector = grub_le_to_cpu64 (sb.data_offset); + + array = grub_diskfilter_make_raid (16, uuid, + grub_le_to_cpu32 (sb.raid_disks), + sb.set_name, + (sb.size) + ? grub_le_to_cpu64 (sb.size) + : grub_le_to_cpu64 (sb.data_size), + grub_le_to_cpu32 (sb.chunksize), + grub_le_to_cpu32 (sb.layout), + grub_le_to_cpu32 (sb.level)); + + return array; + } + + /* not 1.x raid. */ + return NULL; +} + +static struct grub_diskfilter grub_mdraid_dev = { + .name = "mdraid1x", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT (mdraid1x) +{ + grub_diskfilter_register_front (&grub_mdraid_dev); +} + +GRUB_MOD_FINI (mdraid1x) +{ + grub_diskfilter_unregister (&grub_mdraid_dev); +} diff --git a/disk/mdraid_linux.c b/grub-core/disk/mdraid_linux.c similarity index 59% rename from disk/mdraid_linux.c rename to grub-core/disk/mdraid_linux.c index 306c66a8b..e40216f51 100644 --- a/disk/mdraid_linux.c +++ b/grub-core/disk/mdraid_linux.c @@ -1,7 +1,7 @@ -/* mdraid_linux.c - module to handle linux softraid. */ +/* mdraid_linux.c - module to handle Linux Software RAID. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 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 @@ -22,11 +22,29 @@ #include #include #include -#include +#include /* Linux RAID on disk structures and constants, copied from include/linux/raid/md_p.h. */ +GRUB_MOD_LICENSE ("GPLv3+"); + +#ifdef MODE_BIGENDIAN +#define grub_md_to_cpu64 grub_be_to_cpu64 +#define grub_md_to_cpu32 grub_be_to_cpu32 +#define grub_md_to_cpu16 grub_be_to_cpu16 +#define grub_cpu_to_md64_compile_time grub_cpu_to_be64_compile_time +#define grub_cpu_to_md32_compile_time grub_cpu_to_be32_compile_time +#define grub_cpu_to_md16_compile_time grub_cpu_to_be16_compile_time +#else +#define grub_md_to_cpu64 grub_le_to_cpu64 +#define grub_md_to_cpu32 grub_le_to_cpu32 +#define grub_md_to_cpu16 grub_le_to_cpu16 +#define grub_cpu_to_md64_compile_time grub_cpu_to_le64_compile_time +#define grub_cpu_to_md32_compile_time grub_cpu_to_le32_compile_time +#define grub_cpu_to_md16_compile_time grub_cpu_to_le16_compile_time +#endif + #define RESERVED_BYTES (64 * 1024) #define RESERVED_SECTORS (RESERVED_BYTES / 512) @@ -157,77 +175,124 @@ struct grub_raid_super_09 * Active descriptor */ struct grub_raid_disk_09 this_disk; -} __attribute__ ((packed)); +} GRUB_PACKED; -static grub_err_t -grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +static struct grub_diskfilter_vg * +grub_mdraid_detect (grub_disk_t disk, + struct grub_diskfilter_pv_id *id, + grub_disk_addr_t *start_sector) { grub_disk_addr_t sector; grub_uint64_t size; - struct grub_raid_super_09 sb; + struct grub_raid_super_09 *sb = NULL; grub_uint32_t *uuid; + grub_uint32_t level; + struct grub_diskfilter_vg *ret; - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); + /* The sector where the mdraid 0.90 superblock is stored, if available. */ + size = grub_disk_native_sectors (disk); + if (size == GRUB_DISK_SIZE_UNKNOWN) + /* not 0.9x raid. */ + return NULL; sector = NEW_SIZE_SECTORS (size); - if (grub_disk_read (disk, sector, 0, SB_BYTES, &sb)) - return grub_errno; + sb = grub_malloc (sizeof (*sb)); + if (!sb) + return NULL; - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != SB_MAGIC) - return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + if (grub_disk_read (disk, sector, 0, SB_BYTES, sb)) + goto fail; - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); + /* Look whether there is a mdraid 0.90 superblock. */ + if (sb->md_magic != grub_cpu_to_md32_compile_time (SB_MAGIC)) + /* not 0.9x raid. */ + goto fail; - /* FIXME: Check the checksum. */ + if (sb->major_version != grub_cpu_to_md32_compile_time (0) + || sb->minor_version != grub_cpu_to_md32_compile_time (90)) + /* Unsupported version. */ + goto fail; + /* No need for explicit check that sb->size is 0 (unspecified) since + 0 >= non-0 is false. */ + if (((grub_disk_addr_t) grub_md_to_cpu32 (sb->size)) * 2 >= size) + goto fail; + + /* FIXME: Check the checksum. */ + + level = grub_md_to_cpu32 (sb->level); /* Multipath. */ - if ((int) sb.level == -4) - sb.level = 1; + if ((int) level == -4) + level = 1; - if (sb.level != 0 && sb.level != 1 && sb.level != 4 && - sb.level != 5 && sb.level != 6 && sb.level != 10) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "unsupported RAID level: %d", sb.level); + if (level != 0 && level != 1 && level != 4 && + level != 5 && level != 6 && level != 10) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID level: %d", level); + goto fail; + } + if (grub_md_to_cpu32 (sb->this_disk.number) == 0xffff + || grub_md_to_cpu32 (sb->this_disk.number) == 0xfffe) + /* Spares aren't implemented. */ + goto fail; - array->number = sb.md_minor; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.raid_disks; - array->disk_size = (sb.size) ? sb.size * 2 : sector; - array->chunk_size = sb.chunk_size >> 9; - array->index = sb.this_disk.number; - array->uuid_len = 16; - array->uuid = grub_malloc (16); - if (!array->uuid) - return grub_errno; + uuid = grub_malloc (16); + if (!uuid) + goto fail; - uuid = (grub_uint32_t *) array->uuid; - uuid[0] = sb.set_uuid0; - uuid[1] = sb.set_uuid1; - uuid[2] = sb.set_uuid2; - uuid[3] = sb.set_uuid3; + uuid[0] = grub_swap_bytes32 (sb->set_uuid0); + uuid[1] = grub_swap_bytes32 (sb->set_uuid1); + uuid[2] = grub_swap_bytes32 (sb->set_uuid2); + uuid[3] = grub_swap_bytes32 (sb->set_uuid3); - return 0; + *start_sector = 0; + + id->uuidlen = 0; + id->id = grub_md_to_cpu32 (sb->this_disk.number); + + char buf[32]; + grub_snprintf (buf, sizeof (buf), "md%d", grub_md_to_cpu32 (sb->md_minor)); + ret = grub_diskfilter_make_raid (16, (char *) uuid, + grub_md_to_cpu32 (sb->raid_disks), buf, + (sb->size) ? ((grub_disk_addr_t) + grub_md_to_cpu32 (sb->size)) * 2 + : sector, + grub_md_to_cpu32 (sb->chunk_size) >> 9, + grub_md_to_cpu32 (sb->layout), + level); + grub_free (sb); + return ret; + + fail: + grub_free (sb); + return NULL; } -static struct grub_raid grub_mdraid_dev = { - .name = "mdraid", +static struct grub_diskfilter grub_mdraid_dev = { +#ifdef MODE_BIGENDIAN + .name = "mdraid09_be", +#else + .name = "mdraid09", +#endif .detect = grub_mdraid_detect, .next = 0 }; -GRUB_MOD_INIT (mdraid) +#ifdef MODE_BIGENDIAN +GRUB_MOD_INIT (mdraid09_be) +#else +GRUB_MOD_INIT (mdraid09) +#endif { - grub_raid_register (&grub_mdraid_dev); + grub_diskfilter_register_front (&grub_mdraid_dev); } -GRUB_MOD_FINI (mdraid) +#ifdef MODE_BIGENDIAN +GRUB_MOD_FINI (mdraid09_be) +#else +GRUB_MOD_FINI (mdraid09) +#endif { - grub_raid_unregister (&grub_mdraid_dev); + grub_diskfilter_unregister (&grub_mdraid_dev); } diff --git a/grub-core/disk/mdraid_linux_be.c b/grub-core/disk/mdraid_linux_be.c new file mode 100644 index 000000000..539bcf469 --- /dev/null +++ b/grub-core/disk/mdraid_linux_be.c @@ -0,0 +1,2 @@ +#define MODE_BIGENDIAN 1 +#include "mdraid_linux.c" diff --git a/disk/memdisk.c b/grub-core/disk/memdisk.c similarity index 63% rename from disk/memdisk.c rename to grub-core/disk/memdisk.c index 4a0470837..2d7afaea3 100644 --- a/disk/memdisk.c +++ b/grub-core/disk/memdisk.c @@ -23,15 +23,21 @@ #include #include #include -#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static char *memdisk_addr; static grub_off_t memdisk_size = 0; static int -grub_memdisk_iterate (int (*hook) (const char *name)) +grub_memdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) { - return hook ("memdisk"); + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + return hook ("memdisk", hook_data); } static grub_err_t @@ -41,8 +47,8 @@ grub_memdisk_open (const char *name, grub_disk_t disk) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a memdisk"); disk->total_sectors = memdisk_size / GRUB_DISK_SECTOR_SIZE; - disk->id = (unsigned long) "mdsk"; - disk->has_partitions = 0; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; + disk->id = 0; return GRUB_ERR_NONE; } @@ -72,40 +78,40 @@ static struct grub_disk_dev grub_memdisk_dev = { .name = "memdisk", .id = GRUB_DISK_DEVICE_MEMDISK_ID, - .iterate = grub_memdisk_iterate, - .open = grub_memdisk_open, - .close = grub_memdisk_close, - .read = grub_memdisk_read, - .write = grub_memdisk_write, + .disk_iterate = grub_memdisk_iterate, + .disk_open = grub_memdisk_open, + .disk_close = grub_memdisk_close, + .disk_read = grub_memdisk_read, + .disk_write = grub_memdisk_write, .next = 0 }; GRUB_MOD_INIT(memdisk) { - auto int hook (struct grub_module_header *); - int hook (struct grub_module_header *header) - { - if (header->type == OBJ_TYPE_MEMDISK) - { - char *memdisk_orig_addr; - memdisk_orig_addr = (char *) header + sizeof (struct grub_module_header); + struct grub_module_header *header; + FOR_MODULES (header) + if (header->type == OBJ_TYPE_MEMDISK) + { + char *memdisk_orig_addr; + memdisk_orig_addr = (char *) header + sizeof (struct grub_module_header); - grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr); + grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr); - memdisk_size = header->size - sizeof (struct grub_module_header); - memdisk_addr = grub_malloc (memdisk_size); + if (grub_sub (header->size, sizeof (struct grub_module_header), &memdisk_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while obtaining memdisk size"); + return; + } + memdisk_addr = grub_malloc (memdisk_size); + if (memdisk_addr == NULL) + return; - grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n"); - grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size); + grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n"); + grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size); - grub_disk_dev_register (&grub_memdisk_dev); - return 1; - } - - return 0; - } - - grub_module_iterate (hook); + grub_disk_dev_register (&grub_memdisk_dev); + break; + } } GRUB_MOD_FINI(memdisk) diff --git a/grub-core/disk/pata.c b/grub-core/disk/pata.c new file mode 100644 index 000000000..0578a93e1 --- /dev/null +++ b/grub-core/disk/pata.c @@ -0,0 +1,556 @@ +/* ata_pthru.c - ATA pass through for ata.mod. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS +#include +#include +#else +#define GRUB_MACHINE_PCI_IO_BASE 0xb4000000 +#endif +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* At the moment, only two IDE ports are supported. */ +static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1, + GRUB_ATA_CH1_PORT1 }; + +struct grub_pata_device +{ + /* IDE port to use. */ + int port; + + /* IO addresses on which the registers for this device can be + found. */ + grub_port_t ioaddress; + + /* Two devices can be connected to a single cable. Use this field + to select device 0 (commonly known as "master") or device 1 + (commonly known as "slave"). */ + int device; + + int present; + + struct grub_pata_device *next; +}; + +static struct grub_pata_device *grub_pata_devices; + +static inline void +grub_pata_regset (struct grub_pata_device *dev, int reg, int val) +{ + grub_outb (val, dev->ioaddress + reg); +} + +static inline grub_uint8_t +grub_pata_regget (struct grub_pata_device *dev, int reg) +{ + return grub_inb (dev->ioaddress + reg); +} + +/* Wait for !BSY. */ +static grub_err_t +grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds) +{ + /* ATA requires 400ns (after a write to CMD register) or + 1 PIO cycle (after a DRQ block transfer) before + first check of BSY. */ + grub_millisleep (1); + + int i = 1; + grub_uint8_t sts; + while ((sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS)) + & GRUB_ATA_STATUS_BUSY) + { + if (i >= milliseconds) + { + grub_dprintf ("pata", "timeout: %dms, status=0x%x\n", + milliseconds, sts); + return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout"); + } + + grub_millisleep (1); + i++; + } + + return GRUB_ERR_NONE; +} + +static inline grub_err_t +grub_pata_check_ready (struct grub_pata_device *dev, int spinup) +{ + if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY) + return grub_pata_wait_not_busy (dev, spinup ? GRUB_ATA_TOUT_SPINUP + : GRUB_ATA_TOUT_STD); + + return GRUB_ERR_NONE; +} + +static inline void +grub_pata_wait (void) +{ + grub_millisleep (50); +} + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS +#define grub_ata_to_cpu16(x) ((grub_uint16_t) (x)) +#define grub_cpu_to_ata16(x) ((grub_uint16_t) (x)) +#else +#define grub_ata_to_cpu16 grub_le_to_cpu16 +#define grub_cpu_to_ata16 grub_cpu_to_le16 +#endif + +static void +grub_pata_pio_read (struct grub_pata_device *dev, char *buf, grub_size_t size) +{ + unsigned int i; + + /* Read in the data, word by word. */ + for (i = 0; i < size / 2; i++) + grub_set_unaligned16 (buf + 2 * i, + grub_ata_to_cpu16 (grub_inw(dev->ioaddress + + GRUB_ATA_REG_DATA))); + if (size & 1) + buf[size - 1] = (char) grub_ata_to_cpu16 (grub_inw (dev->ioaddress + + GRUB_ATA_REG_DATA)); +} + +static void +grub_pata_pio_write (struct grub_pata_device *dev, char *buf, grub_size_t size) +{ + unsigned int i; + + /* Write the data, word by word. */ + for (i = 0; i < size / 2; i++) + grub_outw(grub_cpu_to_ata16 (grub_get_unaligned16 (buf + 2 * i)), dev->ioaddress + GRUB_ATA_REG_DATA); +} + +/* ATA pass through support, used by hdparm.mod. */ +static grub_err_t +grub_pata_readwrite (struct grub_ata *disk, + struct grub_disk_ata_pass_through_parms *parms, + int spinup) +{ + struct grub_pata_device *dev = (struct grub_pata_device *) disk->data; + grub_size_t nread = 0; + int i; + + if (! (parms->cmdsize == 0 || parms->cmdsize == 12)) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "ATAPI non-12 byte commands not supported"); + + grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n", + parms->taskfile.cmd, + parms->taskfile.features, + parms->taskfile.sectors); + grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%" + PRIuGRUB_SIZE "\n", + parms->taskfile.lba_high, + parms->taskfile.lba_mid, + parms->taskfile.lba_low, parms->size); + + /* Set registers. */ + grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) + | (parms->taskfile.disk & 0xef)); + if (grub_pata_check_ready (dev, spinup)) + return grub_errno; + + for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++) + grub_pata_regset (dev, i, + parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]); + for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++) + grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]); + + /* Start command. */ + grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd); + + /* Wait for !BSY. */ + if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + /* Check status. */ + grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS); + grub_dprintf ("pata", "status=0x%x\n", sts); + + if (parms->cmdsize) + { + grub_uint8_t irs; + /* Wait for !BSY. */ + if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + irs = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON); + /* OK if DRQ is asserted and interrupt reason is as expected. */ + if (!((sts & GRUB_ATA_STATUS_DRQ) + && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_CMD_OUT)) + return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error"); + /* Write the packet. */ + grub_pata_pio_write (dev, parms->cmd, parms->cmdsize); + } + + /* Transfer data. */ + while (nread < parms->size + && (sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) + == GRUB_ATA_STATUS_DRQ) + { + unsigned cnt; + + /* Wait for !BSY. */ + if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + if (parms->cmdsize) + { + if ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON) + & GRUB_ATAPI_IREASON_MASK) != GRUB_ATAPI_IREASON_DATA_IN) + return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error"); + + cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8 + | grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW); + grub_dprintf("pata", "DRQ count=%u\n", cnt); + + /* Count of last transfer may be uneven. */ + if (! (0 < cnt && cnt <= parms->size - nread + && (! (cnt & 1) || cnt == parms->size - nread))) + return grub_error (GRUB_ERR_READ_ERROR, + "invalid ATAPI transfer count"); + } + else + cnt = GRUB_DISK_SECTOR_SIZE; + if (cnt > parms->size - nread) + cnt = parms->size - nread; + + if (parms->write) + grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt); + else + grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt); + + nread += cnt; + } + if (parms->write) + { + /* Check for write error. */ + if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) + & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) + return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error"); + } + parms->size = nread; + + /* Wait for !BSY. */ + if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA)) + return grub_errno; + + /* Return registers. */ + for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++) + parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i); + + grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n", + parms->taskfile.status, + parms->taskfile.error, + parms->taskfile.sectors); + + if (parms->taskfile.status + & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) + return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +check_device (struct grub_pata_device *dev) +{ + grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4); + grub_pata_wait (); + + /* Try to detect if the port is in use by writing to it, + waiting for a while and reading it again. If the value + was preserved, there is a device connected. */ + grub_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A); + grub_pata_wait (); + grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS); + grub_dprintf ("ata", "sectors=0x%x\n", sec); + if (sec != 0x5A) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected"); + + /* The above test may detect a second (slave) device + connected to a SATA controller which supports only one + (master) device. It is not safe to use the status register + READY bit to check for controller channel existence. Some + ATAPI commands (RESET, DIAGNOSTIC) may clear this bit. */ + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_pata_device_initialize (int port, int device, int addr) +{ + struct grub_pata_device *dev; + struct grub_pata_device **devp; + grub_err_t err; + + grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n", + port, device, addr); + + dev = grub_malloc (sizeof(*dev)); + if (! dev) + return grub_errno; + + /* Setup the device information. */ + dev->port = port; + dev->device = device; + dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE; + dev->present = 1; + dev->next = NULL; + + /* Register the device. */ + for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next); + *devp = dev; + + err = check_device (dev); + if (err == GRUB_ERR_UNKNOWN_DEVICE) + { + grub_dprintf ("pata", "%s\n", grub_errmsg); + grub_error_pop (); + } + + if (err) + grub_print_error (); + + return 0; +} + +#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS +static int +grub_pata_pciinit (grub_pci_device_t dev, + grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + static int compat_use[2] = { 0 }; + grub_pci_address_t addr; + grub_uint32_t class; + grub_uint32_t bar1; + grub_uint32_t bar2; + int rega; + int i; + static int controller = 0; + int cs5536 = 0; + int nports = 2; + + /* Read class. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + class = grub_pci_read (addr); + + /* AMD CS5536 Southbridge. */ + if (pciid == GRUB_CS5536_PCIID) + { + cs5536 = 1; + nports = 1; + } + + /* Check if this class ID matches that of a PCI IDE Controller. */ + if (!cs5536 && (class >> 16 != 0x0101)) + return 0; + + for (i = 0; i < nports; i++) + { + /* Set to 0 when the channel operated in compatibility mode. */ + int compat; + + /* We don't support non-compatibility mode for CS5536. */ + if (cs5536) + compat = 0; + else + compat = (class >> (8 + 2 * i)) & 1; + + rega = 0; + + /* If the channel is in compatibility mode, just assign the + default registers. */ + if (compat == 0 && !compat_use[i]) + { + rega = grub_pata_ioaddress[i]; + compat_use[i] = 1; + } + else if (compat) + { + /* Read the BARs, which either contain a mmapped IO address + or the IO port address. */ + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES + + sizeof (grub_uint64_t) * i); + bar1 = grub_pci_read (addr); + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES + + sizeof (grub_uint64_t) * i + + sizeof (grub_uint32_t)); + bar2 = grub_pci_read (addr); + + /* Check if the BARs describe an IO region. */ + if ((bar1 & 1) && (bar2 & 1) && (bar1 & ~3)) + { + rega = bar1 & ~3; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, grub_pci_read_word (addr) + | GRUB_PCI_COMMAND_IO_ENABLED + | GRUB_PCI_COMMAND_MEM_ENABLED + | GRUB_PCI_COMMAND_BUS_MASTER); + + } + } + + grub_dprintf ("pata", + "PCI dev (%d,%d,%d) compat=%d rega=0x%x\n", + grub_pci_get_bus (dev), grub_pci_get_device (dev), + grub_pci_get_function (dev), compat, rega); + + if (rega) + { + grub_errno = GRUB_ERR_NONE; + grub_pata_device_initialize (controller * 2 + i, 0, rega); + + /* Most errors raised by grub_ata_device_initialize() are harmless. + They just indicate this particular drive is not responding, most + likely because it doesn't exist. We might want to ignore specific + error types here, instead of printing them. */ + if (grub_errno) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + + grub_pata_device_initialize (controller * 2 + i, 1, rega); + + /* Likewise. */ + if (grub_errno) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + } + } + } + + controller++; + + return 0; +} + +static grub_err_t +grub_pata_initialize (void) +{ + grub_pci_iterate (grub_pata_pciinit, NULL); + return 0; +} +#else +static grub_err_t +grub_pata_initialize (void) +{ + int i; + for (i = 0; i < 2; i++) + { + grub_pata_device_initialize (i, 0, grub_pata_ioaddress[i]); + grub_pata_device_initialize (i, 1, grub_pata_ioaddress[i]); + } + return 0; +} +#endif + +static grub_err_t +grub_pata_open (int id, int devnum, struct grub_ata *ata) +{ + struct grub_pata_device *dev; + struct grub_pata_device *devfnd = 0; + grub_err_t err; + + if (id != GRUB_SCSI_SUBSYSTEM_PATA) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device"); + + for (dev = grub_pata_devices; dev; dev = dev->next) + { + if (dev->port * 2 + dev->device == devnum) + { + devfnd = dev; + break; + } + } + + grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum); + + if (! devfnd) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device"); + + err = check_device (devfnd); + if (err) + return err; + + ata->data = devfnd; + ata->dma = 0; + ata->maxbuffer = 256 * 512; + ata->present = &devfnd->present; + + return GRUB_ERR_NONE; +} + +static int +grub_pata_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_pata_device *dev; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (dev = grub_pata_devices; dev; dev = dev->next) + if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device, + hook_data)) + return 1; + + return 0; +} + + +static struct grub_ata_dev grub_pata_dev = + { + .iterate = grub_pata_iterate, + .open = grub_pata_open, + .readwrite = grub_pata_readwrite, + }; + + + + +GRUB_MOD_INIT(ata_pthru) +{ + grub_stop_disk_firmware (); + + /* ATA initialization. */ + grub_pata_initialize (); + + grub_ata_dev_register (&grub_pata_dev); +} + +GRUB_MOD_FINI(ata_pthru) +{ + grub_ata_dev_unregister (&grub_pata_dev); +} diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c new file mode 100644 index 000000000..21ec4072c --- /dev/null +++ b/grub-core/disk/plainmount.c @@ -0,0 +1,463 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 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 . + */ + +/* plaimount.c - Open device encrypted in plain mode. */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define PLAINMOUNT_DEFAULT_SECTOR_SIZE 512 +#define PLAINMOUNT_DEFAULT_UUID "109fea84-a6b7-34a8-4bd1-1c506305a400" + + +enum PLAINMOUNT_OPTION + { + OPTION_HASH, + OPTION_CIPHER, + OPTION_KEY_SIZE, + OPTION_SECTOR_SIZE, + OPTION_PASSWORD, + OPTION_KEYFILE, + OPTION_KEYFILE_OFFSET, + OPTION_UUID + }; + +static const struct grub_arg_option options[] = + { + /* TRANSLATORS: It's still restricted to this module only. */ + {"hash", 'h', 0, N_("Password hash"), 0, ARG_TYPE_STRING}, + {"cipher", 'c', 0, N_("Password cipher"), 0, ARG_TYPE_STRING}, + {"key-size", 's', 0, N_("Key size (in bits)"), 0, ARG_TYPE_INT}, + {"sector-size", 'S', 0, N_("Device sector size"), 0, ARG_TYPE_INT}, + {"password", 'p', 0, N_("Password (key)"), 0, ARG_TYPE_STRING}, + {"keyfile", 'd', 0, N_("Keyfile path"), 0, ARG_TYPE_STRING}, + {"keyfile-offset", 'O', 0, N_("Keyfile offset"), 0, ARG_TYPE_INT}, + {"uuid", 'u', 0, N_("Set device UUID"), 0, ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} + }; + +/* Cryptodisk setkey() function wrapper */ +static grub_err_t +plainmount_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, + grub_size_t size) +{ + gcry_err_code_t code = grub_cryptodisk_setkey (dev, key, size); + if (code != GPG_ERR_NO_ERROR) + { + grub_dprintf ("plainmount", "failed to set cipher key with code: %d\n", code); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot set specified key")); + } + return GRUB_ERR_NONE; +} + +/* Configure cryptodisk uuid */ +static void plainmount_set_uuid (grub_cryptodisk_t dev, const char *user_uuid) +{ + grub_size_t pos = 0; + + /* Size of user_uuid is checked in main func */ + if (user_uuid != NULL) + grub_strcpy (dev->uuid, user_uuid); + else + { + /* + * Set default UUID. Last digits start from 1 and are incremented for + * each new plainmount device by snprintf(). + */ + grub_snprintf (dev->uuid, sizeof (dev->uuid) - 1, "%36lx", dev->id + 1); + while (dev->uuid[++pos] == ' '); + grub_memcpy (dev->uuid, PLAINMOUNT_DEFAULT_UUID, pos); + } + COMPILE_TIME_ASSERT (sizeof (dev->uuid) >= sizeof (PLAINMOUNT_DEFAULT_UUID)); +} + +/* Configure cryptodevice sector size (-S option) */ +static grub_err_t +plainmount_configure_sectors (grub_cryptodisk_t dev, grub_disk_t disk, + grub_size_t sector_size) +{ + dev->total_sectors = grub_disk_native_sectors (disk); + if (dev->total_sectors == GRUB_DISK_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot determine disk %s size"), + disk->name); + + /* Convert size to sectors */ + dev->log_sector_size = grub_log2ull (sector_size); + dev->total_sectors = grub_convert_sector (dev->total_sectors, + GRUB_DISK_SECTOR_BITS, + dev->log_sector_size); + if (dev->total_sectors == 0) + return grub_error (GRUB_ERR_BAD_DEVICE, + N_("cannot set specified sector size on disk %s"), + disk->name); + + grub_dprintf ("plainmount", "log_sector_size=%d, total_sectors=%" + PRIuGRUB_UINT64_T"\n", dev->log_sector_size, dev->total_sectors); + return GRUB_ERR_NONE; +} + +/* Hashes a password into a key and stores it with the cipher. */ +static grub_err_t +plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, + grub_uint8_t *key_data, grub_size_t key_size, + grub_size_t password_size) +{ + grub_uint8_t *derived_hash, *dh; + char *p; + unsigned int round, i, len, size; + grub_size_t alloc_size, sz; + grub_err_t err = GRUB_ERR_NONE; + + /* Support none (plain) hash */ + if (grub_strcmp (hash, "plain") == 0) + { + dev->hash = NULL; + return err; + } + + /* Hash argument was checked at main func */ + dev->hash = grub_crypto_lookup_md_by_name (hash); + len = dev->hash->mdlen; + + alloc_size = grub_max (password_size, key_size); + /* + * Allocate buffer for the password and for an added prefix character + * for each hash round ('alloc_size' may not be a multiple of 'len'). + */ + if (grub_add (alloc_size, (alloc_size / len), &sz) || + grub_add (sz, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while allocating size of password buffer")); + + p = grub_zalloc (sz); + derived_hash = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2); + if (p == NULL || derived_hash == NULL) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + goto fail; + } + dh = derived_hash; + + /* + * Hash password. Adapted from cryptsetup. + * https://gitlab.com/cryptsetup/cryptsetup/-/blob/main/lib/crypt_plain.c + */ + for (round = 0, size = alloc_size; size; round++, dh += len, size -= len) + { + for (i = 0; i < round; i++) + p[i] = 'A'; + + grub_memcpy (p + i, (char*) key_data, password_size); + + if (len > size) + len = size; + + grub_crypto_hash (dev->hash, dh, p, password_size + round); + } + grub_memcpy (key_data, derived_hash, key_size); + + fail: + grub_free (p); + grub_free (derived_hash); + return err; +} + +/* Read key material from keyfile */ +static grub_err_t +plainmount_configure_keyfile (char *keyfile, grub_uint8_t *key_data, + grub_size_t key_size, grub_size_t keyfile_offset) +{ + grub_file_t g_keyfile = grub_file_open (keyfile, GRUB_FILE_TYPE_NONE); + if (g_keyfile == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("cannot open keyfile %s"), + keyfile); + + if (grub_file_seek (g_keyfile, keyfile_offset) == (grub_off_t) - 1) + return grub_error (GRUB_ERR_FILE_READ_ERROR, + N_("cannot seek keyfile at offset %"PRIuGRUB_SIZE), + keyfile_offset); + + if (key_size > (g_keyfile->size - keyfile_offset)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Specified key size (%" + PRIuGRUB_SIZE") is too small for keyfile size (%" + PRIuGRUB_UINT64_T") and offset (%"PRIuGRUB_SIZE")"), + key_size, g_keyfile->size, keyfile_offset); + + if (grub_file_read (g_keyfile, key_data, key_size) != (grub_ssize_t) key_size) + return grub_error (GRUB_ERR_FILE_READ_ERROR, N_("error reading key file")); + return GRUB_ERR_NONE; +} + +/* Plainmount command entry point */ +static grub_err_t +grub_cmd_plainmount (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + grub_cryptodisk_t dev = NULL; + grub_disk_t disk = NULL; + const gcry_md_spec_t *gcry_hash; + char *diskname, *disklast = NULL, *cipher, *mode, *hash, *keyfile, *uuid; + grub_size_t len, key_size, sector_size, keyfile_offset = 0, password_size = 0; + grub_err_t err; + const char *p; + grub_uint8_t *key_data; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("device name required")); + + /* Check whether required arguments are specified */ + if (!state[OPTION_CIPHER].set || !state[OPTION_KEY_SIZE].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "cipher and key size must be set"); + if (!state[OPTION_HASH].set && !state[OPTION_KEYFILE].set) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "hash algorithm must be set"); + + /* Check hash */ + if (!state[OPTION_KEYFILE].set) + { + gcry_hash = grub_crypto_lookup_md_by_name (state[OPTION_HASH].arg); + if (!gcry_hash) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("couldn't load hash %s"), + state[OPTION_HASH].arg); + + if (gcry_hash->mdlen > GRUB_CRYPTODISK_MAX_KEYLEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("hash length %"PRIuGRUB_SIZE" exceeds maximum %d bits"), + gcry_hash->mdlen * GRUB_CHAR_BIT, + GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT); + } + + /* Check cipher mode */ + if (!grub_strchr (state[OPTION_CIPHER].arg,'-')) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid cipher mode, must be of format cipher-mode")); + + /* Check password size */ + if (state[OPTION_PASSWORD].set && grub_strlen (state[OPTION_PASSWORD].arg) > + GRUB_CRYPTODISK_MAX_PASSPHRASE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("password exceeds maximium size")); + + /* Check uuid length */ + if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) > + GRUB_CRYPTODISK_MAX_UUID_LENGTH - 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("specified UUID exceeds maximum size")); + if (state[OPTION_UUID].set && grub_strlen (state[OPTION_UUID].arg) == 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("specified UUID too short")); + + /* Parse plainmount arguments */ + grub_errno = GRUB_ERR_NONE; + keyfile_offset = state[OPTION_KEYFILE_OFFSET].set ? + grub_strtoull (state[OPTION_KEYFILE_OFFSET].arg, &p, 0) : 0; + if (state[OPTION_KEYFILE_OFFSET].set && + (state[OPTION_KEYFILE_OFFSET].arg[0] == '\0' || *p != '\0' || + grub_errno != GRUB_ERR_NONE)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized keyfile offset")); + + sector_size = state[OPTION_SECTOR_SIZE].set ? + grub_strtoull (state[OPTION_SECTOR_SIZE].arg, &p, 0) : + PLAINMOUNT_DEFAULT_SECTOR_SIZE; + if (state[OPTION_SECTOR_SIZE].set && (state[OPTION_SECTOR_SIZE].arg[0] == '\0' || + *p != '\0' || grub_errno != GRUB_ERR_NONE)) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized sector size")); + + /* Check key size */ + key_size = grub_strtoull (state[OPTION_KEY_SIZE].arg, &p, 0); + if (state[OPTION_KEY_SIZE].arg[0] == '\0' || *p != '\0' || + grub_errno != GRUB_ERR_NONE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized key size")); + if ((key_size % GRUB_CHAR_BIT) != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("key size is not multiple of %d bits"), GRUB_CHAR_BIT); + key_size = key_size / GRUB_CHAR_BIT; + if (key_size > GRUB_CRYPTODISK_MAX_KEYLEN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("key size %"PRIuGRUB_SIZE" exceeds maximum %d bits"), + key_size * GRUB_CHAR_BIT, + GRUB_CRYPTODISK_MAX_KEYLEN * GRUB_CHAR_BIT); + + /* Check disk sector size */ + if (sector_size < GRUB_DISK_SECTOR_SIZE) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("sector size -S must be at least %d"), + GRUB_DISK_SECTOR_SIZE); + if ((sector_size & (sector_size - 1)) != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("sector size -S %"PRIuGRUB_SIZE" is not power of 2"), + sector_size); + + /* Allocate all stuff here */ + hash = state[OPTION_HASH].set ? grub_strdup (state[OPTION_HASH].arg) : NULL; + cipher = grub_strdup (state[OPTION_CIPHER].arg); + keyfile = state[OPTION_KEYFILE].set ? + grub_strdup (state[OPTION_KEYFILE].arg) : NULL; + dev = grub_zalloc (sizeof *dev); + key_data = grub_zalloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + uuid = state[OPTION_UUID].set ? grub_strdup (state[OPTION_UUID].arg) : NULL; + if ((state[OPTION_HASH].set && hash == NULL) || cipher == NULL || dev == NULL || + (state[OPTION_KEYFILE].set && keyfile == NULL) || key_data == NULL || + (state[OPTION_UUID].set && uuid == NULL)) + { + err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory"); + goto fail; + } + + /* Copy user password from -p option */ + if (state[OPTION_PASSWORD].set) + { + /* + * Password from the '-p' option is limited to C-string. + * Arbitrary data keys are supported via keyfiles. + */ + password_size = grub_strlen (state[OPTION_PASSWORD].arg); + grub_strcpy ((char*) key_data, state[OPTION_PASSWORD].arg); + } + + /* Set cipher mode (tested above) */ + mode = grub_strchr (cipher,'-'); + *mode++ = '\0'; + + /* Check cipher */ + err = grub_cryptodisk_setcipher (dev, cipher, mode); + if (err != GRUB_ERR_NONE) + { + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s"), cipher); + else if (err == GRUB_ERR_BAD_ARGUMENT) + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid mode %s"), mode); + else + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid cipher %s or mode %s"), + cipher, mode); + goto fail; + } + + /* Open SOURCE disk */ + diskname = args[0]; + len = grub_strlen (diskname); + if (len && diskname[0] == '(' && diskname[len - 1] == ')') + { + disklast = &diskname[len - 1]; + *disklast = '\0'; + diskname++; + } + disk = grub_disk_open (diskname); + if (disk == NULL) + { + if (disklast) + *disklast = ')'; + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("cannot open disk %s"), diskname); + goto fail; + } + + /* Get password from console */ + if (!state[OPTION_KEYFILE].set && key_data[0] == '\0') + { + char *part = grub_partition_get_name (disk->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s: "), disk->name, + disk->partition != NULL ? "," : "", + part != NULL ? part : N_("UNKNOWN")); + grub_free (part); + + if (!grub_password_get ((char*) key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE - 1)) + { + err = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("error reading password")); + goto fail; + } + /* + * Password from interactive console is limited to C-string. + * Arbitrary data keys are supported via keyfiles. + */ + password_size = grub_strlen ((char*) key_data); + } + + /* Warn if hash and keyfile are both provided */ + if (state[OPTION_KEYFILE].set && state[OPTION_HASH].arg) + grub_printf_ (N_("warning: hash is ignored if keyfile is specified\n")); + + /* Warn if -p option is specified with keyfile */ + if (state[OPTION_PASSWORD].set && state[OPTION_KEYFILE].set) + grub_printf_ (N_("warning: password specified with -p option " + "is ignored if keyfile is provided\n")); + + /* Warn of -O is provided without keyfile */ + if (state[OPTION_KEYFILE_OFFSET].set && !state[OPTION_KEYFILE].set) + grub_printf_ (N_("warning: keyfile offset option -O " + "specified without keyfile option -d\n")); + + grub_dprintf ("plainmount", "parameters: cipher=%s, hash=%s, key_size=%" + PRIuGRUB_SIZE ", keyfile=%s, keyfile offset=%" PRIuGRUB_SIZE "\n", + cipher, hash, key_size, keyfile, keyfile_offset); + + err = plainmount_configure_sectors (dev, disk, sector_size); + if (err != GRUB_ERR_NONE) + goto fail; + + /* Configure keyfile or password */ + if (state[OPTION_KEYFILE].set) + err = plainmount_configure_keyfile (keyfile, key_data, key_size, keyfile_offset); + else + err = plainmount_configure_password (dev, hash, key_data, key_size, password_size); + if (err != GRUB_ERR_NONE) + goto fail; + + err = plainmount_setkey (dev, key_data, key_size); + if (err != GRUB_ERR_NONE) + goto fail; + + err = grub_cryptodisk_insert (dev, diskname, disk); + if (err != GRUB_ERR_NONE) + goto fail; + + dev->modname = "plainmount"; + dev->source_disk = disk; + plainmount_set_uuid (dev, uuid); + + fail: + grub_free (hash); + grub_free (cipher); + grub_free (keyfile); + grub_free (key_data); + grub_free (uuid); + if (err != GRUB_ERR_NONE && disk != NULL) + grub_disk_close (disk); + if (err != GRUB_ERR_NONE) + grub_free (dev); + return err; +} + +static grub_extcmd_t cmd; +GRUB_MOD_INIT (plainmount) +{ + cmd = grub_register_extcmd ("plainmount", grub_cmd_plainmount, 0, + N_("-c cipher -s key-size [-h hash] [-S sector-size]" + " [-o offset] [-p password] [-u uuid] " + " [[-d keyfile] [-O keyfile offset]] "), + N_("Open partition encrypted in plain mode."), + options); +} + +GRUB_MOD_FINI (plainmount) +{ + grub_unregister_extcmd (cmd); +} diff --git a/disk/raid5_recover.c b/grub-core/disk/raid5_recover.c similarity index 76% rename from disk/raid5_recover.c rename to grub-core/disk/raid5_recover.c index 31cef88b1..4ace9172e 100644 --- a/disk/raid5_recover.c +++ b/grub-core/disk/raid5_recover.c @@ -22,11 +22,14 @@ #include #include #include -#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static grub_err_t -grub_raid5_recover (struct grub_raid_array *array, int disknr, - char *buf, grub_disk_addr_t sector, int size) +grub_raid5_recover (struct grub_diskfilter_segment *array, int disknr, + char *buf, grub_disk_addr_t sector, grub_size_t size) { char *buf2; int i; @@ -38,14 +41,15 @@ grub_raid5_recover (struct grub_raid_array *array, int disknr, grub_memset (buf, 0, size); - for (i = 0; i < (int) array->total_devs; i++) + for (i = 0; i < (int) array->node_count; i++) { grub_err_t err; if (i == disknr) continue; - err = grub_disk_read (array->device[i], sector, 0, size, buf2); + err = grub_diskfilter_read_node (&array->nodes[i], sector, + size >> GRUB_DISK_SECTOR_BITS, buf2); if (err) { @@ -53,7 +57,7 @@ grub_raid5_recover (struct grub_raid_array *array, int disknr, return err; } - grub_raid_block_xor (buf, buf2, size); + grub_crypto_xor (buf, buf, buf2, size); } grub_free (buf2); diff --git a/grub-core/disk/raid6_recover.c b/grub-core/disk/raid6_recover.c new file mode 100644 index 000000000..75fe464a4 --- /dev/null +++ b/grub-core/disk/raid6_recover.c @@ -0,0 +1,218 @@ +/* raid6_recover.c - module to recover from faulty RAID6 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* x**y. */ +static grub_uint8_t powx[255 * 2]; +/* Such an s that x**s = y */ +static unsigned powx_inv[256]; +static const grub_uint8_t poly = 0x1d; + +static void +grub_raid_block_mulx (unsigned mul, char *buf, grub_size_t size) +{ + grub_size_t i; + grub_uint8_t *p; + + p = (grub_uint8_t *) buf; + for (i = 0; i < size; i++, p++) + if (*p) + *p = powx[mul + powx_inv[*p]]; +} + +static void +grub_raid6_init_table (void) +{ + unsigned i; + + grub_uint8_t cur = 1; + for (i = 0; i < 255; i++) + { + powx[i] = cur; + powx[i + 255] = cur; + powx_inv[cur] = i; + if (cur & 0x80) + cur = (cur << 1) ^ poly; + else + cur <<= 1; + } +} + +static unsigned +mod_255 (unsigned x) +{ + while (x > 0xff) + x = (x >> 8) + (x & 0xff); + if (x == 0xff) + return 0; + return x; +} + +static grub_err_t +raid6_recover_read_node (void *data, int disknr, + grub_uint64_t sector, + void *buf, grub_size_t size) +{ + struct grub_diskfilter_segment *array = data; + + return grub_diskfilter_read_node (&array->nodes[disknr], + (grub_disk_addr_t)sector, + size >> GRUB_DISK_SECTOR_BITS, buf); +} + +grub_err_t +grub_raid6_recover_gen (void *data, grub_uint64_t nstripes, int disknr, int p, + char *buf, grub_uint64_t sector, grub_size_t size, + int layout, raid_recover_read_t read_func) +{ + int i, q, pos; + int bad1 = -1, bad2 = -1; + char *pbuf = 0, *qbuf = 0; + + pbuf = grub_zalloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_zalloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) nstripes) + q = 0; + + pos = q + 1; + if (pos == (int) nstripes) + pos = 0; + + for (i = 0; i < (int) nstripes - 2; i++) + { + int c; + if (layout & GRUB_RAID_LAYOUT_MUL_FROM_POS) + c = pos; + else + c = i; + if (pos == disknr) + bad1 = c; + else + { + if (!read_func (data, pos, sector, buf, size)) + { + grub_crypto_xor (pbuf, pbuf, buf, size); + grub_raid_block_mulx (c, buf, size); + grub_crypto_xor (qbuf, qbuf, buf, size); + } + else + { + /* Too many bad devices */ + if (bad2 >= 0) + goto quit; + + bad2 = c; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + if (pos == (int) nstripes) + pos = 0; + } + + /* Invalid disknr or p */ + if (bad1 < 0) + goto quit; + + if (bad2 < 0) + { + /* One bad device */ + if (!read_func (data, p, sector, buf, size)) + { + grub_crypto_xor (buf, buf, pbuf, size); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (read_func (data, q, sector, buf, size)) + goto quit; + + grub_crypto_xor (buf, buf, qbuf, size); + grub_raid_block_mulx (255 - bad1, buf, + size); + } + else + { + /* Two bad devices */ + unsigned c; + + if (read_func (data, p, sector, buf, size)) + goto quit; + + grub_crypto_xor (pbuf, pbuf, buf, size); + + if (read_func (data, q, sector, buf, size)) + goto quit; + + grub_crypto_xor (qbuf, qbuf, buf, size); + + c = mod_255((255 ^ bad1) + + (255 ^ powx_inv[(powx[bad2 + (bad1 ^ 255)] ^ 1)])); + grub_raid_block_mulx (c, qbuf, size); + + c = mod_255((unsigned) bad2 + c); + grub_raid_block_mulx (c, pbuf, size); + + grub_crypto_xor (pbuf, pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +static grub_err_t +grub_raid6_recover (struct grub_diskfilter_segment *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, grub_size_t size) +{ + return grub_raid6_recover_gen (array, array->node_count, disknr, p, buf, + sector, size << GRUB_DISK_SECTOR_BITS, + array->layout, raid6_recover_read_node); +} + +GRUB_MOD_INIT(raid6rec) +{ + grub_raid6_init_table (); + grub_raid6_recover_func = grub_raid6_recover; +} + +GRUB_MOD_FINI(raid6rec) +{ + grub_raid6_recover_func = 0; +} diff --git a/grub-core/disk/scsi.c b/grub-core/disk/scsi.c new file mode 100644 index 000000000..1c3865fd2 --- /dev/null +++ b/grub-core/disk/scsi.c @@ -0,0 +1,764 @@ +/* scsi.c - scsi support. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +static grub_scsi_dev_t grub_scsi_dev_list; + +const char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5] = { + [GRUB_SCSI_SUBSYSTEM_USBMS] = "usb", + [GRUB_SCSI_SUBSYSTEM_PATA] = "ata", + [GRUB_SCSI_SUBSYSTEM_AHCI] = "ahci" +}; + +void +grub_scsi_dev_register (grub_scsi_dev_t dev) +{ + dev->next = grub_scsi_dev_list; + grub_scsi_dev_list = dev; +} + +void +grub_scsi_dev_unregister (grub_scsi_dev_t dev) +{ + grub_scsi_dev_t *p, q; + + for (p = &grub_scsi_dev_list, q = *p; q; p = &(q->next), q = q->next) + if (q == dev) + { + *p = q->next; + break; + } +} + + +/* Check result of previous operation. */ +static grub_err_t +grub_scsi_request_sense (grub_scsi_t scsi) +{ + struct grub_scsi_request_sense rs; + struct grub_scsi_request_sense_data rsd; + grub_err_t err; + + rs.opcode = grub_scsi_cmd_request_sense; + rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rs.reserved1 = 0; + rs.reserved2 = 0; + rs.alloc_length = 0x12; /* XXX: Hardcoded for now */ + rs.control = 0; + grub_memset (rs.pad, 0, sizeof(rs.pad)); + + err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs, + sizeof (rsd), (char *) &rsd); + if (err) + return err; + + return GRUB_ERR_NONE; +} +/* Self commenting... */ +static grub_err_t +grub_scsi_test_unit_ready (grub_scsi_t scsi) +{ + struct grub_scsi_test_unit_ready tur; + grub_err_t err; + grub_err_t err_sense; + + tur.opcode = grub_scsi_cmd_test_unit_ready; + tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + tur.reserved1 = 0; + tur.reserved2 = 0; + tur.reserved3 = 0; + tur.control = 0; + grub_memset (tur.pad, 0, sizeof(tur.pad)); + + err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur, + 0, NULL); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + return GRUB_ERR_NONE; +} + +/* Determine if the device is removable and the type of the device + SCSI. */ +static grub_err_t +grub_scsi_inquiry (grub_scsi_t scsi) +{ + struct grub_scsi_inquiry iq; + struct grub_scsi_inquiry_data iqd; + grub_err_t err; + grub_err_t err_sense; + + iq.opcode = grub_scsi_cmd_inquiry; + iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + iq.page = 0; + iq.reserved = 0; + iq.alloc_length = 0x24; /* XXX: Hardcoded for now */ + iq.control = 0; + grub_memset (iq.pad, 0, sizeof(iq.pad)); + + err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq, + sizeof (iqd), (char *) &iqd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + scsi->devtype = iqd.devtype & GRUB_SCSI_DEVTYPE_MASK; + scsi->removable = iqd.rmb >> GRUB_SCSI_REMOVABLE_BIT; + + return GRUB_ERR_NONE; +} + +/* Read the capacity and block size of SCSI. */ +static grub_err_t +grub_scsi_read_capacity10 (grub_scsi_t scsi) +{ + struct grub_scsi_read_capacity10 rc; + struct grub_scsi_read_capacity10_data rcd; + grub_err_t err; + grub_err_t err_sense; + + rc.opcode = grub_scsi_cmd_read_capacity10; + rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rc.logical_block_addr = 0; + rc.reserved1 = 0; + rc.reserved2 = 0; + rc.PMI = 0; + rc.control = 0; + rc.pad = 0; + + err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, + sizeof (rcd), (char *) &rcd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; +/* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + scsi->last_block = grub_be_to_cpu32 (rcd.last_block); + scsi->blocksize = grub_be_to_cpu32 (rcd.blocksize); + + return GRUB_ERR_NONE; +} + +/* Read the capacity and block size of SCSI. */ +static grub_err_t +grub_scsi_read_capacity16 (grub_scsi_t scsi) +{ + struct grub_scsi_read_capacity16 rc; + struct grub_scsi_read_capacity16_data rcd; + grub_err_t err; + grub_err_t err_sense; + + rc.opcode = grub_scsi_cmd_read_capacity16; + rc.lun = (scsi->lun << GRUB_SCSI_LUN_SHIFT) | 0x10; + rc.logical_block_addr = 0; + rc.alloc_len = grub_cpu_to_be32_compile_time (sizeof (rcd)); + rc.PMI = 0; + rc.control = 0; + + err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc, + sizeof (rcd), (char *) &rcd); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; +/* err_sense is ignored for now and Request Sense Data also... */ + + if (err) + return err; + + scsi->last_block = grub_be_to_cpu64 (rcd.last_block); + scsi->blocksize = grub_be_to_cpu32 (rcd.blocksize); + + return GRUB_ERR_NONE; +} + +/* Send a SCSI request for DISK: read SIZE sectors starting with + sector SECTOR to BUF. */ +static grub_err_t +grub_scsi_read10 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_read10 rd; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + rd.opcode = grub_scsi_cmd_read10; + rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rd.lba = grub_cpu_to_be32 (sector); + rd.reserved = 0; + rd.size = grub_cpu_to_be16 (size); + rd.reserved2 = 0; + rd.pad = 0; + + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} + +/* Send a SCSI request for DISK: read SIZE sectors starting with + sector SECTOR to BUF. */ +static grub_err_t +grub_scsi_read12 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_read12 rd; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + rd.opcode = grub_scsi_cmd_read12; + rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rd.lba = grub_cpu_to_be32 (sector); + rd.size = grub_cpu_to_be32 (size); + rd.reserved = 0; + rd.control = 0; + + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} + +/* Send a SCSI request for DISK: read SIZE sectors starting with + sector SECTOR to BUF. */ +static grub_err_t +grub_scsi_read16 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_read16 rd; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + rd.opcode = grub_scsi_cmd_read16; + rd.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + rd.lba = grub_cpu_to_be64 (sector); + rd.size = grub_cpu_to_be32 (size); + rd.reserved = 0; + rd.control = 0; + + err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} + +/* Send a SCSI request for DISK: write the data stored in BUF to SIZE + sectors starting with SECTOR. */ +static grub_err_t +grub_scsi_write10 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_write10 wr; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + wr.opcode = grub_scsi_cmd_write10; + wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + wr.lba = grub_cpu_to_be32 (sector); + wr.reserved = 0; + wr.size = grub_cpu_to_be16 (size); + wr.reserved2 = 0; + wr.pad = 0; + + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} + +#if 0 + +/* Send a SCSI request for DISK: write the data stored in BUF to SIZE + sectors starting with SECTOR. */ +static grub_err_t +grub_scsi_write12 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_write12 wr; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + wr.opcode = grub_scsi_cmd_write12; + wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + wr.lba = grub_cpu_to_be32 (sector); + wr.size = grub_cpu_to_be32 (size); + wr.reserved = 0; + wr.control = 0; + + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} +#endif + +/* Send a SCSI request for DISK: write the data stored in BUF to SIZE + sectors starting with SECTOR. */ +static grub_err_t +grub_scsi_write16 (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + grub_scsi_t scsi; + struct grub_scsi_write16 wr; + grub_err_t err; + grub_err_t err_sense; + + scsi = disk->data; + + wr.opcode = grub_scsi_cmd_write16; + wr.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT; + wr.lba = grub_cpu_to_be64 (sector); + wr.size = grub_cpu_to_be32 (size); + wr.reserved = 0; + wr.control = 0; + + err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf); + + /* Each SCSI command should be followed by Request Sense. + If not so, many devices STALLs or definitely freezes. */ + err_sense = grub_scsi_request_sense (scsi); + if (err_sense != GRUB_ERR_NONE) + grub_errno = err; + /* err_sense is ignored for now and Request Sense Data also... */ + + return err; +} + + + +/* Context for grub_scsi_iterate. */ +struct grub_scsi_iterate_ctx +{ + grub_disk_dev_iterate_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_scsi_iterate. */ +static int +scsi_iterate (int id, int bus, int luns, void *data) +{ + struct grub_scsi_iterate_ctx *ctx = data; + int i; + + /* In case of a single LUN, just return `usbX'. */ + if (luns == 1) + { + char *sname; + int ret; + sname = grub_xasprintf ("%s%d", grub_scsi_names[id], bus); + if (!sname) + return 1; + ret = ctx->hook (sname, ctx->hook_data); + grub_free (sname); + return ret; + } + + /* In case of multiple LUNs, every LUN will get a prefix to + distinguish it. */ + for (i = 0; i < luns; i++) + { + char *sname; + int ret; + sname = grub_xasprintf ("%s%d%c", grub_scsi_names[id], bus, 'a' + i); + if (!sname) + return 1; + ret = ctx->hook (sname, ctx->hook_data); + grub_free (sname); + if (ret) + return 1; + } + return 0; +} + +static int +grub_scsi_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + struct grub_scsi_iterate_ctx ctx = { hook, hook_data }; + grub_scsi_dev_t p; + + for (p = grub_scsi_dev_list; p; p = p->next) + if (p->iterate && (p->iterate) (scsi_iterate, &ctx, pull)) + return 1; + + return 0; +} + +static grub_err_t +grub_scsi_open (const char *name, grub_disk_t disk) +{ + grub_scsi_dev_t p; + grub_scsi_t scsi; + grub_err_t err; + int lun, bus; + grub_uint64_t maxtime; + const char *nameend; + unsigned id; + + nameend = name + grub_strlen (name) - 1; + /* Try to detect a LUN ('a'-'z'), otherwise just use the first + LUN. */ + if (nameend >= name && *nameend >= 'a' && *nameend <= 'z') + { + lun = *nameend - 'a'; + nameend--; + } + else + lun = 0; + + while (nameend >= name && grub_isdigit (*nameend)) + nameend--; + + if (!nameend[1] || !grub_isdigit (nameend[1])) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); + + bus = grub_strtoul (nameend + 1, 0, 0); + + scsi = grub_malloc (sizeof (*scsi)); + if (! scsi) + return grub_errno; + + for (id = 0; id < ARRAY_SIZE (grub_scsi_names); id++) + if (grub_strncmp (grub_scsi_names[id], name, nameend - name) == 0) + break; + + if (id == ARRAY_SIZE (grub_scsi_names)) + { + grub_free (scsi); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); + } + + for (p = grub_scsi_dev_list; p; p = p->next) + { + if (p->open (id, bus, scsi)) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + disk->id = grub_make_scsi_id (id, bus, lun); + disk->data = scsi; + scsi->dev = p; + scsi->lun = lun; + scsi->bus = bus; + + grub_dprintf ("scsi", "dev opened\n"); + + err = grub_scsi_inquiry (scsi); + if (err) + { + grub_free (scsi); + grub_dprintf ("scsi", "inquiry failed\n"); + return err; + } + + grub_dprintf ("scsi", "inquiry: devtype=0x%02x removable=%d\n", + scsi->devtype, scsi->removable); + + /* Try to be conservative about the device types + supported. */ + if (scsi->devtype != grub_scsi_devtype_direct + && scsi->devtype != grub_scsi_devtype_cdrom) + { + grub_free (scsi); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "unknown SCSI device"); + } + + /* According to USB MS tests specification, issue Test Unit Ready + * until OK */ + maxtime = grub_get_time_ms () + 5000; /* It is safer value */ + do + { + /* Timeout is necessary - for example in case when we have + * universal card reader with more LUNs and we have only + * one card inserted (or none), so only one LUN (or none) + * will be ready - and we want not to hang... */ + if (grub_get_time_ms () > maxtime) + { + err = GRUB_ERR_READ_ERROR; + grub_free (scsi); + grub_dprintf ("scsi", "LUN is not ready - timeout\n"); + return err; + } + err = grub_scsi_test_unit_ready (scsi); + } + while (err == GRUB_ERR_READ_ERROR); + /* Reset grub_errno ! + * It is set to some error code in loop before... */ + grub_errno = GRUB_ERR_NONE; + + /* Read capacity of media */ + err = grub_scsi_read_capacity10 (scsi); + if (err) + { + grub_free (scsi); + grub_dprintf ("scsi", "READ CAPACITY10 failed\n"); + return err; + } + + if (scsi->last_block == 0xffffffff) + { + err = grub_scsi_read_capacity16 (scsi); + if (err) + { + grub_free (scsi); + grub_dprintf ("scsi", "READ CAPACITY16 failed\n"); + return err; + } + } + + disk->total_sectors = scsi->last_block + 1; + /* PATA doesn't support more than 32K reads. + Not sure about AHCI and USB. If it's confirmed that either of + them can do bigger reads reliably this value can be moved to 'scsi' + structure. */ + disk->max_agglomerate = 32768 >> (GRUB_DISK_SECTOR_BITS + + GRUB_DISK_CACHE_BITS); + + if (scsi->blocksize & (scsi->blocksize - 1) || !scsi->blocksize) + { + grub_error (GRUB_ERR_IO, "invalid sector size %d", + scsi->blocksize); + grub_free (scsi); + return grub_errno; + } + disk->log_sector_size = grub_log2ull (scsi->blocksize); + + grub_dprintf ("scsi", "last_block=%" PRIuGRUB_UINT64_T ", blocksize=%u\n", + scsi->last_block, scsi->blocksize); + grub_dprintf ("scsi", "Disk total sectors = %llu\n", + (unsigned long long) disk->total_sectors); + + return GRUB_ERR_NONE; + } + + grub_free (scsi); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk"); +} + +static void +grub_scsi_close (grub_disk_t disk) +{ + grub_scsi_t scsi; + + scsi = disk->data; + if (scsi->dev->close) + scsi->dev->close (scsi); + grub_free (scsi); +} + +static grub_err_t +grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_scsi_t scsi; + + scsi = disk->data; + + grub_err_t err; + /* Depending on the type, select a read function. */ + switch (scsi->devtype) + { + case grub_scsi_devtype_direct: + if (sector >> 32) + err = grub_scsi_read16 (disk, sector, size, buf); + else + err = grub_scsi_read10 (disk, sector, size, buf); + if (err) + return err; + break; + + case grub_scsi_devtype_cdrom: + if (sector >> 32) + err = grub_scsi_read16 (disk, sector, size, buf); + else + err = grub_scsi_read12 (disk, sector, size, buf); + if (err) + return err; + break; + } + + return GRUB_ERR_NONE; + +#if 0 /* Workaround - it works - but very slowly, from some reason + * unknown to me (specially on OHCI). Do not use it. */ + /* Split transfer requests to device sector size because */ + /* some devices are not able to transfer more than 512-1024 bytes */ + grub_err_t err = GRUB_ERR_NONE; + + for ( ; size; size--) + { + /* Depending on the type, select a read function. */ + switch (scsi->devtype) + { + case grub_scsi_devtype_direct: + err = grub_scsi_read10 (disk, sector, 1, buf); + break; + + case grub_scsi_devtype_cdrom: + err = grub_scsi_read12 (disk, sector, 1, buf); + break; + + default: /* This should not happen */ + return GRUB_ERR_READ_ERROR; + } + if (err) + return err; + sector++; + buf += scsi->blocksize; + } + + return err; +#endif +} + +static grub_err_t +grub_scsi_write (grub_disk_t disk, + grub_disk_addr_t sector, + grub_size_t size, + const char *buf) +{ + grub_scsi_t scsi; + + scsi = disk->data; + + if (scsi->devtype == grub_scsi_devtype_cdrom) + return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM")); + + grub_err_t err; + /* Depending on the type, select a read function. */ + switch (scsi->devtype) + { + case grub_scsi_devtype_direct: + if (sector >> 32) + err = grub_scsi_write16 (disk, sector, size, buf); + else + err = grub_scsi_write10 (disk, sector, size, buf); + if (err) + return err; + break; + } + + return GRUB_ERR_NONE; +} + + +static struct grub_disk_dev grub_scsi_dev = + { + .name = "scsi", + .id = GRUB_DISK_DEVICE_SCSI_ID, + .disk_iterate = grub_scsi_iterate, + .disk_open = grub_scsi_open, + .disk_close = grub_scsi_close, + .disk_read = grub_scsi_read, + .disk_write = grub_scsi_write, + .next = 0 + }; + +GRUB_MOD_INIT(scsi) +{ + grub_disk_dev_register (&grub_scsi_dev); +} + +GRUB_MOD_FINI(scsi) +{ + grub_disk_dev_unregister (&grub_scsi_dev); +} diff --git a/grub-core/disk/uboot/ubootdisk.c b/grub-core/disk/uboot/ubootdisk.c new file mode 100644 index 000000000..0e918d4d1 --- /dev/null +++ b/grub-core/disk/uboot/ubootdisk.c @@ -0,0 +1,305 @@ +/* ubootdisk.c - disk subsystem support for U-Boot platforms */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ubootdisk_data *hd_devices; +static int hd_num; +static int hd_max; + +/* + * grub_ubootdisk_register(): + * Called for each disk device enumerated as part of U-Boot initialization + * code. + */ +grub_err_t +grub_ubootdisk_register (struct device_info *newdev) +{ + struct ubootdisk_data *d; + +#define STOR_TYPE(x) ((x) & 0x0ff0) + switch (STOR_TYPE (newdev->type)) + { + case DT_STOR_IDE: + case DT_STOR_SATA: + case DT_STOR_SCSI: + case DT_STOR_MMC: + case DT_STOR_USB: + /* hd */ + if (hd_num == hd_max) + { + int new_num; + new_num = (hd_max ? hd_max * 2 : 1); + d = grub_realloc(hd_devices, + sizeof (struct ubootdisk_data) * new_num); + if (!d) + return grub_errno; + hd_devices = d; + hd_max = new_num; + } + + d = &hd_devices[hd_num]; + hd_num++; + break; + default: + return GRUB_ERR_BAD_DEVICE; + break; + } + + d->dev = newdev; + d->cookie = newdev->cookie; + d->opencount = 0; + + return 0; +} + +/* + * uboot_disk_iterate(): + * Iterator over enumerated disk devices. + */ +static int +uboot_disk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + char buf[16]; + int count; + + switch (pull) + { + case GRUB_DISK_PULL_NONE: + /* "hd" - built-in mass-storage */ + for (count = 0 ; count < hd_num; count++) + { + grub_snprintf (buf, sizeof (buf) - 1, "hd%d", count); + grub_dprintf ("ubootdisk", "iterating %s\n", buf); + if (hook (buf, hook_data)) + return 1; + } + break; + default: + return 0; + } + + return 0; +} + +/* Helper function for uboot_disk_open. */ +static struct ubootdisk_data * +get_hd_device (int num) +{ + if (num < hd_num) + return &hd_devices[num]; + + return NULL; +} + +/* + * uboot_disk_open(): + * Opens a disk device already enumerated. + */ +static grub_err_t +uboot_disk_open (const char *name, struct grub_disk *disk) +{ + struct ubootdisk_data *d; + struct device_info *devinfo; + int num; + int retval; + + grub_dprintf ("ubootdisk", "Opening '%s'\n", name); + + num = grub_strtoul (name + 2, 0, 10); + if (grub_errno != GRUB_ERR_NONE) + { + grub_dprintf ("ubootdisk", "Opening '%s' failed, invalid number\n", + name); + goto fail; + } + + if (name[1] != 'd') + { + grub_dprintf ("ubootdisk", "Opening '%s' failed, invalid name\n", name); + goto fail; + } + + switch (name[0]) + { + case 'h': + d = get_hd_device (num); + break; + default: + goto fail; + } + + if (!d) + goto fail; + + /* + * Subsystems may call open on the same device recursively - but U-Boot + * does not deal with this. So simply keep track of number of calls and + * return success if already open. + */ + if (d->opencount > 0) + { + grub_dprintf ("ubootdisk", "(%s) already open\n", disk->name); + d->opencount++; + retval = 0; + } + else + { + retval = grub_uboot_dev_open (d->dev); + if (retval != 0) + goto fail; + d->opencount = 1; + } + + grub_dprintf ("ubootdisk", "cookie: 0x%08x\n", (grub_addr_t) d->cookie); + disk->id = (grub_addr_t) d->cookie; + + devinfo = d->dev; + + d->block_size = devinfo->di_stor.block_size; + if (d->block_size == 0) + return grub_error (GRUB_ERR_IO, "no block size"); + + disk->log_sector_size = grub_log2ull (d->block_size); + + grub_dprintf ("ubootdisk", "(%s) blocksize=%d, log_sector_size=%d\n", + disk->name, d->block_size, disk->log_sector_size); + + if (devinfo->di_stor.block_count) + disk->total_sectors = devinfo->di_stor.block_count; + else + disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; + + disk->data = d; + + return GRUB_ERR_NONE; + +fail: + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such device"); +} + +static void +uboot_disk_close (struct grub_disk *disk) +{ + struct ubootdisk_data *d; + int retval; + + d = disk->data; + + /* + * In mirror of open function, keep track of number of calls to close and + * send on to U-Boot only when opencount would decrease to 0. + */ + if (d->opencount > 1) + { + grub_dprintf ("ubootdisk", "Closed (%s)\n", disk->name); + + d->opencount--; + } + else if (d->opencount == 1) + { + retval = grub_uboot_dev_close (d->dev); + d->opencount--; + grub_dprintf ("ubootdisk", "closed %s (%d)\n", disk->name, retval); + } + else + { + grub_dprintf ("ubootdisk", "device %s not open!\n", disk->name); + } +} + +/* + * uboot_disk_read(): + * Called from within disk subsystem to read a sequence of blocks into the + * disk cache. Maps directly on top of U-Boot API, only wrap in some error + * handling. + */ +static grub_err_t +uboot_disk_read (struct grub_disk *disk, + grub_disk_addr_t offset, grub_size_t numblocks, char *buf) +{ + struct ubootdisk_data *d; + grub_size_t real_size; + int retval; + + d = disk->data; + + retval = grub_uboot_dev_read (d->dev, buf, numblocks, offset, &real_size); + grub_dprintf ("ubootdisk", + "retval=%d, numblocks=%d, real_size=%llu, sector=%llu\n", + retval, numblocks, (grub_uint64_t) real_size, + (grub_uint64_t) offset); + if (retval != 0) + return grub_error (GRUB_ERR_IO, "U-Boot disk read error"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +uboot_disk_write (struct grub_disk *disk, + grub_disk_addr_t offset, grub_size_t numblocks, const char *buf) +{ + struct ubootdisk_data *d; + int retval; + + d = disk->data; + + retval = grub_uboot_dev_write (d->dev, buf, numblocks, offset); + grub_dprintf ("ubootdisk", + "retval=%d, numblocks=%d, sector=%llu\n", + retval, numblocks, (grub_uint64_t) offset); + + if (retval != 0) + return grub_error (GRUB_ERR_IO, "U-Boot disk write error"); + + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_ubootdisk_dev = { + .name = "ubootdisk", + .id = GRUB_DISK_DEVICE_UBOOTDISK_ID, + .disk_iterate = uboot_disk_iterate, + .disk_open = uboot_disk_open, + .disk_close = uboot_disk_close, + .disk_read = uboot_disk_read, + .disk_write = uboot_disk_write, + .next = 0 +}; + +void +grub_ubootdisk_init (void) +{ + grub_disk_dev_register (&grub_ubootdisk_dev); +} + +void +grub_ubootdisk_fini (void) +{ + grub_disk_dev_unregister (&grub_ubootdisk_dev); +} diff --git a/grub-core/disk/usbms.c b/grub-core/disk/usbms.c new file mode 100644 index 000000000..b81e3ad9d --- /dev/null +++ b/grub-core/disk/usbms.c @@ -0,0 +1,660 @@ +/* usbms.c - USB Mass Storage 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_USBMS_DIRECTION_BIT 7 + +/* Length of CBI command should be always 12 bytes */ +#define GRUB_USBMS_CBI_CMD_SIZE 12 +/* CBI class-specific USB request ADSC - it sends CBI (scsi) command to + * device in DATA stage */ +#define GRUB_USBMS_CBI_ADSC_REQ 0x00 + +/* The USB Mass Storage Command Block Wrapper. */ +struct grub_usbms_cbw +{ + grub_uint32_t signature; + grub_uint32_t tag; + grub_uint32_t transfer_length; + grub_uint8_t flags; + grub_uint8_t lun; + grub_uint8_t length; + grub_uint8_t cbwcb[16]; +} GRUB_PACKED; + +struct grub_usbms_csw +{ + grub_uint32_t signature; + grub_uint32_t tag; + grub_uint32_t residue; + grub_uint8_t status; +} GRUB_PACKED; + +struct grub_usbms_dev +{ + struct grub_usb_device *dev; + + int luns; + + int config; + int interface; + struct grub_usb_desc_endp *in; + struct grub_usb_desc_endp *out; + + int subclass; + int protocol; + struct grub_usb_desc_endp *intrpt; +}; +typedef struct grub_usbms_dev *grub_usbms_dev_t; + +/* FIXME: remove limit. */ +#define MAX_USBMS_DEVICES 128 +static grub_usbms_dev_t grub_usbms_devices[MAX_USBMS_DEVICES]; +static int first_available_slot = 0; + +static grub_usb_err_t +grub_usbms_cbi_cmd (grub_usb_device_t dev, int interface, + grub_uint8_t *cbicb) +{ + return grub_usb_control_msg (dev, + GRUB_USB_REQTYPE_CLASS_INTERFACE_OUT, + GRUB_USBMS_CBI_ADSC_REQ, 0, interface, + GRUB_USBMS_CBI_CMD_SIZE, (char*)cbicb); +} + +static grub_usb_err_t +grub_usbms_cbi_reset (grub_usb_device_t dev, int interface) +{ + /* Prepare array with Command Block Reset (=CBR) */ + /* CBI specific communication reset command should be send to device + * via CBI USB class specific request ADCS */ + struct grub_cbi_reset + { + grub_uint8_t opcode; /* 0x1d = SEND DIAGNOSTIC */ + grub_uint8_t lun; /* 7-5 LUN, 4-0 flags - for CBR always = 0x04 */ + grub_uint8_t pad[10]; + /* XXX: There is collision between CBI and UFI specifications: + * CBI says 0xff, UFI says 0x00 ... probably it does + * not matter ... (?) */ + } cbicb = { 0x1d, 0x04, + { 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff } + }; + + return grub_usbms_cbi_cmd (dev, interface, (grub_uint8_t *)&cbicb); +} + +static grub_usb_err_t +grub_usbms_bo_reset (grub_usb_device_t dev, int interface) +{ + return grub_usb_control_msg (dev, 0x21, 255, 0, interface, 0, 0); +} + +static grub_usb_err_t +grub_usbms_reset (grub_usbms_dev_t dev) +{ + if (dev->protocol == GRUB_USBMS_PROTOCOL_BULK) + return grub_usbms_bo_reset (dev->dev, dev->interface); + else + return grub_usbms_cbi_reset (dev->dev, dev->interface); +} + +static void +grub_usbms_detach (grub_usb_device_t usbdev, int config, int interface) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++) + if (grub_usbms_devices[i] && grub_usbms_devices[i]->dev == usbdev + && grub_usbms_devices[i]->interface == interface + && grub_usbms_devices[i]->config == config) + { + grub_free (grub_usbms_devices[i]); + grub_usbms_devices[i] = 0; + } +} + +static int +grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno) +{ + struct grub_usb_desc_if *interf + = usbdev->config[configno].interf[interfno].descif; + int j; + grub_uint8_t luns = 0; + unsigned curnum; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + + grub_boot_time ("Attaching USB mass storage"); + + if (first_available_slot == ARRAY_SIZE (grub_usbms_devices)) + return 0; + + curnum = first_available_slot; + first_available_slot++; + + interf = usbdev->config[configno].interf[interfno].descif; + + if ((interf->subclass != GRUB_USBMS_SUBCLASS_BULK + /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */ + && interf->subclass != GRUB_USBMS_SUBCLASS_RBC + && interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 + && interf->subclass != GRUB_USBMS_SUBCLASS_UFI + && interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 ) + || (interf->protocol != GRUB_USBMS_PROTOCOL_BULK + && interf->protocol != GRUB_USBMS_PROTOCOL_CBI + && interf->protocol != GRUB_USBMS_PROTOCOL_CB)) + return 0; + + grub_usbms_devices[curnum] = grub_zalloc (sizeof (struct grub_usbms_dev)); + if (! grub_usbms_devices[curnum]) + return 0; + + grub_usbms_devices[curnum]->dev = usbdev; + grub_usbms_devices[curnum]->interface = interfno; + grub_usbms_devices[curnum]->subclass = interf->subclass; + grub_usbms_devices[curnum]->protocol = interf->protocol; + + grub_dprintf ("usbms", "alive\n"); + + /* Iterate over all endpoints of this interface, at least a + IN and OUT bulk endpoint are required. */ + for (j = 0; j < interf->endpointcnt; j++) + { + struct grub_usb_desc_endp *endp; + endp = &usbdev->config[0].interf[interfno].descendp[j]; + + if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) + /* Bulk IN endpoint. */ + grub_usbms_devices[curnum]->in = endp; + else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) + /* Bulk OUT endpoint. */ + grub_usbms_devices[curnum]->out = endp; + else if ((endp->endp_addr & 128) && (endp->attrib & 3) == 3) + /* Interrupt (IN) endpoint. */ + grub_usbms_devices[curnum]->intrpt = endp; + } + + if (!grub_usbms_devices[curnum]->in || !grub_usbms_devices[curnum]->out + || ((grub_usbms_devices[curnum]->protocol == GRUB_USBMS_PROTOCOL_CBI) + && !grub_usbms_devices[curnum]->intrpt)) + { + grub_free (grub_usbms_devices[curnum]); + grub_usbms_devices[curnum] = 0; + return 0; + } + + grub_dprintf ("usbms", "alive\n"); + + /* XXX: Activate the first configuration. */ + grub_usb_set_configuration (usbdev, 1); + + /* Query the amount of LUNs. */ + if (grub_usbms_devices[curnum]->protocol == GRUB_USBMS_PROTOCOL_BULK) + { /* Only Bulk only devices support Get Max LUN command */ + err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, interfno, 1, (char *) &luns); + + if (err) + { + /* In case of a stall, clear the stall. */ + if (err == GRUB_USB_ERR_STALL) + { + grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->in->endp_addr); + grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->out->endp_addr); + } + /* Just set the amount of LUNs to one. */ + grub_errno = GRUB_ERR_NONE; + grub_usbms_devices[curnum]->luns = 1; + } + else + /* luns = 0 means one LUN with ID 0 present ! */ + /* We get from device not number of LUNs but highest + * LUN number. LUNs are numbered from 0, + * i.e. number of LUNs is luns+1 ! */ + grub_usbms_devices[curnum]->luns = luns + 1; + } + else + /* XXX: Does CBI devices support multiple LUNs ? + * I.e., should we detect number of device's LUNs ? (How?) */ + grub_usbms_devices[curnum]->luns = 1; + + grub_dprintf ("usbms", "alive\n"); + + usbdev->config[configno].interf[interfno].detach_hook = grub_usbms_detach; + + grub_boot_time ("Attached USB mass storage"); + +#if 0 /* All this part should be probably deleted. + * This make trouble on some devices if they are not in + * Phase Error state - and there they should be not in such state... + * Bulk only mass storage reset procedure should be used only + * on place and in time when it is really necessary. */ + /* Reset recovery procedure */ + /* Bulk-Only Mass Storage Reset, after the reset commands + will be accepted. */ + grub_usbms_reset (usbdev, i); + grub_usb_clear_halt (usbdev, usbms->in->endp_addr); + grub_usb_clear_halt (usbdev, usbms->out->endp_addr); +#endif + + return 1; +} + + + +static int +grub_usbms_iterate (grub_scsi_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + unsigned i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + grub_usb_poll_devices (1); + + for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++) + if (grub_usbms_devices[i]) + { + if (hook (GRUB_SCSI_SUBSYSTEM_USBMS, i, grub_usbms_devices[i]->luns, + hook_data)) + return 1; + } + + return 0; +} + +static grub_err_t +grub_usbms_transfer_bo (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) +{ + struct grub_usbms_cbw cbw; + grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; + struct grub_usbms_csw status; + static grub_uint32_t tag = 0; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + grub_usb_err_t errCSW = GRUB_USB_ERR_NONE; + int retrycnt = 3 + 1; + + tag++; + + retry: + retrycnt--; + if (retrycnt == 0) + return grub_error (GRUB_ERR_IO, "USB Mass Storage stalled"); + + /* Setup the request. */ + grub_memset (&cbw, 0, sizeof (cbw)); + cbw.signature = grub_cpu_to_le32_compile_time (0x43425355); + cbw.tag = tag; + cbw.transfer_length = grub_cpu_to_le32 (size); + cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT; + cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */ + cbw.length = cmdsize; + grub_memcpy (cbw.cbwcb, cmd, cmdsize); + + /* Debug print of CBW content. */ + grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n", + cbw.signature, cbw.tag, cbw.transfer_length); + grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n", + cbw.flags, cbw.lun, cbw.length); + grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3], + cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7], + cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11], + cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]); + + /* Write the request. + * XXX: Error recovery is maybe still not fully correct. */ + err = grub_usb_bulk_write (dev->dev, dev->out, + sizeof (cbw), (char *) &cbw); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + { + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto CheckCSW; + } + goto retry; + } + + /* Read/write the data, (maybe) according to specification. */ + if (size && (read_write == 0)) + { + err = grub_usb_bulk_read (dev->dev, dev->in, size, buf); + grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + goto CheckCSW; + } + /* Debug print of received data. */ + grub_dprintf ("usb", "buf:\n"); + if (size <= 64) + { + unsigned i; + for (i = 0; i < size; i++) + grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]); + } + else + grub_dprintf ("usb", "Too much data for debug print...\n"); + } + else if (size) + { + err = grub_usb_bulk_write (dev->dev, dev->out, size, buf); + grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL); + grub_dprintf ("usb", "First 16 bytes of sent data:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + buf[ 0], buf[ 1], buf[ 2], buf[ 3], + buf[ 4], buf[ 5], buf[ 6], buf[ 7], + buf[ 8], buf[ 9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto CheckCSW; + } + /* Debug print of sent data. */ + if (size <= 256) + { + unsigned i; + for (i=0; idev, dev->in, + sizeof (status), (char *) &status); + if (errCSW) + { + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + errCSW = grub_usb_bulk_read (dev->dev, dev->in, + sizeof (status), (char *) &status); + if (errCSW) + { /* Bulk-only reset device. */ + grub_dprintf ("usb", "Bulk-only reset device - errCSW\n"); + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto retry; + } + } + + /* Debug print of CSW content. */ + grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n", + status.signature, status.tag, status.residue); + grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status); + + /* If phase error or not valid signature, do bulk-only reset device. */ + if ((status.status == 2) || + (status.signature != grub_cpu_to_le32_compile_time(0x53425355))) + { /* Bulk-only reset device. */ + grub_dprintf ("usb", "Bulk-only reset device - bad status\n"); + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + + goto retry; + } + + /* If "command failed" status or data transfer failed -> error */ + if ((status.status || err) && !read_write) + return grub_error (GRUB_ERR_READ_ERROR, + "error communication with USB Mass Storage device"); + else if ((status.status || err) && read_write) + return grub_error (GRUB_ERR_WRITE_ERROR, + "error communication with USB Mass Storage device"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_usbms_transfer_cbi (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) +{ + grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; + int retrycnt = 3 + 1; + grub_usb_err_t err = GRUB_USB_ERR_NONE; + grub_uint8_t cbicb[GRUB_USBMS_CBI_CMD_SIZE]; + grub_uint16_t status; + + retry: + retrycnt--; + if (retrycnt == 0) + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI failed"); + + /* Setup the request. */ + grub_memset (cbicb, 0, sizeof (cbicb)); + grub_memcpy (cbicb, cmd, + cmdsize >= GRUB_USBMS_CBI_CMD_SIZE + ? GRUB_USBMS_CBI_CMD_SIZE + : cmdsize); + + /* Debug print of CBIcb content. */ + grub_dprintf ("usb", "cbicb:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + cbicb[ 0], cbicb[ 1], cbicb[ 2], cbicb[ 3], + cbicb[ 4], cbicb[ 5], cbicb[ 6], cbicb[ 7], + cbicb[ 8], cbicb[ 9], cbicb[10], cbicb[11]); + + /* Write the request. + * XXX: Error recovery is maybe not correct. */ + err = grub_usbms_cbi_cmd (dev->dev, dev->interface, cbicb); + if (err) + { + grub_dprintf ("usb", "CBI cmdcb setup err=%d\n", err); + if (err == GRUB_USB_ERR_STALL) + { + /* Stall in this place probably means bad or unsupported + * command, so we will not try it again. */ + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI request failed"); + } + else if (dev->protocol == GRUB_USBMS_PROTOCOL_CBI) + { + /* Try to get status from interrupt pipe */ + err = grub_usb_bulk_read (dev->dev, dev->intrpt, + 2, (char*)&status); + grub_dprintf ("usb", "CBI cmdcb setup status: err=%d, status=0x%x\n", err, status); + } + /* Any other error could be transport problem, try it again */ + goto retry; + } + + /* Read/write the data, (maybe) according to specification. */ + if (size && (read_write == 0)) + { + err = grub_usb_bulk_read (dev->dev, dev->in, size, buf); + grub_dprintf ("usb", "read: %d\n", err); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + goto retry; + } + } + else if (size) + { + err = grub_usb_bulk_write (dev->dev, dev->out, size, buf); + grub_dprintf ("usb", "write: %d\n", err); + if (err) + { + if (err == GRUB_USB_ERR_STALL) + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + goto retry; + } + } + + /* XXX: It is not clear to me yet, how to check status of CBI + * data transfer on devices without interrupt pipe. + * AFAIK there is probably no status phase to indicate possibly + * bad transported data. + * Maybe we should do check on higher level, i.e. issue RequestSense + * command (we do it already in scsi.c) and check returned values + * (we do not it yet) - ? */ + if (dev->protocol == GRUB_USBMS_PROTOCOL_CBI) + { /* Check status in interrupt pipe */ + err = grub_usb_bulk_read (dev->dev, dev->intrpt, + 2, (char*)&status); + grub_dprintf ("usb", "read status: %d\n", err); + if (err) + { + /* Try to reset device, because it is probably not standard + * situation */ + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + grub_usb_clear_halt (dev->dev, dev->intrpt->endp_addr); + goto retry; + } + if (dev->subclass == GRUB_USBMS_SUBCLASS_UFI) + { + /* These devices should return bASC and bASCQ */ + if (status != 0) + /* Some error, currently we don't care what it is... */ + goto retry; + } + else if (dev->subclass == GRUB_USBMS_SUBCLASS_RBC) + { + /* XXX: I don't understand what returns RBC subclass devices, + * so I don't check it - maybe somebody helps ? */ + } + else + { + /* Any other device should return bType = 0 and some bValue */ + if (status & 0xff) + return grub_error (GRUB_ERR_IO, "USB Mass Storage CBI status type != 0"); + status = (status & 0x0300) >> 8; + switch (status) + { + case 0 : /* OK */ + break; + case 1 : /* Fail */ + goto retry; + break; + case 2 : /* Phase error */ + case 3 : /* Persistent Failure */ + grub_dprintf ("usb", "CBI reset device - phase error or persistent failure\n"); + grub_usbms_reset (dev); + grub_usb_clear_halt (dev->dev, dev->in->endp_addr); + grub_usb_clear_halt (dev->dev, dev->out->endp_addr); + grub_usb_clear_halt (dev->dev, dev->intrpt->endp_addr); + goto retry; + break; + } + } + } + + if (err) + return grub_error (GRUB_ERR_IO, "USB error %d", err); + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf, int read_write) +{ + grub_usbms_dev_t dev = (grub_usbms_dev_t) scsi->data; + + if (dev->protocol == GRUB_USBMS_PROTOCOL_BULK) + return grub_usbms_transfer_bo (scsi, cmdsize, cmd, size, buf, + read_write); + else + return grub_usbms_transfer_cbi (scsi, cmdsize, cmd, size, buf, + read_write); +} + +static grub_err_t +grub_usbms_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, char *buf) +{ + return grub_usbms_transfer (scsi, cmdsize, cmd, size, buf, 0); +} + +static grub_err_t +grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd, + grub_size_t size, const char *buf) +{ + return grub_usbms_transfer (scsi, cmdsize, cmd, size, (char *) buf, 1); +} + +static grub_err_t +grub_usbms_open (int id, int devnum, struct grub_scsi *scsi) +{ + if (id != GRUB_SCSI_SUBSYSTEM_USBMS) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "not USB Mass Storage device"); + + if (!grub_usbms_devices[devnum]) + grub_usb_poll_devices (1); + + if (!grub_usbms_devices[devnum]) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "unknown USB Mass Storage device"); + + scsi->data = grub_usbms_devices[devnum]; + scsi->luns = grub_usbms_devices[devnum]->luns; + + return GRUB_ERR_NONE; +} + +static struct grub_scsi_dev grub_usbms_dev = + { + .iterate = grub_usbms_iterate, + .open = grub_usbms_open, + .read = grub_usbms_read, + .write = grub_usbms_write + }; + +static struct grub_usb_attach_desc attach_hook = +{ + .class = GRUB_USB_CLASS_MASS_STORAGE, + .hook = grub_usbms_attach +}; + +GRUB_MOD_INIT(usbms) +{ + grub_usb_register_attach_hook_class (&attach_hook); + grub_scsi_dev_register (&grub_usbms_dev); +} + +GRUB_MOD_FINI(usbms) +{ + unsigned i; + for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++) + { + grub_usbms_devices[i]->dev->config[grub_usbms_devices[i]->config] + .interf[grub_usbms_devices[i]->interface].detach_hook = 0; + grub_usbms_devices[i]->dev->config[grub_usbms_devices[i]->config] + .interf[grub_usbms_devices[i]->interface].attached = 0; + } + grub_usb_unregister_attach_hook_class (&attach_hook); + grub_scsi_dev_unregister (&grub_usbms_dev); +} diff --git a/grub-core/disk/xen/xendisk.c b/grub-core/disk/xen/xendisk.c new file mode 100644 index 000000000..496f1ea7b --- /dev/null +++ b/grub-core/disk/xen/xendisk.c @@ -0,0 +1,483 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct virtdisk +{ + int handle; + char *fullname; + char *backend_dir; + char *frontend_dir; + struct blkif_sring *shared_page; + struct blkif_front_ring ring; + grub_xen_grant_t grant; + grub_xen_evtchn_t evtchn; + void *dma_page; + grub_xen_grant_t dma_grant; + struct virtdisk *compat_next; +}; + +#define xen_wmb() mb() +#define xen_mb() mb() + +static struct virtdisk *virtdisks; +static grub_size_t vdiskcnt; +struct virtdisk *compat_head; + +static int +grub_virtdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + grub_size_t i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (i = 0; i < vdiskcnt; i++) + if (hook (virtdisks[i].fullname, hook_data)) + return 1; + return 0; +} + +static grub_err_t +grub_virtdisk_open (const char *name, grub_disk_t disk) +{ + int i; + grub_uint32_t secsize; + char fdir[200]; + char *buf; + int num = -1; + struct virtdisk *vd; + + /* For compatibility with pv-grub legacy menu.lst accept hdX as disk name */ + if (name[0] == 'h' && name[1] == 'd' && name[2]) + { + num = grub_strtoul (name + 2, 0, 10); + if (grub_errno) + { + grub_errno = 0; + num = -1; + } + } + for (i = 0, vd = compat_head; vd; vd = vd->compat_next, i++) + if (i == num || grub_strcmp (name, vd->fullname) == 0) + break; + if (!vd) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a virtdisk"); + disk->data = vd; + disk->id = vd - virtdisks; + + grub_snprintf (fdir, sizeof (fdir), "%s/sectors", vd->backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + return grub_errno; + disk->total_sectors = grub_strtoull (buf, 0, 10); + if (grub_errno) + return grub_errno; + + grub_snprintf (fdir, sizeof (fdir), "%s/sector-size", vd->backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + return grub_errno; + secsize = grub_strtoull (buf, 0, 10); + if (grub_errno) + return grub_errno; + + if ((secsize & (secsize - 1)) || !secsize || secsize < 512 + || secsize > GRUB_XEN_PAGE_SIZE) + return grub_error (GRUB_ERR_IO, "unsupported sector size %d", secsize); + + disk->log_sector_size = grub_log2ull (secsize); + disk->total_sectors >>= disk->log_sector_size - 9; + + return GRUB_ERR_NONE; +} + +static void +grub_virtdisk_close (grub_disk_t disk __attribute__ ((unused))) +{ +} + +static grub_err_t +grub_virtdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + struct virtdisk *data = disk->data; + + while (size) + { + grub_size_t cur; + struct blkif_request *req; + struct blkif_response *resp; + int sta = 0; + struct evtchn_send send; + cur = size; + if (cur > (unsigned) (GRUB_XEN_PAGE_SIZE >> disk->log_sector_size)) + cur = GRUB_XEN_PAGE_SIZE >> disk->log_sector_size; + while (RING_FULL (&data->ring)) + grub_xen_sched_op (SCHEDOP_yield, 0); + req = RING_GET_REQUEST (&data->ring, data->ring.req_prod_pvt); + req->operation = BLKIF_OP_READ; + req->nr_segments = 1; + req->handle = data->handle; + req->id = 0; + req->sector_number = sector << (disk->log_sector_size - 9); + req->seg[0].gref = data->dma_grant; + req->seg[0].first_sect = 0; + req->seg[0].last_sect = (cur << (disk->log_sector_size - 9)) - 1; + data->ring.req_prod_pvt++; + RING_PUSH_REQUESTS (&data->ring); + mb (); + send.port = data->evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + + while (!RING_HAS_UNCONSUMED_RESPONSES (&data->ring)) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + mb (); + } + while (1) + { + int wtd; + RING_FINAL_CHECK_FOR_RESPONSES (&data->ring, wtd); + if (!wtd) + break; + resp = RING_GET_RESPONSE (&data->ring, data->ring.rsp_cons); + data->ring.rsp_cons++; + if (resp->status) + sta = resp->status; + } + if (sta) + return grub_error (GRUB_ERR_IO, "read failed"); + grub_memcpy (buf, data->dma_page, cur << disk->log_sector_size); + size -= cur; + sector += cur; + buf += cur << disk->log_sector_size; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_virtdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + struct virtdisk *data = disk->data; + + while (size) + { + grub_size_t cur; + struct blkif_request *req; + struct blkif_response *resp; + int sta = 0; + struct evtchn_send send; + cur = size; + if (cur > (unsigned) (GRUB_XEN_PAGE_SIZE >> disk->log_sector_size)) + cur = GRUB_XEN_PAGE_SIZE >> disk->log_sector_size; + + grub_memcpy (data->dma_page, buf, cur << disk->log_sector_size); + + while (RING_FULL (&data->ring)) + grub_xen_sched_op (SCHEDOP_yield, 0); + req = RING_GET_REQUEST (&data->ring, data->ring.req_prod_pvt); + req->operation = BLKIF_OP_WRITE; + req->nr_segments = 1; + req->handle = data->handle; + req->id = 0; + req->sector_number = sector << (disk->log_sector_size - 9); + req->seg[0].gref = data->dma_grant; + req->seg[0].first_sect = 0; + req->seg[0].last_sect = (cur << (disk->log_sector_size - 9)) - 1; + data->ring.req_prod_pvt++; + RING_PUSH_REQUESTS (&data->ring); + mb (); + send.port = data->evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + + while (!RING_HAS_UNCONSUMED_RESPONSES (&data->ring)) + { + grub_xen_sched_op (SCHEDOP_yield, 0); + mb (); + } + while (1) + { + int wtd; + RING_FINAL_CHECK_FOR_RESPONSES (&data->ring, wtd); + if (!wtd) + break; + resp = RING_GET_RESPONSE (&data->ring, data->ring.rsp_cons); + data->ring.rsp_cons++; + if (resp->status) + sta = resp->status; + } + if (sta) + return grub_error (GRUB_ERR_IO, "write failed"); + size -= cur; + sector += cur; + buf += cur << disk->log_sector_size; + } + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_virtdisk_dev = { + .name = "xen", + .id = GRUB_DISK_DEVICE_XEN, + .disk_iterate = grub_virtdisk_iterate, + .disk_open = grub_virtdisk_open, + .disk_close = grub_virtdisk_close, + .disk_read = grub_virtdisk_read, + .disk_write = grub_virtdisk_write, + .next = 0 +}; + +static int +count (const char *dir __attribute__ ((unused)), void *data) +{ + grub_size_t *ctr = data; + (*ctr)++; + + return 0; +} + +static int +fill (const char *dir, void *data) +{ + grub_size_t *ctr = data; + domid_t dom; + /* "dir" is just a number, at most 19 characters. */ + char fdir[200]; + char num[20]; + grub_err_t err; + void *buf; + struct evtchn_alloc_unbound alloc_unbound; + struct virtdisk **prev = &compat_head, *vd = compat_head; + + /* Shouldn't happen unles some hotplug happened. */ + if (vdiskcnt >= *ctr) + return 1; + virtdisks[vdiskcnt].handle = grub_strtoul (dir, 0, 10); + if (grub_errno) + { + grub_errno = 0; + return 0; + } + virtdisks[vdiskcnt].fullname = 0; + virtdisks[vdiskcnt].backend_dir = 0; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/backend", dir); + virtdisks[vdiskcnt].backend_dir = grub_xenstore_get_file (fdir, NULL); + if (!virtdisks[vdiskcnt].backend_dir) + goto out_fail_1; + + grub_snprintf (fdir, sizeof (fdir), "%s/dev", + virtdisks[vdiskcnt].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + { + grub_errno = 0; + virtdisks[vdiskcnt].fullname = grub_xasprintf ("xenid/%s", dir); + } + else + { + virtdisks[vdiskcnt].fullname = grub_xasprintf ("xen/%s", (char *) buf); + grub_free (buf); + } + if (!virtdisks[vdiskcnt].fullname) + goto out_fail_1; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/backend-id", dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + goto out_fail_1; + + dom = grub_strtoul (buf, 0, 10); + grub_free (buf); + if (grub_errno) + goto out_fail_1; + + virtdisks[vdiskcnt].shared_page = + grub_xen_alloc_shared_page (dom, &virtdisks[vdiskcnt].grant); + if (!virtdisks[vdiskcnt].shared_page) + goto out_fail_1; + + virtdisks[vdiskcnt].dma_page = + grub_xen_alloc_shared_page (dom, &virtdisks[vdiskcnt].dma_grant); + if (!virtdisks[vdiskcnt].dma_page) + goto out_fail_2; + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = dom; + + grub_xen_event_channel_op (EVTCHNOP_alloc_unbound, &alloc_unbound); + virtdisks[vdiskcnt].evtchn = alloc_unbound.port; + + SHARED_RING_INIT (virtdisks[vdiskcnt].shared_page); + FRONT_RING_INIT (&virtdisks[vdiskcnt].ring, virtdisks[vdiskcnt].shared_page, + GRUB_XEN_PAGE_SIZE); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/ring-ref", dir); + grub_snprintf (num, sizeof (num), "%u", virtdisks[vdiskcnt].grant); + err = grub_xenstore_write_file (fdir, num, grub_strlen (num)); + if (err) + goto out_fail_3; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/event-channel", dir); + grub_snprintf (num, sizeof (num), "%u", virtdisks[vdiskcnt].evtchn); + err = grub_xenstore_write_file (fdir, num, grub_strlen (num)); + if (err) + goto out_fail_3; + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/protocol", dir); + err = grub_xenstore_write_file (fdir, XEN_IO_PROTO_ABI_NATIVE, + grub_strlen (XEN_IO_PROTO_ABI_NATIVE)); + if (err) + goto out_fail_3; + + struct gnttab_dump_table dt; + dt.dom = DOMID_SELF; + grub_xen_grant_table_op (GNTTABOP_dump_table, (void *) &dt, 1); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s/state", dir); + err = grub_xenstore_write_file (fdir, "3", 1); + if (err) + goto out_fail_3; + + while (1) + { + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[vdiskcnt].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + if (!buf) + goto out_fail_3; + if (grub_strcmp (buf, "2") != 0) + break; + grub_free (buf); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_dprintf ("xen", "state=%s\n", (char *) buf); + grub_free (buf); + + grub_snprintf (fdir, sizeof (fdir), "device/vbd/%s", dir); + + virtdisks[vdiskcnt].frontend_dir = grub_strdup (fdir); + + /* For compatibility with pv-grub maintain linked list sorted by handle + value in increasing order. This allows mapping of (hdX) disk names + from legacy menu.lst */ + while (vd) + { + if (vd->handle > virtdisks[vdiskcnt].handle) + break; + prev = &vd->compat_next; + vd = vd->compat_next; + } + virtdisks[vdiskcnt].compat_next = vd; + *prev = &virtdisks[vdiskcnt]; + + vdiskcnt++; + return 0; + +out_fail_3: + grub_xen_free_shared_page (virtdisks[vdiskcnt].dma_page); +out_fail_2: + grub_xen_free_shared_page (virtdisks[vdiskcnt].shared_page); +out_fail_1: + grub_free (virtdisks[vdiskcnt].backend_dir); + grub_free (virtdisks[vdiskcnt].fullname); + + grub_errno = 0; + return 0; +} + +void +grub_xendisk_init (void) +{ + grub_size_t ctr = 0; + if (grub_xenstore_dir ("device/vbd", count, &ctr)) + grub_errno = 0; + + if (!ctr) + return; + + virtdisks = grub_calloc (ctr, sizeof (virtdisks[0])); + if (!virtdisks) + return; + if (grub_xenstore_dir ("device/vbd", fill, &ctr)) + grub_errno = 0; + + grub_disk_dev_register (&grub_virtdisk_dev); +} + +void +grub_xendisk_fini (void) +{ + char fdir[200]; + unsigned i; + + for (i = 0; i < vdiskcnt; i++) + { + char *buf; + struct evtchn_close close_op = {.port = virtdisks[i].evtchn }; + + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, "6", 1); + + while (1) + { + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[i].backend_dir); + buf = grub_xenstore_get_file (fdir, NULL); + grub_dprintf ("xen", "state=%s\n", (char *) buf); + + if (!buf || grub_strcmp (buf, "6") == 0) + break; + grub_free (buf); + grub_xen_sched_op (SCHEDOP_yield, 0); + } + grub_free (buf); + + grub_snprintf (fdir, sizeof (fdir), "%s/ring-ref", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, NULL, 0); + + grub_snprintf (fdir, sizeof (fdir), "%s/event-channel", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, NULL, 0); + + grub_xen_free_shared_page (virtdisks[i].dma_page); + grub_xen_free_shared_page (virtdisks[i].shared_page); + + grub_xen_event_channel_op (EVTCHNOP_close, &close_op); + + /* Prepare for handoff. */ + grub_snprintf (fdir, sizeof (fdir), "%s/state", + virtdisks[i].frontend_dir); + grub_xenstore_write_file (fdir, "1", 1); + } +} diff --git a/kern/mips/init.c b/grub-core/efiemu/i386/coredetect.c similarity index 74% rename from kern/mips/init.c rename to grub-core/efiemu/i386/coredetect.c index 5adcedcbb..a262b5328 100644 --- a/kern/mips/init.c +++ b/grub-core/efiemu/i386/coredetect.c @@ -16,20 +16,12 @@ * along with GRUB. If not, see . */ -#include -#include -#include +#include +#include +#include -void -grub_machine_set_prefix (void) +const char * +grub_efiemu_get_default_core_name (void) { - grub_env_set ("prefix", grub_prefix); -} - -extern char _end[]; - -grub_addr_t -grub_arch_modules_addr (void) -{ - return (grub_addr_t) _end; + return grub_cpuid_has_longmode ? "efiemu64.o" : "efiemu32.o"; } diff --git a/efiemu/i386/loadcore32.c b/grub-core/efiemu/i386/loadcore32.c similarity index 82% rename from efiemu/i386/loadcore32.c rename to grub-core/efiemu/i386/loadcore32.c index 24b63ec98..e746df8df 100644 --- a/efiemu/i386/loadcore32.c +++ b/grub-core/efiemu/i386/loadcore32.c @@ -23,6 +23,7 @@ #include #include #include +#include /* Check if EHDR is a valid ELF header. */ int @@ -86,23 +87,29 @@ grub_arch_efiemu_relocate_symbols32 (grub_efiemu_segment_t segs, switch (ELF32_R_TYPE (rel->r_info)) { case R_386_32: - if ((err = grub_efiemu_write_value - (addr, sym.off + *addr, sym.handle, 0, - seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + err = grub_efiemu_write_value (addr, sym.off + *addr, + sym.handle, 0, + seg->ptv_rel_needed, + sizeof (grub_uint32_t)); + if (err) return err; break; case R_386_PC32: - if ((err = grub_efiemu_write_value - (addr, sym.off + *addr - rel->r_offset - - seg->off, sym.handle, seg->handle, - seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + err = grub_efiemu_write_value (addr, sym.off + *addr + - rel->r_offset + - seg->off, sym.handle, + seg->handle, + seg->ptv_rel_needed, + sizeof (grub_uint32_t)); + if (err) return err; break; default: - return grub_error (GRUB_ERR_BAD_OS, - "unrecognised relocation"); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); } } } diff --git a/efiemu/i386/loadcore64.c b/grub-core/efiemu/i386/loadcore64.c similarity index 74% rename from efiemu/i386/loadcore64.c rename to grub-core/efiemu/i386/loadcore64.c index a69279077..ae476ef2c 100644 --- a/efiemu/i386/loadcore64.c +++ b/grub-core/efiemu/i386/loadcore64.c @@ -23,6 +23,7 @@ #include #include #include +#include /* Check if EHDR is a valid ELF header. */ int @@ -87,30 +88,47 @@ grub_arch_efiemu_relocate_symbols64 (grub_efiemu_segment_t segs, switch (ELF64_R_TYPE (rel->r_info)) { case R_X86_64_64: - if ((err = grub_efiemu_write_value - (addr, *addr64 + rel->r_addend + sym.off, sym.handle, - 0, seg->ptv_rel_needed, sizeof (grub_uint64_t)))) + err = grub_efiemu_write_value (addr, + *addr64 + rel->r_addend + + sym.off, sym.handle, + 0, seg->ptv_rel_needed, + sizeof (grub_uint64_t)); + if (err) return err; break; case R_X86_64_PC32: - if ((err = grub_efiemu_write_value - (addr, *addr32 + rel->r_addend + sym.off - - rel->r_offset - seg->off, sym.handle, seg->handle, - seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + case R_X86_64_PLT32: + err = grub_efiemu_write_value (addr, + *addr32 + rel->r_addend + + sym.off + - rel->r_offset - seg->off, + sym.handle, seg->handle, + seg->ptv_rel_needed, + sizeof (grub_uint32_t)); + if (err) return err; break; case R_X86_64_32: case R_X86_64_32S: - if ((err = grub_efiemu_write_value - (addr, *addr32 + rel->r_addend + sym.off, sym.handle, - 0, seg->ptv_rel_needed, sizeof (grub_uint32_t)))) + err = grub_efiemu_write_value (addr, + *addr32 + rel->r_addend + + sym.off, sym.handle, + 0, seg->ptv_rel_needed, + sizeof (grub_uint32_t)); + if (err) return err; break; default: - return grub_error (GRUB_ERR_BAD_OS, - "unrecognised relocation"); + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } } } } diff --git a/grub-core/efiemu/i386/nocfgtables.c b/grub-core/efiemu/i386/nocfgtables.c new file mode 100644 index 000000000..775f1d03a --- /dev/null +++ b/grub-core/efiemu/i386/nocfgtables.c @@ -0,0 +1,30 @@ +/* Register SMBIOS and ACPI tables. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include + +grub_err_t +grub_machine_efiemu_init_tables (void) +{ + return GRUB_ERR_NONE; +} diff --git a/efiemu/i386/pc/cfgtables.c b/grub-core/efiemu/i386/pc/cfgtables.c similarity index 71% rename from efiemu/i386/pc/cfgtables.c rename to grub-core/efiemu/i386/pc/cfgtables.c index 9c6b4e55e..056ec0bc9 100644 --- a/efiemu/i386/pc/cfgtables.c +++ b/grub-core/efiemu/i386/pc/cfgtables.c @@ -19,20 +19,19 @@ #include #include -#include #include #include #include +#include grub_err_t -grub_machine_efiemu_init_tables () +grub_machine_efiemu_init_tables (void) { - grub_uint8_t *ptr; void *table; grub_err_t err; - grub_efi_guid_t smbios = GRUB_EFI_SMBIOS_TABLE_GUID; - grub_efi_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID; - grub_efi_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID; + static grub_guid_t smbios = GRUB_EFI_SMBIOS_TABLE_GUID; + static grub_guid_t acpi20 = GRUB_EFI_ACPI_20_TABLE_GUID; + static grub_guid_t acpi = GRUB_EFI_ACPI_TABLE_GUID; err = grub_efiemu_unregister_configuration_table (smbios); if (err) @@ -58,17 +57,11 @@ grub_machine_efiemu_init_tables () if (err) return err; } - - for (ptr = (grub_uint8_t *) 0xf0000; ptr < (grub_uint8_t *) 0x100000; - ptr += 16) - if (grub_memcmp (ptr, "_SM_", 4) == 0 - && grub_byte_checksum (ptr, *(ptr + 5)) == 0) - break; - - if (ptr < (grub_uint8_t *) 0x100000) + table = grub_smbios_get_eps (); + if (table) { - grub_dprintf ("efiemu", "Registering SMBIOS\n"); - if ((err = grub_efiemu_register_configuration_table (smbios, 0, 0, ptr))) + err = grub_efiemu_register_configuration_table (smbios, 0, 0, table); + if (err) return err; } diff --git a/efiemu/loadcore.c b/grub-core/efiemu/loadcore.c similarity index 83% rename from efiemu/loadcore.c rename to grub-core/efiemu/loadcore.c index 4bf26ee44..2b924623f 100644 --- a/efiemu/loadcore.c +++ b/grub-core/efiemu/loadcore.c @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include /* ELF symbols and their values */ static struct grub_efiemu_elf_sym *grub_efiemu_elfsyms = 0; @@ -120,9 +120,9 @@ grub_efiemu_get_string (unsigned offset, const Elf_Ehdr *e) unsigned i; Elf_Shdr *s; - for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff); + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; - i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) if (s->sh_type == SHT_STRTAB && offset < s->sh_size) return (char *) e + s->sh_offset + offset; return 0; @@ -135,9 +135,9 @@ grub_efiemu_init_segments (grub_efiemu_segment_t *segs, const Elf_Ehdr *e) unsigned i; Elf_Shdr *s; - for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff); + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; - i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) { if (s->sh_flags & SHF_ALLOC) { @@ -154,7 +154,10 @@ grub_efiemu_init_segments (grub_efiemu_segment_t *segs, const Elf_Ehdr *e) s->sh_flags & SHF_EXECINSTR ? GRUB_EFI_RUNTIME_SERVICES_CODE : GRUB_EFI_RUNTIME_SERVICES_DATA); if (seg->handle < 0) - return grub_errno; + { + grub_free (seg); + return grub_errno; + } seg->off = 0; } @@ -194,11 +197,11 @@ grub_efiemu_count_symbols (const Elf_Ehdr *e) break; if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_OS, "no symbol table"); + return grub_error (GRUB_ERR_BAD_OS, N_("no symbol table")); grub_efiemu_nelfsyms = (unsigned) s->sh_size / (unsigned) s->sh_entsize; grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *) - grub_malloc (sizeof (struct grub_efiemu_elf_sym) * grub_efiemu_nelfsyms); + grub_calloc (grub_efiemu_nelfsyms, sizeof (struct grub_efiemu_elf_sym)); /* Relocators */ for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); @@ -231,7 +234,7 @@ grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e) break; if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_OS, "no symbol table"); + return grub_error (GRUB_ERR_BAD_OS, N_("no symbol table")); sym = (Elf_Sym *) ((char *) e + s->sh_offset); size = s->sh_size; @@ -257,7 +260,8 @@ grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e) /* Resolve a global symbol. */ if (sym->st_name != 0 && sym->st_shndx == 0) { - if ((err = grub_efiemu_resolve_symbol (name, &handle, &off))) + err = grub_efiemu_resolve_symbol (name, &handle, &off); + if (err) return err; grub_efiemu_elfsyms[i].handle = handle; grub_efiemu_elfsyms[i].off = off; @@ -267,34 +271,43 @@ grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e) break; case STT_OBJECT: - if ((err = grub_efiemu_get_section_addr - (segs, sym->st_shndx, &handle, &off))) + err = grub_efiemu_get_section_addr (segs, sym->st_shndx, + &handle, &off); + if (err) return err; off += sym->st_value; if (bind != STB_LOCAL) - if ((err = grub_efiemu_register_symbol (name, handle, off))) - return err; + { + err = grub_efiemu_register_symbol (name, handle, off); + if (err) + return err; + } grub_efiemu_elfsyms[i].handle = handle; grub_efiemu_elfsyms[i].off = off; break; case STT_FUNC: - if ((err = grub_efiemu_get_section_addr - (segs, sym->st_shndx, &handle, &off))) + err = grub_efiemu_get_section_addr (segs, sym->st_shndx, + &handle, &off); + if (err) return err; off += sym->st_value; if (bind != STB_LOCAL) - if ((err = grub_efiemu_register_symbol (name, handle, off))) - return err; + { + err = grub_efiemu_register_symbol (name, handle, off); + if (err) + return err; + } grub_efiemu_elfsyms[i].handle = handle; grub_efiemu_elfsyms[i].off = off; break; case STT_SECTION: - if ((err = grub_efiemu_get_section_addr - (segs, sym->st_shndx, &handle, &off))) + err = grub_efiemu_get_section_addr (segs, sym->st_shndx, + &handle, &off); + if (err) { grub_efiemu_elfsyms[i].handle = 0; grub_efiemu_elfsyms[i].off = 0; @@ -322,22 +335,26 @@ grub_efiemu_resolve_symbols (grub_efiemu_segment_t segs, Elf_Ehdr *e) /* Load runtime to the memory and request memory for definitive location*/ grub_err_t -SUFFIX (grub_efiemu_loadcore_init) (void *core, grub_size_t core_size, +SUFFIX (grub_efiemu_loadcore_init) (void *core, const char *filename, + grub_size_t core_size, grub_efiemu_segment_t *segments) { Elf_Ehdr *e = (Elf_Ehdr *) core; grub_err_t err; if (e->e_type != ET_REL) - return grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type"); + return grub_error (GRUB_ERR_BAD_MODULE, N_("this ELF file is not of the right type")); /* Make sure that every section is within the core. */ - if ((grub_size_t) core_size < e->e_shoff + e->e_shentsize * e->e_shnum) - return grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core"); + if ((grub_size_t) core_size < e->e_shoff + (grub_uint32_t) e->e_shentsize * e->e_shnum) + return grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), + filename); - if ((err = grub_efiemu_init_segments (segments, core))) + err = grub_efiemu_init_segments (segments, core); + if (err) return err; - if ((err = grub_efiemu_count_symbols (core))) + err = grub_efiemu_count_symbols (core); + if (err) return err; grub_efiemu_request_symbols (1); diff --git a/efiemu/loadcore32.c b/grub-core/efiemu/loadcore32.c similarity index 100% rename from efiemu/loadcore32.c rename to grub-core/efiemu/loadcore32.c diff --git a/efiemu/loadcore64.c b/grub-core/efiemu/loadcore64.c similarity index 100% rename from efiemu/loadcore64.c rename to grub-core/efiemu/loadcore64.c diff --git a/efiemu/loadcore_common.c b/grub-core/efiemu/loadcore_common.c similarity index 84% rename from efiemu/loadcore_common.c rename to grub-core/efiemu/loadcore_common.c index a4db0ee9b..64d79608f 100644 --- a/efiemu/loadcore_common.c +++ b/grub-core/efiemu/loadcore_common.c @@ -111,7 +111,8 @@ grub_efiemu_loadcore_unload(void) /* Load runtime file and do some initial preparations */ grub_err_t -grub_efiemu_loadcore_init (grub_file_t file) +grub_efiemu_loadcore_init (grub_file_t file, + const char *filename) { grub_err_t err; @@ -140,8 +141,10 @@ grub_efiemu_loadcore_init (grub_file_t file) switch (grub_efiemu_mode) { case GRUB_EFIEMU32: - if ((err = grub_efiemu_loadcore_init32 (efiemu_core, efiemu_core_size, - &efiemu_segments))) + err = grub_efiemu_loadcore_init32 (efiemu_core, filename, + efiemu_core_size, + &efiemu_segments); + if (err) { grub_free (efiemu_core); efiemu_core = 0; @@ -151,8 +154,10 @@ grub_efiemu_loadcore_init (grub_file_t file) break; case GRUB_EFIEMU64: - if ((err = grub_efiemu_loadcore_init64 (efiemu_core, efiemu_core_size, - &efiemu_segments))) + err = grub_efiemu_loadcore_init64 (efiemu_core, filename, + efiemu_core_size, + &efiemu_segments); + if (err) { grub_free (efiemu_core); efiemu_core = 0; @@ -162,7 +167,7 @@ grub_efiemu_loadcore_init (grub_file_t file) break; default: - return grub_error (GRUB_ERR_BAD_OS, "unknown EFI runtime"); + return grub_error (GRUB_ERR_BUG, "unknown EFI runtime"); } return GRUB_ERR_NONE; } @@ -174,16 +179,18 @@ grub_efiemu_loadcore_load (void) switch (grub_efiemu_mode) { case GRUB_EFIEMU32: - if ((err = grub_efiemu_loadcore_load32 (efiemu_core, efiemu_core_size, - efiemu_segments))) - grub_efiemu_loadcore_unload (); + err = grub_efiemu_loadcore_load32 (efiemu_core, efiemu_core_size, + efiemu_segments); + if (err) + grub_efiemu_loadcore_unload (); return err; case GRUB_EFIEMU64: - if ((err = grub_efiemu_loadcore_load64 (efiemu_core, efiemu_core_size, - efiemu_segments))) - grub_efiemu_loadcore_unload (); + err = grub_efiemu_loadcore_load64 (efiemu_core, efiemu_core_size, + efiemu_segments); + if (err) + grub_efiemu_loadcore_unload (); return err; default: - return grub_error (GRUB_ERR_BAD_OS, "unknown EFI runtime"); + return grub_error (GRUB_ERR_BUG, "unknown EFI runtime"); } } diff --git a/efiemu/main.c b/grub-core/efiemu/main.c similarity index 82% rename from efiemu/main.c rename to grub-core/efiemu/main.c index 9480bfc4d..e7037f4ed 100644 --- a/efiemu/main.c +++ b/grub-core/efiemu/main.c @@ -29,8 +29,10 @@ #include #include #include -#include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); /* System table. Two version depending on mode */ grub_efi_system_table32_t *grub_efiemu_system_table32 = 0; @@ -78,7 +80,7 @@ grub_efiemu_unload (void) /* Remove previously registered table from the list */ grub_err_t -grub_efiemu_unregister_configuration_table (grub_efi_guid_t guid) +grub_efiemu_unregister_configuration_table (grub_guid_t guid) { struct grub_efiemu_configuration_table *cur, *prev; @@ -119,11 +121,9 @@ grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data), void *data) { struct grub_efiemu_prepare_hook *nhook; - if (! hook) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "you must supply the hook"); nhook = (struct grub_efiemu_prepare_hook *) grub_malloc (sizeof (*nhook)); if (! nhook) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't prepare hook"); + return grub_errno; nhook->hook = hook; nhook->unload = unload; nhook->data = data; @@ -136,7 +136,7 @@ grub_efiemu_register_prepare_hook (grub_err_t (*hook) (void *data), or with a hook */ grub_err_t -grub_efiemu_register_configuration_table (grub_efi_guid_t guid, +grub_efiemu_register_configuration_table (grub_guid_t guid, void * (*get_table) (void *data), void (*unload) (void *data), void *data) @@ -144,15 +144,13 @@ grub_efiemu_register_configuration_table (grub_efi_guid_t guid, struct grub_efiemu_configuration_table *tbl; grub_err_t err; - if (! get_table && ! data) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "you must set at least get_table or data"); - if ((err = grub_efiemu_unregister_configuration_table (guid))) + err = grub_efiemu_unregister_configuration_table (guid); + if (err) return err; tbl = (struct grub_efiemu_configuration_table *) grub_malloc (sizeof (*tbl)); if (! tbl) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't register table"); + return grub_errno; tbl->guid = guid; tbl->get_table = get_table; @@ -182,22 +180,6 @@ grub_cmd_efiemu_prepare (grub_command_t cmd __attribute__ ((unused)), - -int -grub_efiemu_exit_boot_services (grub_efi_uintn_t map_key - __attribute__ ((unused))) -{ - /* Nothing to do here yet */ - return 1; -} - -int -grub_efiemu_finish_boot_services (void) -{ - /* Nothing to do here yet */ - return 1; -} - /* Load the runtime from the file FILENAME. */ static grub_err_t grub_efiemu_load_file (const char *filename) @@ -205,21 +187,21 @@ grub_efiemu_load_file (const char *filename) grub_file_t file; grub_err_t err; - file = grub_file_open (filename); + file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); if (! file) - return 0; + return grub_errno; err = grub_efiemu_mm_init (); if (err) { grub_file_close (file); grub_efiemu_unload (); - return grub_error (grub_errno, "couldn't init memory management"); + return err; } grub_dprintf ("efiemu", "mm initialized\n"); - err = grub_efiemu_loadcore_init (file); + err = grub_efiemu_loadcore_init (file, filename); if (err) { grub_file_close (file); @@ -240,7 +222,7 @@ grub_efiemu_autocore (void) { const char *prefix; char *filename; - char *suffix; + const char *suffix; grub_err_t err; if (grub_efiemu_sizeof_uintn_t () != 0) @@ -250,22 +232,20 @@ grub_efiemu_autocore (void) if (! prefix) return grub_error (GRUB_ERR_FILE_NOT_FOUND, - "couldn't find efiemu core because prefix " - "isn't set"); + N_("variable `%s' isn't set"), "prefix"); suffix = grub_efiemu_get_default_core_name (); - filename = grub_xasprintf ("%s/%s", prefix, suffix); + filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s", + prefix, suffix); if (! filename) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate temporary space"); - + return grub_errno; err = grub_efiemu_load_file (filename); grub_free (filename); if (err) return err; -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU err = grub_machine_efiemu_init_tables (); if (err) return err; @@ -282,11 +262,13 @@ grub_efiemu_prepare (void) if (prepared) return GRUB_ERR_NONE; + err = grub_efiemu_autocore (); + if (err) + return err; + grub_dprintf ("efiemu", "Preparing %d-bit efiemu\n", 8 * grub_efiemu_sizeof_uintn_t ()); - err = grub_efiemu_autocore (); - /* Create NVRAM. */ grub_efiemu_pnvram (); @@ -308,12 +290,12 @@ grub_cmd_efiemu_load (grub_command_t cmd __attribute__ ((unused)), grub_efiemu_unload (); if (argc != 1) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "filename required"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); err = grub_efiemu_load_file (args[0]); if (err) return err; -#ifndef GRUB_UTIL +#ifndef GRUB_MACHINE_EMU err = grub_machine_efiemu_init_tables (); if (err) return err; @@ -327,15 +309,15 @@ GRUB_MOD_INIT(efiemu) { cmd_loadcore = grub_register_command ("efiemu_loadcore", grub_cmd_efiemu_load, - "FILE", - "Load and initialize EFI emulator."); + N_("FILE"), + N_("Load and initialize EFI emulator.")); cmd_prepare = grub_register_command ("efiemu_prepare", grub_cmd_efiemu_prepare, 0, - "Finalize loading of EFI emulator."); + N_("Finalize loading of EFI emulator.")); cmd_unload = grub_register_command ("efiemu_unload", grub_cmd_efiemu_unload, 0, - "Unload EFI emulator."); + N_("Unload EFI emulator.")); } GRUB_MOD_FINI(efiemu) diff --git a/efiemu/mm.c b/grub-core/efiemu/mm.c similarity index 81% rename from efiemu/mm.c rename to grub-core/efiemu/mm.c index 6099a14ee..9b8e0d0ad 100644 --- a/efiemu/mm.c +++ b/grub-core/efiemu/mm.c @@ -29,8 +29,8 @@ #include #include #include -#include #include +#include struct grub_efiemu_memrequest { @@ -62,12 +62,17 @@ grub_efiemu_add_to_mmap (grub_uint64_t start, grub_uint64_t size, /* Extend map if necessary*/ if (mmap_num >= mmap_reserved_size) { + void *old; + mmap_reserved_size = 2 * (mmap_reserved_size + 1); + old = efiemu_mmap; efiemu_mmap = (grub_efi_memory_descriptor_t *) - grub_realloc (efiemu_mmap, (++mmap_reserved_size) + grub_realloc (efiemu_mmap, mmap_reserved_size * sizeof (grub_efi_memory_descriptor_t)); if (!efiemu_mmap) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "not enough space for memory map"); + { + grub_free (old); + return grub_errno; + } } /* Fill slot*/ @@ -94,7 +99,8 @@ grub_efiemu_request_memalign (grub_size_t align, grub_size_t size, grub_size_t align_overhead; struct grub_efiemu_memrequest *ret, *cur, *prev; /* Check that the request is correct */ - if (type >= GRUB_EFI_MAX_MEMORY_TYPE || type <= GRUB_EFI_LOADER_CODE) + if (type <= GRUB_EFI_LOADER_CODE || type == GRUB_EFI_PERSISTENT_MEMORY || + type >= GRUB_EFI_MAX_MEMORY_TYPE) return -2; /* Add new size to requested size */ @@ -161,6 +167,13 @@ efiemu_alloc_requests (void) GRUB_EFI_MEMORY_MAPPED_IO, GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE, GRUB_EFI_PAL_CODE + + /* + * These are not allocatable: + * GRUB_EFI_RESERVED_MEMORY_TYPE + * GRUB_EFI_PERSISTENT_MEMORY + * >= GRUB_EFI_MAX_MEMORY_TYPE + */ }; /* Compute total memory needed */ @@ -176,8 +189,7 @@ efiemu_alloc_requests (void) /* Allocate the whole memory in one block */ resident_memory = grub_memalign (GRUB_EFIEMU_PAGESIZE, total_alloc); if (!resident_memory) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate resident memory"); + return grub_errno; /* Split the memory into blocks by type */ curptr = resident_memory; @@ -201,10 +213,10 @@ efiemu_alloc_requests (void) - (requested_memory[reqorder[i]] % GRUB_EFIEMU_PAGESIZE); if (align_overhead == GRUB_EFIEMU_PAGESIZE) align_overhead = 0; - curptr = ((grub_uint8_t *)curptr) + align_overhead; + curptr = ((grub_uint8_t *) curptr) + align_overhead; /* Add the region to memory map */ - grub_efiemu_add_to_mmap (PTR_TO_UINT64 (typestart), + grub_efiemu_add_to_mmap ((grub_addr_t) typestart, curptr - typestart, reqorder[i]); } @@ -264,25 +276,26 @@ grub_efiemu_mm_return_request (int handle) } } +/* Helper for grub_efiemu_mmap_init. */ +static int +bounds_hook (grub_uint64_t addr __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + grub_memory_type_t type __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + mmap_reserved_size++; + return 0; +} + /* Reserve space for memory map */ static grub_err_t grub_efiemu_mmap_init (void) { - auto int NESTED_FUNC_ATTR bounds_hook (grub_uint64_t, grub_uint64_t, - grub_uint32_t); - int NESTED_FUNC_ATTR bounds_hook (grub_uint64_t addr __attribute__ ((unused)), - grub_uint64_t size __attribute__ ((unused)), - grub_uint32_t type __attribute__ ((unused))) - { - mmap_reserved_size++; - return 0; - } - // the place for memory used by efiemu itself mmap_reserved_size = GRUB_EFI_MAX_MEMORY_TYPE + 1; -#ifndef GRUB_UTIL - grub_machine_mmap_iterate (bounds_hook); +#ifndef GRUB_MACHINE_EMU + grub_machine_mmap_iterate (bounds_hook, NULL); #endif return GRUB_ERR_NONE; @@ -323,6 +336,25 @@ grub_efiemu_get_memory_map (grub_efi_uintn_t *memory_map_size, return 1; } +grub_err_t +grub_efiemu_finish_boot_services (grub_efi_uintn_t *memory_map_size, + grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t *map_key, + grub_efi_uintn_t *descriptor_size, + grub_efi_uint32_t *descriptor_version) +{ + int val = grub_efiemu_get_memory_map (memory_map_size, + memory_map, map_key, + descriptor_size, + descriptor_version); + if (val == 1) + return GRUB_ERR_NONE; + if (val == -1) + return grub_errno; + return grub_error (GRUB_ERR_IO, "memory map buffer is too small"); +} + + /* Free everything */ grub_err_t grub_efiemu_mm_unload (void) @@ -359,52 +391,52 @@ grub_efiemu_mm_init (void) return GRUB_ERR_NONE; } +/* Helper for grub_efiemu_mmap_fill. */ +static int +fill_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) + { + switch (type) + { + case GRUB_MEMORY_AVAILABLE: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_CONVENTIONAL_MEMORY); + + case GRUB_MEMORY_ACPI: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_ACPI_RECLAIM_MEMORY); + + case GRUB_MEMORY_NVS: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_ACPI_MEMORY_NVS); + + case GRUB_MEMORY_PERSISTENT: + case GRUB_MEMORY_PERSISTENT_LEGACY: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_PERSISTENT_MEMORY); + default: + grub_dprintf ("efiemu", + "Unknown memory type %d. Assuming unusable\n", type); + /* FALLTHROUGH */ + case GRUB_MEMORY_RESERVED: + return grub_efiemu_add_to_mmap (addr, size, + GRUB_EFI_UNUSABLE_MEMORY); + } + } + /* Copy host memory map */ static grub_err_t grub_efiemu_mmap_fill (void) { - auto int NESTED_FUNC_ATTR fill_hook (grub_uint64_t, grub_uint64_t, grub_uint32_t); - int NESTED_FUNC_ATTR fill_hook (grub_uint64_t addr, - grub_uint64_t size, - grub_uint32_t type) - { - switch (type) - { - case GRUB_MACHINE_MEMORY_AVAILABLE: - return grub_efiemu_add_to_mmap (addr, size, - GRUB_EFI_CONVENTIONAL_MEMORY); - -#ifdef GRUB_MACHINE_MEMORY_ACPI - case GRUB_MACHINE_MEMORY_ACPI: - return grub_efiemu_add_to_mmap (addr, size, - GRUB_EFI_ACPI_RECLAIM_MEMORY); -#endif - -#ifdef GRUB_MACHINE_MEMORY_NVS - case GRUB_MACHINE_MEMORY_NVS: - return grub_efiemu_add_to_mmap (addr, size, - GRUB_EFI_ACPI_MEMORY_NVS); -#endif - - default: - grub_printf ("Unknown memory type %d. Marking as unusable\n", type); - case GRUB_MACHINE_MEMORY_RESERVED: - return grub_efiemu_add_to_mmap (addr, size, - GRUB_EFI_UNUSABLE_MEMORY); - } - } - -#ifndef GRUB_UTIL - grub_machine_mmap_iterate (fill_hook); +#ifndef GRUB_MACHINE_EMU + grub_machine_mmap_iterate (fill_hook, NULL); #endif return GRUB_ERR_NONE; } grub_err_t -grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, - grub_uint64_t, - grub_uint32_t)) +grub_efiemu_mmap_iterate (grub_memory_hook_t hook, void *hook_data) { unsigned i; @@ -413,18 +445,22 @@ grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, { case GRUB_EFI_RUNTIME_SERVICES_CODE: hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, - GRUB_EFIEMU_MEMORY_CODE); + GRUB_MEMORY_CODE, hook_data); + break; + + case GRUB_EFI_UNUSABLE_MEMORY: + hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, + GRUB_MEMORY_BADRAM, hook_data); break; case GRUB_EFI_RESERVED_MEMORY_TYPE: case GRUB_EFI_RUNTIME_SERVICES_DATA: - case GRUB_EFI_UNUSABLE_MEMORY: case GRUB_EFI_MEMORY_MAPPED_IO: case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE: case GRUB_EFI_PAL_CODE: - case GRUB_EFI_MAX_MEMORY_TYPE: + default: hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, - GRUB_EFIEMU_MEMORY_RESERVED); + GRUB_MEMORY_RESERVED, hook_data); break; case GRUB_EFI_LOADER_CODE: @@ -433,18 +469,24 @@ grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, case GRUB_EFI_BOOT_SERVICES_DATA: case GRUB_EFI_CONVENTIONAL_MEMORY: hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, - GRUB_EFIEMU_MEMORY_AVAILABLE); + GRUB_MEMORY_AVAILABLE, hook_data); break; case GRUB_EFI_ACPI_RECLAIM_MEMORY: hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, - GRUB_EFIEMU_MEMORY_ACPI); + GRUB_MEMORY_ACPI, hook_data); break; case GRUB_EFI_ACPI_MEMORY_NVS: hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, - GRUB_EFIEMU_MEMORY_NVS); + GRUB_MEMORY_NVS, hook_data); break; + + case GRUB_EFI_PERSISTENT_MEMORY: + hook (efiemu_mmap[i].physical_start, efiemu_mmap[i].num_pages * 4096, + GRUB_MEMORY_PERSISTENT, hook_data); + break; + } return 0; @@ -480,7 +522,8 @@ grub_efiemu_mmap_sort_and_uniq (void) [GRUB_EFI_ACPI_MEMORY_NVS] = 3, [GRUB_EFI_MEMORY_MAPPED_IO] = 4, [GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE] = 4, - [GRUB_EFI_PAL_CODE] = 4 + [GRUB_EFI_PAL_CODE] = 4, + [GRUB_EFI_PERSISTENT_MEMORY] = 4 }; int i, j, k, done; @@ -511,17 +554,16 @@ grub_efiemu_mmap_sort_and_uniq (void) /* Initialize variables*/ grub_memset (present, 0, sizeof (int) * GRUB_EFI_MAX_MEMORY_TYPE); scanline_events = (struct grub_efiemu_mmap_scan *) - grub_malloc (sizeof (struct grub_efiemu_mmap_scan) * 2 * mmap_num); + grub_calloc (mmap_num, sizeof (struct grub_efiemu_mmap_scan) * 2); /* Number of chunks can't increase more than by factor of 2 */ result = (grub_efi_memory_descriptor_t *) - grub_malloc (sizeof (grub_efi_memory_descriptor_t) * 2 * mmap_num); + grub_calloc (mmap_num, sizeof (grub_efi_memory_descriptor_t) * 2); if (!result || !scanline_events) { grub_free (result); grub_free (scanline_events); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, - "couldn't allocate space for new memory map"); + return grub_errno; } /* Register scanline events */ @@ -618,16 +660,18 @@ grub_efiemu_mm_do_alloc (void) /* Preallocate mmap */ efiemu_mmap = (grub_efi_memory_descriptor_t *) - grub_malloc (mmap_reserved_size * sizeof (grub_efi_memory_descriptor_t)); + grub_calloc (mmap_reserved_size, sizeof (grub_efi_memory_descriptor_t)); if (!efiemu_mmap) { grub_efiemu_unload (); - return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't initialize mmap"); + return grub_errno; } - if ((err = efiemu_alloc_requests ())) + err = efiemu_alloc_requests (); + if (err) return err; - if ((err = grub_efiemu_mmap_fill ())) + err = grub_efiemu_mmap_fill (); + if (err) return err; return grub_efiemu_mmap_sort_and_uniq (); } diff --git a/efiemu/pnvram.c b/grub-core/efiemu/pnvram.c similarity index 91% rename from efiemu/pnvram.c rename to grub-core/efiemu/pnvram.c index e58fce84e..dd42bc691 100644 --- a/efiemu/pnvram.c +++ b/grub-core/efiemu/pnvram.c @@ -39,7 +39,7 @@ static grub_size_t nvramsize; /* Parse signed value */ static int -grub_strtosl (const char *arg, char **end, int base) +grub_strtosl (const char *arg, const char ** const end, int base) { if (arg[0] == '-') return -grub_strtoul (arg + 1, end, base); @@ -102,98 +102,7 @@ nvram_set (void * data __attribute__ ((unused))) grub_uint32_t *accuracy = grub_efiemu_mm_obtain_request (accuracy_handle); char *nvramptr; - - auto int iterate_env (struct grub_env_var *var); - int iterate_env (struct grub_env_var *var) - { - char *guid, *attr, *name, *varname; - struct efi_variable *efivar; - int len = 0; - int i; - grub_uint64_t guidcomp; - - if (grub_memcmp (var->name, "EfiEmu.pnvram.", - sizeof ("EfiEmu.pnvram.") - 1) != 0) - return 0; - - guid = var->name + sizeof ("EfiEmu.pnvram.") - 1; - - attr = grub_strchr (guid, '.'); - if (!attr) - return 0; - attr++; - - name = grub_strchr (attr, '.'); - if (!name) - return 0; - name++; - - efivar = (struct efi_variable *) nvramptr; - if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize) - { - grub_error (GRUB_ERR_OUT_OF_MEMORY, - "too many NVRAM variables for reserved variable space." - " Try increasing EfiEmu.pnvram.size"); - return 1; - } - - nvramptr += sizeof (struct efi_variable); - - efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16)); - if (*guid != '-') - return 0; - guid++; - - efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16)); - if (*guid != '-') - return 0; - guid++; - - efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16)); - if (*guid != '-') - return 0; - guid++; - - guidcomp = grub_strtoull (guid, 0, 16); - for (i = 0; i < 8; i++) - efivar->guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff; - - efivar->attributes = grub_strtoull (attr, 0, 16); - - varname = grub_malloc (grub_strlen (name) + 1); - if (! varname) - return 1; - - if (unescape (name, varname, varname + grub_strlen (name) + 1, &len)) - return 1; - - len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr, - (nvramsize - (nvramptr - nvram)) / 2, - (grub_uint8_t *) varname, len, NULL); - - if (len < 0) - { - grub_error (GRUB_ERR_BAD_ARGUMENT, "broken UTF-8 in variable name"); - return 1; - } - - nvramptr += 2 * len; - *((grub_uint16_t *) nvramptr) = 0; - nvramptr += 2; - efivar->namelen = 2 * len + 2; - - if (unescape (var->value, nvramptr, nvram + nvramsize, &len)) - { - efivar->namelen = 0; - return 1; - } - - nvramptr += len; - - efivar->size = len; - - return 0; - } + struct grub_env_var *var; /* Copy to definitive loaction */ grub_dprintf ("efiemu", "preparing pnvram\n"); @@ -209,9 +118,89 @@ nvram_set (void * data __attribute__ ((unused))) nvramptr = nvram; grub_memset (nvram, 0, nvramsize); - grub_env_iterate (iterate_env); + FOR_SORTED_ENV (var) + { + const char *guid; + char *attr, *name, *varname; + struct efi_variable *efivar; + int len = 0; + int i; + grub_uint64_t guidcomp; + + if (grub_memcmp (var->name, "EfiEmu.pnvram.", + sizeof ("EfiEmu.pnvram.") - 1) != 0) + continue; + + guid = var->name + sizeof ("EfiEmu.pnvram.") - 1; + + attr = grub_strchr (guid, '.'); + if (!attr) + continue; + attr++; + + name = grub_strchr (attr, '.'); + if (!name) + continue; + name++; + + efivar = (struct efi_variable *) nvramptr; + if (nvramptr - nvram + sizeof (struct efi_variable) > nvramsize) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "too many NVRAM variables for reserved variable space." + " Try increasing EfiEmu.pnvram.size"); + + nvramptr += sizeof (struct efi_variable); + + efivar->guid.data1 = grub_cpu_to_le32 (grub_strtoul (guid, &guid, 16)); + if (*guid != '-') + continue; + guid++; + + efivar->guid.data2 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16)); + if (*guid != '-') + continue; + guid++; + + efivar->guid.data3 = grub_cpu_to_le16 (grub_strtoul (guid, &guid, 16)); + if (*guid != '-') + continue; + guid++; + + guidcomp = grub_strtoull (guid, 0, 16); + for (i = 0; i < 8; i++) + efivar->guid.data4[i] = (guidcomp >> (56 - 8 * i)) & 0xff; + + efivar->attributes = grub_strtoull (attr, 0, 16); + + varname = grub_malloc (grub_strlen (name) + 1); + if (! varname) + return grub_errno; + + if (unescape (name, varname, varname + grub_strlen (name) + 1, &len)) + break; + + len = grub_utf8_to_utf16 ((grub_uint16_t *) nvramptr, + (nvramsize - (nvramptr - nvram)) / 2, + (grub_uint8_t *) varname, len, NULL); + + nvramptr += 2 * len; + *((grub_uint16_t *) nvramptr) = 0; + nvramptr += 2; + efivar->namelen = 2 * len + 2; + + if (unescape (var->value, nvramptr, nvram + nvramsize, &len)) + { + efivar->namelen = 0; + break; + } + + nvramptr += len; + + efivar->size = len; + } if (grub_errno) return grub_errno; + *nvramsize_def = nvramsize; /* Register symbols */ diff --git a/efiemu/prepare.c b/grub-core/efiemu/prepare.c similarity index 81% rename from efiemu/prepare.c rename to grub-core/efiemu/prepare.c index 620260049..99ddb5abb 100644 --- a/efiemu/prepare.c +++ b/grub-core/efiemu/prepare.c @@ -20,9 +20,10 @@ #include #include +#include #include #include -#include +#include grub_err_t SUFFIX (grub_efiemu_prepare) (struct grub_efiemu_prepare_hook *prepare_hooks, @@ -82,10 +83,16 @@ SUFFIX (grub_efiemu_prepare) (struct grub_efiemu_prepare_hook *prepare_hooks, ((grub_uint8_t *) grub_efiemu_mm_obtain_request (handle) + off); /* Put pointer to the list of configuration tables in system table */ - grub_efiemu_write_value - (&(SUFFIX (grub_efiemu_system_table)->configuration_table), 0, - conftable_handle, 0, 1, - sizeof (SUFFIX (grub_efiemu_system_table)->configuration_table)); + err = grub_efiemu_write_value + (&(SUFFIX (grub_efiemu_system_table)->configuration_table), 0, + conftable_handle, 0, 1, + sizeof (SUFFIX (grub_efiemu_system_table)->configuration_table)); + if (err) + { + grub_efiemu_unload (); + return err; + } + SUFFIX(grub_efiemu_system_table)->num_table_entries = cntconftables; /* Fill the list of configuration tables */ @@ -97,10 +104,9 @@ SUFFIX (grub_efiemu_prepare) (struct grub_efiemu_prepare_hook *prepare_hooks, grub_memcpy (&(conftables[i].vendor_guid), &(cur->guid), sizeof (cur->guid)); if (cur->get_table) - conftables[i].vendor_table - = PTR_TO_UINT64 (cur->get_table (cur->data)); + conftables[i].vendor_table = (grub_addr_t) cur->get_table (cur->data); else - conftables[i].vendor_table = PTR_TO_UINT64 (cur->data); + conftables[i].vendor_table = (grub_addr_t) cur->data; } err = SUFFIX (grub_efiemu_crc) (); @@ -123,6 +129,10 @@ SUFFIX (grub_efiemu_crc) (void) int handle; grub_off_t off; struct SUFFIX (grub_efiemu_runtime_services) *runtime_services; + grub_uint32_t crc32_val; + + if (GRUB_MD_CRC32->mdlen != 4) + return grub_error (GRUB_ERR_BUG, "incorrect mdlen"); /* compute CRC32 of runtime_services */ err = grub_efiemu_resolve_symbol ("efiemu_runtime_services", @@ -132,9 +142,13 @@ SUFFIX (grub_efiemu_crc) (void) runtime_services = (struct SUFFIX (grub_efiemu_runtime_services) *) ((grub_uint8_t *) grub_efiemu_mm_obtain_request (handle) + off); + runtime_services->hdr.crc32 = 0; - runtime_services->hdr.crc32 = grub_getcrc32 - (0, runtime_services, runtime_services->hdr.header_size); + + grub_crypto_hash (GRUB_MD_CRC32, &crc32_val, + runtime_services, runtime_services->hdr.header_size); + runtime_services->hdr.crc32 = + grub_be_to_cpu32(crc32_val); err = grub_efiemu_resolve_symbol ("efiemu_system_table", &handle, &off); if (err) @@ -142,9 +156,11 @@ SUFFIX (grub_efiemu_crc) (void) /* compute CRC32 of system table */ SUFFIX (grub_efiemu_system_table)->hdr.crc32 = 0; - SUFFIX (grub_efiemu_system_table)->hdr.crc32 - = grub_getcrc32 (0, SUFFIX (grub_efiemu_system_table), - SUFFIX (grub_efiemu_system_table)->hdr.header_size); + grub_crypto_hash (GRUB_MD_CRC32, &crc32_val, + SUFFIX (grub_efiemu_system_table), + SUFFIX (grub_efiemu_system_table)->hdr.header_size); + SUFFIX (grub_efiemu_system_table)->hdr.crc32 = + grub_be_to_cpu32(crc32_val); grub_dprintf ("efiemu","system_table = %p, runtime_services = %p\n", SUFFIX (grub_efiemu_system_table), runtime_services); diff --git a/efiemu/prepare32.c b/grub-core/efiemu/prepare32.c similarity index 100% rename from efiemu/prepare32.c rename to grub-core/efiemu/prepare32.c diff --git a/efiemu/prepare64.c b/grub-core/efiemu/prepare64.c similarity index 100% rename from efiemu/prepare64.c rename to grub-core/efiemu/prepare64.c diff --git a/efiemu/runtime/config.h b/grub-core/efiemu/runtime/config.h similarity index 93% rename from efiemu/runtime/config.h rename to grub-core/efiemu/runtime/config.h index 26fb2ff08..c9fe02716 100644 --- a/efiemu/runtime/config.h +++ b/grub-core/efiemu/runtime/config.h @@ -19,16 +19,18 @@ #define GRUB_TYPES_CPU_HEADER 1 -#ifdef ELF32 +#ifdef __i386__ # define SIZEOF_VOID_P 4 # define SIZEOF_LONG 4 # define GRUB_TARGET_SIZEOF_VOID_P 4 # define GRUB_TARGET_SIZEOF_LONG 4 # define EFI_FUNC(x) x -#else +#elif defined (__x86_64__) # define SIZEOF_VOID_P 8 # define SIZEOF_LONG 8 # define GRUB_TARGET_SIZEOF_VOID_P 8 # define GRUB_TARGET_SIZEOF_LONG 8 # define EFI_FUNC(x) x ## _real +#else +#error "Unknown architecture" #endif diff --git a/efiemu/runtime/efiemu.S b/grub-core/efiemu/runtime/efiemu.S similarity index 100% rename from efiemu/runtime/efiemu.S rename to grub-core/efiemu/runtime/efiemu.S diff --git a/efiemu/runtime/efiemu.c b/grub-core/efiemu/runtime/efiemu.c similarity index 83% rename from efiemu/runtime/efiemu.c rename to grub-core/efiemu/runtime/efiemu.c index 73893414a..51dc02114 100644 --- a/efiemu/runtime/efiemu.c +++ b/grub-core/efiemu/runtime/efiemu.c @@ -21,76 +21,82 @@ As it emulates only runtime serviceit isn't able to chainload EFI bootloader on non-EFI system (TODO) */ +#ifdef __i386__ +#include +#else +#include +#endif + #include #include #include #include -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_get_time (grub_efi_time_t *time, grub_efi_time_capabilities_t *capabilities); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_set_time (grub_efi_time_t *time); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_get_wakeup_time (grub_efi_boolean_t *enabled, grub_efi_boolean_t *pending, grub_efi_time_t *time); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_set_wakeup_time (grub_efi_boolean_t enabled, grub_efi_time_t *time); -#ifdef APPLE_CC +#ifdef __APPLE__ #define PHYSICAL_ATTRIBUTE __attribute__ ((section("_text-physical, _text-physical"))); #else #define PHYSICAL_ATTRIBUTE __attribute__ ((section(".text-physical"))); #endif -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size, grub_efi_uintn_t descriptor_size, grub_efi_uint32_t descriptor_version, grub_efi_memory_descriptor_t *virtual_map) PHYSICAL_ATTRIBUTE; -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_convert_pointer (grub_efi_uintn_t debug_disposition, void **address) PHYSICAL_ATTRIBUTE; -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_get_variable (grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t *attributes, grub_efi_uintn_t *data_size, void *data); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_get_next_variable_name (grub_efi_uintn_t *variable_name_size, grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid); + grub_packed_guid_t *vendor_guid); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_set_variable (grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid, + const grub_packed_guid_t *vendor_guid, grub_efi_uint32_t attributes, grub_efi_uintn_t data_size, void *data); -grub_efi_status_t +grub_efi_status_t __grub_efi_api efiemu_get_next_high_monotonic_count (grub_efi_uint32_t *high_count); -void +void __grub_efi_api efiemu_reset_system (grub_efi_reset_type_t reset_type, grub_efi_status_t reset_status, grub_efi_uintn_t data_size, grub_efi_char16_t *reset_data); -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_virtual_address_map) (grub_efi_uintn_t, grub_efi_uintn_t, grub_efi_uint32_t, grub_efi_memory_descriptor_t *) PHYSICAL_ATTRIBUTE; -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition, void **address) PHYSICAL_ATTRIBUTE; @@ -125,11 +131,11 @@ extern grub_uint32_t efiemu_time_accuracy; /* Some standard functions because we need to be standalone */ static void -efiemu_memcpy (void *to, void *from, int count) +efiemu_memcpy (void *to, const void *from, int count) { int i; for (i = 0; i < count; i++) - ((grub_uint8_t *) to)[i] = ((grub_uint8_t *) from)[i]; + ((grub_uint8_t *) to)[i] = ((const grub_uint8_t *) from)[i]; } static int @@ -149,7 +155,7 @@ efiemu_str16len (grub_uint16_t *a) } static int -efiemu_memequal (void *a, void *b, grub_size_t n) +efiemu_memequal (const void *a, const void *b, grub_size_t n) { grub_uint8_t *ptr1, *ptr2; for (ptr1 = (grub_uint8_t *) a, ptr2 = (grub_uint8_t *)b; @@ -168,17 +174,17 @@ efiemu_memset (grub_uint8_t *a, grub_uint8_t b, grub_size_t n) static inline void write_cmos (grub_uint8_t addr, grub_uint8_t val) { - __asm__ __volatile__ ("outb %%al,$0x70\n" - "mov %%cl, %%al\n" - "outb %%al,$0x71": :"a" (addr), "c" (val)); + asm volatile ("outb %%al,$0x70\n" + "mov %%cl, %%al\n" + "outb %%al,$0x71": :"a" (addr), "c" (val)); } static inline grub_uint8_t read_cmos (grub_uint8_t addr) { grub_uint8_t ret; - __asm__ __volatile__ ("outb %%al, $0x70\n" - "inb $0x71, %%al": "=a"(ret) :"a" (addr)); + asm volatile ("outb %%al, $0x70\n" + "inb $0x71, %%al": "=a"(ret) :"a" (addr)); return ret; } @@ -196,7 +202,7 @@ bcd_to_hex (grub_uint8_t in) return 10 * ((in & 0xf0) >> 4) + (in & 0x0f); } -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time, grub_efi_time_capabilities_t *capabilities) { @@ -240,7 +246,7 @@ EFI_FUNC (efiemu_get_time) (grub_efi_time_t *time, return GRUB_EFI_SUCCESS; } -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time) { LOG ('b'); @@ -259,7 +265,7 @@ EFI_FUNC (efiemu_set_time) (grub_efi_time_t *time) } /* Following 2 functions are vendor specific. So announce it as unsupported */ -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled, grub_efi_boolean_t *pending, grub_efi_time_t *time) @@ -268,7 +274,7 @@ EFI_FUNC (efiemu_get_wakeup_time) (grub_efi_boolean_t *enabled, return GRUB_EFI_UNSUPPORTED; } -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_wakeup_time) (grub_efi_boolean_t enabled, grub_efi_time_t *time) { @@ -331,7 +337,7 @@ efiemu_getcrc32 (grub_uint32_t crc, void *buf, int size) } -grub_efi_status_t EFI_FUNC +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_virtual_address_map) (grub_efi_uintn_t memory_map_size, grub_efi_uintn_t descriptor_size, grub_efi_uint32_t descriptor_version, @@ -369,16 +375,16 @@ grub_efi_status_t EFI_FUNC switch (cur_relloc->size) { case 8: - *((grub_uint64_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 4: - *((grub_uint32_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 2: - *((grub_uint16_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 1: - *((grub_uint8_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr; break; } } @@ -397,7 +403,7 @@ grub_efi_status_t EFI_FUNC /* since efiemu_set_virtual_address_map corrects all the pointers we don't need efiemu_convert_pointer */ -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition, void **address) { @@ -410,7 +416,7 @@ EFI_FUNC (efiemu_convert_pointer) (grub_efi_uintn_t debug_disposition, /* Find variable by name and GUID. */ static struct efi_variable * -find_variable (grub_efi_guid_t *vendor_guid, +find_variable (const grub_packed_guid_t *vendor_guid, grub_efi_char16_t *variable_name) { grub_uint8_t *ptr; @@ -430,12 +436,12 @@ find_variable (grub_efi_guid_t *vendor_guid, return 0; } -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid, - grub_efi_uint32_t *attributes, - grub_efi_uintn_t *data_size, - void *data) + const grub_packed_guid_t *vendor_guid, + grub_efi_uint32_t *attributes, + grub_efi_uintn_t *data_size, + void *data) { struct efi_variable *efivar; LOG ('g'); @@ -455,10 +461,10 @@ EFI_FUNC (efiemu_get_variable) (grub_efi_char16_t *variable_name, return GRUB_EFI_SUCCESS; } -grub_efi_status_t EFI_FUNC +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_next_variable_name) (grub_efi_uintn_t *variable_name_size, grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid) + grub_packed_guid_t *vendor_guid) { struct efi_variable *efivar; LOG ('l'); @@ -495,12 +501,12 @@ grub_efi_status_t EFI_FUNC return GRUB_EFI_SUCCESS; } -grub_efi_status_t +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name, - grub_efi_guid_t *vendor_guid, - grub_efi_uint32_t attributes, - grub_efi_uintn_t data_size, - void *data) + const grub_packed_guid_t *vendor_guid, + grub_efi_uint32_t attributes, + grub_efi_uintn_t data_size, + void *data) { struct efi_variable *efivar; grub_uint8_t *ptr; @@ -550,7 +556,7 @@ EFI_FUNC (efiemu_set_variable) (grub_efi_char16_t *variable_name, return GRUB_EFI_SUCCESS; } -grub_efi_status_t EFI_FUNC +grub_efi_status_t __grub_efi_api EFI_FUNC (efiemu_get_next_high_monotonic_count) (grub_efi_uint32_t *high_count) { LOG ('j'); @@ -564,7 +570,7 @@ grub_efi_status_t EFI_FUNC Besides EFI specification says that this function shouldn't be used on systems supporting ACPI */ -void +void __grub_efi_api EFI_FUNC (efiemu_reset_system) (grub_efi_reset_type_t reset_type, grub_efi_status_t reset_status, grub_efi_uintn_t data_size, @@ -591,9 +597,30 @@ struct grub_efi_runtime_services efiemu_runtime_services = .set_virtual_address_map = efiemu_set_virtual_address_map, .convert_pointer = efiemu_convert_pointer, - .get_variable = efiemu_get_variable, - .get_next_variable_name = efiemu_get_next_variable_name, - .set_variable = efiemu_set_variable, + /* + The code is structured in a way to accept unaligned inputs + in most cases and supply 4-byte aligned outputs. + + Efiemu case is a bit ugly because there inputs and outputs are + reversed and so we need careful casts to account for this + inversion. + */ + .get_variable = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_char16_t *variable_name, + const grub_guid_t *vendor_guid, + grub_efi_uint32_t *attributes, + grub_efi_uintn_t *data_size, + void *data)) efiemu_get_variable, + .get_next_variable_name = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_uintn_t *variable_name_size, + grub_efi_char16_t *variable_name, + grub_guid_t *vendor_guid)) efiemu_get_next_variable_name, + .set_variable = (grub_efi_status_t + (__grub_efi_api *) (grub_efi_char16_t *variable_name, + const grub_guid_t *vendor_guid, + grub_efi_uint32_t attributes, + grub_efi_uintn_t data_size, + void *data)) efiemu_set_variable, .get_next_high_monotonic_count = efiemu_get_next_high_monotonic_count, .reset_system = efiemu_reset_system diff --git a/efiemu/symbols.c b/grub-core/efiemu/symbols.c similarity index 89% rename from efiemu/symbols.c rename to grub-core/efiemu/symbols.c index 4fc546b59..cc148552d 100644 --- a/efiemu/symbols.c +++ b/grub-core/efiemu/symbols.c @@ -22,6 +22,7 @@ #include #include #include +#include static int ptv_written = 0; static int ptv_alloc = 0; @@ -69,7 +70,7 @@ grub_efiemu_request_symbols (int num) return grub_error (GRUB_ERR_BAD_ARGUMENT, "symbols have already been allocated"); if (num < 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, + return grub_error (GRUB_ERR_BUG, "can't request negative symbols"); ptv_requested += num; return GRUB_ERR_NONE; @@ -88,7 +89,7 @@ grub_efiemu_resolve_symbol (const char *name, int *handle, grub_off_t *off) return GRUB_ERR_NONE; } grub_dprintf ("efiemu", "%s not found\n", name); - return grub_error (GRUB_ERR_BAD_OS, "symbol %s isn't found", name); + return grub_error (GRUB_ERR_BAD_OS, N_("symbol `%s' not found"), name); } /* Register symbol named NAME in memory handle HANDLE at offset OFF */ @@ -99,7 +100,7 @@ grub_efiemu_register_symbol (const char *name, int handle, grub_off_t off) cur = (struct grub_efiemu_sym *) grub_malloc (sizeof (*cur)); grub_dprintf ("efiemu", "registering symbol '%s'\n", name); if (!cur) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't register symbol"); + return grub_errno; cur->name = grub_strdup (name); cur->next = efiemu_syms; cur->handle = handle; @@ -153,7 +154,7 @@ grub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle, = grub_efiemu_mm_obtain_request (ptv_handle); if (ptv_needed && ptv_written >= ptv_alloc) - return grub_error (GRUB_ERR_OUT_OF_MEMORY, + return grub_error (GRUB_ERR_BUG, "your module didn't declare efiemu " " relocators correctly"); @@ -169,7 +170,7 @@ grub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle, else ptv_rels[ptv_written].plustype = 0; - ptv_rels[ptv_written].addr = PTR_TO_UINT64 (addr); + ptv_rels[ptv_written].addr = (grub_addr_t) addr; ptv_rels[ptv_written].size = size; ptv_written++; @@ -179,10 +180,10 @@ grub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle, /* Compute the value */ if (minus_handle) - value -= PTR_TO_UINT32 (grub_efiemu_mm_obtain_request (minus_handle)); + value -= (grub_addr_t) grub_efiemu_mm_obtain_request (minus_handle); if (plus_handle) - value += PTR_TO_UINT32 (grub_efiemu_mm_obtain_request (plus_handle)); + value += (grub_addr_t) grub_efiemu_mm_obtain_request (plus_handle); /* Write the value */ switch (size) @@ -200,7 +201,7 @@ grub_efiemu_write_value (void *addr, grub_uint32_t value, int plus_handle, *((grub_uint8_t *) addr) = value; break; default: - return grub_error (GRUB_ERR_BAD_ARGUMENT, "wrong symbol size"); + return grub_error (GRUB_ERR_BUG, "wrong symbol size"); } return GRUB_ERR_NONE; @@ -222,7 +223,7 @@ grub_efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size, /* Ensure that we are called only once */ if (*ptv_relocated) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "EfiEmu is already relocated"); + return grub_error (GRUB_ERR_BUG, "EfiEmu is already relocated"); *ptv_relocated = 1; /* Correct addresses using information supplied by grub */ @@ -248,16 +249,16 @@ grub_efiemu_set_virtual_address_map (grub_efi_uintn_t memory_map_size, switch (cur_relloc->size) { case 8: - *((grub_uint64_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint64_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 4: - *((grub_uint32_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint32_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 2: - *((grub_uint16_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint16_t *) (grub_addr_t) cur_relloc->addr) += corr; break; case 1: - *((grub_uint8_t *) UINT_TO_PTR (cur_relloc->addr)) += corr; + *((grub_uint8_t *) (grub_addr_t) cur_relloc->addr) += corr; break; } } diff --git a/grub-core/font/font.c b/grub-core/font/font.c new file mode 100644 index 000000000..18de52562 --- /dev/null +++ b/grub-core/font/font.c @@ -0,0 +1,1660 @@ +/* font.c - Font API and font file loader. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#if HAVE_FONT_SOURCE +#include "ascii.h" +#endif + +#ifndef FONT_DEBUG +#define FONT_DEBUG 0 +#endif + +struct char_index_entry +{ + grub_uint32_t code; + grub_uint8_t storage_flags; + grub_uint32_t offset; + + /* Glyph if loaded, or NULL otherwise. */ + struct grub_font_glyph *glyph; +}; + +#define FONT_WEIGHT_NORMAL 100 +#define FONT_WEIGHT_BOLD 200 +#define ASCII_BITMAP_SIZE 16 + +/* Definition of font registry. */ +struct grub_font_node *grub_font_list; + +static int register_font (grub_font_t font); +static void font_init (grub_font_t font); +static void free_font (grub_font_t font); +static void remove_font (grub_font_t font); + +struct font_file_section +{ + /* The file this section is in. */ + grub_file_t file; + + /* FOURCC name of the section. */ + char name[4]; + + /* Length of the section contents. */ + grub_uint32_t length; + + /* Set by open_section() on EOF. */ + int eof; +}; + +/* Replace unknown glyphs with a rounded question mark. */ +static grub_uint8_t unknown_glyph_bitmap[] = { + /* 76543210 */ + 0x7C, /* ooooo */ + 0x82, /* o o */ + 0xBA, /* o ooo o */ + 0xAA, /* o o o o */ + 0xAA, /* o o o o */ + 0x8A, /* o o o */ + 0x9A, /* o oo o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x7C, /* ooooo */ + 0x00 /* */ +}; + +/* The "unknown glyph" glyph, used as a last resort. */ +static struct grub_font_glyph *unknown_glyph; + +/* The font structure used when no other font is loaded. This functions + as a "Null Object" pattern, so that code everywhere does not have to + check for a NULL grub_font_t to avoid dereferencing a null pointer. */ +static struct grub_font null_font; + +/* Flag to ensure module is initialized only once. */ +static grub_uint8_t font_loader_initialized; + +#if HAVE_FONT_SOURCE +static struct grub_font_glyph *ascii_font_glyph[0x80]; +#endif + +static struct grub_font_glyph * +ascii_glyph_lookup (grub_uint32_t code) +{ +#if HAVE_FONT_SOURCE + static int ascii_failback_initialized = 0; + + if (code >= 0x80) + return NULL; + + if (ascii_failback_initialized == 0) + { + int current; + for (current = 0; current < 0x80; current++) + { + ascii_font_glyph[current] = + grub_malloc (sizeof (struct grub_font_glyph) + ASCII_BITMAP_SIZE); + if (ascii_font_glyph[current] == NULL) + { + ascii_font_glyph[current] = unknown_glyph; + continue; + } + + ascii_font_glyph[current]->width = 8; + ascii_font_glyph[current]->height = 16; + ascii_font_glyph[current]->offset_x = 0; + ascii_font_glyph[current]->offset_y = -2; + ascii_font_glyph[current]->device_width = 8; + ascii_font_glyph[current]->font = &null_font; + + grub_memcpy (ascii_font_glyph[current]->bitmap, + &ascii_bitmaps[current * ASCII_BITMAP_SIZE], + ASCII_BITMAP_SIZE); + } + + ascii_failback_initialized = 1; + } + + return ascii_font_glyph[code]; +#else + (void) code; + return NULL; +#endif +} + +void +grub_font_loader_init (void) +{ + /* Only initialize font loader once. */ + if (font_loader_initialized) + return; + + /* Make glyph for unknown glyph. */ + unknown_glyph = grub_malloc (sizeof (struct grub_font_glyph) + + sizeof (unknown_glyph_bitmap)); + if (!unknown_glyph) + return; + + unknown_glyph->width = 8; + unknown_glyph->height = 16; + unknown_glyph->offset_x = 0; + unknown_glyph->offset_y = -3; + unknown_glyph->device_width = 8; + unknown_glyph->font = &null_font; + grub_memcpy (unknown_glyph->bitmap, + unknown_glyph_bitmap, sizeof (unknown_glyph_bitmap)); + + /* Initialize the null font. */ + font_init (&null_font); + /* FIXME: Fix this slightly improper cast. */ + null_font.name = (char *) ""; + null_font.ascent = unknown_glyph->height - 3; + null_font.descent = 3; + null_font.max_char_width = unknown_glyph->width; + null_font.max_char_height = unknown_glyph->height; + + font_loader_initialized = 1; +} + +/* Initialize the font object with initial default values. */ +static void +font_init (grub_font_t font) +{ + font->name = 0; + font->file = 0; + font->family = 0; + font->point_size = 0; + font->weight = 0; + + /* Default leading value, not in font file yet. */ + font->leading = 1; + + font->max_char_width = 0; + font->max_char_height = 0; + font->ascent = 0; + font->descent = 0; + font->num_chars = 0; + font->char_index = 0; + font->bmp_idx = 0; +} + +/* Open the next section in the file. + + On success, the section name is stored in section->name and the length in + section->length, and 0 is returned. On failure, 1 is returned and + grub_errno is set appropriately with an error message. + + If 1 is returned due to being at the end of the file, then section->eof is + set to 1; otherwise, section->eof is set to 0. */ +static int +open_section (grub_file_t file, struct font_file_section *section) +{ + grub_ssize_t retval; + grub_uint32_t raw_length; + + section->file = file; + section->eof = 0; + + /* Read the FOURCC section name. */ + retval = grub_file_read (file, section->name, 4); + if (retval >= 0 && retval < 4) + { + /* EOF encountered. */ + section->eof = 1; + return 1; + } + else if (retval < 0) + { + /* Read error. */ + return 1; + } + + /* Read the big-endian 32-bit section length. */ + retval = grub_file_read (file, &raw_length, 4); + if (retval >= 0 && retval < 4) + { + /* EOF encountered. */ + section->eof = 1; + return 1; + } + else if (retval < 0) + { + /* Read error. */ + return 1; + } + + /* Convert byte-order and store in *length. */ + section->length = grub_be_to_cpu32 (raw_length); + + return 0; +} + +/* Size in bytes of each character index (CHIX section) + entry in the font file. */ +#define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4) + +/* Load the character index (CHIX) section contents from the font file. This + presumes that the position of FILE is positioned immediately after the + section length for the CHIX section (i.e., at the start of the section + contents). Returns 0 upon success, nonzero for failure (in which case + grub_errno is set appropriately). */ +static int +load_font_index (grub_file_t file, grub_uint32_t sect_length, struct + grub_font *font) +{ + unsigned i; + grub_uint32_t last_code; + +#if FONT_DEBUG >= 2 + grub_dprintf ("font", "load_font_index(sect_length=%d)\n", sect_length); +#endif + + /* Sanity check: ensure section length is divisible by the entry size. */ + if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "font file format error: character index length %d " + "is not a multiple of the entry size %d", + sect_length, FONT_CHAR_INDEX_ENTRY_SIZE); + return 1; + } + + /* Calculate the number of characters. */ + font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE; + + /* Allocate the character index array. */ + font->char_index = grub_calloc (font->num_chars, sizeof (struct char_index_entry)); + if (!font->char_index) + return 1; + font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t)); + if (!font->bmp_idx) + return 1; + + /* Init the BMP index array to 0xffff. */ + grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t)); + + +#if FONT_DEBUG >= 2 + grub_dprintf ("font", "num_chars=%d)\n", font->num_chars); +#endif + + last_code = 0; + + /* Load the character index data from the file. */ + for (i = 0; i < font->num_chars; i++) + { + struct char_index_entry *entry = &font->char_index[i]; + + /* Read code point value; convert to native byte order. */ + if (grub_file_read (file, &entry->code, 4) != 4) + return 1; + entry->code = grub_be_to_cpu32 (entry->code); + + /* Verify that characters are in ascending order. */ + if (i != 0 && entry->code <= last_code) + { + grub_error (GRUB_ERR_BAD_FONT, + "font characters not in ascending order: %u <= %u", + entry->code, last_code); + return 1; + } + + if (entry->code < 0x10000 && i < 0xffff) + font->bmp_idx[entry->code] = i; + + last_code = entry->code; + + /* Read storage flags byte. */ + if (grub_file_read (file, &entry->storage_flags, 1) != 1) + return 1; + + /* Read glyph data offset; convert to native byte order. */ + if (grub_file_read (file, &entry->offset, 4) != 4) + return 1; + entry->offset = grub_be_to_cpu32 (entry->offset); + + /* No glyph loaded. Will be loaded on demand and cached thereafter. */ + entry->glyph = 0; + +#if FONT_DEBUG >= 5 + /* Print the 1st 10 characters. */ + if (i < 10) + grub_dprintf ("font", "c=%d o=%d\n", entry->code, entry->offset); +#endif + } + + return 0; +} + +/* Read the contents of the specified section as a string, which is + allocated on the heap. Returns 0 if there is an error. */ +static char * +read_section_as_string (struct font_file_section *section) +{ + char *str; + grub_size_t sz; + grub_ssize_t ret; + + if (grub_add (section->length, 1, &sz)) + return NULL; + + str = grub_malloc (sz); + if (!str) + return 0; + + ret = grub_file_read (section->file, str, section->length); + if (ret < 0 || ret != (grub_ssize_t) section->length) + { + grub_free (str); + return 0; + } + + str[section->length] = '\0'; + return str; +} + +/* Read the contents of the current section as a 16-bit integer value, + which is stored into *VALUE. + Returns 0 upon success, nonzero upon failure. */ +static int +read_section_as_short (struct font_file_section *section, + grub_int16_t * value) +{ + grub_uint16_t raw_value; + + if (section->length != 2) + { + grub_error (GRUB_ERR_BAD_FONT, + "font file format error: section %c%c%c%c length " + "is %d but should be 2", + section->name[0], section->name[1], + section->name[2], section->name[3], section->length); + return 1; + } + if (grub_file_read (section->file, &raw_value, 2) != 2) + return 1; + + *value = grub_be_to_cpu16 (raw_value); + return 0; +} + +static grub_file_t +try_open_from_prefix (const char *prefix, const char *filename) +{ + grub_file_t file; + char *fullname, *ptr; + + fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1 + + sizeof ("/fonts/") + sizeof (".pf2")); + if (!fullname) + return NULL; + ptr = grub_stpcpy (fullname, prefix); + ptr = grub_stpcpy (ptr, "/fonts/"); + ptr = grub_stpcpy (ptr, filename); + ptr = grub_stpcpy (ptr, ".pf2"); + *ptr = '\0'; + + file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024); + grub_free (fullname); + return file; +} + +/* Load a font and add it to the beginning of the global font list. + Returns 0 upon success, nonzero upon failure. */ +grub_font_t +grub_font_load (const char *filename) +{ + grub_file_t file = 0; + struct font_file_section section; + char magic[4]; + grub_font_t font = 0; + +#if FONT_DEBUG >= 1 + grub_dprintf ("font", "add_font(%s)\n", filename); +#endif + + if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+') + file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024); + else + { + file = try_open_from_prefix ("(memdisk)", filename); + if (!file) + { + const char *prefix = grub_env_get ("prefix"); + + if (!prefix) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); + goto fail; + } + file = try_open_from_prefix (prefix, filename); + } + } + if (!file) + goto fail; + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "file opened\n"); +#endif + + /* Read the FILE section. It indicates the file format. */ + if (open_section (file, §ion) != 0) + goto fail; + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "opened FILE section\n"); +#endif + if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE, + sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "font file format error: 1st section must be FILE"); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "section name ok\n"); +#endif + if (section.length != 4) + { + grub_error (GRUB_ERR_BAD_FONT, + "font file format error (file type ID length is %d " + "but should be 4)", section.length); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "section length ok\n"); +#endif + /* Check the file format type code. */ + if (grub_file_read (file, magic, 4) != 4) + goto fail; + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "read magic ok\n"); +#endif + + if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0) + { + grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x", + magic[0], magic[1], magic[2], magic[3]); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "compare magic ok\n"); +#endif + + /* Allocate the font object. */ + font = (grub_font_t) grub_zalloc (sizeof (struct grub_font)); + if (!font) + goto fail; + + font_init (font); + font->file = file; + +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "allocate font ok; loading font info\n"); +#endif + + /* Load the font information. */ + while (1) + { + if (open_section (file, §ion) != 0) + { + if (section.eof) + break; /* Done reading the font file. */ + else + goto fail; + } + +#if FONT_DEBUG >= 2 + grub_dprintf ("font", "opened section %c%c%c%c ok\n", + section.name[0], section.name[1], + section.name[2], section.name[3]); +#endif + + if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME, + sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0) + { + if (font->name != NULL) + { + grub_error (GRUB_ERR_BAD_FONT, "invalid font file: too many NAME sections"); + goto fail; + } + + font->name = read_section_as_string (§ion); + if (!font->name) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_POINT_SIZE, + sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) - + 1) == 0) + { + if (read_section_as_short (§ion, &font->point_size) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT, + sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1) + == 0) + { + char *wt; + wt = read_section_as_string (§ion); + if (!wt) + continue; + /* Convert the weight string 'normal' or 'bold' into a number. */ + if (grub_strcmp (wt, "normal") == 0) + font->weight = FONT_WEIGHT_NORMAL; + else if (grub_strcmp (wt, "bold") == 0) + font->weight = FONT_WEIGHT_BOLD; + grub_free (wt); + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH, + sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH) + - 1) == 0) + { + if (read_section_as_short (§ion, &font->max_char_width) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT, + sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT) + - 1) == 0) + { + if (read_section_as_short (§ion, &font->max_char_height) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_ASCENT, + sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1) + == 0) + { + if (read_section_as_short (§ion, &font->ascent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT, + sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1) + == 0) + { + if (read_section_as_short (§ion, &font->descent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, + FONT_FORMAT_SECTION_NAMES_CHAR_INDEX, + sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) - + 1) == 0) + { + if (load_font_index (file, section.length, font) != 0) + goto fail; + } + else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA, + sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0) + { + /* When the DATA section marker is reached, we stop reading. */ + break; + } + else + { + /* Unhandled section type, simply skip past it. */ +#if FONT_DEBUG >= 3 + grub_dprintf ("font", "Unhandled section type, skipping.\n"); +#endif + grub_off_t section_end = grub_file_tell (file) + section.length; + if ((int) grub_file_seek (file, section_end) == -1) + goto fail; + } + } + + if (!font->name) + { + grub_dprintf ("font", "Font has no name.\n"); + font->name = grub_strdup ("Unknown"); + } + +#if FONT_DEBUG >= 1 + grub_dprintf ("font", "Loaded font `%s'.\n" + "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n", + font->name, + font->ascent, font->descent, + font->max_char_width, font->max_char_height, font->num_chars); +#endif + + if (font->max_char_width <= 0 + || font->max_char_height <= 0 + || font->num_chars == 0 + || font->char_index == 0 || font->ascent == 0 || font->descent == 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "invalid font file: missing some required data"); + goto fail; + } + + /* Add the font to the global font registry. */ + if (register_font (font) != 0) + goto fail; + + return font; + +fail: + if (file) + grub_file_close (file); + if (font) + font->file = 0; + + free_font (font); + return 0; +} + +/* Read a 16-bit big-endian integer from FILE, convert it to native byte + order, and store it in *VALUE. + Returns 0 on success, 1 on failure. */ +static int +read_be_uint16 (grub_file_t file, grub_uint16_t * value) +{ + if (grub_file_read (file, value, 2) != 2) + return 1; + *value = grub_be_to_cpu16 (*value); + return 0; +} + +static int +read_be_int16 (grub_file_t file, grub_int16_t * value) +{ + /* For the signed integer version, use the same code as for unsigned. */ + return read_be_uint16 (file, (grub_uint16_t *) value); +} + +/* Return a pointer to the character index entry for the glyph corresponding to + the codepoint CODE in the font FONT. If not found, return zero. */ +static inline struct char_index_entry * +find_glyph (const grub_font_t font, grub_uint32_t code) +{ + struct char_index_entry *table, *first, *end; + grub_size_t len; + + table = font->char_index; + if (table == NULL) + return NULL; + + /* Use BMP index if possible. */ + if (code < 0x10000 && font->bmp_idx) + { + if (font->bmp_idx[code] < 0xffff) + return &table[font->bmp_idx[code]]; + /* + * When we are here then lookup in BMP index result in miss, + * fallthough to binary-search. + */ + } + + /* + * Do a binary search in char_index which is ordered by code point. + * The code below is the same as libstdc++'s std::lower_bound(). + */ + first = table; + len = font->num_chars; + end = first + len; + + while (len > 0) + { + grub_size_t half = len >> 1; + struct char_index_entry *middle = first + half; + + if (middle->code < code) + { + first = middle + 1; + len = len - half - 1; + } + else + len = half; + } + + return (first < end && first->code == code) ? first : NULL; +} + +/* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded + from the font file if has not been loaded yet. + Returns a pointer to the glyph if found, or 0 if it is not found. */ +static struct grub_font_glyph * +grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) +{ + struct char_index_entry *index_entry; + + index_entry = find_glyph (font, code); + if (index_entry) + { + struct grub_font_glyph *glyph = 0; + grub_uint16_t width; + grub_uint16_t height; + grub_int16_t xoff; + grub_int16_t yoff; + grub_int16_t dwidth; + grub_ssize_t len; + grub_size_t sz; + + if (index_entry->glyph) + /* Return cached glyph. */ + return index_entry->glyph; + + if (!font->file) + /* No open file, can't load any glyphs. */ + return 0; + + /* Make sure we can find glyphs for error messages. Push active + error message to error stack and reset error message. */ + grub_error_push (); + + grub_file_seek (font->file, index_entry->offset); + + /* Read the glyph width, height, and baseline. */ + if (read_be_uint16 (font->file, &width) != 0 + || read_be_uint16 (font->file, &height) != 0 + || read_be_int16 (font->file, &xoff) != 0 + || read_be_int16 (font->file, &yoff) != 0 + || read_be_int16 (font->file, &dwidth) != 0 + || width > font->max_char_width + || height > font->max_char_height) + { + remove_font (font); + return 0; + } + + /* Calculate real struct size of current glyph. */ + if (grub_video_bitmap_calc_1bpp_bufsz (width, height, &len) || + grub_add (sizeof (struct grub_font_glyph), len, &sz)) + { + remove_font (font); + return 0; + } + + /* Allocate and initialize the glyph struct. */ + glyph = grub_malloc (sz); + if (glyph == NULL) + { + remove_font (font); + return 0; + } + + glyph->font = font; + glyph->width = width; + glyph->height = height; + glyph->offset_x = xoff; + glyph->offset_y = yoff; + glyph->device_width = dwidth; + + /* Don't try to read empty bitmaps (e.g., space characters). */ + if (len != 0) + { + if (grub_file_read (font->file, glyph->bitmap, len) != len) + { + remove_font (font); + grub_free (glyph); + return 0; + } + } + + /* Restore old error message. */ + grub_error_pop (); + + /* Cache the glyph. */ + index_entry->glyph = glyph; + + return glyph; + } + + return 0; +} + +/* Free the memory used by FONT. + This should not be called if the font has been made available to + users (once it is added to the global font list), since there would + be the possibility of a dangling pointer. */ +static void +free_font (grub_font_t font) +{ + if (font) + { + if (font->file) + grub_file_close (font->file); + grub_free (font->name); + grub_free (font->family); + grub_free (font->char_index); + grub_free (font->bmp_idx); + grub_free (font); + } +} + +/* Add FONT to the global font registry. + Returns 0 upon success, nonzero on failure + (the font was not registered). */ +static int +register_font (grub_font_t font) +{ + struct grub_font_node *node = 0; + + node = grub_malloc (sizeof (struct grub_font_node)); + if (!node) + return 1; + + node->value = font; + node->next = grub_font_list; + grub_font_list = node; + + return 0; +} + +/* Remove the font from the global font list. We don't actually free the + font's memory since users could be holding references to the font. */ +static void +remove_font (grub_font_t font) +{ + struct grub_font_node **nextp, *cur; + + for (nextp = &grub_font_list, cur = *nextp; + cur; nextp = &cur->next, cur = cur->next) + { + if (cur->value == font) + { + *nextp = cur->next; + + /* Free the node, but not the font itself. */ + grub_free (cur); + + return; + } + } +} + +/* Get a font from the list of loaded fonts. This function will return + another font if the requested font is not available. If no fonts are + loaded, then a special 'null font' is returned, which contains no glyphs, + but is not a null pointer so the caller may omit checks for NULL. */ +grub_font_t +grub_font_get (const char *font_name) +{ + struct grub_font_node *node; + + for (node = grub_font_list; node; node = node->next) + { + grub_font_t font = node->value; + if (grub_strcmp (font->name, font_name) == 0) + return font; + } + + /* If no font by that name is found, return the first font in the list + as a fallback. */ + if (grub_font_list && grub_font_list->value) + return grub_font_list->value; + else + /* The null_font is a last resort. */ + return &null_font; +} + +/* Get the full name of the font. */ +const char * +grub_font_get_name (grub_font_t font) +{ + return font->name; +} + +/* Get the maximum width of any character in the font in pixels. */ +int +grub_font_get_max_char_width (grub_font_t font) +{ + return font->max_char_width; +} + +/* Get the distance in pixels from the baseline to the lowest descenders + (for instance, in a lowercase 'y', 'g', etc.). */ +int +grub_font_get_descent (grub_font_t font) +{ + return font->descent; +} + +/* FIXME: not correct for all fonts. */ +int +grub_font_get_xheight (grub_font_t font) +{ + return font->ascent / 2; +} + +/* Get the *standard leading* of the font in pixel, which is the spacing + between two lines of text. Specifically, it is the space between the + descent of one line and the ascent of the next line. This is included + in the *height* metric. */ +int +grub_font_get_leading (grub_font_t font) +{ + return font->leading; +} + +/* Get the distance in pixels between baselines of adjacent lines of text. */ +int +grub_font_get_height (grub_font_t font) +{ + return font->ascent + font->descent + font->leading; +} + +/* Get the glyph for FONT corresponding to the Unicode code point CODE. + Returns the ASCII glyph for the code if no other fonts are available. + The glyphs are cached once loaded. */ +struct grub_font_glyph * +grub_font_get_glyph (grub_font_t font, grub_uint32_t code) +{ + struct grub_font_glyph *glyph = 0; + if (font) + glyph = grub_font_get_glyph_internal (font, code); + if (glyph == 0) + { + glyph = ascii_glyph_lookup (code); + } + return glyph; +} + + +/* Calculate a subject value representing "how similar" two fonts are. + This is used to prioritize the order that fonts are scanned for missing + glyphs. The object is to select glyphs from the most similar font + possible, for the best appearance. + The heuristic is crude, but it helps greatly when fonts of similar + sizes are used so that tiny 8 point glyphs are not mixed into a string + of 24 point text unless there is no other choice. */ +static int +get_font_diversity (grub_font_t a, grub_font_t b) +{ + int d; + + d = 0; + + if (a->ascent && b->ascent) + d += grub_abs (a->ascent - b->ascent) * 8; + else + /* Penalty for missing attributes. */ + d += 50; + + if (a->max_char_height && b->max_char_height) + d += grub_abs (a->max_char_height - b->max_char_height) * 8; + else + /* Penalty for missing attributes. */ + d += 50; + + /* Weight is a minor factor. */ + d += (a->weight != b->weight) ? 5 : 0; + + return d; +} + +/* Get a glyph corresponding to the codepoint CODE. If FONT contains the + specified glyph, then it is returned. Otherwise, all other loaded fonts + are searched until one is found that contains a glyph for CODE. + If no glyph is available for CODE in the loaded fonts, then a glyph + representing an unknown character is returned. + This function never returns NULL. + The returned glyph is owned by the font manager and should not be freed + by the caller. The glyphs are cached. */ +struct grub_font_glyph * +grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code) +{ + struct grub_font_glyph *glyph; + struct grub_font_node *node; + /* Keep track of next node, in case there's an I/O error in + grub_font_get_glyph_internal() and the font is removed from the list. */ + struct grub_font_node *next; + /* Information on the best glyph found so far, to help find the glyph in + the best matching to the requested one. */ + int best_diversity; + struct grub_font_glyph *best_glyph; + + if (font) + { + /* First try to get the glyph from the specified font. */ + glyph = grub_font_get_glyph_internal (font, code); + if (glyph) + return glyph; + } + + /* Otherwise, search all loaded fonts for the glyph and use the one from + the font that best matches the requested font. */ + best_diversity = 10000; + best_glyph = 0; + + for (node = grub_font_list; node; node = next) + { + grub_font_t curfont; + + curfont = node->value; + next = node->next; + + glyph = grub_font_get_glyph_internal (curfont, code); + if (glyph && !font) + return glyph; + if (glyph) + { + int d; + + d = get_font_diversity (curfont, font); + if (d < best_diversity) + { + best_diversity = d; + best_glyph = glyph; + } + } + } + + return best_glyph; +} + +/* FIXME: suboptimal. */ +static void +grub_font_blit_glyph (struct grub_font_glyph *target, + struct grub_font_glyph *src, unsigned dx, unsigned dy) +{ + grub_uint16_t max_x, max_y; + unsigned src_bit, tgt_bit, src_byte, tgt_byte; + unsigned i, j; + + /* Harden against out-of-bound writes. */ + if ((grub_add (dx, src->width, &max_x) || max_x > target->width) || + (grub_add (dy, src->height, &max_y) || max_y > target->height)) + return; + + for (i = 0; i < src->height; i++) + { + src_bit = (src->width * i) % 8; + src_byte = (src->width * i) / 8; + tgt_bit = (target->width * (dy + i) + dx) % 8; + tgt_byte = (target->width * (dy + i) + dx) / 8; + for (j = 0; j < src->width; j++) + { + target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit) + & 0x80) >> tgt_bit; + src_bit++; + tgt_bit++; + if (src_bit == 8) + { + src_byte++; + src_bit = 0; + } + if (tgt_bit == 8) + { + tgt_byte++; + tgt_bit = 0; + } + } + } +} + +static void +grub_font_blit_glyph_mirror (struct grub_font_glyph *target, + struct grub_font_glyph *src, + unsigned dx, unsigned dy) +{ + grub_uint16_t max_x, max_y; + unsigned tgt_bit, src_byte, tgt_byte; + signed src_bit; + unsigned i, j; + + /* Harden against out-of-bound writes. */ + if ((grub_add (dx, src->width, &max_x) || max_x > target->width) || + (grub_add (dy, src->height, &max_y) || max_y > target->height)) + return; + + for (i = 0; i < src->height; i++) + { + src_bit = (src->width * i + src->width - 1) % 8; + src_byte = (src->width * i + src->width - 1) / 8; + tgt_bit = (target->width * (dy + i) + dx) % 8; + tgt_byte = (target->width * (dy + i) + dx) / 8; + for (j = 0; j < src->width; j++) + { + target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit) + & 0x80) >> tgt_bit; + src_bit--; + tgt_bit++; + if (src_bit == -1) + { + src_byte--; + src_bit = 7; + } + if (tgt_bit == 8) + { + tgt_byte++; + tgt_bit = 0; + } + } + } +} + +/* Context for blit_comb. */ +struct blit_comb_ctx +{ + struct grub_font_glyph *glyph; + int *device_width; + struct grub_video_signed_rect bounds; +}; + +/* Helper for blit_comb. */ +static void +do_blit (struct grub_font_glyph *src, signed dx, signed dy, + struct blit_comb_ctx *ctx) +{ + if (ctx->glyph) + grub_font_blit_glyph (ctx->glyph, src, dx - ctx->glyph->offset_x, + (ctx->glyph->height + ctx->glyph->offset_y) + dy); + if (dx < ctx->bounds.x) + { + ctx->bounds.width += ctx->bounds.x - dx; + ctx->bounds.x = dx; + } + if (ctx->bounds.y > -src->height - dy) + { + ctx->bounds.height += ctx->bounds.y - (-src->height - dy); + ctx->bounds.y = (-src->height - dy); + } + if (dx + src->width - ctx->bounds.x >= (signed) ctx->bounds.width) + ctx->bounds.width = dx + src->width - ctx->bounds.x + 1; + if ((signed) ctx->bounds.height < src->height + (-src->height - dy) + - ctx->bounds.y) + ctx->bounds.height = src->height + (-src->height - dy) - ctx->bounds.y; +} + +/* Helper for blit_comb. */ +static inline void +add_device_width (int val, struct blit_comb_ctx *ctx) +{ + if (ctx->glyph) + ctx->glyph->device_width += val; + if (ctx->device_width) + *ctx->device_width += val; +} + +static void +blit_comb (const struct grub_unicode_glyph *glyph_id, + struct grub_font_glyph *glyph, + struct grub_video_signed_rect *bounds_out, + struct grub_font_glyph *main_glyph, + struct grub_font_glyph **combining_glyphs, int *device_width) +{ + struct blit_comb_ctx ctx = { + .glyph = glyph, + .device_width = device_width + }; + unsigned i; + signed above_rightx, above_righty; + signed above_leftx, above_lefty; + signed below_rightx, below_righty; + signed min_devwidth = 0; + const struct grub_unicode_combining *comb; + + if (glyph) + glyph->device_width = main_glyph->device_width; + if (device_width) + *device_width = main_glyph->device_width; + + ctx.bounds.x = main_glyph->offset_x; + ctx.bounds.y = main_glyph->offset_y; + ctx.bounds.width = main_glyph->width; + ctx.bounds.height = main_glyph->height; + + above_rightx = main_glyph->offset_x + main_glyph->width; + above_righty = ctx.bounds.y + (int) ctx.bounds.height; + + above_leftx = main_glyph->offset_x; + above_lefty = ctx.bounds.y + (int) ctx.bounds.height; + + below_rightx = ctx.bounds.x + (int) ctx.bounds.width; + below_righty = ctx.bounds.y; + + comb = grub_unicode_get_comb (glyph_id); + + for (i = 0; i < glyph_id->ncomb; i++) + { + grub_int16_t space = 0; + /* Center by default. */ + grub_int16_t targetx; + + if (!combining_glyphs[i]) + continue; + targetx = ((int) ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x; + /* CGJ is to avoid diacritics reordering. */ + if (comb[i].code + == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER) + continue; + switch (comb[i].type) + { + case GRUB_UNICODE_COMB_OVERLAY: + do_blit (combining_glyphs[i], + targetx, + ((int) ctx.bounds.height - combining_glyphs[i]->height) / 2 + - ((int) ctx.bounds.height + ctx.bounds.y), &ctx); + if (min_devwidth < combining_glyphs[i]->width) + min_devwidth = combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT: + do_blit (combining_glyphs[i], above_rightx, -above_righty, &ctx); + above_rightx += combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_ABOVE_RIGHT: + do_blit (combining_glyphs[i], above_rightx, + -(above_righty + combining_glyphs[i]->height), &ctx); + above_rightx += combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_ABOVE_LEFT: + above_leftx -= combining_glyphs[i]->width; + do_blit (combining_glyphs[i], above_leftx, + -(above_lefty + combining_glyphs[i]->height), &ctx); + break; + + case GRUB_UNICODE_COMB_BELOW_RIGHT: + do_blit (combining_glyphs[i], below_rightx, below_righty, &ctx); + below_rightx += combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_HEBREW_HOLAM: + if (glyph_id->base != GRUB_UNICODE_HEBREW_WAW) + targetx = + main_glyph->offset_x - combining_glyphs[i]->width - + (combining_glyphs[i]->width + 3) / 4; + goto above_on_main; + + case GRUB_UNICODE_COMB_HEBREW_SIN_DOT: + targetx = main_glyph->offset_x + combining_glyphs[i]->width / 4; + goto above_on_main; + + case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT: + targetx = + main_glyph->width + main_glyph->offset_x - + combining_glyphs[i]->width; + above_on_main: + space = combining_glyphs[i]->offset_y + - grub_font_get_xheight (combining_glyphs[i]->font) - 1; + if (space <= 0) + space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8; + do_blit (combining_glyphs[i], targetx, + -(main_glyph->height + main_glyph->offset_y + space + + combining_glyphs[i]->height), &ctx); + if (min_devwidth < combining_glyphs[i]->width) + min_devwidth = combining_glyphs[i]->width; + break; + + /* TODO: Put dammah, fathah and alif nearer to shadda. */ + case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH: + case GRUB_UNICODE_COMB_ARABIC_DAMMAH: + case GRUB_UNICODE_COMB_ARABIC_DAMMATAN: + case GRUB_UNICODE_COMB_ARABIC_FATHATAN: + case GRUB_UNICODE_COMB_ARABIC_FATHAH: + case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF: + case GRUB_UNICODE_COMB_ARABIC_SUKUN: + case GRUB_UNICODE_COMB_ARABIC_SHADDA: + case GRUB_UNICODE_COMB_HEBREW_RAFE: + case GRUB_UNICODE_STACK_ABOVE: + stacked_above: + space = combining_glyphs[i]->offset_y + - grub_font_get_xheight (combining_glyphs[i]->font) - 1; + if (space <= 0) + space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8; + /* Fallthrough. */ + case GRUB_UNICODE_STACK_ATTACHED_ABOVE: + do_blit (combining_glyphs[i], targetx, + -((int) ctx.bounds.height + ctx.bounds.y + space + + combining_glyphs[i]->height), &ctx); + if (min_devwidth < combining_glyphs[i]->width) + min_devwidth = combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_HEBREW_DAGESH: + do_blit (combining_glyphs[i], targetx, + -((int) ctx.bounds.height / 2 + ctx.bounds.y + + combining_glyphs[i]->height / 2), &ctx); + if (min_devwidth < combining_glyphs[i]->width) + min_devwidth = combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_HEBREW_SHEVA: + case GRUB_UNICODE_COMB_HEBREW_HIRIQ: + case GRUB_UNICODE_COMB_HEBREW_QAMATS: + case GRUB_UNICODE_COMB_HEBREW_TSERE: + case GRUB_UNICODE_COMB_HEBREW_SEGOL: + /* TODO: placement in final kaf and under reish. */ + + case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL: + case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH: + case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS: + case GRUB_UNICODE_COMB_HEBREW_PATAH: + case GRUB_UNICODE_COMB_HEBREW_QUBUTS: + case GRUB_UNICODE_COMB_HEBREW_METEG: + /* TODO: Put kasra and kasratan under shadda. */ + case GRUB_UNICODE_COMB_ARABIC_KASRA: + case GRUB_UNICODE_COMB_ARABIC_KASRATAN: + /* I don't know how ypogegrammeni differs from subscript. */ + case GRUB_UNICODE_COMB_YPOGEGRAMMENI: + case GRUB_UNICODE_STACK_BELOW: + stacked_below: + space = -(combining_glyphs[i]->offset_y + + combining_glyphs[i]->height); + if (space <= 0) + space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8; + /* Fallthrough. */ + + case GRUB_UNICODE_STACK_ATTACHED_BELOW: + do_blit (combining_glyphs[i], targetx, -(ctx.bounds.y - space), + &ctx); + if (min_devwidth < combining_glyphs[i]->width) + min_devwidth = combining_glyphs[i]->width; + break; + + case GRUB_UNICODE_COMB_MN: + switch (comb[i].code) + { + case GRUB_UNICODE_THAANA_ABAFILI: + case GRUB_UNICODE_THAANA_AABAAFILI: + case GRUB_UNICODE_THAANA_UBUFILI: + case GRUB_UNICODE_THAANA_OOBOOFILI: + case GRUB_UNICODE_THAANA_EBEFILI: + case GRUB_UNICODE_THAANA_EYBEYFILI: + case GRUB_UNICODE_THAANA_OBOFILI: + case GRUB_UNICODE_THAANA_OABOAFILI: + case GRUB_UNICODE_THAANA_SUKUN: + goto stacked_above; + case GRUB_UNICODE_THAANA_IBIFILI: + case GRUB_UNICODE_THAANA_EEBEEFILI: + goto stacked_below; + } + /* Fall through. */ + default: + { + /* Default handling. Just draw combining character on top + of base character. + FIXME: support more unicode types correctly. + */ + do_blit (combining_glyphs[i], + main_glyph->device_width + + combining_glyphs[i]->offset_x, + -(combining_glyphs[i]->height + + combining_glyphs[i]->offset_y), &ctx); + add_device_width (combining_glyphs[i]->device_width, &ctx); + } + } + } + add_device_width ((above_rightx > + below_rightx ? above_rightx : below_rightx) - + (main_glyph->offset_x + main_glyph->width), &ctx); + add_device_width (above_leftx - main_glyph->offset_x, &ctx); + if (glyph && glyph->device_width < min_devwidth) + glyph->device_width = min_devwidth; + if (device_width && *device_width < min_devwidth) + *device_width = min_devwidth; + + if (bounds_out) + *bounds_out = ctx.bounds; +} + +static struct grub_font_glyph * +grub_font_construct_dry_run (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id, + struct grub_video_signed_rect *bounds, + struct grub_font_glyph **combining_glyphs, + int *device_width) +{ + struct grub_font_glyph *main_glyph = NULL; + grub_uint32_t desired_attributes = 0; + unsigned i; + grub_uint32_t base = glyph_id->base; + const struct grub_unicode_combining *comb; + + if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED) + desired_attributes |= GRUB_FONT_CODE_RIGHT_JOINED; + + if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED) + desired_attributes |= GRUB_FONT_CODE_LEFT_JOINED; + + comb = grub_unicode_get_comb (glyph_id); + + if (base == 'i' || base == 'j') + { + for (i = 0; i < glyph_id->ncomb; i++) + if (comb[i].type == GRUB_UNICODE_STACK_ABOVE) + break; + if (i < glyph_id->ncomb && base == 'i') + base = GRUB_UNICODE_DOTLESS_LOWERCASE_I; + if (i < glyph_id->ncomb && base == 'j') + base = GRUB_UNICODE_DOTLESS_LOWERCASE_J; + } + + main_glyph = grub_font_get_glyph_with_fallback (hinted_font, base + | desired_attributes); + + if (!main_glyph) + main_glyph = grub_font_get_glyph_with_fallback (hinted_font, + base); + + /* Glyph not available in any font. Use ASCII fallback. */ + if (!main_glyph) + main_glyph = ascii_glyph_lookup (base); + + /* Glyph not available in any font. Return unknown glyph. */ + if (!main_glyph) + return NULL; + + if (device_width) + *device_width = main_glyph->device_width; + + if (!glyph_id->ncomb && !glyph_id->attributes) + return main_glyph; + + if (glyph_id->ncomb && !combining_glyphs) + { + grub_errno = GRUB_ERR_NONE; + return main_glyph; + } + + for (i = 0; i < glyph_id->ncomb; i++) + combining_glyphs[i] + = grub_font_get_glyph_with_fallback (main_glyph->font, + comb[i].code); + + blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs, + device_width); + + return main_glyph; +} + +static struct grub_font_glyph **render_combining_glyphs = 0; +static grub_size_t render_max_comb_glyphs = 0; + +static void +ensure_comb_space (const struct grub_unicode_glyph *glyph_id) +{ + if (glyph_id->ncomb <= render_max_comb_glyphs) + return; + + if (grub_mul (glyph_id->ncomb, 2, &render_max_comb_glyphs)) + render_max_comb_glyphs = 0; + if (render_max_comb_glyphs > 0 && render_max_comb_glyphs < 8) + render_max_comb_glyphs = 8; + grub_free (render_combining_glyphs); + render_combining_glyphs = (render_max_comb_glyphs > 0) ? + grub_calloc (render_max_comb_glyphs, sizeof (render_combining_glyphs[0])) : NULL; + if (!render_combining_glyphs) + { + render_max_comb_glyphs = 0; + grub_errno = GRUB_ERR_NONE; + } +} + +int +grub_font_get_constructed_device_width (grub_font_t hinted_font, + const struct grub_unicode_glyph + *glyph_id) +{ + int ret; + struct grub_font_glyph *main_glyph; + + ensure_comb_space (glyph_id); + + main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL, + render_combining_glyphs, &ret); + if (!main_glyph) + return unknown_glyph->device_width; + return ret; +} + +struct grub_font_glyph * +grub_font_construct_glyph (grub_font_t hinted_font, + const struct grub_unicode_glyph *glyph_id) +{ + struct grub_font_glyph *main_glyph; + struct grub_video_signed_rect bounds; + static struct grub_font_glyph *glyph = 0; + static grub_size_t max_glyph_size = 0; + grub_size_t cur_glyph_size; + + ensure_comb_space (glyph_id); + + main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, + &bounds, render_combining_glyphs, + NULL); + + if (!main_glyph) + return unknown_glyph; + + if (!render_combining_glyphs && glyph_id->ncomb) + return main_glyph; + + if (!glyph_id->ncomb && !glyph_id->attributes) + return main_glyph; + + if (grub_video_bitmap_calc_1bpp_bufsz (bounds.width, bounds.height, &cur_glyph_size) || + grub_add (sizeof (*glyph), cur_glyph_size, &cur_glyph_size)) + return main_glyph; + + if (max_glyph_size < cur_glyph_size) + { + grub_free (glyph); + if (grub_mul (cur_glyph_size, 2, &max_glyph_size)) + max_glyph_size = 0; + glyph = max_glyph_size > 0 ? grub_malloc (max_glyph_size) : NULL; + } + if (!glyph) + { + max_glyph_size = 0; + grub_errno = GRUB_ERR_NONE; + return main_glyph; + } + + grub_memset (glyph, 0, cur_glyph_size); + + glyph->font = main_glyph->font; + if (bounds.width == 0 || bounds.height == 0 || + grub_cast (bounds.width, &glyph->width) || + grub_cast (bounds.height, &glyph->height) || + grub_cast (bounds.x, &glyph->offset_x) || + grub_cast (bounds.y, &glyph->offset_y)) + return main_glyph; + + if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR) + grub_font_blit_glyph_mirror (glyph, main_glyph, + main_glyph->offset_x - glyph->offset_x, + (glyph->height + glyph->offset_y) + - (main_glyph->height + + main_glyph->offset_y)); + else + grub_font_blit_glyph (glyph, main_glyph, + main_glyph->offset_x - glyph->offset_x, + (glyph->height + glyph->offset_y) + - (main_glyph->height + main_glyph->offset_y)); + + blit_comb (glyph_id, glyph, NULL, main_glyph, render_combining_glyphs, NULL); + + return glyph; +} + +/* Draw the specified glyph at (x, y). The y coordinate designates the + baseline of the character, while the x coordinate designates the left + side location of the character. */ +grub_err_t +grub_font_draw_glyph (struct grub_font_glyph * glyph, + grub_video_color_t color, int left_x, int baseline_y) +{ + struct grub_video_bitmap glyph_bitmap; + + /* Don't try to draw empty glyphs (U+0020, etc.). */ + if (glyph->width == 0 || glyph->height == 0) + return GRUB_ERR_NONE; + + glyph_bitmap.mode_info.width = glyph->width; + glyph_bitmap.mode_info.height = glyph->height; + glyph_bitmap.mode_info.mode_type + = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; + glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; + glyph_bitmap.mode_info.bpp = 1; + + /* Really 1 bit per pixel. */ + glyph_bitmap.mode_info.bytes_per_pixel = 0; + + /* Packed densely as bits. */ + glyph_bitmap.mode_info.pitch = glyph->width; + + glyph_bitmap.mode_info.number_of_colors = 2; + glyph_bitmap.mode_info.bg_red = 0; + glyph_bitmap.mode_info.bg_green = 0; + glyph_bitmap.mode_info.bg_blue = 0; + glyph_bitmap.mode_info.bg_alpha = 0; + grub_video_unmap_color (color, + &glyph_bitmap.mode_info.fg_red, + &glyph_bitmap.mode_info.fg_green, + &glyph_bitmap.mode_info.fg_blue, + &glyph_bitmap.mode_info.fg_alpha); + glyph_bitmap.data = glyph->bitmap; + + int bitmap_left = left_x + glyph->offset_x; + int bitmap_bottom = baseline_y - glyph->offset_y; + int bitmap_top = bitmap_bottom - glyph->height; + + return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND, + bitmap_left, bitmap_top, + 0, 0, glyph->width, glyph->height); +} diff --git a/font/font_cmd.c b/grub-core/font/font_cmd.c similarity index 74% rename from font/font_cmd.c rename to grub-core/font/font_cmd.c index 98216ae44..f3b36f2d6 100644 --- a/font/font_cmd.c +++ b/grub-core/font/font_cmd.c @@ -21,6 +21,7 @@ #include #include #include +#include static grub_err_t loadfont_command (grub_command_t cmd __attribute__ ((unused)), @@ -28,11 +29,15 @@ loadfont_command (grub_command_t cmd __attribute__ ((unused)), char **args) { if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no font specified"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); while (argc--) - if (grub_font_load (*args++) != 0) - return GRUB_ERR_BAD_FONT; + if (grub_font_load (*args++) == 0) + { + if (!grub_errno) + return grub_error (GRUB_ERR_BAD_FONT, "invalid font"); + return grub_errno; + } return GRUB_ERR_NONE; } @@ -44,7 +49,7 @@ lsfonts_command (grub_command_t cmd __attribute__ ((unused)), { struct grub_font_node *node; - grub_printf ("Loaded fonts:\n"); + grub_puts_ (N_("Loaded fonts:")); for (node = grub_font_list; node; node = node->next) { grub_font_t font = node->value; @@ -56,20 +61,28 @@ lsfonts_command (grub_command_t cmd __attribute__ ((unused)), static grub_command_t cmd_loadfont, cmd_lsfonts; -GRUB_MOD_INIT(font_manager) +#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_COREBOOT) +void grub_font_init (void) +#else +GRUB_MOD_INIT(font) +#endif { grub_font_loader_init (); cmd_loadfont = grub_register_command ("loadfont", loadfont_command, - "FILE...", - "Specify one or more font files to load."); + N_("FILE..."), + N_("Specify one or more font files to load.")); cmd_lsfonts = grub_register_command ("lsfonts", lsfonts_command, - 0, "List the loaded fonts."); + 0, N_("List the loaded fonts.")); } -GRUB_MOD_FINI(font_manager) +#if defined (GRUB_MACHINE_MIPS_LOONGSON) || defined (GRUB_MACHINE_COREBOOT) +void grub_font_fini (void) +#else +GRUB_MOD_FINI(font) +#endif { /* TODO: Determine way to free allocated resources. Warning: possible pointer references could be in use. */ diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c new file mode 100644 index 000000000..520a001c7 --- /dev/null +++ b/grub-core/fs/affs.c @@ -0,0 +1,719 @@ +/* affs.c - Amiga Fast FileSystem. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* The affs bootblock. */ +struct grub_affs_bblock +{ + grub_uint8_t type[3]; + grub_uint8_t flags; + grub_uint32_t checksum; + grub_uint32_t rootblock; +} GRUB_PACKED; + +/* Set if the filesystem is a AFFS filesystem. Otherwise this is an + OFS filesystem. */ +#define GRUB_AFFS_FLAG_FFS 1 + +/* The affs rootblock. */ +struct grub_affs_rblock +{ + grub_uint32_t type; + grub_uint8_t unused1[8]; + grub_uint32_t htsize; + grub_uint32_t unused2; + grub_uint32_t checksum; + grub_uint32_t hashtable[1]; +} GRUB_PACKED; + +struct grub_affs_time +{ + grub_int32_t day; + grub_uint32_t min; + grub_uint32_t hz; +} GRUB_PACKED; + +/* The second part of a file header block. */ +struct grub_affs_file +{ + grub_uint8_t unused1[12]; + grub_uint32_t size; + grub_uint8_t unused2[92]; + struct grub_affs_time mtime; + grub_uint8_t namelen; + grub_uint8_t name[30]; + grub_uint8_t unused3[5]; + grub_uint32_t hardlink; + grub_uint32_t unused4[6]; + grub_uint32_t next; + grub_uint32_t parent; + grub_uint32_t extension; + grub_uint32_t type; +} GRUB_PACKED; + +/* The location of `struct grub_affs_file' relative to the end of a + file header block. */ +#define GRUB_AFFS_FILE_LOCATION 200 + +/* The offset in both the rootblock and the file header block for the + hashtable, symlink and block pointers (all synonyms). */ +#define GRUB_AFFS_HASHTABLE_OFFSET 24 +#define GRUB_AFFS_BLOCKPTR_OFFSET 24 +#define GRUB_AFFS_SYMLINK_OFFSET 24 + +enum + { + GRUB_AFFS_FILETYPE_DIR = 2, + GRUB_AFFS_FILETYPE_SYMLINK = 3, + GRUB_AFFS_FILETYPE_HARDLINK = 0xfffffffc, + GRUB_AFFS_FILETYPE_REG = 0xfffffffd + }; + +#define AFFS_MAX_LOG_BLOCK_SIZE 4 +#define AFFS_MAX_SUPERBLOCK 1 + + + +struct grub_fshelp_node +{ + struct grub_affs_data *data; + grub_uint32_t block; + struct grub_fshelp_node *parent; + struct grub_affs_file di; + grub_uint32_t *block_cache; + grub_uint32_t last_block_cache; +}; + +/* Information about a "mounted" affs filesystem. */ +struct grub_affs_data +{ + struct grub_affs_bblock bblock; + struct grub_fshelp_node diropen; + grub_disk_t disk; + + /* Log blocksize in sectors. */ + int log_blocksize; + + /* The number of entries in the hashtable. */ + unsigned int htsize; +}; + +static grub_dl_t my_mod; + + +static grub_disk_addr_t +grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) +{ + grub_uint32_t target, curblock; + grub_uint32_t pos; + struct grub_affs_file file; + struct grub_affs_data *data = node->data; + grub_uint64_t mod; + + if (!node->block_cache) + { + node->block_cache = grub_malloc (((grub_be_to_cpu32 (node->di.size) + >> (9 + node->data->log_blocksize)) + / data->htsize + 2) + * sizeof (node->block_cache[0])); + if (!node->block_cache) + return -1; + node->last_block_cache = 0; + node->block_cache[0] = node->block; + } + + /* Files are at most 2G on AFFS, so no need for 64-bit division. */ + target = (grub_uint32_t) fileblock / data->htsize; + mod = (grub_uint32_t) fileblock % data->htsize; + /* Find the block that points to the fileblock we are looking up by + following the chain until the right table is reached. */ + for (curblock = node->last_block_cache + 1; curblock < target + 1; curblock++) + { + grub_disk_read (data->disk, + (((grub_uint64_t) node->block_cache[curblock - 1] + 1) + << data->log_blocksize) - 1, + GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION, + sizeof (file), &file); + if (grub_errno) + return 0; + + node->block_cache[curblock] = grub_be_to_cpu32 (file.extension); + node->last_block_cache = curblock; + } + + /* Translate the fileblock to the block within the right table. */ + grub_disk_read (data->disk, (grub_uint64_t) node->block_cache[target] + << data->log_blocksize, + GRUB_AFFS_BLOCKPTR_OFFSET + + (data->htsize - mod - 1) * sizeof (pos), + sizeof (pos), &pos); + if (grub_errno) + return 0; + + return grub_be_to_cpu32 (pos); +} + +static struct grub_affs_data * +grub_affs_mount (grub_disk_t disk) +{ + struct grub_affs_data *data; + grub_uint32_t *rootblock = 0; + struct grub_affs_rblock *rblock = 0; + int log_blocksize = 0; + int bsnum = 0; + + data = grub_zalloc (sizeof (struct grub_affs_data)); + if (!data) + return 0; + + for (bsnum = 0; bsnum < AFFS_MAX_SUPERBLOCK + 1; bsnum++) + { + /* Read the bootblock. */ + grub_disk_read (disk, bsnum, 0, sizeof (struct grub_affs_bblock), + &data->bblock); + if (grub_errno) + goto fail; + + /* Make sure this is an affs filesystem. */ + if (grub_strncmp ((char *) (data->bblock.type), "DOS", 3) != 0 + /* Test if the filesystem is a OFS filesystem. */ + || !(data->bblock.flags & GRUB_AFFS_FLAG_FFS)) + continue; + + /* No sane person uses more than 8KB for a block. At least I hope + for that person because in that case this won't work. */ + if (!rootblock) + rootblock = grub_malloc (GRUB_DISK_SECTOR_SIZE + << AFFS_MAX_LOG_BLOCK_SIZE); + if (!rootblock) + goto fail; + + rblock = (struct grub_affs_rblock *) rootblock; + + /* The filesystem blocksize is not stored anywhere in the filesystem + itself. One way to determine it is try reading blocks for the + rootblock until the checksum is correct. */ + for (log_blocksize = 0; log_blocksize <= AFFS_MAX_LOG_BLOCK_SIZE; + log_blocksize++) + { + grub_uint32_t *currblock = rootblock; + unsigned int i; + grub_uint32_t checksum = 0; + + /* Read the rootblock. */ + grub_disk_read (disk, + (grub_uint64_t) grub_be_to_cpu32 (data->bblock.rootblock) + << log_blocksize, 0, + GRUB_DISK_SECTOR_SIZE << log_blocksize, rootblock); + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + { + grub_errno = 0; + break; + } + if (grub_errno) + goto fail; + + if (rblock->type != grub_cpu_to_be32_compile_time (2) + || rblock->htsize == 0 + || currblock[(GRUB_DISK_SECTOR_SIZE << log_blocksize) + / sizeof (*currblock) - 1] + != grub_cpu_to_be32_compile_time (1)) + continue; + + for (i = 0; i < (GRUB_DISK_SECTOR_SIZE << log_blocksize) + / sizeof (*currblock); + i++) + checksum += grub_be_to_cpu32 (currblock[i]); + + if (checksum == 0) + { + data->log_blocksize = log_blocksize; + data->disk = disk; + data->htsize = grub_be_to_cpu32 (rblock->htsize); + data->diropen.data = data; + data->diropen.block = grub_be_to_cpu32 (data->bblock.rootblock); + data->diropen.parent = NULL; + grub_memcpy (&data->diropen.di, rootblock, + sizeof (data->diropen.di)); + grub_free (rootblock); + + return data; + } + } + } + + fail: + if (grub_errno == GRUB_ERR_NONE || grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not an AFFS filesystem"); + + grub_free (data); + grub_free (rootblock); + return 0; +} + + +static char * +grub_affs_read_symlink (grub_fshelp_node_t node) +{ + struct grub_affs_data *data = node->data; + grub_uint8_t *latin1, *utf8; + const grub_size_t symlink_size = ((GRUB_DISK_SECTOR_SIZE + << data->log_blocksize) - GRUB_AFFS_SYMLINK_OFFSET); + + latin1 = grub_malloc (symlink_size + 1); + if (!latin1) + return 0; + + grub_disk_read (data->disk, + (grub_uint64_t) node->block << data->log_blocksize, + GRUB_AFFS_SYMLINK_OFFSET, + symlink_size, latin1); + if (grub_errno) + { + grub_free (latin1); + return 0; + } + latin1[symlink_size] = 0; + utf8 = grub_calloc (GRUB_MAX_UTF8_PER_LATIN1 + 1, symlink_size); + if (!utf8) + { + grub_free (latin1); + return 0; + } + *grub_latin1_to_utf8 (utf8, latin1, symlink_size) = '\0'; + grub_dprintf ("affs", "Symlink: `%s'\n", utf8); + grub_free (latin1); + if (utf8[0] == ':') + utf8[0] = '/'; + return (char *) utf8; +} + + +/* Helper for grub_affs_iterate_dir. */ +static int +grub_affs_create_node (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data, + struct grub_fshelp_node **node, + grub_uint32_t block, const struct grub_affs_file *fil) +{ + struct grub_affs_data *data = dir->data; + int type = GRUB_FSHELP_REG; + grub_uint8_t name_u8[sizeof (fil->name) * GRUB_MAX_UTF8_PER_LATIN1 + 1]; + grub_size_t len; + unsigned int nest; + + *node = grub_zalloc (sizeof (**node)); + if (!*node) + return 1; + + (*node)->data = data; + (*node)->block = block; + (*node)->parent = dir; + + len = fil->namelen; + if (len > sizeof (fil->name)) + len = sizeof (fil->name); + *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0'; + + (*node)->di = *fil; + for (nest = 0; nest < 8; nest++) + { + switch ((*node)->di.type) + { + case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_REG): + type = GRUB_FSHELP_REG; + break; + case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_DIR): + type = GRUB_FSHELP_DIR; + break; + case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_SYMLINK): + type = GRUB_FSHELP_SYMLINK; + break; + case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_HARDLINK): + { + grub_err_t err; + (*node)->block = grub_be_to_cpu32 ((*node)->di.hardlink); + err = grub_disk_read (data->disk, + (((grub_uint64_t) (*node)->block + 1) << data->log_blocksize) + - 1, + GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION, + sizeof ((*node)->di), (char *) &(*node)->di); + if (err) + { + grub_free (*node); + return 1; + } + continue; + } + default: + { + grub_free (*node); + return 0; + } + } + break; + } + + if (nest == 8) + { + grub_free (*node); + return 0; + } + + type |= GRUB_FSHELP_CASE_INSENSITIVE; + + if (hook ((char *) name_u8, type, *node, hook_data)) + { + *node = 0; + return 1; + } + *node = 0; + return 0; +} + +static int +grub_affs_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + unsigned int i; + struct grub_affs_file file; + struct grub_fshelp_node *node, *orig_node; + struct grub_affs_data *data = dir->data; + grub_uint32_t *hashtable; + + /* Create the directory entries for `.' and `..'. */ + node = orig_node = grub_zalloc (sizeof (*node)); + if (!node) + return 1; + + *node = *dir; + if (hook (".", GRUB_FSHELP_DIR, node, hook_data)) + return 1; + if (dir->parent) + { + *node = *dir->parent; + if (hook ("..", GRUB_FSHELP_DIR, node, hook_data)) + return 1; + } + + hashtable = grub_calloc (data->htsize, sizeof (*hashtable)); + if (!hashtable) + return 1; + + grub_disk_read (data->disk, + (grub_uint64_t) dir->block << data->log_blocksize, + GRUB_AFFS_HASHTABLE_OFFSET, + data->htsize * sizeof (*hashtable), (char *) hashtable); + if (grub_errno) + goto fail; + + for (i = 0; i < data->htsize; i++) + { + grub_uint32_t next; + + if (!hashtable[i]) + continue; + + /* Every entry in the hashtable can be chained. Read the entire + chain. */ + next = grub_be_to_cpu32 (hashtable[i]); + + while (next) + { + grub_disk_read (data->disk, + (((grub_uint64_t) next + 1) << data->log_blocksize) + - 1, + GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION, + sizeof (file), (char *) &file); + if (grub_errno) + goto fail; + + if (grub_affs_create_node (dir, hook, hook_data, &node, next, &file)) + { + /* Node has been replaced in function. */ + grub_free (orig_node); + grub_free (hashtable); + return 1; + } + + next = grub_be_to_cpu32 (file.next); + } + } + + fail: + grub_free (orig_node); + grub_free (hashtable); + return 0; +} + + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_affs_open (struct grub_file *file, const char *name) +{ + struct grub_affs_data *data; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_affs_mount (file->device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_affs_iterate_dir, + grub_affs_read_symlink, GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + file->size = grub_be_to_cpu32 (fdiro->di.size); + data->diropen = *fdiro; + grub_free (fdiro); + + file->data = data; + file->offset = 0; + + return 0; + + fail: + if (data && fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_affs_close (grub_file_t file) +{ + struct grub_affs_data *data = + (struct grub_affs_data *) file->data; + + grub_free (data->diropen.block_cache); + grub_free (file->data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +/* Read LEN bytes data from FILE into BUF. */ +static grub_ssize_t +grub_affs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_affs_data *data = + (struct grub_affs_data *) file->data; + + return grub_fshelp_read_file (data->diropen.data->disk, &data->diropen, + file->read_hook, file->read_hook_data, + file->offset, len, buf, grub_affs_read_block, + grub_be_to_cpu32 (data->diropen.di.size), + data->log_blocksize, 0); +} + +static grub_int32_t +aftime2ctime (const struct grub_affs_time *t) +{ + return grub_be_to_cpu32 (t->day) * 86400 + + grub_be_to_cpu32 (t->min) * 60 + + grub_be_to_cpu32 (t->hz) / 50 + + 8 * 365 * 86400 + 86400 * 2; +} + +/* Context for grub_affs_dir. */ +struct grub_affs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_affs_dir. */ +static int +grub_affs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_affs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = aftime2ctime (&node->di.mtime); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_affs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_affs_dir_ctx ctx = { hook, hook_data }; + struct grub_affs_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_affs_mount (device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_affs_iterate_dir, + grub_affs_read_symlink, GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_affs_iterate_dir (fdiro, grub_affs_dir_iter, &ctx); + + fail: + if (data && fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + + +static grub_err_t +grub_affs_label (grub_device_t device, char **label) +{ + struct grub_affs_data *data; + struct grub_affs_file file; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_affs_mount (disk); + if (data) + { + grub_size_t len; + /* The rootblock maps quite well on a file header block, it's + something we can use here. */ + grub_disk_read (data->disk, + (((grub_uint64_t) + grub_be_to_cpu32 (data->bblock.rootblock) + 1) + << data->log_blocksize) - 1, + GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION, + sizeof (file), &file); + if (grub_errno) + return grub_errno; + + len = file.namelen; + if (len > sizeof (file.name)) + len = sizeof (file.name); + *label = grub_calloc (GRUB_MAX_UTF8_PER_LATIN1 + 1, len); + if (*label) + *grub_latin1_to_utf8 ((grub_uint8_t *) *label, file.name, len) = '\0'; + } + else + *label = 0; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_affs_mtime (grub_device_t device, grub_int64_t *t) +{ + struct grub_affs_data *data; + grub_disk_t disk = device->disk; + struct grub_affs_time af_time; + + *t = 0; + + grub_dl_ref (my_mod); + + data = grub_affs_mount (disk); + if (!data) + { + grub_dl_unref (my_mod); + return grub_errno; + } + + grub_disk_read (data->disk, + (((grub_uint64_t) + grub_be_to_cpu32 (data->bblock.rootblock) + 1) + << data->log_blocksize) - 1, + GRUB_DISK_SECTOR_SIZE - 40, + sizeof (af_time), &af_time); + if (grub_errno) + { + grub_dl_unref (my_mod); + grub_free (data); + return grub_errno; + } + + *t = aftime2ctime (&af_time); + grub_dl_unref (my_mod); + + grub_free (data); + + return GRUB_ERR_NONE; +} + + +static struct grub_fs grub_affs_fs = + { + .name = "affs", + .fs_dir = grub_affs_dir, + .fs_open = grub_affs_open, + .fs_read = grub_affs_read, + .fs_close = grub_affs_close, + .fs_label = grub_affs_label, + .fs_mtime = grub_affs_mtime, + +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 1, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(affs) +{ + if (!grub_is_lockdown ()) + { + grub_affs_fs.mod = mod; + grub_fs_register (&grub_affs_fs); + } + my_mod = mod; +} + +GRUB_MOD_FINI(affs) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_affs_fs); +} diff --git a/grub-core/fs/afs.c b/grub-core/fs/afs.c new file mode 100644 index 000000000..00a5e3113 --- /dev/null +++ b/grub-core/fs/afs.c @@ -0,0 +1,3 @@ +#define MODE_AFS 1 +#include "bfs.c" + diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c new file mode 100644 index 000000000..0816b28de --- /dev/null +++ b/grub-core/fs/archelp.c @@ -0,0 +1,316 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static inline void +canonicalize (char *name) +{ + char *iptr, *optr; + for (iptr = name, optr = name; *iptr; ) + { + while (*iptr == '/') + iptr++; + if (iptr[0] == '.' && (iptr[1] == '/' || iptr[1] == 0)) + { + iptr++; + continue; + } + if (iptr[0] == '.' && iptr[1] == '.' && (iptr[2] == '/' || iptr[2] == 0)) + { + iptr += 2; + if (optr == name) + continue; + for (optr -= 2; optr >= name && *optr != '/'; optr--); + optr++; + continue; + } + while (*iptr && *iptr != '/') + *optr++ = *iptr++; + if (*iptr) + *optr++ = *iptr++; + } + *optr = 0; +} + +static grub_err_t +handle_symlink (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *fn, char **name, + grub_uint32_t mode, int *restart) +{ + grub_size_t flen; + char *target; + char *ptr; + char *lastslash; + grub_size_t prefixlen; + char *rest; + char *linktarget; + grub_size_t linktarget_len; + grub_size_t sz; + + *restart = 0; + + if ((mode & GRUB_ARCHELP_ATTR_TYPE) != GRUB_ARCHELP_ATTR_LNK + || !arcops->get_link_target) + return GRUB_ERR_NONE; + flen = grub_strlen (fn); + if (grub_memcmp (*name, fn, flen) != 0 + || ((*name)[flen] != 0 && (*name)[flen] != '/')) + return GRUB_ERR_NONE; + rest = *name + flen; + lastslash = rest; + if (*rest) + rest++; + while (lastslash >= *name && *lastslash != '/') + lastslash--; + if (lastslash >= *name) + prefixlen = lastslash - *name; + else + prefixlen = 0; + + if (prefixlen) + prefixlen++; + + linktarget = arcops->get_link_target (data); + if (!linktarget) + return grub_errno; + if (linktarget[0] == '\0') + return GRUB_ERR_NONE; + linktarget_len = grub_strlen (linktarget); + + if (grub_add (linktarget_len, grub_strlen (*name), &sz) || + grub_add (sz, 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("link target length overflow")); + + target = grub_malloc (sz); + if (!target) + return grub_errno; + + grub_strcpy (target + prefixlen, linktarget); + grub_free (linktarget); + if (target[prefixlen] == '/') + { + ptr = grub_stpcpy (target, target + prefixlen); + ptr = grub_stpcpy (ptr, rest); + *ptr = 0; + grub_dprintf ("archelp", "symlink redirected %s to %s\n", + *name, target); + grub_free (*name); + + canonicalize (target); + *name = target; + *restart = 1; + return GRUB_ERR_NONE; + } + if (prefixlen) + { + grub_memcpy (target, *name, prefixlen); + target[prefixlen-1] = '/'; + } + grub_strcpy (target + prefixlen + linktarget_len, rest); + grub_dprintf ("archelp", "symlink redirected %s to %s\n", + *name, target); + grub_free (*name); + canonicalize (target); + *name = target; + *restart = 1; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_archelp_dir (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + char *prev, *name, *path, *ptr; + grub_size_t len; + int symlinknest = 0; + + path = grub_strdup (path_in + 1); + if (!path) + return grub_errno; + canonicalize (path); + for (ptr = path + grub_strlen (path) - 1; ptr >= path && *ptr == '/'; ptr--) + *ptr = 0; + + prev = 0; + + len = grub_strlen (path); + while (1) + { + grub_int32_t mtime; + grub_uint32_t mode; + grub_err_t err; + + if (arcops->find_file (data, &name, &mtime, &mode)) + goto fail; + + if (mode == GRUB_ARCHELP_ATTR_END) + break; + + canonicalize (name); + + if (grub_memcmp (path, name, len) == 0 + && (name[len] == 0 || name[len] == '/' || len == 0)) + { + char *p, *n; + + n = name + len; + while (*n == '/') + n++; + + p = grub_strchr (n, '/'); + if (p) + *p = 0; + + if ((*n == 0) && ((mode & GRUB_ARCHELP_ATTR_TYPE) + != GRUB_ARCHELP_ATTR_DIR)) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + grub_free (name); + goto fail; + } + + if (((!prev) || (grub_strcmp (prev, name) != 0)) && *n != 0) + { + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + info.dir = (p != NULL) || ((mode & GRUB_ARCHELP_ATTR_TYPE) + == GRUB_ARCHELP_ATTR_DIR); + if (!(mode & GRUB_ARCHELP_ATTR_NOTIME)) + { + info.mtime = mtime; + info.mtimeset = 1; + } + if (hook (n, &info, hook_data)) + { + grub_free (name); + goto fail; + } + grub_free (prev); + prev = name; + } + else + { + int restart = 0; + err = handle_symlink (data, arcops, name, + &path, mode, &restart); + grub_free (name); + if (err) + goto fail; + if (restart) + { + len = grub_strlen (path); + if (++symlinknest == 8) + { + grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + goto fail; + } + arcops->rewind (data); + } + } + } + else + grub_free (name); + } + +fail: + + grub_free (path); + grub_free (prev); + + return grub_errno; +} + +grub_err_t +grub_archelp_open (struct grub_archelp_data *data, + struct grub_archelp_ops *arcops, + const char *name_in) +{ + char *fn; + char *name = grub_strdup (name_in + 1); + int symlinknest = 0; + + if (!name) + return grub_errno; + + canonicalize (name); + + while (1) + { + grub_uint32_t mode; + grub_int32_t mtime; + int restart; + + if (arcops->find_file (data, &fn, &mtime, &mode)) + goto fail; + + if (mode == GRUB_ARCHELP_ATTR_END) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name_in); + break; + } + + canonicalize (fn); + + if (handle_symlink (data, arcops, fn, &name, mode, &restart)) + { + grub_free (fn); + goto fail; + } + + if (restart) + { + arcops->rewind (data); + if (++symlinknest == 8) + { + grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + goto fail; + } + goto no_match; + } + + if (grub_strcmp (name, fn) != 0) + goto no_match; + + grub_free (fn); + grub_free (name); + + return GRUB_ERR_NONE; + + no_match: + + grub_free (fn); + } + +fail: + grub_free (name); + + return grub_errno; +} diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c new file mode 100644 index 000000000..78aeb051f --- /dev/null +++ b/grub-core/fs/bfs.c @@ -0,0 +1,1125 @@ +/* bfs.c - The Bee File System. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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 . + */ +/* + Based on the book "Practical File System Design by Dominic Giampaolo + with corrections and completitions based on Haiku code. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#ifdef MODE_AFS +#define BTREE_ALIGN 4 +#define SUPERBLOCK 2 +#else +#define BTREE_ALIGN 8 +#define SUPERBLOCK 1 +#endif + +#define grub_bfs_to_cpu16 grub_le_to_cpu16 +#define grub_bfs_to_cpu32 grub_le_to_cpu32 +#define grub_bfs_to_cpu64 grub_le_to_cpu64 +#define grub_cpu_to_bfs32_compile_time grub_cpu_to_le32_compile_time + +#ifdef MODE_AFS +#define grub_bfs_to_cpu_treehead grub_bfs_to_cpu32 +#else +#define grub_bfs_to_cpu_treehead grub_bfs_to_cpu16 +#endif + +#ifdef MODE_AFS +#define SUPER_BLOCK_MAGIC1 0x41465331 +#else +#define SUPER_BLOCK_MAGIC1 0x42465331 +#endif +#define SUPER_BLOCK_MAGIC2 0xdd121031 +#define SUPER_BLOCK_MAGIC3 0x15b6830e +#define POINTER_INVALID 0xffffffffffffffffULL + +#define ATTR_TYPE 0160000 +#define ATTR_REG 0100000 +#define ATTR_DIR 0040000 +#define ATTR_LNK 0120000 + +#define DOUBLE_INDIRECT_SHIFT 2 + +#define LOG_EXTENT_SIZE 3 +struct grub_bfs_extent +{ + grub_uint32_t ag; + grub_uint16_t start; + grub_uint16_t len; +} GRUB_PACKED; + +struct grub_bfs_superblock +{ + char label[32]; + grub_uint32_t magic1; + grub_uint32_t unused1; + grub_uint32_t bsize; + grub_uint32_t log2_bsize; + grub_uint8_t unused[20]; + grub_uint32_t magic2; + grub_uint32_t unused2; + grub_uint32_t log2_ag_size; + grub_uint8_t unused3[32]; + grub_uint32_t magic3; + struct grub_bfs_extent root_dir; +} GRUB_PACKED; + +struct grub_bfs_inode +{ + grub_uint8_t unused[20]; + grub_uint32_t mode; + grub_uint32_t flags; +#ifdef MODE_AFS + grub_uint8_t unused2[12]; +#else + grub_uint8_t unused2[8]; +#endif + grub_uint64_t mtime; + grub_uint8_t unused3[8]; + struct grub_bfs_extent attr; + grub_uint8_t unused4[12]; + + union + { + struct + { + struct grub_bfs_extent direct[12]; + grub_uint64_t max_direct_range; + struct grub_bfs_extent indirect; + grub_uint64_t max_indirect_range; + struct grub_bfs_extent double_indirect; + grub_uint64_t max_double_indirect_range; + grub_uint64_t size; + grub_uint32_t pad[4]; + } GRUB_PACKED; + char inplace_link[144]; + } GRUB_PACKED; + grub_uint8_t small_data[0]; +} GRUB_PACKED; + +enum +{ + LONG_SYMLINK = 0x40 +}; + +struct grub_bfs_small_data_element_header +{ + grub_uint32_t type; + grub_uint16_t name_len; + grub_uint16_t value_len; +} GRUB_PACKED; + +struct grub_bfs_btree_header +{ + grub_uint32_t magic; +#ifdef MODE_AFS + grub_uint64_t root; + grub_uint32_t level; + grub_uint32_t node_size; + grub_uint32_t unused; +#else + grub_uint32_t node_size; + grub_uint32_t level; + grub_uint32_t unused; + grub_uint64_t root; +#endif + grub_uint32_t unused2[2]; +} GRUB_PACKED; + +struct grub_bfs_btree_node +{ + grub_uint64_t unused; + grub_uint64_t right; + grub_uint64_t overflow; +#ifdef MODE_AFS + grub_uint32_t count_keys; + grub_uint32_t total_key_len; +#else + grub_uint16_t count_keys; + grub_uint16_t total_key_len; +#endif +} GRUB_PACKED; + +struct grub_bfs_data +{ + struct grub_bfs_superblock sb; + struct grub_bfs_inode ino; +}; + +/* Context for grub_bfs_dir. */ +struct grub_bfs_dir_ctx +{ + grub_device_t device; + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_bfs_superblock sb; +}; + +static grub_err_t +read_extent (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + const struct grub_bfs_extent *in, + grub_off_t off, grub_off_t byteoff, void *buf, grub_size_t len) +{ +#ifdef MODE_AFS + return grub_disk_read (disk, ((grub_bfs_to_cpu32 (in->ag) + << (grub_bfs_to_cpu32 (sb->log2_ag_size) + - GRUB_DISK_SECTOR_BITS)) + + ((grub_bfs_to_cpu16 (in->start) + off) + << (grub_bfs_to_cpu32 (sb->log2_bsize) + - GRUB_DISK_SECTOR_BITS))), + byteoff, len, buf); +#else + return grub_disk_read (disk, (((grub_bfs_to_cpu32 (in->ag) + << grub_bfs_to_cpu32 (sb->log2_ag_size)) + + grub_bfs_to_cpu16 (in->start) + off) + << (grub_bfs_to_cpu32 (sb->log2_bsize) + - GRUB_DISK_SECTOR_BITS)), + byteoff, len, buf); +#endif +} + +#ifdef MODE_AFS +#define RANGE_SHIFT grub_bfs_to_cpu32 (sb->log2_bsize) +#else +#define RANGE_SHIFT 0 +#endif + +static grub_err_t +read_bfs_file (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + const struct grub_bfs_inode *ino, + grub_off_t off, void *buf, grub_size_t len, + grub_disk_read_hook_t read_hook, void *read_hook_data) +{ + if (len == 0) + return GRUB_ERR_NONE; + + if (off + len > grub_bfs_to_cpu64 (ino->size)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read past the end of file")); + + if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT)) + { + unsigned i; + grub_uint64_t pos = 0; + for (i = 0; i < ARRAY_SIZE (ino->direct); i++) + { + grub_uint64_t newpos; + newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (ino->direct[i].len)) + << grub_bfs_to_cpu32 (sb->log2_bsize)); + if (newpos > off) + { + grub_size_t read_size; + grub_err_t err; + read_size = newpos - off; + if (read_size > len) + read_size = len; + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + err = read_extent (disk, sb, &ino->direct[i], 0, off - pos, + buf, read_size); + disk->read_hook = 0; + if (err) + return err; + off += read_size; + len -= read_size; + buf = (char *) buf + read_size; + if (len == 0) + return GRUB_ERR_NONE; + } + pos = newpos; + } + } + + if (off < (grub_bfs_to_cpu64 (ino->max_direct_range) << RANGE_SHIFT)) + return grub_error (GRUB_ERR_BAD_FS, "incorrect direct blocks"); + + if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT)) + { + unsigned i; + struct grub_bfs_extent *entries; + grub_size_t nentries; + grub_err_t err; + grub_uint64_t pos = (grub_bfs_to_cpu64 (ino->max_direct_range) + << RANGE_SHIFT); + nentries = (((grub_size_t) grub_bfs_to_cpu16 (ino->indirect.len)) + << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE)); + entries = grub_malloc (nentries << LOG_EXTENT_SIZE); + if (!entries) + return grub_errno; + err = read_extent (disk, sb, &ino->indirect, 0, 0, + entries, nentries << LOG_EXTENT_SIZE); + for (i = 0; i < nentries; i++) + { + grub_uint64_t newpos; + newpos = pos + (((grub_uint64_t) grub_bfs_to_cpu16 (entries[i].len)) + << grub_bfs_to_cpu32 (sb->log2_bsize)); + if (newpos > off) + { + grub_size_t read_size; + read_size = newpos - off; + if (read_size > len) + read_size = len; + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + err = read_extent (disk, sb, &entries[i], 0, off - pos, + buf, read_size); + disk->read_hook = 0; + if (err) + { + grub_free (entries); + return err; + } + off += read_size; + len -= read_size; + buf = (char *) buf + read_size; + if (len == 0) + { + grub_free (entries); + return GRUB_ERR_NONE; + } + } + pos = newpos; + } + grub_free (entries); + } + + if (off < (grub_bfs_to_cpu64 (ino->max_indirect_range) << RANGE_SHIFT)) + return grub_error (GRUB_ERR_BAD_FS, "incorrect indirect blocks"); + + { + struct grub_bfs_extent *l1_entries, *l2_entries; + grub_size_t nl1_entries, nl2_entries; + grub_off_t last_l1n = ~0ULL; + grub_err_t err; + nl1_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (ino->double_indirect.len)) + << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE)); + l1_entries = grub_malloc (nl1_entries << LOG_EXTENT_SIZE); + if (!l1_entries) + return grub_errno; + nl2_entries = 0; + l2_entries = grub_malloc (1 << (DOUBLE_INDIRECT_SHIFT + + grub_bfs_to_cpu32 (sb->log2_bsize))); + if (!l2_entries) + { + grub_free (l1_entries); + return grub_errno; + } + err = read_extent (disk, sb, &ino->double_indirect, 0, 0, + l1_entries, nl1_entries << LOG_EXTENT_SIZE); + if (err) + { + grub_free (l1_entries); + grub_free (l2_entries); + return err; + } + + while (len > 0) + { + grub_off_t boff, l2n, l1n; + grub_size_t read_size; + grub_off_t double_indirect_offset; + double_indirect_offset = off + - grub_bfs_to_cpu64 (ino->max_indirect_range); + boff = (double_indirect_offset + & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize) + + DOUBLE_INDIRECT_SHIFT)) - 1)); + l2n = ((double_indirect_offset >> (grub_bfs_to_cpu32 (sb->log2_bsize) + + DOUBLE_INDIRECT_SHIFT)) + & ((1 << (grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE + + DOUBLE_INDIRECT_SHIFT)) - 1)); + l1n = + (double_indirect_offset >> + (2 * grub_bfs_to_cpu32 (sb->log2_bsize) - LOG_EXTENT_SIZE + + 2 * DOUBLE_INDIRECT_SHIFT)); + if (l1n > nl1_entries) + { + grub_free (l1_entries); + grub_free (l2_entries); + return grub_error (GRUB_ERR_BAD_FS, + "incorrect double-indirect block"); + } + if (l1n != last_l1n) + { + nl2_entries = (((grub_uint64_t) grub_bfs_to_cpu16 (l1_entries[l1n].len)) + << (grub_bfs_to_cpu32 (sb->log2_bsize) + - LOG_EXTENT_SIZE)); + if (nl2_entries > (1U << (grub_bfs_to_cpu32 (sb->log2_bsize) + - LOG_EXTENT_SIZE + + DOUBLE_INDIRECT_SHIFT))) + nl2_entries = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize) + - LOG_EXTENT_SIZE + + DOUBLE_INDIRECT_SHIFT)); + err = read_extent (disk, sb, &l1_entries[l1n], 0, 0, + l2_entries, nl2_entries << LOG_EXTENT_SIZE); + if (err) + { + grub_free (l1_entries); + grub_free (l2_entries); + return err; + } + last_l1n = l1n; + } + if (l2n > nl2_entries) + { + grub_free (l1_entries); + grub_free (l2_entries); + return grub_error (GRUB_ERR_BAD_FS, + "incorrect double-indirect block"); + } + + read_size = (1 << (grub_bfs_to_cpu32 (sb->log2_bsize) + + DOUBLE_INDIRECT_SHIFT)) - boff; + if (read_size > len) + read_size = len; + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + err = read_extent (disk, sb, &l2_entries[l2n], 0, boff, + buf, read_size); + disk->read_hook = 0; + if (err) + { + grub_free (l1_entries); + grub_free (l2_entries); + return err; + } + off += read_size; + len -= read_size; + buf = (char *) buf + read_size; + } + grub_free (l1_entries); + grub_free (l2_entries); + return GRUB_ERR_NONE; + } +} + +static grub_err_t +read_b_node (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + const struct grub_bfs_inode *ino, + grub_uint64_t node_off, + struct grub_bfs_btree_node **node, + char **key_data, grub_uint16_t **keylen_idx, + grub_unaligned_uint64_t **key_values) +{ + void *ret; + struct grub_bfs_btree_node node_head; + grub_size_t total_size; + grub_err_t err; + + *node = NULL; + *key_data = NULL; + *keylen_idx = NULL; + *key_values = NULL; + + err = read_bfs_file (disk, sb, ino, node_off, &node_head, sizeof (node_head), + 0, 0); + if (err) + return err; + + total_size = ALIGN_UP (sizeof (node_head) + + grub_bfs_to_cpu_treehead + (node_head.total_key_len), + BTREE_ALIGN) + + grub_bfs_to_cpu_treehead (node_head.count_keys) * + sizeof (grub_uint16_t) + + grub_bfs_to_cpu_treehead (node_head.count_keys) * + sizeof (grub_uint64_t); + + ret = grub_malloc (total_size); + if (!ret) + return grub_errno; + + err = read_bfs_file (disk, sb, ino, node_off, ret, total_size, 0, 0); + if (err) + { + grub_free (ret); + return err; + } + + *node = ret; + *key_data = (char *) ret + sizeof (node_head); + *keylen_idx = (grub_uint16_t *) ret + + ALIGN_UP (sizeof (node_head) + + grub_bfs_to_cpu_treehead (node_head.total_key_len), + BTREE_ALIGN) / 2; + *key_values = (grub_unaligned_uint64_t *) + (*keylen_idx + + grub_bfs_to_cpu_treehead (node_head.count_keys)); + + return GRUB_ERR_NONE; +} + +static int +iterate_in_b_tree (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + const struct grub_bfs_inode *ino, + int (*hook) (const char *name, grub_uint64_t value, + struct grub_bfs_dir_ctx *ctx), + struct grub_bfs_dir_ctx *ctx) +{ + struct grub_bfs_btree_header head; + grub_err_t err; + int level; + grub_uint64_t node_off; + + err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0); + if (err) + return 0; + node_off = grub_bfs_to_cpu64 (head.root); + + level = grub_bfs_to_cpu32 (head.level) - 1; + while (level--) + { + struct grub_bfs_btree_node node; + grub_uint64_t key_value; + err = read_bfs_file (disk, sb, ino, node_off, &node, sizeof (node), + 0, 0); + if (err) + return 0; + err = read_bfs_file (disk, sb, ino, node_off + + ALIGN_UP (sizeof (node) + + grub_bfs_to_cpu_treehead (node. + total_key_len), + BTREE_ALIGN) + + grub_bfs_to_cpu_treehead (node.count_keys) * + sizeof (grub_uint16_t), &key_value, + sizeof (grub_uint64_t), 0, 0); + if (err) + return 0; + + node_off = grub_bfs_to_cpu64 (key_value); + } + + while (1) + { + struct grub_bfs_btree_node *node; + char *key_data; + grub_uint16_t *keylen_idx; + grub_unaligned_uint64_t *key_values; + unsigned i; + grub_uint16_t start = 0, end = 0; + + err = read_b_node (disk, sb, ino, + node_off, + &node, + &key_data, + &keylen_idx, + &key_values); + + if (err) + return 0; + + for (i = 0; i < grub_bfs_to_cpu_treehead (node->count_keys); i++) + { + char c; + start = end; + end = grub_bfs_to_cpu16 (keylen_idx[i]); + if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end) + end = grub_bfs_to_cpu_treehead (node->total_key_len); + c = key_data[end]; + key_data[end] = 0; + if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i].val), + ctx)) + { + grub_free (node); + return 1; + } + key_data[end] = c; + } + node_off = grub_bfs_to_cpu64 (node->right); + grub_free (node); + if (node_off == POINTER_INVALID) + return 0; + } +} + +static int +bfs_strcmp (const char *a, const char *b, grub_size_t alen) +{ + char ac, bc; + while (*b && alen) + { + if (*a != *b) + break; + + a++; + b++; + alen--; + } + + ac = alen ? *a : 0; + bc = *b; + +#ifdef MODE_AFS + return (int) (grub_int8_t) ac - (int) (grub_int8_t) bc; +#else + return (int) (grub_uint8_t) ac - (int) (grub_uint8_t) bc; +#endif +} + +static grub_err_t +find_in_b_tree (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + const struct grub_bfs_inode *ino, const char *name, + grub_uint64_t * res) +{ + struct grub_bfs_btree_header head; + grub_err_t err; + int level; + grub_uint64_t node_off; + + err = read_bfs_file (disk, sb, ino, 0, &head, sizeof (head), 0, 0); + if (err) + return err; + node_off = grub_bfs_to_cpu64 (head.root); + + level = grub_bfs_to_cpu32 (head.level) - 1; + while (1) + { + struct grub_bfs_btree_node *node; + char *key_data; + grub_uint16_t *keylen_idx; + grub_unaligned_uint64_t *key_values; + int lg, j; + unsigned i; + + err = read_b_node (disk, sb, ino, node_off, &node, &key_data, &keylen_idx, &key_values); + if (err) + return err; + + if (node->count_keys == 0) + { + grub_free (node); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + name); + } + + for (lg = 0; grub_bfs_to_cpu_treehead (node->count_keys) >> lg; lg++); + + i = 0; + + for (j = lg - 1; j >= 0; j--) + { + int cmp; + grub_uint16_t start = 0, end = 0; + if ((i | (1 << j)) >= grub_bfs_to_cpu_treehead (node->count_keys)) + continue; + start = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j)) - 1]); + end = grub_bfs_to_cpu16 (keylen_idx[(i | (1 << j))]); + if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end) + end = grub_bfs_to_cpu_treehead (node->total_key_len); + cmp = bfs_strcmp (key_data + start, name, end - start); + if (cmp == 0 && level == 0) + { + *res = grub_bfs_to_cpu64 (key_values[i | (1 << j)].val); + grub_free (node); + return GRUB_ERR_NONE; + } +#ifdef MODE_AFS + if (cmp <= 0) +#else + if (cmp < 0) +#endif + i |= (1 << j); + } + if (i == 0) + { + grub_uint16_t end = 0; + int cmp; + end = grub_bfs_to_cpu16 (keylen_idx[0]); + if (grub_bfs_to_cpu_treehead (node->total_key_len) <= end) + end = grub_bfs_to_cpu_treehead (node->total_key_len); + cmp = bfs_strcmp (key_data, name, end); + if (cmp == 0 && level == 0) + { + *res = grub_bfs_to_cpu64 (key_values[0].val); + grub_free (node); + return GRUB_ERR_NONE; + } +#ifdef MODE_AFS + if (cmp > 0 && level != 0) +#else + if (cmp >= 0 && level != 0) +#endif + { + node_off = grub_bfs_to_cpu64 (key_values[0].val); + level--; + grub_free (node); + continue; + } + else if (level != 0 + && grub_bfs_to_cpu_treehead (node->count_keys) >= 2) + { + node_off = grub_bfs_to_cpu64 (key_values[1].val); + level--; + grub_free (node); + continue; + } + } + else if (level != 0 + && i + 1 < grub_bfs_to_cpu_treehead (node->count_keys)) + { + node_off = grub_bfs_to_cpu64 (key_values[i + 1].val); + level--; + grub_free (node); + continue; + } + if (node->overflow != POINTER_INVALID) + { + node_off = grub_bfs_to_cpu64 (node->overflow); + /* This level-- isn't specified but is needed. */ + level--; + grub_free (node); + continue; + } + grub_free (node); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + name); + } +} + +struct grub_fshelp_node +{ + grub_disk_t disk; + const struct grub_bfs_superblock *sb; + struct grub_bfs_inode ino; +}; + +static grub_err_t +lookup_file (grub_fshelp_node_t dir, + const char *name, + grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype) +{ + grub_err_t err; + struct grub_bfs_inode *new_ino; + grub_uint64_t res = 0; + + err = find_in_b_tree (dir->disk, dir->sb, &dir->ino, name, &res); + if (err) + return err; + + *foundnode = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!*foundnode) + return grub_errno; + + (*foundnode)->disk = dir->disk; + (*foundnode)->sb = dir->sb; + new_ino = &(*foundnode)->ino; + + if (grub_disk_read (dir->disk, res + << (grub_bfs_to_cpu32 (dir->sb->log2_bsize) + - GRUB_DISK_SECTOR_BITS), 0, + sizeof (*new_ino), (char *) new_ino)) + { + grub_free (*foundnode); + return grub_errno; + } + switch (grub_bfs_to_cpu32 (new_ino->mode) & ATTR_TYPE) + { + default: + case ATTR_REG: + *foundtype = GRUB_FSHELP_REG; + break; + case ATTR_DIR: + *foundtype = GRUB_FSHELP_DIR; + break; + case ATTR_LNK: + *foundtype = GRUB_FSHELP_SYMLINK; + break; + } + return GRUB_ERR_NONE; +} + +static char * +read_symlink (grub_fshelp_node_t node) +{ + char *alloc = NULL; + grub_err_t err; + +#ifndef MODE_AFS + if (!(grub_bfs_to_cpu32 (node->ino.flags) & LONG_SYMLINK)) + { + alloc = grub_malloc (sizeof (node->ino.inplace_link) + 1); + if (!alloc) + { + return NULL; + } + grub_memcpy (alloc, node->ino.inplace_link, + sizeof (node->ino.inplace_link)); + alloc[sizeof (node->ino.inplace_link)] = 0; + } + else +#endif + { + grub_size_t symsize = grub_bfs_to_cpu64 (node->ino.size); + alloc = grub_malloc (symsize + 1); + if (!alloc) + return NULL; + err = read_bfs_file (node->disk, node->sb, &node->ino, 0, alloc, symsize, 0, 0); + if (err) + { + grub_free (alloc); + return NULL; + } + alloc[symsize] = 0; + } + + return alloc; +} + +static grub_err_t +find_file (const char *path, grub_disk_t disk, + const struct grub_bfs_superblock *sb, struct grub_bfs_inode *ino, + enum grub_fshelp_filetype exptype) +{ + grub_err_t err; + struct grub_fshelp_node root = { + .disk = disk, + .sb = sb, + }; + struct grub_fshelp_node *found = NULL; + + err = read_extent (disk, sb, &sb->root_dir, 0, 0, &root.ino, + sizeof (root.ino)); + if (err) + return err; + err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, read_symlink, exptype); + if (!err) + grub_memcpy (ino, &found->ino, sizeof (*ino)); + + if (&root != found) + grub_free (found); + return err; +} + +static grub_err_t +mount (grub_disk_t disk, struct grub_bfs_superblock *sb) +{ + grub_err_t err; + err = grub_disk_read (disk, SUPERBLOCK, 0, sizeof (*sb), sb); + if (err == GRUB_ERR_OUT_OF_RANGE) + return grub_error (GRUB_ERR_BAD_FS, +#ifdef MODE_AFS + "not an AFS filesystem" +#else + "not a BFS filesystem" +#endif + ); + if (err) + return err; + if (sb->magic1 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC1) + || sb->magic2 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC2) + || sb->magic3 != grub_cpu_to_bfs32_compile_time (SUPER_BLOCK_MAGIC3) + || sb->bsize == 0 + || (grub_bfs_to_cpu32 (sb->bsize) + != (1U << grub_bfs_to_cpu32 (sb->log2_bsize))) + || grub_bfs_to_cpu32 (sb->log2_bsize) < GRUB_DISK_SECTOR_BITS) + return grub_error (GRUB_ERR_BAD_FS, +#ifdef MODE_AFS + "not an AFS filesystem" +#else + "not a BFS filesystem" +#endif + ); + return GRUB_ERR_NONE; +} + +/* Helper for grub_bfs_dir. */ +static int +grub_bfs_dir_iter (const char *name, grub_uint64_t value, + struct grub_bfs_dir_ctx *ctx) +{ + grub_err_t err2; + struct grub_bfs_inode ino; + struct grub_dirhook_info info; + + err2 = grub_disk_read (ctx->device->disk, value + << (grub_bfs_to_cpu32 (ctx->sb.log2_bsize) + - GRUB_DISK_SECTOR_BITS), 0, + sizeof (ino), (char *) &ino); + if (err2) + { + grub_print_error (); + return 0; + } + + info.mtimeset = 1; +#ifdef MODE_AFS + info.mtime = + grub_divmod64 (grub_bfs_to_cpu64 (ino.mtime), 1000000, 0); +#else + info.mtime = grub_bfs_to_cpu64 (ino.mtime) >> 16; +#endif + info.dir = ((grub_bfs_to_cpu32 (ino.mode) & ATTR_TYPE) == ATTR_DIR); + return ctx->hook (name, &info, ctx->hook_data); +} + +static grub_err_t +grub_bfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_bfs_dir_ctx ctx = { + .device = device, + .hook = hook, + .hook_data = hook_data + }; + grub_err_t err; + + err = mount (device->disk, &ctx.sb); + if (err) + return err; + + { + struct grub_bfs_inode ino; + err = find_file (path, device->disk, &ctx.sb, &ino, GRUB_FSHELP_DIR); + if (err) + return err; + iterate_in_b_tree (device->disk, &ctx.sb, &ino, grub_bfs_dir_iter, + &ctx); + } + + return grub_errno; +} + +static grub_err_t +grub_bfs_open (struct grub_file *file, const char *name) +{ + struct grub_bfs_superblock sb; + grub_err_t err; + + err = mount (file->device->disk, &sb); + if (err) + return err; + + { + struct grub_bfs_inode ino; + struct grub_bfs_data *data; + err = find_file (name, file->device->disk, &sb, &ino, GRUB_FSHELP_REG); + if (err) + return err; + + data = grub_zalloc (sizeof (struct grub_bfs_data)); + if (!data) + return grub_errno; + data->sb = sb; + grub_memcpy (&data->ino, &ino, sizeof (data->ino)); + file->data = data; + file->size = grub_bfs_to_cpu64 (ino.size); + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_bfs_close (grub_file_t file) +{ + grub_free (file->data); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_bfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_err_t err; + struct grub_bfs_data *data = file->data; + + err = read_bfs_file (file->device->disk, &data->sb, + &data->ino, file->offset, buf, len, + file->read_hook, file->read_hook_data); + if (err) + return -1; + return len; +} + +static grub_err_t +grub_bfs_label (grub_device_t device, char **label) +{ + struct grub_bfs_superblock sb; + grub_err_t err; + + *label = 0; + + err = mount (device->disk, &sb); + if (err) + return err; + + *label = grub_strndup (sb.label, sizeof (sb.label)); + return GRUB_ERR_NONE; +} + +#ifndef MODE_AFS +static grub_ssize_t +read_bfs_attr (grub_disk_t disk, + const struct grub_bfs_superblock *sb, + struct grub_bfs_inode *ino, + const char *name, void *buf, grub_size_t len) +{ + grub_uint8_t *ptr = (grub_uint8_t *) ino->small_data; + grub_uint8_t *end = ((grub_uint8_t *) ino + grub_bfs_to_cpu32 (sb->bsize)); + + while (ptr + sizeof (struct grub_bfs_small_data_element_header) < end) + { + struct grub_bfs_small_data_element_header *el; + char *el_name; + grub_uint8_t *data; + el = (struct grub_bfs_small_data_element_header *) ptr; + if (el->name_len == 0) + break; + el_name = (char *) (el + 1); + data = (grub_uint8_t *) el_name + grub_bfs_to_cpu16 (el->name_len) + 3; + ptr = data + grub_bfs_to_cpu16 (el->value_len) + 1; + if (grub_memcmp (name, el_name, grub_bfs_to_cpu16 (el->name_len)) == 0 + && name[el->name_len] == 0) + { + grub_size_t copy; + copy = len; + if (grub_bfs_to_cpu16 (el->value_len) > copy) + copy = grub_bfs_to_cpu16 (el->value_len); + grub_memcpy (buf, data, copy); + return copy; + } + } + + if (ino->attr.len != 0) + { + grub_size_t read; + grub_err_t err; + grub_uint64_t res; + + err = read_extent (disk, sb, &ino->attr, 0, 0, ino, + grub_bfs_to_cpu32 (sb->bsize)); + if (err) + return -1; + + err = find_in_b_tree (disk, sb, ino, name, &res); + if (err) + return -1; + grub_disk_read (disk, res + << (grub_bfs_to_cpu32 (sb->log2_bsize) + - GRUB_DISK_SECTOR_BITS), 0, + grub_bfs_to_cpu32 (sb->bsize), (char *) ino); + read = grub_bfs_to_cpu64 (ino->size); + if (read > len) + read = len; + + err = read_bfs_file (disk, sb, ino, 0, buf, read, 0, 0); + if (err) + return -1; + return read; + } + return -1; +} + +static grub_err_t +grub_bfs_uuid (grub_device_t device, char **uuid) +{ + struct grub_bfs_superblock sb; + grub_err_t err; + struct grub_bfs_inode *ino; + grub_uint64_t vid; + + *uuid = 0; + + err = mount (device->disk, &sb); + if (err) + return err; + + ino = grub_malloc (grub_bfs_to_cpu32 (sb.bsize)); + if (!ino) + return grub_errno; + + err = read_extent (device->disk, &sb, &sb.root_dir, 0, 0, + ino, grub_bfs_to_cpu32 (sb.bsize)); + if (err) + { + grub_free (ino); + return err; + } + if (read_bfs_attr (device->disk, &sb, ino, "be:volume_id", + &vid, sizeof (vid)) == sizeof (vid)) + *uuid = + grub_xasprintf ("%016" PRIxGRUB_UINT64_T, grub_bfs_to_cpu64 (vid)); + + grub_free (ino); + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_fs grub_bfs_fs = { +#ifdef MODE_AFS + .name = "afs", +#else + .name = "bfs", +#endif + .fs_dir = grub_bfs_dir, + .fs_open = grub_bfs_open, + .fs_read = grub_bfs_read, + .fs_close = grub_bfs_close, + .fs_label = grub_bfs_label, +#ifndef MODE_AFS + .fs_uuid = grub_bfs_uuid, +#endif +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif +}; + +#ifdef MODE_AFS +GRUB_MOD_INIT (afs) +#else +GRUB_MOD_INIT (bfs) +#endif +{ + COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE == + sizeof (struct grub_bfs_extent)); + if (!grub_is_lockdown ()) + { + grub_bfs_fs.mod = mod; + grub_fs_register (&grub_bfs_fs); + } +} + +#ifdef MODE_AFS +GRUB_MOD_FINI (afs) +#else +GRUB_MOD_FINI (bfs) +#endif +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_bfs_fs); +} diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c new file mode 100644 index 000000000..7bf8d922f --- /dev/null +++ b/grub-core/fs/btrfs.c @@ -0,0 +1,2449 @@ +/* btrfs.c - B-tree file system. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011,2012,2013 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 . + */ + +/* + * Tell zstd to expose functions that aren't part of the stable API, which + * aren't safe to use when linking against a dynamic library. We vendor in a + * specific zstd version, so we know what we're getting. We need these unstable + * functions to provide our own allocator, which uses grub_malloc(), to zstd. + */ +#define ZSTD_STATIC_LINKING_ONLY + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_BTRFS_SIGNATURE "_BHRfS_M" + +/* From http://www.oberhumer.com/opensource/lzo/lzofaq.php + * LZO will expand incompressible data by a little amount. I still haven't + * computed the exact values, but I suggest using these formulas for + * a worst-case expansion calculation: + * + * output_block_size = input_block_size + (input_block_size / 16) + 64 + 3 + * */ +#define GRUB_BTRFS_LZO_BLOCK_SIZE 4096 +#define GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE (GRUB_BTRFS_LZO_BLOCK_SIZE + \ + (GRUB_BTRFS_LZO_BLOCK_SIZE / 16) + 64 + 3) + +#define ZSTD_BTRFS_MAX_WINDOWLOG 17 +#define ZSTD_BTRFS_MAX_INPUT (1 << ZSTD_BTRFS_MAX_WINDOWLOG) + +typedef grub_uint8_t grub_btrfs_checksum_t[0x20]; +typedef grub_uint16_t grub_btrfs_uuid_t[8]; + +struct grub_btrfs_device +{ + grub_uint64_t device_id; + grub_uint64_t size; + grub_uint8_t dummy[0x62 - 0x10]; +} GRUB_PACKED; + +struct grub_btrfs_superblock +{ + grub_btrfs_checksum_t checksum; + grub_btrfs_uuid_t uuid; + grub_uint8_t dummy[0x10]; + grub_uint8_t signature[sizeof (GRUB_BTRFS_SIGNATURE) - 1]; + grub_uint64_t generation; + grub_uint64_t root_tree; + grub_uint64_t chunk_tree; + grub_uint8_t dummy2[0x20]; + grub_uint64_t root_dir_objectid; + grub_uint8_t dummy3[0x41]; + struct grub_btrfs_device this_device; + char label[0x100]; + grub_uint8_t dummy4[0x100]; + grub_uint8_t bootstrap_mapping[0x800]; +} GRUB_PACKED; + +struct btrfs_header +{ + grub_btrfs_checksum_t checksum; + grub_btrfs_uuid_t uuid; + grub_uint64_t bytenr; + grub_uint8_t dummy[0x28]; + grub_uint32_t nitems; + grub_uint8_t level; +} GRUB_PACKED; + +struct grub_btrfs_device_desc +{ + grub_device_t dev; + grub_uint64_t id; +}; + +struct grub_btrfs_data +{ + struct grub_btrfs_superblock sblock; + grub_uint64_t tree; + grub_uint64_t inode; + + struct grub_btrfs_device_desc *devices_attached; + unsigned n_devices_attached; + unsigned n_devices_allocated; + + /* Cached extent data. */ + grub_uint64_t extstart; + grub_uint64_t extend; + grub_uint64_t extino; + grub_uint64_t exttree; + grub_size_t extsize; + struct grub_btrfs_extent_data *extent; +}; + +struct grub_btrfs_chunk_item +{ + grub_uint64_t size; + grub_uint64_t dummy; + grub_uint64_t stripe_length; + grub_uint64_t type; +#define GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE 0x07 +#define GRUB_BTRFS_CHUNK_TYPE_SINGLE 0x00 +#define GRUB_BTRFS_CHUNK_TYPE_RAID0 0x08 +#define GRUB_BTRFS_CHUNK_TYPE_RAID1 0x10 +#define GRUB_BTRFS_CHUNK_TYPE_DUPLICATED 0x20 +#define GRUB_BTRFS_CHUNK_TYPE_RAID10 0x40 +#define GRUB_BTRFS_CHUNK_TYPE_RAID5 0x80 +#define GRUB_BTRFS_CHUNK_TYPE_RAID6 0x100 +#define GRUB_BTRFS_CHUNK_TYPE_RAID1C3 0x200 +#define GRUB_BTRFS_CHUNK_TYPE_RAID1C4 0x400 + grub_uint8_t dummy2[0xc]; + grub_uint16_t nstripes; + grub_uint16_t nsubstripes; +} GRUB_PACKED; + +struct grub_btrfs_chunk_stripe +{ + grub_uint64_t device_id; + grub_uint64_t offset; + grub_btrfs_uuid_t device_uuid; +} GRUB_PACKED; + +struct grub_btrfs_leaf_node +{ + struct grub_btrfs_key key; + grub_uint32_t offset; + grub_uint32_t size; +} GRUB_PACKED; + +struct grub_btrfs_internal_node +{ + struct grub_btrfs_key key; + grub_uint64_t addr; + grub_uint64_t dummy; +} GRUB_PACKED; + +struct grub_btrfs_dir_item +{ + struct grub_btrfs_key key; + grub_uint8_t dummy[8]; + grub_uint16_t m; + grub_uint16_t n; +#define GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR 1 +#define GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY 2 +#define GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK 7 + grub_uint8_t type; + char name[0]; +} GRUB_PACKED; + +struct grub_btrfs_leaf_descriptor +{ + unsigned depth; + unsigned allocated; + struct + { + grub_disk_addr_t addr; + unsigned iter; + unsigned maxiter; + int leaf; + } *data; +}; + +struct grub_btrfs_time +{ + grub_int64_t sec; + grub_uint32_t nanosec; +} GRUB_PACKED; + +struct grub_btrfs_inode +{ + grub_uint8_t dummy1[0x10]; + grub_uint64_t size; + grub_uint8_t dummy2[0x70]; + struct grub_btrfs_time mtime; +} GRUB_PACKED; + +struct grub_btrfs_extent_data +{ + grub_uint64_t dummy; + grub_uint64_t size; + grub_uint8_t compression; + grub_uint8_t encryption; + grub_uint16_t encoding; + grub_uint8_t type; + union + { + char inl[0]; + struct + { + grub_uint64_t laddr; + grub_uint64_t compressed_size; + grub_uint64_t offset; + grub_uint64_t filled; + }; + }; +} GRUB_PACKED; + +#define GRUB_BTRFS_EXTENT_INLINE 0 +#define GRUB_BTRFS_EXTENT_REGULAR 1 + +#define GRUB_BTRFS_COMPRESSION_NONE 0 +#define GRUB_BTRFS_COMPRESSION_ZLIB 1 +#define GRUB_BTRFS_COMPRESSION_LZO 2 +#define GRUB_BTRFS_COMPRESSION_ZSTD 3 + +#define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 + +static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, + 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 +}; + +static grub_err_t +grub_btrfs_read_logical (struct grub_btrfs_data *data, + grub_disk_addr_t addr, void *buf, grub_size_t size, + int recursion_depth); + +static grub_err_t +read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) +{ + struct grub_btrfs_superblock sblock; + unsigned i; + grub_err_t err = GRUB_ERR_NONE; + for (i = 0; i < ARRAY_SIZE (superblock_sectors); i++) + { + /* Don't try additional superblocks beyond device size. */ + if (i && (grub_le_to_cpu64 (sblock.this_device.size) + >> GRUB_DISK_SECTOR_BITS) <= superblock_sectors[i]) + break; + err = grub_disk_read (disk, superblock_sectors[i], 0, + sizeof (sblock), &sblock); + if (err == GRUB_ERR_OUT_OF_RANGE) + break; + + if (grub_memcmp ((char *) sblock.signature, GRUB_BTRFS_SIGNATURE, + sizeof (GRUB_BTRFS_SIGNATURE) - 1) != 0) + break; + if (i == 0 || grub_le_to_cpu64 (sblock.generation) + > grub_le_to_cpu64 (sb->generation)) + grub_memcpy (sb, &sblock, sizeof (sblock)); + } + + if ((err == GRUB_ERR_OUT_OF_RANGE || !err) && i == 0) + return grub_error (GRUB_ERR_BAD_FS, "not a Btrfs filesystem"); + + if (err == GRUB_ERR_OUT_OF_RANGE) + grub_errno = err = GRUB_ERR_NONE; + + return err; +} + +static int +key_cmp (const struct grub_btrfs_key *a, const struct grub_btrfs_key *b) +{ + if (grub_le_to_cpu64 (a->object_id) < grub_le_to_cpu64 (b->object_id)) + return -1; + if (grub_le_to_cpu64 (a->object_id) > grub_le_to_cpu64 (b->object_id)) + return +1; + + if (a->type < b->type) + return -1; + if (a->type > b->type) + return +1; + + if (grub_le_to_cpu64 (a->offset) < grub_le_to_cpu64 (b->offset)) + return -1; + if (grub_le_to_cpu64 (a->offset) > grub_le_to_cpu64 (b->offset)) + return +1; + return 0; +} + +static void +free_iterator (struct grub_btrfs_leaf_descriptor *desc) +{ + grub_free (desc->data); +} + +static grub_err_t +check_btrfs_header (struct grub_btrfs_data *data, struct btrfs_header *header, + grub_disk_addr_t addr) +{ + if (grub_le_to_cpu64 (header->bytenr) != addr) + { + grub_dprintf ("btrfs", "btrfs_header.bytenr is not equal node addr\n"); + return grub_error (GRUB_ERR_BAD_FS, + "header bytenr is not equal node addr"); + } + if (grub_memcmp (data->sblock.uuid, header->uuid, sizeof(grub_btrfs_uuid_t))) + { + grub_dprintf ("btrfs", "btrfs_header.uuid doesn't match sblock uuid\n"); + return grub_error (GRUB_ERR_BAD_FS, + "header uuid doesn't match sblock uuid"); + } + return GRUB_ERR_NONE; +} + +static grub_err_t +save_ref (struct grub_btrfs_leaf_descriptor *desc, + grub_disk_addr_t addr, unsigned i, unsigned m, int l) +{ + desc->depth++; + if (desc->allocated < desc->depth) + { + void *newdata; + grub_size_t sz; + + if (grub_mul (desc->allocated, 2, &desc->allocated) || + grub_mul (desc->allocated, sizeof (desc->data[0]), &sz)) + return GRUB_ERR_OUT_OF_RANGE; + + newdata = grub_realloc (desc->data, sz); + if (!newdata) + return grub_errno; + desc->data = newdata; + } + desc->data[desc->depth - 1].addr = addr; + desc->data[desc->depth - 1].iter = i; + desc->data[desc->depth - 1].maxiter = m; + desc->data[desc->depth - 1].leaf = l; + return GRUB_ERR_NONE; +} + +static int +next (struct grub_btrfs_data *data, + struct grub_btrfs_leaf_descriptor *desc, + grub_disk_addr_t * outaddr, grub_size_t * outsize, + struct grub_btrfs_key *key_out) +{ + grub_err_t err; + struct grub_btrfs_leaf_node leaf; + + for (; desc->depth > 0; desc->depth--) + { + desc->data[desc->depth - 1].iter++; + if (desc->data[desc->depth - 1].iter + < desc->data[desc->depth - 1].maxiter) + break; + } + if (desc->depth == 0) + return 0; + while (!desc->data[desc->depth - 1].leaf) + { + struct grub_btrfs_internal_node node; + struct btrfs_header head; + + err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter + * sizeof (node) + + sizeof (struct btrfs_header) + + desc->data[desc->depth - 1].addr, + &node, sizeof (node), 0); + if (err) + return -err; + + err = grub_btrfs_read_logical (data, grub_le_to_cpu64 (node.addr), + &head, sizeof (head), 0); + if (err) + return -err; + check_btrfs_header (data, &head, grub_le_to_cpu64 (node.addr)); + + save_ref (desc, grub_le_to_cpu64 (node.addr), 0, + grub_le_to_cpu32 (head.nitems), !head.level); + } + err = grub_btrfs_read_logical (data, desc->data[desc->depth - 1].iter + * sizeof (leaf) + + sizeof (struct btrfs_header) + + desc->data[desc->depth - 1].addr, &leaf, + sizeof (leaf), 0); + if (err) + return -err; + *outsize = grub_le_to_cpu32 (leaf.size); + *outaddr = desc->data[desc->depth - 1].addr + sizeof (struct btrfs_header) + + grub_le_to_cpu32 (leaf.offset); + *key_out = leaf.key; + return 1; +} + +static grub_err_t +lower_bound (struct grub_btrfs_data *data, + const struct grub_btrfs_key *key_in, + struct grub_btrfs_key *key_out, + grub_uint64_t root, + grub_disk_addr_t *outaddr, grub_size_t *outsize, + struct grub_btrfs_leaf_descriptor *desc, + int recursion_depth) +{ + grub_disk_addr_t addr = grub_le_to_cpu64 (root); + int depth = -1; + + if (desc) + { + desc->allocated = 16; + desc->depth = 0; + desc->data = grub_calloc (desc->allocated, sizeof (desc->data[0])); + if (!desc->data) + return grub_errno; + } + + /* > 2 would work as well but be robust and allow a bit more just in case. + */ + if (recursion_depth > 10) + return grub_error (GRUB_ERR_BAD_FS, "too deep btrfs virtual nesting"); + + grub_dprintf ("btrfs", + "retrieving %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", + key_in->object_id, key_in->type, key_in->offset); + + while (1) + { + grub_err_t err; + struct btrfs_header head; + + reiter: + depth++; + /* FIXME: preread few nodes into buffer. */ + err = grub_btrfs_read_logical (data, addr, &head, sizeof (head), + recursion_depth + 1); + if (err) + return err; + check_btrfs_header (data, &head, addr); + addr += sizeof (head); + if (head.level) + { + unsigned i; + struct grub_btrfs_internal_node node, node_last; + int have_last = 0; + grub_memset (&node_last, 0, sizeof (node_last)); + for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) + { + err = grub_btrfs_read_logical (data, addr + i * sizeof (node), + &node, sizeof (node), + recursion_depth + 1); + if (err) + return err; + + grub_dprintf ("btrfs", + "internal node (depth %d) %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", depth, + node.key.object_id, node.key.type, + node.key.offset); + + if (key_cmp (&node.key, key_in) == 0) + { + err = GRUB_ERR_NONE; + if (desc) + err = save_ref (desc, addr - sizeof (head), i, + grub_le_to_cpu32 (head.nitems), 0); + if (err) + return err; + addr = grub_le_to_cpu64 (node.addr); + goto reiter; + } + if (key_cmp (&node.key, key_in) > 0) + break; + node_last = node; + have_last = 1; + } + if (have_last) + { + err = GRUB_ERR_NONE; + if (desc) + err = save_ref (desc, addr - sizeof (head), i - 1, + grub_le_to_cpu32 (head.nitems), 0); + if (err) + return err; + addr = grub_le_to_cpu64 (node_last.addr); + goto reiter; + } + *outsize = 0; + *outaddr = 0; + grub_memset (key_out, 0, sizeof (*key_out)); + if (desc) + return save_ref (desc, addr - sizeof (head), -1, + grub_le_to_cpu32 (head.nitems), 0); + return GRUB_ERR_NONE; + } + { + unsigned i; + struct grub_btrfs_leaf_node leaf, leaf_last; + int have_last = 0; + for (i = 0; i < grub_le_to_cpu32 (head.nitems); i++) + { + err = grub_btrfs_read_logical (data, addr + i * sizeof (leaf), + &leaf, sizeof (leaf), + recursion_depth + 1); + if (err) + return err; + + grub_dprintf ("btrfs", + "leaf (depth %d) %" PRIxGRUB_UINT64_T + " %x %" PRIxGRUB_UINT64_T "\n", depth, + leaf.key.object_id, leaf.key.type, leaf.key.offset); + + if (key_cmp (&leaf.key, key_in) == 0) + { + grub_memcpy (key_out, &leaf.key, sizeof (*key_out)); + *outsize = grub_le_to_cpu32 (leaf.size); + *outaddr = addr + grub_le_to_cpu32 (leaf.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + + if (key_cmp (&leaf.key, key_in) > 0) + break; + + have_last = 1; + leaf_last = leaf; + } + + if (have_last) + { + grub_memcpy (key_out, &leaf_last.key, sizeof (*key_out)); + *outsize = grub_le_to_cpu32 (leaf_last.size); + *outaddr = addr + grub_le_to_cpu32 (leaf_last.offset); + if (desc) + return save_ref (desc, addr - sizeof (head), i - 1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + *outsize = 0; + *outaddr = 0; + grub_memset (key_out, 0, sizeof (*key_out)); + if (desc) + return save_ref (desc, addr - sizeof (head), -1, + grub_le_to_cpu32 (head.nitems), 1); + return GRUB_ERR_NONE; + } + } +} + +/* Context for find_device. */ +struct find_device_ctx +{ + struct grub_btrfs_data *data; + grub_uint64_t id; + grub_device_t dev_found; +}; + +/* Helper for find_device. */ +static int +find_device_iter (const char *name, void *data) +{ + struct find_device_ctx *ctx = data; + grub_device_t dev; + grub_err_t err; + struct grub_btrfs_superblock sb; + + dev = grub_device_open (name); + if (!dev) + return 0; + if (!dev->disk) + { + grub_device_close (dev); + return 0; + } + err = read_sblock (dev->disk, &sb); + if (err == GRUB_ERR_BAD_FS) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (err) + { + grub_device_close (dev); + grub_print_error (); + return 0; + } + if (grub_memcmp (ctx->data->sblock.uuid, sb.uuid, sizeof (sb.uuid)) != 0 + || sb.this_device.device_id != ctx->id) + { + grub_device_close (dev); + return 0; + } + + ctx->dev_found = dev; + return 1; +} + +static grub_device_t +find_device (struct grub_btrfs_data *data, grub_uint64_t id) +{ + struct find_device_ctx ctx = { + .data = data, + .id = id, + .dev_found = NULL + }; + unsigned i; + + for (i = 0; i < data->n_devices_attached; i++) + if (id == data->devices_attached[i].id) + return data->devices_attached[i].dev; + + grub_device_iterate (find_device_iter, &ctx); + + data->n_devices_attached++; + if (data->n_devices_attached > data->n_devices_allocated) + { + void *tmp; + grub_size_t sz; + + if (grub_mul (data->n_devices_attached, 2, &data->n_devices_allocated) || + grub_add (data->n_devices_allocated, 1, &data->n_devices_allocated) || + grub_mul (data->n_devices_allocated, sizeof (data->devices_attached[0]), &sz)) + goto fail; + + data->devices_attached = grub_realloc (tmp = data->devices_attached, sz); + if (!data->devices_attached) + { + data->devices_attached = tmp; + + fail: + if (ctx.dev_found) + grub_device_close (ctx.dev_found); + return NULL; + } + } + data->devices_attached[data->n_devices_attached - 1].id = id; + data->devices_attached[data->n_devices_attached - 1].dev = ctx.dev_found; + return ctx.dev_found; +} + +static grub_err_t +btrfs_read_from_chunk (struct grub_btrfs_data *data, + struct grub_btrfs_chunk_item *chunk, + grub_uint64_t stripen, grub_uint64_t stripe_offset, + int redundancy, grub_uint64_t csize, + void *buf) +{ + struct grub_btrfs_chunk_stripe *stripe; + grub_disk_addr_t paddr; + grub_device_t dev; + grub_err_t err; + + stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1); + /* Right now the redundancy handling is easy. + With RAID5-like it will be more difficult. */ + stripe += stripen + redundancy; + + paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset; + + grub_dprintf ("btrfs", "stripe %" PRIxGRUB_UINT64_T + " maps to 0x%" PRIxGRUB_UINT64_T "\n" + "reading paddr 0x%" PRIxGRUB_UINT64_T "\n", + stripen, stripe->offset, paddr); + + dev = find_device (data, stripe->device_id); + if (!dev) + { + grub_dprintf ("btrfs", + "couldn't find a necessary member device " + "of multi-device filesystem\n"); + grub_errno = GRUB_ERR_NONE; + return GRUB_ERR_READ_ERROR; + } + + err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS, + paddr & (GRUB_DISK_SECTOR_SIZE - 1), + csize, buf); + return err; +} + +struct raid56_buffer { + void *buf; + int data_is_valid; +}; + +static void +rebuild_raid5 (char *dest, struct raid56_buffer *buffers, + grub_uint64_t nstripes, grub_uint64_t csize) +{ + grub_uint64_t i; + int first; + + for(i = 0; buffers[i].data_is_valid && i < nstripes; i++); + + if (i == nstripes) + { + grub_dprintf ("btrfs", "called rebuild_raid5(), but all disks are OK\n"); + return; + } + + grub_dprintf ("btrfs", "rebuilding RAID 5 stripe #%" PRIuGRUB_UINT64_T "\n", i); + + for (i = 0, first = 1; i < nstripes; i++) + { + if (!buffers[i].data_is_valid) + continue; + + if (first) { + grub_memcpy(dest, buffers[i].buf, csize); + first = 0; + } else + grub_crypto_xor (dest, dest, buffers[i].buf, csize); + } +} + +static grub_err_t +raid6_recover_read_buffer (void *data, int disk_nr, + grub_uint64_t addr __attribute__ ((unused)), + void *dest, grub_size_t size) +{ + struct raid56_buffer *buffers = data; + + if (!buffers[disk_nr].data_is_valid) + return grub_errno = GRUB_ERR_READ_ERROR; + + grub_memcpy(dest, buffers[disk_nr].buf, size); + + return grub_errno = GRUB_ERR_NONE; +} + +static void +rebuild_raid6 (struct raid56_buffer *buffers, grub_uint64_t nstripes, + grub_uint64_t csize, grub_uint64_t parities_pos, void *dest, + grub_uint64_t stripen) + +{ + grub_raid6_recover_gen (buffers, nstripes, stripen, parities_pos, + dest, 0, csize, 0, raid6_recover_read_buffer); +} + +static grub_err_t +raid56_read_retry (struct grub_btrfs_data *data, + struct grub_btrfs_chunk_item *chunk, + grub_uint64_t stripe_offset, grub_uint64_t stripen, + grub_uint64_t csize, void *buf, grub_uint64_t parities_pos) +{ + struct raid56_buffer *buffers; + grub_uint64_t nstripes = grub_le_to_cpu16 (chunk->nstripes); + grub_uint64_t chunk_type = grub_le_to_cpu64 (chunk->type); + grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY; + grub_uint64_t i, failed_devices; + + buffers = grub_calloc (nstripes, sizeof (*buffers)); + if (!buffers) + goto cleanup; + + for (i = 0; i < nstripes; i++) + { + buffers[i].buf = grub_zalloc (csize); + if (!buffers[i].buf) + goto cleanup; + } + + for (failed_devices = 0, i = 0; i < nstripes; i++) + { + struct grub_btrfs_chunk_stripe *stripe; + grub_disk_addr_t paddr; + grub_device_t dev; + grub_err_t err; + + /* + * The struct grub_btrfs_chunk_stripe array lives + * behind struct grub_btrfs_chunk_item. + */ + stripe = (struct grub_btrfs_chunk_stripe *) (chunk + 1) + i; + + paddr = grub_le_to_cpu64 (stripe->offset) + stripe_offset; + grub_dprintf ("btrfs", "reading paddr %" PRIxGRUB_UINT64_T + " from stripe ID %" PRIxGRUB_UINT64_T "\n", + paddr, stripe->device_id); + + dev = find_device (data, stripe->device_id); + if (!dev) + { + grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " FAILED (dev ID %" + PRIxGRUB_UINT64_T ")\n", i, stripe->device_id); + failed_devices++; + continue; + } + + err = grub_disk_read (dev->disk, paddr >> GRUB_DISK_SECTOR_BITS, + paddr & (GRUB_DISK_SECTOR_SIZE - 1), + csize, buffers[i].buf); + if (err == GRUB_ERR_NONE) + { + buffers[i].data_is_valid = 1; + grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T " OK (dev ID %" + PRIxGRUB_UINT64_T ")\n", i, stripe->device_id); + } + else + { + grub_dprintf ("btrfs", "stripe %" PRIuGRUB_UINT64_T + " READ FAILED (dev ID %" PRIxGRUB_UINT64_T ")\n", + i, stripe->device_id); + failed_devices++; + } + } + + if (failed_devices > 1 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5)) + { + grub_dprintf ("btrfs", "not enough disks for RAID 5: total %" PRIuGRUB_UINT64_T + ", missing %" PRIuGRUB_UINT64_T "\n", + nstripes, failed_devices); + ret = GRUB_ERR_READ_ERROR; + goto cleanup; + } + else if (failed_devices > 2 && (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID6)) + { + grub_dprintf ("btrfs", "not enough disks for RAID 6: total %" PRIuGRUB_UINT64_T + ", missing %" PRIuGRUB_UINT64_T "\n", + nstripes, failed_devices); + ret = GRUB_ERR_READ_ERROR; + goto cleanup; + } + else + grub_dprintf ("btrfs", "enough disks for RAID 5: total %" + PRIuGRUB_UINT64_T ", missing %" PRIuGRUB_UINT64_T "\n", + nstripes, failed_devices); + + /* We have enough disks. So, rebuild the data. */ + if (chunk_type & GRUB_BTRFS_CHUNK_TYPE_RAID5) + rebuild_raid5 (buf, buffers, nstripes, csize); + else + rebuild_raid6 (buffers, nstripes, csize, parities_pos, buf, stripen); + + ret = GRUB_ERR_NONE; + cleanup: + if (buffers) + for (i = 0; i < nstripes; i++) + grub_free (buffers[i].buf); + grub_free (buffers); + + return ret; +} + +static grub_err_t +grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, + void *buf, grub_size_t size, int recursion_depth) +{ + while (size > 0) + { + grub_uint8_t *ptr; + struct grub_btrfs_key *key; + struct grub_btrfs_chunk_item *chunk; + grub_uint64_t csize; + grub_err_t err = 0; + struct grub_btrfs_key key_out; + int challoc = 0; + struct grub_btrfs_key key_in; + grub_size_t chsize; + grub_disk_addr_t chaddr; + + grub_dprintf ("btrfs", "searching for laddr %" PRIxGRUB_UINT64_T "\n", + addr); + for (ptr = data->sblock.bootstrap_mapping; + ptr < data->sblock.bootstrap_mapping + + sizeof (data->sblock.bootstrap_mapping) + - sizeof (struct grub_btrfs_key);) + { + key = (struct grub_btrfs_key *) ptr; + if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK) + break; + chunk = (struct grub_btrfs_chunk_item *) (key + 1); + grub_dprintf ("btrfs", + "%" PRIxGRUB_UINT64_T " %" PRIxGRUB_UINT64_T " \n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size)); + if (grub_le_to_cpu64 (key->offset) <= addr + && addr < grub_le_to_cpu64 (key->offset) + + grub_le_to_cpu64 (chunk->size)) + goto chunk_found; + ptr += sizeof (*key) + sizeof (*chunk) + + sizeof (struct grub_btrfs_chunk_stripe) + * grub_le_to_cpu16 (chunk->nstripes); + } + + key_in.object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key_in.type = GRUB_BTRFS_ITEM_TYPE_CHUNK; + key_in.offset = grub_cpu_to_le64 (addr); + err = lower_bound (data, &key_in, &key_out, + data->sblock.chunk_tree, + &chaddr, &chsize, NULL, recursion_depth); + if (err) + return err; + key = &key_out; + if (key->type != GRUB_BTRFS_ITEM_TYPE_CHUNK + || !(grub_le_to_cpu64 (key->offset) <= addr)) + return grub_error (GRUB_ERR_BAD_FS, + "couldn't find the chunk descriptor"); + + if (!chsize) + { + grub_dprintf ("btrfs", "zero-size chunk\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid zero-size chunk"); + } + + /* + * The space being allocated for a chunk should at least be able to + * contain one chunk item. + */ + if (chsize < sizeof (struct grub_btrfs_chunk_item)) + { + grub_dprintf ("btrfs", "chunk-size too small\n"); + return grub_error (GRUB_ERR_BAD_FS, + "got an invalid chunk size"); + } + chunk = grub_malloc (chsize); + if (!chunk) + return grub_errno; + + challoc = 1; + err = grub_btrfs_read_logical (data, chaddr, chunk, chsize, + recursion_depth); + if (err) + { + grub_free (chunk); + return err; + } + + chunk_found: + { + grub_uint64_t stripen; + grub_uint64_t stripe_offset; + grub_uint64_t off = addr - grub_le_to_cpu64 (key->offset); + grub_uint64_t chunk_stripe_length; + grub_uint16_t nstripes; + unsigned redundancy = 1; + unsigned i, j; + int is_raid56; + grub_uint64_t parities_pos = 0; + + is_raid56 = !!(grub_le_to_cpu64 (chunk->type) & + (GRUB_BTRFS_CHUNK_TYPE_RAID5 | + GRUB_BTRFS_CHUNK_TYPE_RAID6)); + + if (grub_le_to_cpu64 (chunk->size) <= off) + { + grub_dprintf ("btrfs", "no chunk\n"); + return grub_error (GRUB_ERR_BAD_FS, + "couldn't find the chunk descriptor"); + } + + nstripes = grub_le_to_cpu16 (chunk->nstripes) ? : 1; + chunk_stripe_length = grub_le_to_cpu64 (chunk->stripe_length) ? : 512; + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T + " (%d stripes (%d substripes) of %" + PRIxGRUB_UINT64_T ")\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + nstripes, + grub_le_to_cpu16 (chunk->nsubstripes), + chunk_stripe_length); + + switch (grub_le_to_cpu64 (chunk->type) + & ~GRUB_BTRFS_CHUNK_TYPE_BITS_DONTCARE) + { + case GRUB_BTRFS_CHUNK_TYPE_SINGLE: + { + grub_uint64_t stripe_length; + grub_dprintf ("btrfs", "single\n"); + stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size), + nstripes, + NULL); + + /* For single, there should be exactly 1 stripe. */ + if (grub_le_to_cpu16 (chunk->nstripes) != 1) + { + grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n", + grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID_SINGLE: nstripes != 1 (%u)", + grub_le_to_cpu16 (chunk->nstripes)); + } + if (stripe_length == 0) + stripe_length = 512; + stripen = grub_divmod64 (off, stripe_length, &stripe_offset); + csize = (stripen + 1) * stripe_length - off; + break; + } + case GRUB_BTRFS_CHUNK_TYPE_RAID1C4: + redundancy++; + /* fall through */ + case GRUB_BTRFS_CHUNK_TYPE_RAID1C3: + redundancy++; + /* fall through */ + case GRUB_BTRFS_CHUNK_TYPE_DUPLICATED: + case GRUB_BTRFS_CHUNK_TYPE_RAID1: + { + grub_dprintf ("btrfs", "RAID1 (copies: %d)\n", ++redundancy); + stripen = 0; + stripe_offset = off; + csize = grub_le_to_cpu64 (chunk->size) - off; + + /* + * Redundancy, and substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nstripes) != redundancy) + { + grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID1: nstripes != %u (%u)", + redundancy, grub_le_to_cpu16 (chunk->nstripes)); + } + break; + } + case GRUB_BTRFS_CHUNK_TYPE_RAID0: + { + grub_uint64_t middle, high; + grub_uint64_t low; + grub_dprintf ("btrfs", "RAID0\n"); + middle = grub_divmod64 (off, + chunk_stripe_length, + &low); + + high = grub_divmod64 (middle, nstripes, + &stripen); + stripe_offset = + low + chunk_stripe_length * high; + csize = chunk_stripe_length - low; + break; + } + case GRUB_BTRFS_CHUNK_TYPE_RAID10: + { + grub_uint64_t middle, high; + grub_uint64_t low; + grub_uint16_t nsubstripes; + nsubstripes = grub_le_to_cpu16 (chunk->nsubstripes) ? : 1; + middle = grub_divmod64 (off, + chunk_stripe_length, + &low); + + high = grub_divmod64 (middle, + nstripes / nsubstripes ? : 1, + &stripen); + stripen *= nsubstripes; + redundancy = nsubstripes; + stripe_offset = low + chunk_stripe_length + * high; + csize = chunk_stripe_length - low; + + /* + * Substripes only apply to RAID10, and there + * should be exactly 2 sub-stripes. + */ + if (grub_le_to_cpu16 (chunk->nsubstripes) != 2) + { + grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID10: nsubstripes != 2 (%u)", + grub_le_to_cpu16 (chunk->nsubstripes)); + } + + break; + } + case GRUB_BTRFS_CHUNK_TYPE_RAID5: + case GRUB_BTRFS_CHUNK_TYPE_RAID6: + { + grub_uint64_t nparities, stripe_nr, high, low; + + redundancy = 1; /* no redundancy for now */ + + if (grub_le_to_cpu64 (chunk->type) & GRUB_BTRFS_CHUNK_TYPE_RAID5) + { + grub_dprintf ("btrfs", "RAID5\n"); + nparities = 1; + } + else + { + grub_dprintf ("btrfs", "RAID6\n"); + nparities = 2; + } + + /* + * RAID 6 layout consists of several stripes spread over + * the disks, e.g.: + * + * Disk_0 Disk_1 Disk_2 Disk_3 + * A0 B0 P0 Q0 + * Q1 A1 B1 P1 + * P2 Q2 A2 B2 + * + * Note: placement of the parities depend on row number. + * + * Pay attention that the btrfs terminology may differ from + * terminology used in other RAID implementations, e.g. LVM, + * dm or md. The main difference is that btrfs calls contiguous + * block of data on a given disk, e.g. A0, stripe instead of chunk. + * + * The variables listed below have following meaning: + * - stripe_nr is the stripe number excluding the parities + * (A0 = 0, B0 = 1, A1 = 2, B1 = 3, etc.), + * - high is the row number (0 for A0...Q0, 1 for Q1...P1, etc.), + * - stripen is the disk number in a row (0 for A0, Q1, P2, + * 1 for B0, A1, Q2, etc.), + * - off is the logical address to read, + * - chunk_stripe_length is the size of a stripe (typically 64 KiB), + * - nstripes is the number of disks in a row, + * - low is the offset of the data inside a stripe, + * - stripe_offset is the data offset in an array, + * - csize is the "potential" data to read; it will be reduced + * to size if the latter is smaller, + * - nparities is the number of parities (1 for RAID 5, 2 for + * RAID 6); used only in RAID 5/6 code. + */ + stripe_nr = grub_divmod64 (off, chunk_stripe_length, &low); + + /* + * stripen is computed without the parities + * (0 for A0, A1, A2, 1 for B0, B1, B2, etc.). + */ + if (nparities >= nstripes) + return grub_error (GRUB_ERR_BAD_FS, + "invalid RAID5/6: nparities >= nstripes"); + high = grub_divmod64 (stripe_nr, nstripes - nparities, &stripen); + + /* + * The stripes are spread over the disks. Every each row their + * positions are shifted by 1 place. So, the real disks number + * change. Hence, we have to take into account current row number + * modulo nstripes (0 for A0, 1 for A1, 2 for A2, etc.). + */ + grub_divmod64 (high + stripen, nstripes, &stripen); + + /* + * parities_pos is equal to ((high - nparities) % nstripes) + * (see the diagram above). However, (high - nparities) can + * be negative, e.g. when high == 0, leading to an incorrect + * results. (high + nstripes - nparities) is always positive and + * modulo nstripes is equal to ((high - nparities) % nstripes). + */ + grub_divmod64 (high + nstripes - nparities, nstripes, &parities_pos); + + stripe_offset = chunk_stripe_length * high + low; + csize = chunk_stripe_length - low; + + break; + } + default: + grub_dprintf ("btrfs", "unsupported RAID\n"); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported RAID flags %" PRIxGRUB_UINT64_T, + grub_le_to_cpu64 (chunk->type)); + } + if (csize == 0) + return grub_error (GRUB_ERR_BUG, + "couldn't find the chunk descriptor"); + if (csize > (grub_uint64_t) size) + csize = size; + + /* + * The space for a chunk stripe is limited to the space provide in the super-block's + * bootstrap mapping with an initial btrfs key at the start of each chunk. + */ + grub_size_t avail_stripes = sizeof (data->sblock.bootstrap_mapping) / + (sizeof (struct grub_btrfs_key) + sizeof (struct grub_btrfs_chunk_stripe)); + + for (j = 0; j < 2; j++) + { + grub_size_t est_chunk_alloc = 0; + + grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T + "+0x%" PRIxGRUB_UINT64_T + " (%d stripes (%d substripes) of %" + PRIxGRUB_UINT64_T ")\n", + grub_le_to_cpu64 (key->offset), + grub_le_to_cpu64 (chunk->size), + grub_le_to_cpu16 (chunk->nstripes), + grub_le_to_cpu16 (chunk->nsubstripes), + grub_le_to_cpu64 (chunk->stripe_length)); + grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n", + addr); + + if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe), + grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) || + grub_add (est_chunk_alloc, + sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) || + est_chunk_alloc > chunk->size) + { + err = GRUB_ERR_BAD_FS; + break; + } + + if (grub_le_to_cpu16 (chunk->nstripes) > avail_stripes) + { + err = GRUB_ERR_BAD_FS; + break; + } + + if (is_raid56) + { + err = btrfs_read_from_chunk (data, chunk, stripen, + stripe_offset, + 0, /* no mirror */ + csize, buf); + grub_errno = GRUB_ERR_NONE; + if (err) + err = raid56_read_retry (data, chunk, stripe_offset, + stripen, csize, buf, parities_pos); + } + else + for (i = 0; i < redundancy; i++) + { + err = btrfs_read_from_chunk (data, chunk, stripen, + stripe_offset, + i, /* redundancy */ + csize, buf); + if (!err) + break; + grub_errno = GRUB_ERR_NONE; + } + if (!err) + break; + } + if (err) + return grub_errno = err; + } + size -= csize; + buf = (grub_uint8_t *) buf + csize; + addr += csize; + if (challoc) + grub_free (chunk); + } + return GRUB_ERR_NONE; +} + +static struct grub_btrfs_data * +grub_btrfs_mount (grub_device_t dev) +{ + struct grub_btrfs_data *data; + grub_err_t err; + + if (!dev->disk) + { + grub_error (GRUB_ERR_BAD_FS, "not BtrFS"); + return NULL; + } + + data = grub_zalloc (sizeof (*data)); + if (!data) + return NULL; + + err = read_sblock (dev->disk, &data->sblock); + if (err) + { + grub_free (data); + return NULL; + } + + data->n_devices_allocated = 16; + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); + if (!data->devices_attached) + { + grub_free (data); + return NULL; + } + data->n_devices_attached = 1; + data->devices_attached[0].dev = dev; + data->devices_attached[0].id = data->sblock.this_device.device_id; + + return data; +} + +static void +grub_btrfs_unmount (struct grub_btrfs_data *data) +{ + unsigned i; + /* The device 0 is closed one layer upper. */ + for (i = 1; i < data->n_devices_attached; i++) + if (data->devices_attached[i].dev) + grub_device_close (data->devices_attached[i].dev); + grub_free (data->devices_attached); + grub_free (data->extent); + grub_free (data); +} + +static grub_err_t +grub_btrfs_read_inode (struct grub_btrfs_data *data, + struct grub_btrfs_inode *inode, grub_uint64_t num, + grub_uint64_t tree) +{ + struct grub_btrfs_key key_in, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_err_t err; + + key_in.object_id = num; + key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM; + key_in.offset = 0; + + err = lower_bound (data, &key_in, &key_out, tree, &elemaddr, &elemsize, NULL, + 0); + if (err) + return err; + if (num != key_out.object_id + || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM) + return grub_error (GRUB_ERR_BAD_FS, "inode not found"); + + return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode), 0); +} + +static void *grub_zstd_malloc (void *state __attribute__((unused)), size_t size) +{ + return grub_malloc (size); +} + +static void grub_zstd_free (void *state __attribute__((unused)), void *address) +{ + return grub_free (address); +} + +static ZSTD_customMem grub_zstd_allocator (void) +{ + ZSTD_customMem allocator; + + allocator.customAlloc = &grub_zstd_malloc; + allocator.customFree = &grub_zstd_free; + allocator.opaque = NULL; + + return allocator; +} + +static grub_ssize_t +grub_btrfs_zstd_decompress (char *ibuf, grub_size_t isize, grub_off_t off, + char *obuf, grub_size_t osize) +{ + void *allocated = NULL; + char *otmpbuf = obuf; + grub_size_t otmpsize = osize; + ZSTD_DCtx *dctx = NULL; + grub_size_t zstd_ret; + grub_ssize_t ret = -1; + + /* + * Zstd will fail if it can't fit the entire output in the destination + * buffer, so if osize isn't large enough, allocate a temporary buffer. + */ + if (otmpsize < ZSTD_BTRFS_MAX_INPUT) + { + allocated = grub_malloc (ZSTD_BTRFS_MAX_INPUT); + if (!allocated) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed allocate a zstd buffer"); + goto err; + } + otmpbuf = (char *) allocated; + otmpsize = ZSTD_BTRFS_MAX_INPUT; + } + + /* Create the ZSTD_DCtx. */ + dctx = ZSTD_createDCtx_advanced (grub_zstd_allocator ()); + if (!dctx) + { + /* ZSTD_createDCtx_advanced() only fails if it is out of memory. */ + grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to create a zstd context"); + goto err; + } + + /* + * Get the real input size, there may be junk at the + * end of the frame. + */ + isize = ZSTD_findFrameCompressedSize (ibuf, isize); + if (ZSTD_isError (isize)) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "zstd data corrupted"); + goto err; + } + + /* Decompress and check for errors. */ + zstd_ret = ZSTD_decompressDCtx (dctx, otmpbuf, otmpsize, ibuf, isize); + if (ZSTD_isError (zstd_ret)) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "zstd data corrupted"); + goto err; + } + + /* + * Move the requested data into the obuf. obuf may be equal + * to otmpbuf, which is why grub_memmove() is required. + */ + grub_memmove (obuf, otmpbuf + off, osize); + ret = osize; + +err: + grub_free (allocated); + ZSTD_freeDCtx (dctx); + + return ret; +} + +static grub_ssize_t +grub_btrfs_lzo_decompress(char *ibuf, grub_size_t isize, grub_off_t off, + char *obuf, grub_size_t osize) +{ + grub_uint32_t total_size, cblock_size; + grub_size_t ret = 0; + char *ibuf0 = ibuf; + + total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (total_size); + + if (isize < total_size) + return -1; + + /* Jump forward to first block with requested data. */ + while (off >= GRUB_BTRFS_LZO_BLOCK_SIZE) + { + /* Don't let following uint32_t cross the page boundary. */ + if (((ibuf - ibuf0) & 0xffc) == 0xffc) + ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0; + + cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (cblock_size); + + if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE) + return -1; + + off -= GRUB_BTRFS_LZO_BLOCK_SIZE; + ibuf += cblock_size; + } + + while (osize > 0) + { + lzo_uint usize = GRUB_BTRFS_LZO_BLOCK_SIZE; + + /* Don't let following uint32_t cross the page boundary. */ + if (((ibuf - ibuf0) & 0xffc) == 0xffc) + ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0; + + cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf)); + ibuf += sizeof (cblock_size); + + if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE) + return -1; + + /* Block partially filled with requested data. */ + if (off > 0 || osize < GRUB_BTRFS_LZO_BLOCK_SIZE) + { + grub_size_t to_copy = GRUB_BTRFS_LZO_BLOCK_SIZE - off; + grub_uint8_t *buf; + + if (to_copy > osize) + to_copy = osize; + + buf = grub_malloc (GRUB_BTRFS_LZO_BLOCK_SIZE); + if (!buf) + return -1; + + if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, buf, &usize, + NULL) != LZO_E_OK) + { + grub_free (buf); + return -1; + } + + if (to_copy > usize) + to_copy = usize; + grub_memcpy(obuf, buf + off, to_copy); + + osize -= to_copy; + ret += to_copy; + obuf += to_copy; + ibuf += cblock_size; + off = 0; + + grub_free (buf); + continue; + } + + /* Decompress whole block directly to output buffer. */ + if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, (lzo_bytep)obuf, + &usize, NULL) != LZO_E_OK) + return -1; + + osize -= usize; + ret += usize; + obuf += usize; + ibuf += cblock_size; + } + + return ret; +} + +static grub_ssize_t +grub_btrfs_extent_read (struct grub_btrfs_data *data, + grub_uint64_t ino, grub_uint64_t tree, + grub_off_t pos0, char *buf, grub_size_t len) +{ + grub_off_t pos = pos0; + while (len) + { + grub_size_t csize; + grub_err_t err; + grub_off_t extoff; + struct grub_btrfs_leaf_descriptor desc; + + if (!data->extent || data->extstart > pos || data->extino != ino + || data->exttree != tree || data->extend <= pos) + { + struct grub_btrfs_key key_in, key_out; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + + grub_free (data->extent); + key_in.object_id = ino; + key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM; + key_in.offset = grub_cpu_to_le64 (pos); + err = lower_bound (data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + { + grub_free (desc.data); + return -1; + } + if (key_out.object_id != ino + || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + { + grub_error (GRUB_ERR_BAD_FS, "extent not found"); + return -1; + } + if ((grub_ssize_t) elemsize < ((char *) &data->extent->inl + - (char *) data->extent)) + { + grub_error (GRUB_ERR_BAD_FS, "extent descriptor is too short"); + return -1; + } + data->extstart = grub_le_to_cpu64 (key_out.offset); + data->extsize = elemsize; + data->extent = grub_malloc (elemsize); + data->extino = ino; + data->exttree = tree; + if (!data->extent) + return grub_errno; + + err = grub_btrfs_read_logical (data, elemaddr, data->extent, + elemsize, 0); + if (err) + return err; + + data->extend = data->extstart + grub_le_to_cpu64 (data->extent->size); + if (data->extent->type == GRUB_BTRFS_EXTENT_REGULAR + && (char *) data->extent + elemsize + >= (char *) &data->extent->filled + sizeof (data->extent->filled)) + data->extend = + data->extstart + grub_le_to_cpu64 (data->extent->filled); + + grub_dprintf ("btrfs", "regular extent 0x%" PRIxGRUB_UINT64_T "+0x%" + PRIxGRUB_UINT64_T "\n", + grub_le_to_cpu64 (key_out.offset), + grub_le_to_cpu64 (data->extent->size)); + /* + * The way of extent item iteration is pretty bad, it completely + * requires all extents are contiguous, which is not ensured. + * + * Features like NO_HOLE and mixed inline/regular extents can cause + * gaps between file extent items. + * + * The correct way is to follow Linux kernel/U-boot to iterate item + * by item, without any assumption on the file offset continuity. + * + * Here we just manually skip to next item and re-do the verification. + * + * TODO: Rework the whole extent item iteration code, if not the + * whole btrfs implementation. + */ + if (data->extend <= pos) + { + err = next (data, &desc, &elemaddr, &elemsize, &key_out); + if (err < 0) + return -1; + /* No next item for the inode, we hit the end. */ + if (err == 0 || key_out.object_id != ino || + key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM) + return pos - pos0; + + csize = grub_le_to_cpu64 (key_out.offset) - pos; + if (csize > len) + csize = len; + + grub_memset (buf, 0, csize); + buf += csize; + pos += csize; + len -= csize; + continue; + } + } + csize = data->extend - pos; + extoff = pos - data->extstart; + if (csize > len) + csize = len; + + if (data->extent->encryption) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "encryption not supported"); + return -1; + } + + if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE + && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZLIB + && data->extent->compression != GRUB_BTRFS_COMPRESSION_LZO + && data->extent->compression != GRUB_BTRFS_COMPRESSION_ZSTD) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression type 0x%x not supported", + data->extent->compression); + return -1; + } + + if (data->extent->encoding) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "encoding not supported"); + return -1; + } + + switch (data->extent->type) + { + case GRUB_BTRFS_EXTENT_INLINE: + if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) + { + if (grub_zlib_decompress (data->extent->inl, data->extsize - + ((grub_uint8_t *) data->extent->inl + - (grub_uint8_t *) data->extent), + extoff, buf, csize) + != (grub_ssize_t) csize) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "premature end of compressed"); + return -1; + } + } + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO) + { + if (grub_btrfs_lzo_decompress(data->extent->inl, data->extsize - + ((grub_uint8_t *) data->extent->inl + - (grub_uint8_t *) data->extent), + extoff, buf, csize) + != (grub_ssize_t) csize) + return -1; + } + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZSTD) + { + if (grub_btrfs_zstd_decompress (data->extent->inl, data->extsize - + ((grub_uint8_t *) data->extent->inl + - (grub_uint8_t *) data->extent), + extoff, buf, csize) + != (grub_ssize_t) csize) + return -1; + } + else + grub_memcpy (buf, data->extent->inl + extoff, csize); + break; + case GRUB_BTRFS_EXTENT_REGULAR: + if (!data->extent->laddr) + { + grub_memset (buf, 0, csize); + break; + } + + if (data->extent->compression != GRUB_BTRFS_COMPRESSION_NONE) + { + char *tmp; + grub_uint64_t zsize; + grub_ssize_t ret; + + zsize = grub_le_to_cpu64 (data->extent->compressed_size); + tmp = grub_malloc (zsize); + if (!tmp) + return -1; + err = grub_btrfs_read_logical (data, + grub_le_to_cpu64 (data->extent->laddr), + tmp, zsize, 0); + if (err) + { + grub_free (tmp); + return -1; + } + + if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZLIB) + ret = grub_zlib_decompress (tmp, zsize, extoff + + grub_le_to_cpu64 (data->extent->offset), + buf, csize); + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_LZO) + ret = grub_btrfs_lzo_decompress (tmp, zsize, extoff + + grub_le_to_cpu64 (data->extent->offset), + buf, csize); + else if (data->extent->compression == GRUB_BTRFS_COMPRESSION_ZSTD) + ret = grub_btrfs_zstd_decompress (tmp, zsize, extoff + + grub_le_to_cpu64 (data->extent->offset), + buf, csize); + else + ret = -1; + + grub_free (tmp); + + if (ret != (grub_ssize_t) csize) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "premature end of compressed"); + return -1; + } + + break; + } + err = grub_btrfs_read_logical (data, + grub_le_to_cpu64 (data->extent->laddr) + + grub_le_to_cpu64 (data->extent->offset) + + extoff, buf, csize, 0); + if (err) + return -1; + break; + default: + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported extent type 0x%x", data->extent->type); + return -1; + } + buf += csize; + pos += csize; + len -= csize; + } + return pos - pos0; +} + +static grub_err_t +get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type) +{ + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key_out, key_in; + struct grub_btrfs_root_item ri; + + key_in.object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_ROOT_VOL_OBJECTID); + key_in.offset = 0; + key_in.type = GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM; + err = lower_bound (data, &key_in, &key_out, + data->sblock.root_tree, + &elemaddr, &elemsize, NULL, 0); + if (err) + return err; + if (key_in.object_id != key_out.object_id + || key_in.type != key_out.type + || key_in.offset != key_out.offset) + return grub_error (GRUB_ERR_BAD_FS, "no root"); + err = grub_btrfs_read_logical (data, elemaddr, &ri, + sizeof (ri), 0); + if (err) + return err; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + *tree = ri.tree; + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + return GRUB_ERR_NONE; +} + +static grub_err_t +find_path (struct grub_btrfs_data *data, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type) +{ + const char *slash = path; + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_key key_out; + const char *ctoken; + grub_size_t ctokenlen; + char *path_alloc = NULL; + char *origpath = NULL; + unsigned symlinks_max = 32; + grub_size_t sz; + + err = get_root (data, key, tree, type); + if (err) + return err; + + origpath = grub_strdup (path); + if (!origpath) + return grub_errno; + + while (1) + { + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + ctoken = path; + ctokenlen = slash - path; + + if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + } + + if (ctokenlen == 1 && ctoken[0] == '.') + { + path = slash; + continue; + } + if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') + { + key->type = GRUB_BTRFS_ITEM_TYPE_INODE_REF; + key->offset = -1; + + err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + + if (key_out.type != key->type + || key->object_id != key_out.object_id) + { + grub_free (direl); + grub_free (path_alloc); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); + grub_free (origpath); + return err; + } + + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + key->object_id = key_out.offset; + + path = slash; + + continue; + } + + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); + + err = lower_bound (data, key, &key_out, *tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + if (key_cmp (key, &key_out) != 0) + { + grub_free (direl); + grub_free (path_alloc); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); + grub_free (origpath); + return err; + } + + struct grub_btrfs_dir_item *cdirel; + if (elemsize > allocated) + { + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory item size overflow")); + } + grub_free (direl); + direl = grub_malloc (sz); + if (!direl) + { + grub_free (path_alloc); + grub_free (origpath); + return grub_errno; + } + } + + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) + break; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + grub_free (path_alloc); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); + grub_free (origpath); + return err; + } + + path = slash; + if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) + { + struct grub_btrfs_inode inode; + char *tmp; + if (--symlinks_max == 0) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + } + + err = grub_btrfs_read_inode (data, &inode, + cdirel->key.object_id, *tree); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + + if (grub_add (grub_le_to_cpu64 (inode.size), grub_strlen (path), &sz) || + grub_add (sz, 1, &sz)) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + } + tmp = grub_malloc (sz); + if (!tmp) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return grub_errno; + } + + if (grub_btrfs_extent_read (data, cdirel->key.object_id, + *tree, 0, tmp, + grub_le_to_cpu64 (inode.size)) + != (grub_ssize_t) grub_le_to_cpu64 (inode.size)) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + grub_free (tmp); + return grub_errno; + } + grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path, + grub_strlen (path) + 1); + grub_free (path_alloc); + path = path_alloc = tmp; + if (path[0] == '/') + { + err = get_root (data, key, tree, type); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + } + continue; + } + *type = cdirel->type; + + switch (cdirel->key.type) + { + case GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM: + { + struct grub_btrfs_root_item ri; + err = lower_bound (data, &cdirel->key, &key_out, + data->sblock.root_tree, + &elemaddr, &elemsize, NULL, 0); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + if (cdirel->key.object_id != key_out.object_id + || cdirel->key.type != key_out.type) + { + grub_free (direl); + grub_free (path_alloc); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); + grub_free (origpath); + return err; + } + err = grub_btrfs_read_logical (data, elemaddr, &ri, + sizeof (ri), 0); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + *tree = ri.tree; + break; + } + case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM: + if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) + { + grub_free (direl); + grub_free (path_alloc); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath); + grub_free (origpath); + return err; + } + *key = cdirel->key; + if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + break; + default: + grub_free (path_alloc); + grub_free (origpath); + grub_free (direl); + return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x", + cdirel->key.type); + } + } + + grub_free (direl); + grub_free (origpath); + grub_free (path_alloc); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_btrfs_data *data = grub_btrfs_mount (device); + struct grub_btrfs_key key_in, key_out; + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t allocated = 0; + struct grub_btrfs_dir_item *direl = NULL; + struct grub_btrfs_leaf_descriptor desc; + int r = 0; + grub_uint64_t tree; + grub_uint8_t type; + grub_size_t est_size = 0; + grub_size_t sz; + + if (!data) + return grub_errno; + + err = find_path (data, path, &key_in, &tree, &type); + if (err) + { + grub_btrfs_unmount (data); + return err; + } + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { + grub_btrfs_unmount (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + } + + err = lower_bound (data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + { + grub_btrfs_unmount (data); + grub_free (desc.data); + return err; + } + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM + || key_out.object_id != key_in.object_id) + { + r = next (data, &desc, &elemaddr, &elemsize, &key_out); + if (r <= 0) + goto out; + } + do + { + struct grub_btrfs_dir_item *cdirel; + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_DIR_ITEM + || key_out.object_id != key_in.object_id) + { + r = 0; + break; + } + if (elemsize > allocated) + { + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory element size overflow")); + r = -grub_errno; + break; + } + grub_free (direl); + direl = grub_malloc (sz); + if (!direl) + { + r = -grub_errno; + break; + } + } + + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + r = -err; + break; + } + + if (direl == NULL || + grub_add (grub_le_to_cpu16 (direl->n), + grub_le_to_cpu16 (direl->m), &est_size) || + grub_add (est_size, sizeof (*direl), &est_size) || + grub_sub (est_size, sizeof (direl->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + char c; + struct grub_btrfs_inode inode; + struct grub_dirhook_info info; + + if (cdirel == NULL || + grub_add (grub_le_to_cpu16 (cdirel->n), + grub_le_to_cpu16 (cdirel->m), &est_size) || + grub_add (est_size, sizeof (*cdirel), &est_size) || + grub_sub (est_size, sizeof (cdirel->name), &est_size) || + est_size > allocated) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + r = -grub_errno; + goto out; + } + + err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id, + tree); + grub_memset (&info, 0, sizeof (info)); + if (err) + grub_errno = GRUB_ERR_NONE; + else + { + info.mtime = grub_le_to_cpu64 (inode.mtime.sec); + info.mtimeset = 1; + } + c = cdirel->name[grub_le_to_cpu16 (cdirel->n)]; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0; + info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY); + if (hook (cdirel->name, &info, hook_data)) + goto out; + cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c; + } + r = next (data, &desc, &elemaddr, &elemsize, &key_out); + } + while (r > 0); + +out: + grub_free (direl); + + free_iterator (&desc); + grub_btrfs_unmount (data); + + return -r; +} + +static grub_err_t +grub_btrfs_open (struct grub_file *file, const char *name) +{ + struct grub_btrfs_data *data = grub_btrfs_mount (file->device); + grub_err_t err; + struct grub_btrfs_inode inode; + grub_uint8_t type; + struct grub_btrfs_key key_in; + + if (!data) + return grub_errno; + + err = find_path (data, name, &key_in, &data->tree, &type); + if (err) + { + grub_btrfs_unmount (data); + return err; + } + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR) + { + grub_btrfs_unmount (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); + } + + data->inode = key_in.object_id; + err = grub_btrfs_read_inode (data, &inode, data->inode, data->tree); + if (err) + { + grub_btrfs_unmount (data); + return err; + } + + file->data = data; + file->size = grub_le_to_cpu64 (inode.size); + + return err; +} + +static grub_err_t +grub_btrfs_close (grub_file_t file) +{ + grub_btrfs_unmount (file->data); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_btrfs_data *data = file->data; + + return grub_btrfs_extent_read (data, data->inode, + data->tree, file->offset, buf, len); +} + +static grub_err_t +grub_btrfs_uuid (grub_device_t device, char **uuid) +{ + struct grub_btrfs_data *data; + + *uuid = NULL; + + data = grub_btrfs_mount (device); + if (!data) + return grub_errno; + + *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); + + grub_btrfs_unmount (data); + + return grub_errno; +} + +static grub_err_t +grub_btrfs_label (grub_device_t device, char **label) +{ + struct grub_btrfs_data *data; + + *label = NULL; + + data = grub_btrfs_mount (device); + if (!data) + return grub_errno; + + *label = grub_strndup (data->sblock.label, sizeof (data->sblock.label)); + + grub_btrfs_unmount (data); + + return grub_errno; +} + +#ifdef GRUB_UTIL + +struct embed_region { + unsigned int start; + unsigned int secs; +}; + +/* + * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT + * The first 1 MiB on each device is unused with the exception of primary + * superblock that is on the offset 64 KiB and spans 4 KiB. + */ + +static const struct { + struct embed_region available; + struct embed_region used[6]; +} btrfs_head = { + .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ + .used = { + {0, 1}, /* boot.S. */ + {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ + {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ + {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ + {0, 0} /* Array terminator. */ + } +}; + +static grub_err_t +grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), + unsigned int *nsectors, + unsigned int max_nsectors, + grub_embed_type_t embed_type, + grub_disk_addr_t **sectors) +{ + unsigned int i, j, n = 0; + const struct embed_region *u; + grub_disk_addr_t *map; + + if (embed_type != GRUB_EMBED_PCBIOS) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "BtrFS currently supports only PC-BIOS embedding"); + + map = grub_calloc (btrfs_head.available.secs, sizeof (*map)); + if (map == NULL) + return grub_errno; + + /* + * Populating the map array so that it can be used to index if a disk + * address is available to embed: + * - 0: available, + * - 1: unavailable. + */ + for (u = btrfs_head.used; u->secs; ++u) + { + unsigned int end = u->start + u->secs; + + if (end > btrfs_head.available.secs) + end = btrfs_head.available.secs; + for (i = u->start; i < end; ++i) + map[i] = 1; + } + + /* Adding up n until it matches total size of available embedding area. */ + for (i = 0; i < btrfs_head.available.secs; ++i) + if (map[i] == 0) + n++; + + if (n < *nsectors) + { + grub_free (map); + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("your core.img is unusually large. " + "It won't fit in the embedding area")); + } + + if (n > max_nsectors) + n = max_nsectors; + + /* + * Populating the array so that it can used to index disk block address for + * an image file's offset to be embedded on disk (the unit is in sectors): + * - i: The disk block address relative to btrfs_head.available.start, + * - j: The offset in image file. + */ + for (i = 0, j = 0; i < btrfs_head.available.secs && j < n; ++i) + if (map[i] == 0) + map[j++] = btrfs_head.available.start + i; + + *nsectors = n; + *sectors = map; + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_fs grub_btrfs_fs = { + .name = "btrfs", + .fs_dir = grub_btrfs_dir, + .fs_open = grub_btrfs_open, + .fs_read = grub_btrfs_read, + .fs_close = grub_btrfs_close, + .fs_uuid = grub_btrfs_uuid, + .fs_label = grub_btrfs_label, +#ifdef GRUB_UTIL + .fs_embed = grub_btrfs_embed, + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif +}; + +GRUB_MOD_INIT (btrfs) +{ + grub_btrfs_fs.mod = mod; + grub_fs_register (&grub_btrfs_fs); +} + +GRUB_MOD_FINI (btrfs) +{ + grub_fs_unregister (&grub_btrfs_fs); +} diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c new file mode 100644 index 000000000..b62c8777c --- /dev/null +++ b/grub-core/fs/cbfs.c @@ -0,0 +1,408 @@ +/* cbfs.c - cbfs and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + + +struct grub_archelp_data +{ + grub_disk_t disk; + grub_off_t hofs, next_hofs; + grub_off_t dofs; + grub_off_t size; + grub_off_t cbfs_start; + grub_off_t cbfs_end; + grub_off_t cbfs_align; +}; + +static grub_err_t +grub_cbfs_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_uint32_t *mode) +{ + grub_size_t offset; + for (;; + data->dofs = data->hofs + offset, + data->next_hofs = ALIGN_UP (data->dofs + data->size, data->cbfs_align)) + { + struct cbfs_file hd; + grub_size_t namesize; + + data->hofs = data->next_hofs; + + if (data->hofs >= data->cbfs_end) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) + return grub_errno; + + if (grub_memcmp (hd.magic, CBFS_FILE_MAGIC, sizeof (hd.magic)) != 0) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + data->size = grub_be_to_cpu32 (hd.len); + (void) mtime; + offset = grub_be_to_cpu32 (hd.offset); + + *mode = GRUB_ARCHELP_ATTR_FILE | GRUB_ARCHELP_ATTR_NOTIME; + + namesize = offset; + if (namesize >= sizeof (hd)) + namesize -= sizeof (hd); + if (namesize == 0) + continue; + *name = grub_malloc (namesize + 1); + if (*name == NULL) + return grub_errno; + + if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd), + namesize, *name)) + { + grub_free (*name); + return grub_errno; + } + + if ((*name)[0] == '\0') + { + grub_free (*name); + *name = NULL; + continue; + } + + (*name)[namesize] = 0; + + data->dofs = data->hofs + offset; + data->next_hofs = ALIGN_UP (data->dofs + data->size, data->cbfs_align); + return GRUB_ERR_NONE; + } +} + +static void +grub_cbfs_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = data->cbfs_start; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cbfs_find_file, + .rewind = grub_cbfs_rewind + }; + +static int +validate_head (struct cbfs_header *head) +{ + return (head->magic == grub_cpu_to_be32_compile_time (CBFS_HEADER_MAGIC) + && (head->version + == grub_cpu_to_be32_compile_time (CBFS_HEADER_VERSION1) + || head->version + == grub_cpu_to_be32_compile_time (CBFS_HEADER_VERSION2)) + && (grub_be_to_cpu32 (head->bootblocksize) + < grub_be_to_cpu32 (head->romsize)) + && (grub_be_to_cpu32 (head->offset) + < grub_be_to_cpu32 (head->romsize)) + && (grub_be_to_cpu32 (head->offset) + + grub_be_to_cpu32 (head->bootblocksize) + < grub_be_to_cpu32 (head->romsize)) + && head->align != 0 + && (head->align & (head->align - 1)) == 0 + && head->romsize != 0); +} + +static struct grub_archelp_data * +grub_cbfs_mount (grub_disk_t disk) +{ + struct cbfs_file hd; + struct grub_archelp_data *data = NULL; + grub_uint32_t ptr; + grub_off_t header_off; + struct cbfs_header head; + + if (grub_disk_native_sectors (disk) == GRUB_DISK_SIZE_UNKNOWN) + goto fail; + + if (grub_disk_read (disk, grub_disk_native_sectors (disk) - 1, + GRUB_DISK_SECTOR_SIZE - sizeof (ptr), + sizeof (ptr), &ptr)) + goto fail; + + ptr = grub_cpu_to_le32 (ptr); + header_off = (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS) + + (grub_int32_t) ptr; + + if (grub_disk_read (disk, 0, header_off, + sizeof (head), &head)) + goto fail; + + if (!validate_head (&head)) + goto fail; + + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->cbfs_start = (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS) + - (grub_be_to_cpu32 (head.romsize) - grub_be_to_cpu32 (head.offset)); + data->cbfs_end = (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS) + - grub_be_to_cpu32 (head.bootblocksize); + data->cbfs_align = grub_be_to_cpu32 (head.align); + + if (data->cbfs_start >= (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS)) + goto fail; + if (data->cbfs_end > (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS)) + data->cbfs_end = (grub_disk_native_sectors (disk) << GRUB_DISK_SECTOR_BITS); + + data->next_hofs = data->cbfs_start; + + if (grub_disk_read (disk, 0, data->cbfs_start, sizeof (hd), &hd)) + goto fail; + + if (grub_memcmp (hd.magic, CBFS_FILE_MAGIC, sizeof (CBFS_FILE_MAGIC) - 1)) + goto fail; + + data->disk = disk; + + return data; + +fail: + grub_free (data); + grub_error (GRUB_ERR_BAD_FS, "not a cbfs filesystem"); + return 0; +} + +static grub_err_t +grub_cbfs_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cbfs_mount (device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); + + grub_free (data); + + return err; +} + +static grub_err_t +grub_cbfs_open (grub_file_t file, const char *name_in) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cbfs_mount (file->device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) + { + grub_free (data); + } + else + { + file->data = data; + file->size = data->size; + } + return err; +} + +static grub_ssize_t +grub_cbfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_archelp_data *data; + grub_ssize_t ret; + + data = file->data; + data->disk->read_hook = file->read_hook; + data->disk->read_hook_data = file->read_hook_data; + + ret = (grub_disk_read (data->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; + data->disk->read_hook = 0; + + return ret; +} + +static grub_err_t +grub_cbfs_close (grub_file_t file) +{ + struct grub_archelp_data *data; + + data = file->data; + grub_free (data); + + return grub_errno; +} + +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) \ + && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) + +static char *cbfsdisk_addr; +static grub_off_t cbfsdisk_size = 0; + +static int +grub_cbfsdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + return hook ("cbfsdisk", hook_data); +} + +static grub_err_t +grub_cbfsdisk_open (const char *name, grub_disk_t disk) +{ + if (grub_strcmp (name, "cbfsdisk")) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a cbfsdisk"); + + disk->total_sectors = cbfsdisk_size / GRUB_DISK_SECTOR_SIZE; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; + disk->id = 0; + + return GRUB_ERR_NONE; +} + +static void +grub_cbfsdisk_close (grub_disk_t disk __attribute((unused))) +{ +} + +static grub_err_t +grub_cbfsdisk_read (grub_disk_t disk __attribute((unused)), + grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + grub_memcpy (buf, cbfsdisk_addr + (sector << GRUB_DISK_SECTOR_BITS), + size << GRUB_DISK_SECTOR_BITS); + return 0; +} + +static grub_err_t +grub_cbfsdisk_write (grub_disk_t disk __attribute__ ((unused)), + grub_disk_addr_t sector __attribute__ ((unused)), + grub_size_t size __attribute__ ((unused)), + const char *buf __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "rom flashing isn't implemented yet"); +} + +static struct grub_disk_dev grub_cbfsdisk_dev = + { + .name = "cbfsdisk", + .id = GRUB_DISK_DEVICE_CBFSDISK_ID, + .disk_iterate = grub_cbfsdisk_iterate, + .disk_open = grub_cbfsdisk_open, + .disk_close = grub_cbfsdisk_close, + .disk_read = grub_cbfsdisk_read, + .disk_write = grub_cbfsdisk_write, + .next = 0 + }; + +static void +init_cbfsdisk (void) +{ + grub_uint32_t ptr; + struct cbfs_header *head; + + ptr = *((grub_uint32_t *) grub_absolute_pointer (0xfffffffc)); + head = (struct cbfs_header *) (grub_addr_t) ptr; + grub_dprintf ("cbfs", "head=%p\n", head); + + /* coreboot current supports only ROMs <= 16 MiB. Bigger ROMs will + have problems as RCBA is 18 MiB below end of 32-bit typically, + so either memory map would have to be rearranged or we'd need to support + reading ROMs through controller directly. + */ + if (ptr < 0xff000000 + || 0xffffffff - ptr < (grub_uint32_t) sizeof (*head) + 0xf + || !validate_head (head)) + return; + + cbfsdisk_size = ALIGN_UP (grub_be_to_cpu32 (head->romsize), + GRUB_DISK_SECTOR_SIZE); + cbfsdisk_addr = (void *) (grub_addr_t) (0x100000000ULL - cbfsdisk_size); + + grub_disk_dev_register (&grub_cbfsdisk_dev); +} + +static void +fini_cbfsdisk (void) +{ + if (! cbfsdisk_size) + return; + grub_disk_dev_unregister (&grub_cbfsdisk_dev); +} + +#endif + +static struct grub_fs grub_cbfs_fs = { + .name = "cbfs", + .fs_dir = grub_cbfs_dir, + .fs_open = grub_cbfs_open, + .fs_read = grub_cbfs_read, + .fs_close = grub_cbfs_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; + +GRUB_MOD_INIT (cbfs) +{ +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) + init_cbfsdisk (); +#endif + if (!grub_is_lockdown ()) + { + grub_cbfs_fs.mod = mod; + grub_fs_register (&grub_cbfs_fs); + } +} + +GRUB_MOD_FINI (cbfs) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_cbfs_fs); +#if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) + fini_cbfsdisk (); +#endif +} diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c new file mode 100644 index 000000000..1799f7ff5 --- /dev/null +++ b/grub-core/fs/cpio.c @@ -0,0 +1,62 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +/* cpio support */ +#define ALIGN_CPIO(x) (ALIGN_UP ((x), 2)) +#define MAGIC "\xc7\x71" +struct head +{ + grub_uint16_t magic[1]; + grub_uint16_t dev; + grub_uint16_t ino; + grub_uint16_t mode[1]; + grub_uint16_t uid; + grub_uint16_t gid; + grub_uint16_t nlink; + grub_uint16_t rdev; + grub_uint16_t mtime[2]; + grub_uint16_t namesize[1]; + grub_uint16_t filesize[2]; +} GRUB_PACKED; + +static inline unsigned long long +read_number (const grub_uint16_t *arr, grub_size_t size) +{ + long long ret = 0; + while (size--) + ret = (ret << 16) | grub_le_to_cpu16 (*arr++); + return ret; +} + +#define FSNAME "cpiofs" + +#include "cpio_common.c" + +GRUB_MOD_INIT (cpio) +{ + grub_cpio_fs.mod = mod; + grub_fs_register (&grub_cpio_fs); +} + +GRUB_MOD_FINI (cpio) +{ + grub_fs_unregister (&grub_cpio_fs); +} diff --git a/grub-core/fs/cpio_be.c b/grub-core/fs/cpio_be.c new file mode 100644 index 000000000..7bed1b848 --- /dev/null +++ b/grub-core/fs/cpio_be.c @@ -0,0 +1,62 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#define ALIGN_CPIO(x) (ALIGN_UP ((x), 2)) +#define MAGIC "\x71\xc7" + +struct head +{ + grub_uint16_t magic[1]; + grub_uint16_t dev; + grub_uint16_t ino; + grub_uint16_t mode[1]; + grub_uint16_t uid; + grub_uint16_t gid; + grub_uint16_t nlink; + grub_uint16_t rdev; + grub_uint16_t mtime[2]; + grub_uint16_t namesize[1]; + grub_uint16_t filesize[2]; +} GRUB_PACKED; + +static inline unsigned long long +read_number (const grub_uint16_t *arr, grub_size_t size) +{ + long long ret = 0; + while (size--) + ret = (ret << 16) | grub_be_to_cpu16 (*arr++); + return ret; +} + +#define FSNAME "cpiofs_be" + +#include "cpio_common.c" + +GRUB_MOD_INIT (cpio_be) +{ + grub_cpio_fs.mod = mod; + grub_fs_register (&grub_cpio_fs); +} + +GRUB_MOD_FINI (cpio_be) +{ + grub_fs_unregister (&grub_cpio_fs); +} diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c new file mode 100644 index 000000000..45ac119a8 --- /dev/null +++ b/grub-core/fs/cpio_common.c @@ -0,0 +1,275 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_archelp_data +{ + grub_disk_t disk; + grub_off_t hofs; + grub_off_t next_hofs; + grub_off_t dofs; + grub_off_t size; +}; + +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + +static grub_err_t +grub_cpio_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, grub_uint32_t *mode) +{ + struct head hd; + grub_size_t namesize; + grub_uint32_t modeval; + grub_size_t sz; + + data->hofs = data->next_hofs; + + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) + return grub_errno; + + if (grub_memcmp (hd.magic, MAGIC, sizeof (hd.magic)) != 0 +#ifdef MAGIC2 + && grub_memcmp (hd.magic, MAGIC2, sizeof (hd.magic)) != 0 +#endif + ) + return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive"); + + if (grub_cast (read_number (hd.filesize, ARRAY_SIZE (hd.filesize)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + + if (mtime) + { + if (grub_cast (read_number (hd.mtime, ARRAY_SIZE (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } + + if (grub_cast (read_number (hd.mode, ARRAY_SIZE (hd.mode)), &modeval)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + + if (grub_cast (read_number (hd.namesize, ARRAY_SIZE (hd.namesize)), &namesize)) + return grub_error (GRUB_ERR_BAD_FS, N_("namesize overflow")); + + /* Don't allow negative numbers. */ + if (namesize >= 0x80000000) + { + /* Probably a corruption, don't attempt to recover. */ + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + + *mode = modeval; + + if (grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("file name size overflow")); + + *name = grub_malloc (sz); + if (*name == NULL) + return grub_errno; + + if (grub_disk_read (data->disk, 0, data->hofs + sizeof (hd), + namesize, *name)) + { + grub_free (*name); + return grub_errno; + } + (*name)[namesize] = 0; + + if (data->size == 0 && modeval == 0 && namesize == 11 + && grub_memcmp(*name, "TRAILER!!!", 11) == 0) + { + *mode = GRUB_ARCHELP_ATTR_END; + grub_free (*name); + return GRUB_ERR_NONE; + } + + data->dofs = data->hofs + ALIGN_CPIO (sizeof (hd) + namesize); + data->next_hofs = data->dofs + ALIGN_CPIO (data->size); + return GRUB_ERR_NONE; +} + +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + +static char * +grub_cpio_get_link_target (struct grub_archelp_data *data) +{ + char *ret; + grub_err_t err; + grub_size_t sz; + + if (data->size == 0) + return grub_strdup (""); + + if (grub_add (data->size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("target data size overflow")); + return NULL; + } + ret = grub_malloc (sz); + if (!ret) + return NULL; + + err = grub_disk_read (data->disk, 0, data->dofs, data->size, + ret); + if (err) + { + grub_free (ret); + return NULL; + } + ret[data->size] = '\0'; + return ret; +} + +static void +grub_cpio_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = 0; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cpio_find_file, + .get_link_target = grub_cpio_get_link_target, + .rewind = grub_cpio_rewind + }; + +static struct grub_archelp_data * +grub_cpio_mount (grub_disk_t disk) +{ + struct head hd; + struct grub_archelp_data *data; + + if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd)) + goto fail; + + if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1) +#ifdef MAGIC2 + && grub_memcmp (hd.magic, MAGIC2, sizeof (MAGIC2) - 1) +#endif + ) + goto fail; + + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->disk = disk; + + return data; + +fail: + grub_error (GRUB_ERR_BAD_FS, "not a " FSNAME " filesystem"); + return 0; +} + +static grub_err_t +grub_cpio_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); + + grub_free (data); + + return err; +} + +static grub_err_t +grub_cpio_open (grub_file_t file, const char *name_in) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (file->device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) + { + grub_free (data); + } + else + { + file->data = data; + file->size = data->size; + } + return err; +} + +static grub_ssize_t +grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_archelp_data *data; + grub_ssize_t ret; + + data = file->data; + data->disk->read_hook = file->read_hook; + data->disk->read_hook_data = file->read_hook_data; + + ret = (grub_disk_read (data->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; + data->disk->read_hook = 0; + + return ret; +} + +static grub_err_t +grub_cpio_close (grub_file_t file) +{ + struct grub_archelp_data *data; + + data = file->data; + grub_free (data); + + return grub_errno; +} + +static struct grub_fs grub_cpio_fs = { + .name = FSNAME, + .fs_dir = grub_cpio_dir, + .fs_open = grub_cpio_open, + .fs_read = grub_cpio_read, + .fs_close = grub_cpio_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c new file mode 100644 index 000000000..82a05051d --- /dev/null +++ b/grub-core/fs/erofs.c @@ -0,0 +1,1006 @@ +/* erofs.c - Enhanced Read-Only File System */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define EROFS_SUPER_OFFSET 1024 +#define EROFS_MAGIC 0xE0F5E1E2 +#define EROFS_ISLOTBITS 5 + +#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004 +#define EROFS_ALL_FEATURE_INCOMPAT EROFS_FEATURE_INCOMPAT_CHUNKED_FILE + +struct grub_erofs_super +{ + grub_uint32_t magic; + grub_uint32_t checksum; + grub_uint32_t feature_compat; + grub_uint8_t log2_blksz; + grub_uint8_t sb_extslots; + + grub_uint16_t root_nid; + grub_uint64_t inos; + + grub_uint64_t build_time; + grub_uint32_t build_time_nsec; + grub_uint32_t blocks; + grub_uint32_t meta_blkaddr; + grub_uint32_t xattr_blkaddr; + grub_packed_guid_t uuid; + grub_uint8_t volume_name[16]; + grub_uint32_t feature_incompat; + + union + { + grub_uint16_t available_compr_algs; + grub_uint16_t lz4_max_distance; + } GRUB_PACKED u1; + + grub_uint16_t extra_devices; + grub_uint16_t devt_slotoff; + grub_uint8_t log2_dirblksz; + grub_uint8_t xattr_prefix_count; + grub_uint32_t xattr_prefix_start; + grub_uint64_t packed_nid; + grub_uint8_t reserved2[24]; +} GRUB_PACKED; + +#define EROFS_INODE_LAYOUT_COMPACT 0 +#define EROFS_INODE_LAYOUT_EXTENDED 1 + +#define EROFS_INODE_FLAT_PLAIN 0 +#define EROFS_INODE_COMPRESSED_FULL 1 +#define EROFS_INODE_FLAT_INLINE 2 +#define EROFS_INODE_COMPRESSED_COMPACT 3 +#define EROFS_INODE_CHUNK_BASED 4 + +#define EROFS_I_VERSION_MASKS 0x01 +#define EROFS_I_DATALAYOUT_MASKS 0x07 + +#define EROFS_I_VERSION_BIT 0 +#define EROFS_I_DATALAYOUT_BIT 1 + +struct grub_erofs_inode_chunk_info +{ + grub_uint16_t format; + grub_uint16_t reserved; +} GRUB_PACKED; + +#define EROFS_CHUNK_FORMAT_BLKBITS_MASK 0x001F +#define EROFS_CHUNK_FORMAT_INDEXES 0x0020 + +#define EROFS_BLOCK_MAP_ENTRY_SIZE 4 +#define EROFS_MAP_MAPPED 0x02 + +#define EROFS_NULL_ADDR 1 +#define EROFS_NAME_LEN 255 +#define EROFS_PATH_LEN 4096 +#define EROFS_MIN_LOG2_BLOCK_SIZE 9 +#define EROFS_MAX_LOG2_BLOCK_SIZE 16 + +struct grub_erofs_inode_chunk_index +{ + grub_uint16_t advise; + grub_uint16_t device_id; + grub_uint32_t blkaddr; +}; + +union grub_erofs_inode_i_u +{ + grub_uint32_t compressed_blocks; + grub_uint32_t raw_blkaddr; + + grub_uint32_t rdev; + + struct grub_erofs_inode_chunk_info c; +}; + +struct grub_erofs_inode_compact +{ + grub_uint16_t i_format; + + grub_uint16_t i_xattr_icount; + grub_uint16_t i_mode; + grub_uint16_t i_nlink; + grub_uint32_t i_size; + grub_uint32_t i_reserved; + + union grub_erofs_inode_i_u i_u; + + grub_uint32_t i_ino; + grub_uint16_t i_uid; + grub_uint16_t i_gid; + grub_uint32_t i_reserved2; +} GRUB_PACKED; + +struct grub_erofs_inode_extended +{ + grub_uint16_t i_format; + + grub_uint16_t i_xattr_icount; + grub_uint16_t i_mode; + grub_uint16_t i_reserved; + grub_uint64_t i_size; + + union grub_erofs_inode_i_u i_u; + + grub_uint32_t i_ino; + + grub_uint32_t i_uid; + grub_uint32_t i_gid; + grub_uint64_t i_mtime; + grub_uint32_t i_mtime_nsec; + grub_uint32_t i_nlink; + grub_uint8_t i_reserved2[16]; +} GRUB_PACKED; + +union grub_erofs_inode +{ + struct grub_erofs_inode_compact c; + struct grub_erofs_inode_extended e; +} GRUB_PACKED; + +#define EROFS_FT_UNKNOWN 0 +#define EROFS_FT_REG_FILE 1 +#define EROFS_FT_DIR 2 +#define EROFS_FT_CHRDEV 3 +#define EROFS_FT_BLKDEV 4 +#define EROFS_FT_FIFO 5 +#define EROFS_FT_SOCK 6 +#define EROFS_FT_SYMLINK 7 + +struct grub_erofs_dirent +{ + grub_uint64_t nid; + grub_uint16_t nameoff; + grub_uint8_t file_type; + grub_uint8_t reserved; +} GRUB_PACKED; + +struct grub_erofs_map_blocks +{ + grub_uint64_t m_pa; /* physical address */ + grub_uint64_t m_la; /* logical address */ + grub_uint64_t m_plen; /* physical length */ + grub_uint64_t m_llen; /* logical length */ + grub_uint32_t m_flags; +}; + +struct grub_erofs_xattr_ibody_header +{ + grub_uint32_t h_reserved; + grub_uint8_t h_shared_count; + grub_uint8_t h_reserved2[7]; + grub_uint32_t h_shared_xattrs[0]; +}; + +struct grub_fshelp_node +{ + struct grub_erofs_data *data; + union grub_erofs_inode inode; + + grub_uint64_t ino; + grub_uint8_t inode_type; + grub_uint8_t inode_datalayout; + + /* If the inode has been read into memory? */ + bool inode_loaded; +}; + +struct grub_erofs_data +{ + grub_disk_t disk; + struct grub_erofs_super sb; + + struct grub_fshelp_node inode; +}; + +#define erofs_blocksz(data) (((grub_uint32_t) 1) << data->sb.log2_blksz) + +static grub_size_t +grub_erofs_strnlen (const char *s, grub_size_t n) +{ + const char *p = s; + + if (n == 0) + return 0; + + while (n-- && *p) + p++; + + return p - s; +} + +static grub_uint64_t +erofs_iloc (grub_fshelp_node_t node) +{ + struct grub_erofs_super *sb = &node->data->sb; + + return ((grub_uint64_t) grub_le_to_cpu32 (sb->meta_blkaddr) << sb->log2_blksz) + + (node->ino << EROFS_ISLOTBITS); +} + +static grub_err_t +erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node) +{ + union grub_erofs_inode *di; + grub_err_t err; + grub_uint16_t i_format; + grub_uint64_t addr = erofs_iloc (node); + + di = (union grub_erofs_inode *) &node->inode; + + err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS, + addr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (struct grub_erofs_inode_compact), &di->c); + if (err != GRUB_ERR_NONE) + return err; + + i_format = grub_le_to_cpu16 (di->c.i_format); + node->inode_type = (i_format >> EROFS_I_VERSION_BIT) & EROFS_I_VERSION_MASKS; + node->inode_datalayout = (i_format >> EROFS_I_DATALAYOUT_BIT) & EROFS_I_DATALAYOUT_MASKS; + + switch (node->inode_type) + { + case EROFS_INODE_LAYOUT_EXTENDED: + addr += sizeof (struct grub_erofs_inode_compact); + err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS, + addr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (struct grub_erofs_inode_extended) - sizeof (struct grub_erofs_inode_compact), + (grub_uint8_t *) di + sizeof (struct grub_erofs_inode_compact)); + if (err != GRUB_ERR_NONE) + return err; + break; + case EROFS_INODE_LAYOUT_COMPACT: + break; + default: + return grub_error (GRUB_ERR_BAD_FS, "invalid type %u @ inode %" PRIuGRUB_UINT64_T, + node->inode_type, node->ino); + } + + node->inode_loaded = true; + + return 0; +} + +static grub_uint64_t +erofs_inode_size (grub_fshelp_node_t node) +{ + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? sizeof (struct grub_erofs_inode_compact) + : sizeof (struct grub_erofs_inode_extended); +} + +static grub_uint64_t +erofs_inode_file_size (grub_fshelp_node_t node) +{ + union grub_erofs_inode *di = (union grub_erofs_inode *) &node->inode; + + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? grub_le_to_cpu32 (di->c.i_size) + : grub_le_to_cpu64 (di->e.i_size); +} + +static grub_uint32_t +erofs_inode_xattr_ibody_size (grub_fshelp_node_t node) +{ + grub_uint16_t cnt = grub_le_to_cpu16 (node->inode.e.i_xattr_icount); + + if (cnt == 0) + return 0; + + return sizeof (struct grub_erofs_xattr_ibody_header) + ((cnt - 1) * sizeof (grub_uint32_t)); +} + +static grub_uint64_t +erofs_inode_mtime (grub_fshelp_node_t node) +{ + return node->inode_type == EROFS_INODE_LAYOUT_COMPACT + ? grub_le_to_cpu64 (node->data->sb.build_time) + : grub_le_to_cpu64 (node->inode.e.i_mtime); +} + +static grub_err_t +erofs_map_blocks_flatmode (grub_fshelp_node_t node, + struct grub_erofs_map_blocks *map) +{ + grub_uint64_t nblocks, lastblk, file_size; + bool tailendpacking = (node->inode_datalayout == EROFS_INODE_FLAT_INLINE); + grub_uint64_t blocksz = erofs_blocksz (node->data); + + /* `file_size` is checked by caller and cannot be zero, hence nblocks > 0. */ + file_size = erofs_inode_file_size (node); + if (grub_add (file_size, blocksz - 1, &nblocks)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "nblocks overflow"); + nblocks >>= node->data->sb.log2_blksz; + lastblk = nblocks - tailendpacking; + + map->m_flags = EROFS_MAP_MAPPED; + + /* No overflow as (lastblk <= nblocks) && (nblocks * blocksz <= UINT64_MAX - blocksz + 1). */ + if (map->m_la < (lastblk * blocksz)) + { + if (grub_mul ((grub_uint64_t) grub_le_to_cpu32 (node->inode.e.i_u.raw_blkaddr), blocksz, &map->m_pa) || + grub_add (map->m_pa, map->m_la, &map->m_pa)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_pa overflow"); + if (grub_sub (lastblk * blocksz, map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen underflow"); + } + else if (tailendpacking) + { + if (grub_add (erofs_iloc (node), erofs_inode_size (node), &map->m_pa) || + grub_add (map->m_pa, erofs_inode_xattr_ibody_size (node), &map->m_pa) || + grub_add (map->m_pa, map->m_la & (blocksz - 1), &map->m_pa)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_pa overflow when handling tailpacking"); + if (grub_sub (file_size, map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen overflow when handling tailpacking"); + + /* No overflow as map->m_plen <= UINT64_MAX - blocksz + 1. */ + if (((map->m_pa & (blocksz - 1)) + map->m_plen) > blocksz) + return grub_error (GRUB_ERR_BAD_FS, + "inline data cross block boundary @ inode %" PRIuGRUB_UINT64_T, + node->ino); + } + else + return grub_error (GRUB_ERR_BAD_FS, + "invalid map->m_la=%" PRIuGRUB_UINT64_T + " @ inode %" PRIuGRUB_UINT64_T, + map->m_la, node->ino); + + map->m_llen = map->m_plen; + return GRUB_ERR_NONE; +} + +static grub_err_t +erofs_map_blocks_chunkmode (grub_fshelp_node_t node, + struct grub_erofs_map_blocks *map) +{ + grub_uint16_t chunk_format = grub_le_to_cpu16 (node->inode.e.i_u.c.format); + grub_uint64_t unit, pos, chunknr, blkaddr; + grub_uint8_t chunkbits; + grub_err_t err; + + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) + unit = sizeof (struct grub_erofs_inode_chunk_index); + else + unit = EROFS_BLOCK_MAP_ENTRY_SIZE; + + chunkbits = node->data->sb.log2_blksz + (chunk_format & EROFS_CHUNK_FORMAT_BLKBITS_MASK); + if (chunkbits > 63) + return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode %" PRIuGRUB_UINT64_T, + chunkbits, node->ino); + + chunknr = map->m_la >> chunkbits; + + if (grub_add (erofs_iloc (node), erofs_inode_size (node), &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when adding inode size"); + + if (grub_add (pos, erofs_inode_xattr_ibody_size (node), &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when adding xattr size"); + + if (ALIGN_UP_OVF (pos, unit, &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "position overflow when seeking at the start of chunkmap"); + + /* No overflow for multiplication as chunkbits >= 9 and sizeof(unit) <= 8. */ + if (grub_add (pos, chunknr * unit, &pos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "chunkmap position overflow when finding the specific chunk"); + + map->m_la = chunknr << chunkbits; + + if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "m_plen underflow"); + map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits, + ALIGN_UP (map->m_plen, erofs_blocksz (node->data))); + + if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES) + { + struct grub_erofs_inode_chunk_index idx; + + err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS, + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &idx); + if (err != GRUB_ERR_NONE) + return err; + + blkaddr = grub_le_to_cpu32 (idx.blkaddr); + } + else + { + grub_uint32_t blkaddr_le; + + err = grub_disk_read (node->data->disk, pos >> GRUB_DISK_SECTOR_BITS, + pos & (GRUB_DISK_SECTOR_SIZE - 1), unit, &blkaddr_le); + if (err != GRUB_ERR_NONE) + return err; + + blkaddr = grub_le_to_cpu32 (blkaddr_le); + } + + if (blkaddr == EROFS_NULL_ADDR) + { + map->m_pa = 0; + map->m_flags = 0; + } + else + { + map->m_pa = blkaddr << node->data->sb.log2_blksz; + map->m_flags = EROFS_MAP_MAPPED; + } + + map->m_llen = map->m_plen; + return GRUB_ERR_NONE; +} + +static grub_err_t +erofs_map_blocks (grub_fshelp_node_t node, struct grub_erofs_map_blocks *map) +{ + if (map->m_la >= erofs_inode_file_size (node)) + { + map->m_llen = map->m_plen = 0; + map->m_pa = 0; + map->m_flags = 0; + return GRUB_ERR_NONE; + } + + if (node->inode_datalayout != EROFS_INODE_CHUNK_BASED) + return erofs_map_blocks_flatmode (node, map); + else + return erofs_map_blocks_chunkmode (node, map); +} + +static grub_err_t +erofs_read_raw_data (grub_fshelp_node_t node, grub_uint8_t *buf, grub_uint64_t size, + grub_uint64_t offset, grub_uint64_t *bytes) +{ + struct grub_erofs_map_blocks map = {0}; + grub_uint64_t cur; + grub_err_t err; + + if (bytes) + *bytes = 0; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (node->data, node); + if (err != GRUB_ERR_NONE) + return err; + } + + cur = offset; + while (cur < offset + size) + { + grub_uint8_t *const estart = buf + cur - offset; + grub_uint64_t eend, moff = 0; + + map.m_la = cur; + err = erofs_map_blocks (node, &map); + if (err != GRUB_ERR_NONE) + return err; + + if (grub_add(map.m_la, map.m_llen, &eend)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "eend overflow"); + + eend = grub_min (eend, offset + size); + if (!(map.m_flags & EROFS_MAP_MAPPED)) + { + if (!map.m_llen) + { + /* Reached EOF. */ + grub_memset (estart, 0, offset + size - cur); + cur = offset + size; + continue; + } + + /* It's a hole. */ + grub_memset (estart, 0, eend - cur); + if (bytes) + *bytes += eend - cur; + cur = eend; + continue; + } + + if (cur > map.m_la) + { + moff = cur - map.m_la; + map.m_la = cur; + } + + err = grub_disk_read (node->data->disk, + (map.m_pa + moff) >> GRUB_DISK_SECTOR_BITS, + (map.m_pa + moff) & (GRUB_DISK_SECTOR_SIZE - 1), + eend - map.m_la, estart); + if (err != GRUB_ERR_NONE) + return err; + + if (bytes) + *bytes += eend - map.m_la; + + cur = eend; + } + + return GRUB_ERR_NONE; +} + +static int +erofs_iterate_dir (grub_fshelp_node_t dir, grub_fshelp_iterate_dir_hook_t hook, + void *hook_data) +{ + grub_uint64_t offset = 0, file_size; + grub_uint32_t blocksz = erofs_blocksz (dir->data); + grub_uint8_t *buf; + grub_err_t err; + + if (dir->inode_loaded == false) + { + err = erofs_read_inode (dir->data, dir); + if (err != GRUB_ERR_NONE) + return 0; + } + + file_size = erofs_inode_file_size (dir); + buf = grub_malloc (blocksz); + if (buf == NULL) + return 0; + + while (offset < file_size) + { + grub_uint64_t maxsize = grub_min (blocksz, file_size - offset); + struct grub_erofs_dirent *de = (void *) buf, *end; + grub_uint16_t nameoff; + + err = erofs_read_raw_data (dir, buf, maxsize, offset, NULL); + if (err != GRUB_ERR_NONE) + goto not_found; + + nameoff = grub_le_to_cpu16 (de->nameoff); + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= maxsize) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, + nameoff, dir->ino); + goto not_found; + } + + end = (struct grub_erofs_dirent *) ((grub_uint8_t *) de + nameoff); + while (de < end) + { + struct grub_fshelp_node *fdiro; + enum grub_fshelp_filetype type; + char filename[EROFS_NAME_LEN + 1]; + grub_size_t de_namelen; + const char *de_name; + + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); + if (fdiro == NULL) + goto not_found; + + fdiro->data = dir->data; + fdiro->ino = grub_le_to_cpu64 (de->nid); + fdiro->inode_loaded = false; + + nameoff = grub_le_to_cpu16 (de->nameoff); + if (nameoff < sizeof (struct grub_erofs_dirent) || nameoff >= maxsize) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid nameoff %u @ inode %" PRIuGRUB_UINT64_T, + nameoff, dir->ino); + grub_free (fdiro); + goto not_found; + } + + de_name = (char *) buf + nameoff; + if (de + 1 >= end) + de_namelen = grub_erofs_strnlen (de_name, maxsize - nameoff); + else + { + if (grub_sub (grub_le_to_cpu16 (de[1].nameoff), nameoff, &de_namelen)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "de_namelen underflow"); + grub_free (fdiro); + goto not_found; + } + } + + if (nameoff + de_namelen > maxsize || de_namelen > EROFS_NAME_LEN) + { + grub_error (GRUB_ERR_BAD_FS, + "invalid de_namelen %" PRIuGRUB_SIZE + " @ inode %" PRIuGRUB_UINT64_T, + de_namelen, dir->ino); + grub_free (fdiro); + goto not_found; + } + + grub_memcpy (filename, de_name, de_namelen); + filename[de_namelen] = '\0'; + + switch (grub_le_to_cpu16 (de->file_type)) + { + case EROFS_FT_REG_FILE: + case EROFS_FT_BLKDEV: + case EROFS_FT_CHRDEV: + case EROFS_FT_FIFO: + case EROFS_FT_SOCK: + type = GRUB_FSHELP_REG; + break; + case EROFS_FT_DIR: + type = GRUB_FSHELP_DIR; + break; + case EROFS_FT_SYMLINK: + type = GRUB_FSHELP_SYMLINK; + break; + case EROFS_FT_UNKNOWN: + default: + type = GRUB_FSHELP_UNKNOWN; + } + + if (hook (filename, type, fdiro, hook_data)) + { + grub_free (buf); + return 1; + } + + ++de; + } + + offset += maxsize; + } + + not_found: + grub_free (buf); + return 0; +} + +static char * +erofs_read_symlink (grub_fshelp_node_t node) +{ + char *symlink; + grub_size_t sz, lsz; + grub_err_t err; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (node->data, node); + if (err != GRUB_ERR_NONE) + return NULL; + } + + sz = erofs_inode_file_size (node); + if (sz >= EROFS_PATH_LEN) + { + grub_error (GRUB_ERR_BAD_FS, + "symlink too long @ inode %" PRIuGRUB_UINT64_T, node->ino); + return NULL; + } + + if (grub_add (sz, 1, &lsz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return NULL; + } + symlink = grub_malloc (lsz); + if (symlink == NULL) + return NULL; + + err = erofs_read_raw_data (node, (grub_uint8_t *) symlink, sz, 0, NULL); + if (err != GRUB_ERR_NONE) + { + grub_free (symlink); + return NULL; + } + + symlink[sz] = '\0'; + return symlink; +} + +static struct grub_erofs_data * +erofs_mount (grub_disk_t disk, bool read_root) +{ + struct grub_erofs_super sb; + grub_err_t err; + struct grub_erofs_data *data; + grub_uint32_t feature; + + err = grub_disk_read (disk, EROFS_SUPER_OFFSET >> GRUB_DISK_SECTOR_BITS, 0, + sizeof (sb), &sb); + if (err != GRUB_ERR_NONE) + return NULL; + if (sb.magic != grub_cpu_to_le32_compile_time (EROFS_MAGIC) || + grub_le_to_cpu32 (sb.log2_blksz) < EROFS_MIN_LOG2_BLOCK_SIZE || + grub_le_to_cpu32 (sb.log2_blksz) > EROFS_MAX_LOG2_BLOCK_SIZE) + { + grub_error (GRUB_ERR_BAD_FS, "not a valid erofs filesystem"); + return NULL; + } + + feature = grub_le_to_cpu32 (sb.feature_incompat); + if (feature & ~EROFS_ALL_FEATURE_INCOMPAT) + { + grub_error (GRUB_ERR_BAD_FS, "unsupported features: 0x%x", + feature & ~EROFS_ALL_FEATURE_INCOMPAT); + return NULL; + } + + data = grub_malloc (sizeof (*data)); + if (data == NULL) + return NULL; + + data->disk = disk; + data->sb = sb; + + if (read_root) + { + data->inode.data = data; + data->inode.ino = grub_le_to_cpu16 (sb.root_nid); + err = erofs_read_inode (data, &data->inode); + if (err != GRUB_ERR_NONE) + { + grub_free (data); + return NULL; + } + } + + return data; +} + +/* Context for grub_erofs_dir. */ +struct grub_erofs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_erofs_data *data; +}; + +/* Helper for grub_erofs_dir. */ +static int +erofs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_erofs_dir_ctx *ctx = data; + struct grub_dirhook_info info = {0}; + grub_err_t err; + + if (node->inode_loaded == false) + { + err = erofs_read_inode (ctx->data, node); + if (err != GRUB_ERR_NONE) + return 0; + } + + if (node->inode_loaded == true) + { + info.mtimeset = 1; + info.mtime = erofs_inode_mtime (node); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_erofs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, + void *hook_data) +{ + grub_fshelp_node_t fdiro = NULL; + grub_err_t err; + struct grub_erofs_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + + ctx.data = erofs_mount (device->disk, true); + if (ctx.data == NULL) + goto fail; + + err = grub_fshelp_find_file (path, &ctx.data->inode, &fdiro, erofs_iterate_dir, + erofs_read_symlink, GRUB_FSHELP_DIR); + if (err != GRUB_ERR_NONE) + goto fail; + + erofs_iterate_dir (fdiro, erofs_dir_iter, &ctx); + + fail: + if (fdiro != &ctx.data->inode) + grub_free (fdiro); + grub_free (ctx.data); + + return grub_errno; +} + +static grub_err_t +grub_erofs_open (grub_file_t file, const char *name) +{ + struct grub_erofs_data *data; + struct grub_fshelp_node *fdiro = NULL; + grub_err_t err; + + data = erofs_mount (file->device->disk, true); + if (data == NULL) + { + err = grub_errno; + goto fail; + } + + err = grub_fshelp_find_file (name, &data->inode, &fdiro, erofs_iterate_dir, + erofs_read_symlink, GRUB_FSHELP_REG); + if (err != GRUB_ERR_NONE) + goto fail; + + if (fdiro->inode_loaded == false) + { + err = erofs_read_inode (data, fdiro); + if (err != GRUB_ERR_NONE) + goto fail; + } + + grub_memcpy (&data->inode, fdiro, sizeof (*fdiro)); + grub_free (fdiro); + + file->data = data; + file->size = erofs_inode_file_size (&data->inode); + + return GRUB_ERR_NONE; + + fail: + if (fdiro != &data->inode) + grub_free (fdiro); + grub_free (data); + + return err; +} + +static grub_ssize_t +grub_erofs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_erofs_data *data = file->data; + struct grub_fshelp_node *inode = &data->inode; + grub_off_t off = file->offset; + grub_uint64_t ret = 0, file_size; + grub_err_t err; + + if (inode->inode_loaded == false) + { + err = erofs_read_inode (data, inode); + if (err != GRUB_ERR_NONE) + return -1; + } + + file_size = erofs_inode_file_size (inode); + + if (off > file_size) + { + grub_error (GRUB_ERR_IO, "read past EOF @ inode %" PRIuGRUB_UINT64_T, inode->ino); + return -1; + } + if (off == file_size) + return 0; + + if (off + len > file_size) + len = file_size - off; + + err = erofs_read_raw_data (inode, (grub_uint8_t *) buf, len, off, &ret); + if (err != GRUB_ERR_NONE) + return -1; + + return ret; +} + +static grub_err_t +grub_erofs_close (grub_file_t file) +{ + grub_free (file->data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_uuid (grub_device_t device, char **uuid) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *uuid = NULL; + return grub_errno; + } + + *uuid = grub_xasprintf ("%pG", &data->sb.uuid); + + grub_free (data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_label (grub_device_t device, char **label) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *label = NULL; + return grub_errno; + } + + *label = grub_strndup ((char *) data->sb.volume_name, sizeof (data->sb.volume_name)); + grub_free (data); + + if (*label == NULL) + return grub_errno; + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_erofs_mtime (grub_device_t device, grub_int64_t *tm) +{ + struct grub_erofs_data *data; + + data = erofs_mount (device->disk, false); + if (data == NULL) + { + *tm = 0; + return grub_errno; + } + + *tm = grub_le_to_cpu64 (data->sb.build_time); + + grub_free (data); + + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_erofs_fs = { + .name = "erofs", + .fs_dir = grub_erofs_dir, + .fs_open = grub_erofs_open, + .fs_read = grub_erofs_read, + .fs_close = grub_erofs_close, + .fs_uuid = grub_erofs_uuid, + .fs_label = grub_erofs_label, + .fs_mtime = grub_erofs_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif + .next = 0, +}; + +GRUB_MOD_INIT (erofs) +{ + grub_erofs_fs.mod = mod; + grub_fs_register (&grub_erofs_fs); +} + +GRUB_MOD_FINI (erofs) +{ + grub_fs_unregister (&grub_erofs_fs); +} diff --git a/grub-core/fs/exfat.c b/grub-core/fs/exfat.c new file mode 100644 index 000000000..fe149ddd3 --- /dev/null +++ b/grub-core/fs/exfat.c @@ -0,0 +1,2 @@ +#define MODE_EXFAT 1 +#include "fat.c" diff --git a/fs/ext2.c b/grub-core/fs/ext2.c similarity index 59% rename from fs/ext2.c rename to grub-core/fs/ext2.c index f2fec828a..2f262dc34 100644 --- a/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -21,10 +21,6 @@ #define EXT2_MAGIC 0xEF53 /* Amount of indirect blocks in an inode. */ #define INDIRECT_BLOCKS 12 -/* Maximum length of a pathname. */ -#define EXT2_PATH_MAX 4096 -/* Maximum nesting of symlinks, used to prevent a loop. */ -#define EXT2_MAX_SYMLINKCNT 8 /* The good old revision and the default inode size. */ #define EXT2_GOOD_OLD_REVISION 0 @@ -50,6 +46,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); /* Log2 size of ext2 block in 512 blocks. */ #define LOG2_EXT2_BLOCK_SIZE(data) \ @@ -60,14 +59,15 @@ (grub_le_to_cpu32 (data->sblock.log2_block_size) + 10) /* The size of an ext2 block in bytes. */ -#define EXT2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data)) +#define EXT2_BLOCK_SIZE(data) (1U << LOG2_BLOCK_SIZE (data)) /* The revision level. */ #define EXT2_REVISION(data) grub_le_to_cpu32 (data->sblock.revision_level) /* The inode size. */ #define EXT2_INODE_SIZE(data) \ - (EXT2_REVISION (data) == EXT2_GOOD_OLD_REVISION \ + (data->sblock.revision_level \ + == grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION) \ ? EXT2_GOOD_OLD_INODE_SIZE \ : grub_le_to_cpu16 (data->sblock.inode_size)) @@ -101,22 +101,46 @@ #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* Extents used */ #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x2000 +#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3 level htree */ +#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 /* The set of back-incompatible features this driver DOES support. Add (OR) * flags here as the related features are implemented into the driver. */ #define EXT2_DRIVER_SUPPORTED_INCOMPAT ( EXT2_FEATURE_INCOMPAT_FILETYPE \ | EXT4_FEATURE_INCOMPAT_EXTENTS \ - | EXT4_FEATURE_INCOMPAT_FLEX_BG ) + | EXT4_FEATURE_INCOMPAT_FLEX_BG \ + | EXT2_FEATURE_INCOMPAT_META_BG \ + | EXT4_FEATURE_INCOMPAT_64BIT \ + | EXT4_FEATURE_INCOMPAT_ENCRYPT) /* List of rationales for the ignored "incompatible" features: * needs_recovery: Not really back-incompatible - was added as such to forbid * ext2 drivers from mounting an ext3 volume with a dirty * journal because they will ignore the journal, but the next * ext3 driver to mount the volume will find the journal and * replay it, potentially corrupting the metadata written by - * the ext2 drivers. Safe to ignore for this RO driver. */ -#define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER ) - + * the ext2 drivers. Safe to ignore for this RO driver. + * mmp: Not really back-incompatible - was added as such to + * avoid multiple read-write mounts. Safe to ignore for this + * RO driver. + * checksum seed: Not really back-incompatible - was added to allow tools + * such as tune2fs to change the UUID on a mounted metadata + * checksummed filesystem. Safe to ignore for now since the + * driver doesn't support checksum verification. However, it + * has to be removed from this list if the support is added later. + * large_dir: Not back-incompatible given that the GRUB ext2 driver does + * not implement EXT2_FEATURE_COMPAT_DIR_INDEX. If the GRUB + * eventually supports the htree feature (aka dir_index) + * it should support 3 level htrees and then move + * EXT4_FEATURE_INCOMPAT_LARGEDIR to + * EXT2_DRIVER_SUPPORTED_INCOMPAT. + */ +#define EXT2_DRIVER_IGNORED_INCOMPAT ( EXT3_FEATURE_INCOMPAT_RECOVER \ + | EXT4_FEATURE_INCOMPAT_MMP \ + | EXT4_FEATURE_INCOMPAT_CSUM_SEED \ + | EXT4_FEATURE_INCOMPAT_LARGEDIR) #define EXT3_JOURNAL_MAGIC_NUMBER 0xc03b3998U @@ -131,6 +155,7 @@ #define EXT3_JOURNAL_FLAG_DELETED 4 #define EXT3_JOURNAL_FLAG_LAST_TAG 8 +#define EXT4_ENCRYPT_FLAG 0x800 #define EXT4_EXTENTS_FLAG 0x80000 /* The ext2 superblock. */ @@ -181,7 +206,7 @@ struct grub_ext2_sblock grub_uint32_t hash_seed[4]; grub_uint8_t def_hash_version; grub_uint8_t jnl_backup_type; - grub_uint16_t reserved_word_pad; + grub_uint16_t group_desc_size; grub_uint32_t default_mount_opts; grub_uint32_t first_meta_bg; grub_uint32_t mkfs_time; @@ -199,6 +224,14 @@ struct grub_ext2_block_group grub_uint16_t used_dirs; grub_uint16_t pad; grub_uint32_t reserved[3]; + grub_uint32_t block_id_hi; + grub_uint32_t inode_id_hi; + grub_uint32_t inode_table_id_hi; + grub_uint16_t free_blocks_hi; + grub_uint16_t free_inodes_hi; + grub_uint16_t used_dirs_hi; + grub_uint16_t pad2; + grub_uint32_t reserved2[3]; }; /* The ext2 inode. */ @@ -229,7 +262,7 @@ struct grub_ext2_inode }; grub_uint32_t version; grub_uint32_t acl; - grub_uint32_t dir_acl; + grub_uint32_t size_high; grub_uint32_t fragment_addr; grub_uint32_t osd2[3]; }; @@ -239,6 +272,7 @@ struct ext2_dirent { grub_uint32_t inode; grub_uint16_t direntlen; +#define MAX_NAMELEN 255 grub_uint8_t namelen; grub_uint8_t filetype; }; @@ -312,6 +346,7 @@ struct grub_fshelp_node struct grub_ext2_data { struct grub_ext2_sblock sblock; + int log_group_desc_size; grub_disk_t disk; struct grub_ext2_inode *inode; struct grub_fshelp_node diropen; @@ -321,25 +356,80 @@ static grub_dl_t my_mod; +/* Check is a = b^x for some x. */ +static inline int +is_power_of (grub_uint64_t a, grub_uint32_t b) +{ + grub_uint64_t c; + /* Prevent overflow assuming b < 8. */ + if (a >= (1LL << 60)) + return 0; + for (c = 1; c <= a; c *= b); + return (c == a); +} + + +static inline int +group_has_super_block (struct grub_ext2_data *data, grub_uint64_t group) +{ + if (!(data->sblock.feature_ro_compat + & grub_cpu_to_le32_compile_time(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))) + return 1; + /* Algorithm looked up in Linux source. */ + if (group <= 1) + return 1; + /* Even number is never a power of odd number. */ + if (!(group & 1)) + return 0; + return (is_power_of(group, 7) || is_power_of(group, 5) || + is_power_of(group, 3)); +} + /* Read into BLKGRP the blockgroup descriptor of blockgroup GROUP of the mounted filesystem DATA. */ inline static grub_err_t -grub_ext2_blockgroup (struct grub_ext2_data *data, int group, +grub_ext2_blockgroup (struct grub_ext2_data *data, grub_uint64_t group, struct grub_ext2_block_group *blkgrp) { + grub_uint64_t full_offset = (group << data->log_group_desc_size); + grub_uint64_t block, offset; + block = (full_offset >> LOG2_BLOCK_SIZE (data)); + offset = (full_offset & ((1 << LOG2_BLOCK_SIZE (data)) - 1)); + if ((data->sblock.feature_incompat + & grub_cpu_to_le32_compile_time (EXT2_FEATURE_INCOMPAT_META_BG)) + && block >= grub_le_to_cpu32(data->sblock.first_meta_bg)) + { + grub_uint64_t first_block_group; + /* Find the first block group for which a descriptor + is stored in given block. */ + first_block_group = (block << (LOG2_BLOCK_SIZE (data) + - data->log_group_desc_size)); + + block = (first_block_group + * grub_le_to_cpu32(data->sblock.blocks_per_group)); + + if (group_has_super_block (data, first_block_group)) + block++; + } + else + /* Superblock. */ + block++; return grub_disk_read (data->disk, - ((grub_le_to_cpu32 (data->sblock.first_data_block) + 1) - << LOG2_EXT2_BLOCK_SIZE (data)), - group * sizeof (struct grub_ext2_block_group), + ((grub_le_to_cpu32 (data->sblock.first_data_block) + + block) + << LOG2_EXT2_BLOCK_SIZE (data)), offset, sizeof (struct grub_ext2_block_group), blkgrp); } -static struct grub_ext4_extent_header * -grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf, +static grub_err_t +grub_ext4_find_leaf (struct grub_ext2_data *data, struct grub_ext4_extent_header *ext_block, - grub_uint32_t fileblock) + grub_uint32_t fileblock, + struct grub_ext4_extent_header **leaf) { struct grub_ext4_extent_idx *index; + void *buf = NULL; + *leaf = NULL; while (1) { @@ -348,11 +438,14 @@ grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf, index = (struct grub_ext4_extent_idx *) (ext_block + 1); - if (grub_le_to_cpu16(ext_block->magic) != EXT4_EXT_MAGIC) - return 0; + if (ext_block->magic != grub_cpu_to_le16_compile_time (EXT4_EXT_MAGIC)) + goto fail; if (ext_block->depth == 0) - return ext_block; + { + *leaf = ext_block; + return GRUB_ERR_NONE; + } for (i = 0; i < grub_le_to_cpu16 (ext_block->entries); i++) { @@ -361,17 +454,27 @@ grub_ext4_find_leaf (struct grub_ext2_data *data, char *buf, } if (--i < 0) - return 0; + { + grub_free (buf); + return GRUB_ERR_NONE; + } block = grub_le_to_cpu16 (index[i].leaf_hi); - block = (block << 32) + grub_le_to_cpu32 (index[i].leaf); + block = (block << 32) | grub_le_to_cpu32 (index[i].leaf); + if (!buf) + buf = grub_malloc (EXT2_BLOCK_SIZE(data)); + if (!buf) + goto fail; if (grub_disk_read (data->disk, block << LOG2_EXT2_BLOCK_SIZE (data), 0, EXT2_BLOCK_SIZE(data), buf)) - return 0; + goto fail; - ext_block = (struct grub_ext4_extent_header *) buf; + ext_block = buf; } + fail: + grub_free (buf); + return GRUB_ERR_BAD_FS; } static grub_disk_addr_t @@ -379,28 +482,54 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_ext2_data *data = node->data; struct grub_ext2_inode *inode = &node->inode; - int blknr = -1; unsigned int blksz = EXT2_BLOCK_SIZE (data); + grub_disk_addr_t blksz_quarter = blksz / 4; int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data); + int log_perblock = log2_blksz + 9 - 2; + grub_uint32_t indir; + int shift; - if (grub_le_to_cpu32(inode->flags) & EXT4_EXTENTS_FLAG) + if (inode->flags & grub_cpu_to_le32_compile_time (EXT4_EXTENTS_FLAG)) { - char buf[EXT2_BLOCK_SIZE(data)]; struct grub_ext4_extent_header *leaf; struct grub_ext4_extent *ext; int i; + grub_disk_addr_t ret; + grub_uint16_t nent; + /* Maximum number of extent entries in the inode's inline extent area. */ + const grub_uint16_t max_inline_ext = sizeof (inode->blocks) / sizeof (*ext) - 1; /* Minus 1 extent header. */ + /* Maximum number of extent entries in the external extent block. */ + const grub_uint16_t max_external_ext = EXT2_BLOCK_SIZE (data) / sizeof (*ext) - 1; /* Minus 1 extent header. */ - leaf = grub_ext4_find_leaf (data, buf, - (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, - fileblock); - if (! leaf) + if (grub_ext4_find_leaf (data, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, + fileblock, &leaf) != GRUB_ERR_NONE) { grub_error (GRUB_ERR_BAD_FS, "invalid extent"); return -1; } + if (leaf == NULL) + /* Leaf for the given block is absent (i.e. sparse) */ + return 0; + ext = (struct grub_ext4_extent *) (leaf + 1); - for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++) + + nent = grub_le_to_cpu16 (leaf->entries); + + /* + * Determine the effective number of extent entries (nent) to process. + * If the extent header (leaf) is stored inline in the inode’s block + * area, i.e. at inode->blocks.dir_blocks, then only max_inline_ext + * entries can fit. Otherwise, if the header was read from an external + * extent block use the larger limit, max_external_ext, based on the + * full block size. + */ + if (leaf == (struct grub_ext4_extent_header *) inode->blocks.dir_blocks) + nent = grub_min (nent, max_inline_ext); + else + nent = grub_min (nent, max_external_ext); + + for (i = 0; i < nent; i++) { if (fileblock < grub_le_to_cpu32 (ext[i].block)) break; @@ -410,7 +539,7 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { fileblock -= grub_le_to_cpu32 (ext[i].block); if (fileblock >= grub_le_to_cpu16 (ext[i].len)) - return 0; + ret = 0; else { grub_disk_addr_t start; @@ -418,79 +547,84 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) start = grub_le_to_cpu16 (ext[i].start_hi); start = (start << 32) + grub_le_to_cpu32 (ext[i].start); - return fileblock + start; + ret = fileblock + start; } } else { grub_error (GRUB_ERR_BAD_FS, "something wrong with extent"); - return -1; + ret = -1; } + + if (leaf != (struct grub_ext4_extent_header *) inode->blocks.dir_blocks) + grub_free (leaf); + + return ret; } + /* Direct blocks. */ if (fileblock < INDIRECT_BLOCKS) - blknr = grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]); + return grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]); + fileblock -= INDIRECT_BLOCKS; /* Indirect. */ - else if (fileblock < INDIRECT_BLOCKS + blksz / 4) + if (fileblock < blksz_quarter) { - grub_uint32_t indir[blksz / 4]; - - if (grub_disk_read (data->disk, - ((grub_disk_addr_t) - grub_le_to_cpu32 (inode->blocks.indir_block)) - << log2_blksz, - 0, blksz, indir)) - return grub_errno; - - blknr = grub_le_to_cpu32 (indir[fileblock - INDIRECT_BLOCKS]); + indir = inode->blocks.indir_block; + shift = 0; + goto indirect; } + fileblock -= blksz_quarter; /* Double indirect. */ - else if (fileblock < INDIRECT_BLOCKS + blksz / 4 * (blksz / 4 + 1)) + if (fileblock < blksz_quarter * blksz_quarter) { - unsigned int perblock = blksz / 4; - unsigned int rblock = fileblock - (INDIRECT_BLOCKS - + blksz / 4); - grub_uint32_t indir[blksz / 4]; - - if (grub_disk_read (data->disk, - ((grub_disk_addr_t) - grub_le_to_cpu32 (inode->blocks.double_indir_block)) - << log2_blksz, - 0, blksz, indir)) - return grub_errno; - - if (grub_disk_read (data->disk, - ((grub_disk_addr_t) - grub_le_to_cpu32 (indir[rblock / perblock])) - << log2_blksz, - 0, blksz, indir)) - return grub_errno; - - - blknr = grub_le_to_cpu32 (indir[rblock % perblock]); + indir = inode->blocks.double_indir_block; + shift = 1; + goto indirect; } - /* triple indirect. */ - else + fileblock -= blksz_quarter * blksz_quarter; + /* Triple indirect. */ + if (fileblock < blksz_quarter * blksz_quarter * (blksz_quarter + 1)) { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "ext2fs doesn't support triple indirect blocks"); + indir = inode->blocks.triple_indir_block; + shift = 2; + goto indirect; } + grub_error (GRUB_ERR_BAD_FS, + "ext2fs doesn't support quadruple indirect blocks"); + return -1; - return blknr; +indirect: + do { + /* If the indirect block is zero, all child blocks are absent + (i.e. filled with zeros.) */ + if (indir == 0) + return 0; + if (grub_disk_read (data->disk, + ((grub_disk_addr_t) grub_le_to_cpu32 (indir)) + << log2_blksz, + ((fileblock >> (log_perblock * shift)) + & ((1 << log_perblock) - 1)) + * sizeof (indir), + sizeof (indir), &indir)) + return -1; + } while (shift--); + + return grub_le_to_cpu32 (indir); } /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_ext2_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) { - return grub_fshelp_read_file (node->data->disk, node, read_hook, + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, pos, len, buf, grub_ext2_read_block, - node->inode.size, - LOG2_EXT2_BLOCK_SIZE (node->data)); + grub_cpu_to_le32 (node->inode.size) + | (((grub_off_t) grub_cpu_to_le32 (node->inode.size_high)) << 32), + LOG2_EXT2_BLOCK_SIZE (node->data), 0); } @@ -505,6 +639,7 @@ grub_ext2_read_inode (struct grub_ext2_data *data, int inodes_per_block; unsigned int blkno; unsigned int blkoff; + grub_disk_addr_t base; /* It is easier to calculate if the first inode is 0. */ ino--; @@ -521,10 +656,14 @@ grub_ext2_read_inode (struct grub_ext2_data *data, blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group)) % inodes_per_block; + base = grub_le_to_cpu32 (blkgrp.inode_table_id); + if (data->log_group_desc_size >= 6) + base |= (((grub_disk_addr_t) grub_le_to_cpu32 (blkgrp.inode_table_id_hi)) + << 32); + /* Read the inode. */ if (grub_disk_read (data->disk, - ((grub_le_to_cpu32 (blkgrp.inode_table_id) + blkno) - << LOG2_EXT2_BLOCK_SIZE (data)), + ((base + blkno) << LOG2_EXT2_BLOCK_SIZE (data)), EXT2_INODE_SIZE (data) * blkoff, sizeof (struct grub_ext2_inode), inode)) return grub_errno; @@ -548,20 +687,42 @@ grub_ext2_mount (grub_disk_t disk) goto fail; /* Make sure this is an ext2 filesystem. */ - if (grub_le_to_cpu16 (data->sblock.magic) != EXT2_MAGIC) + if (data->sblock.magic != grub_cpu_to_le16_compile_time (EXT2_MAGIC) + || grub_le_to_cpu32 (data->sblock.log2_block_size) >= 16 + || data->sblock.inodes_per_group == 0 + /* 20 already means 1GiB blocks. We don't want to deal with blocks overflowing int32. */ + || grub_le_to_cpu32 (data->sblock.log2_block_size) > 20 + || EXT2_INODE_SIZE (data) == 0 + || EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data) == 0) { grub_error (GRUB_ERR_BAD_FS, "not an ext2 filesystem"); goto fail; } /* Check the FS doesn't have feature bits enabled that we don't support. */ - if (grub_le_to_cpu32 (data->sblock.feature_incompat) - & ~(EXT2_DRIVER_SUPPORTED_INCOMPAT | EXT2_DRIVER_IGNORED_INCOMPAT)) + if (data->sblock.revision_level != grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION) + && (data->sblock.feature_incompat + & grub_cpu_to_le32_compile_time (~(EXT2_DRIVER_SUPPORTED_INCOMPAT + | EXT2_DRIVER_IGNORED_INCOMPAT)))) { grub_error (GRUB_ERR_BAD_FS, "filesystem has unsupported incompatible features"); goto fail; } + if (data->sblock.revision_level != grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION) + && (data->sblock.feature_incompat + & grub_cpu_to_le32_compile_time (EXT4_FEATURE_INCOMPAT_64BIT)) + && data->sblock.group_desc_size != 0 + && ((data->sblock.group_desc_size & (data->sblock.group_desc_size - 1)) + == 0) + && (data->sblock.group_desc_size & grub_cpu_to_le16_compile_time (0x1fe0))) + { + grub_uint16_t b = grub_le_to_cpu16 (data->sblock.group_desc_size); + for (data->log_group_desc_size = 0; b != (1 << data->log_group_desc_size); + data->log_group_desc_size++); + } + else + data->log_group_desc_size = 5; data->disk = disk; @@ -590,28 +751,42 @@ grub_ext2_read_symlink (grub_fshelp_node_t node) { char *symlink; struct grub_fshelp_node *diro = node; + grub_size_t sz; if (! diro->inode_read) { grub_ext2_read_inode (diro->data, diro->ino, &diro->inode); if (grub_errno) return 0; + + if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "symlink is encrypted"); + return 0; + } } - symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1); + if (grub_add (grub_le_to_cpu32 (diro->inode.size), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + + symlink = grub_malloc (sz); if (! symlink) return 0; - /* If the filesize of the symlink is bigger than - 60 the symlink is stored in a separate block, - otherwise it is stored in the inode. */ - if (grub_le_to_cpu32 (diro->inode.size) <= 60) - grub_strncpy (symlink, - diro->inode.symlink, - grub_le_to_cpu32 (diro->inode.size)); + /* + * If the filesize of the symlink is equal to or bigger than 60 the symlink + * is stored in a separate block, otherwise it is stored in the inode. + */ + if (grub_le_to_cpu32 (diro->inode.size) < sizeof (diro->inode.symlink)) + grub_memcpy (symlink, + diro->inode.symlink, + grub_le_to_cpu32 (diro->inode.size)); else { - grub_ext2_read_file (diro, 0, 0, + grub_ext2_read_file (diro, 0, 0, 0, grub_le_to_cpu32 (diro->inode.size), symlink); if (grub_errno) @@ -627,10 +802,7 @@ grub_ext2_read_symlink (grub_fshelp_node_t node) static int grub_ext2_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) { unsigned int fpos = 0; struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; @@ -642,12 +814,18 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir, return 0; } + if (diro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "directory is encrypted"); + return 0; + } + /* Search the file. */ while (fpos < grub_le_to_cpu32 (diro->inode.size)) { struct ext2_dirent dirent; - grub_ext2_read_file (diro, 0, fpos, sizeof (struct ext2_dirent), + grub_ext2_read_file (diro, 0, 0, fpos, sizeof (struct ext2_dirent), (char *) &dirent); if (grub_errno) return 0; @@ -655,13 +833,13 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir, if (dirent.direntlen == 0) return 0; - if (dirent.namelen != 0) + if (dirent.inode != 0 && dirent.namelen != 0) { - char filename[dirent.namelen + 1]; + char filename[MAX_NAMELEN + 1]; struct grub_fshelp_node *fdiro; enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; - grub_ext2_read_file (diro, 0, fpos + sizeof (struct ext2_dirent), + grub_ext2_read_file (diro, 0, 0, fpos + sizeof (struct ext2_dirent), dirent.namelen, filename); if (grub_errno) return 0; @@ -712,7 +890,7 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir, type = GRUB_FSHELP_REG; } - if (hook (filename, type, fdiro)) + if (hook (filename, type, fdiro, hook_data)) return 1; } @@ -728,29 +906,41 @@ grub_ext2_open (struct grub_file *file, const char *name) { struct grub_ext2_data *data; struct grub_fshelp_node *fdiro = 0; + grub_err_t err; grub_dl_ref (my_mod); data = grub_ext2_mount (file->device->disk); if (! data) - goto fail; + { + err = grub_errno; + goto fail; + } - grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_ext2_iterate_dir, - grub_ext2_read_symlink, GRUB_FSHELP_REG); - if (grub_errno) + err = grub_fshelp_find_file (name, &data->diropen, &fdiro, + grub_ext2_iterate_dir, + grub_ext2_read_symlink, GRUB_FSHELP_REG); + if (err) goto fail; if (! fdiro->inode_read) { - grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode); - if (grub_errno) + err = grub_ext2_read_inode (data, fdiro->ino, &fdiro->inode); + if (err) goto fail; } + if (fdiro->inode.flags & grub_cpu_to_le32_compile_time (EXT4_ENCRYPT_FLAG)) + { + err = grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "file is encrypted"); + goto fail; + } + grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_ext2_inode)); grub_free (fdiro); file->size = grub_le_to_cpu32 (data->inode->size); + file->size |= ((grub_off_t) grub_le_to_cpu32 (data->inode->size_high)) << 32; file->data = data; file->offset = 0; @@ -763,7 +953,7 @@ grub_ext2_open (struct grub_file *file, const char *name) grub_dl_unref (my_mod); - return grub_errno; + return err; } static grub_err_t @@ -782,64 +972,75 @@ grub_ext2_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_ext2_data *data = (struct grub_ext2_data *) file->data; - return grub_ext2_read_file (&data->diropen, file->read_hook, + return grub_ext2_read_file (&data->diropen, + file->read_hook, file->read_hook_data, file->offset, len, buf); } -static grub_err_t -grub_ext2_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) +/* Context for grub_ext2_dir. */ +struct grub_ext2_dir_ctx { - struct grub_ext2_data *data = 0; - struct grub_fshelp_node *fdiro = 0; + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_ext2_data *data; +}; - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); +/* Helper for grub_ext2_dir. */ +static int +grub_ext2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_ext2_dir_ctx *ctx = data; + struct grub_dirhook_info info; - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) + grub_memset (&info, 0, sizeof (info)); + if (! node->inode_read) { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - if (! node->inode_read) - { - grub_ext2_read_inode (data, node->ino, &node->inode); - if (!grub_errno) - node->inode_read = 1; - grub_errno = GRUB_ERR_NONE; - } - if (node->inode_read) - { - info.mtimeset = 1; - info.mtime = grub_le_to_cpu32 (node->inode.mtime); - } - - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); + grub_ext2_read_inode (ctx->data, node->ino, &node->inode); + if (!grub_errno) + node->inode_read = 1; + grub_errno = GRUB_ERR_NONE; } + if (node->inode_read) + { + info.mtimeset = 1; + info.mtime = grub_le_to_cpu32 (node->inode.mtime); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_ext2_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, + void *hook_data) +{ + struct grub_ext2_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + struct grub_fshelp_node *fdiro = 0; grub_dl_ref (my_mod); - data = grub_ext2_mount (device->disk); - if (! data) + ctx.data = grub_ext2_mount (device->disk); + if (! ctx.data) goto fail; - grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir, - grub_ext2_read_symlink, GRUB_FSHELP_DIR); + grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro, + grub_ext2_iterate_dir, grub_ext2_read_symlink, + GRUB_FSHELP_DIR); if (grub_errno) goto fail; - grub_ext2_iterate_dir (fdiro, iterate); + grub_ext2_iterate_dir (fdiro, grub_ext2_dir_iter, &ctx); fail: - if (fdiro != &data->diropen) + if (fdiro != &ctx.data->diropen) grub_free (fdiro); - grub_free (data); + grub_free (ctx.data); grub_dl_unref (my_mod); @@ -856,7 +1057,8 @@ grub_ext2_label (grub_device_t device, char **label) data = grub_ext2_mount (disk); if (data) - *label = grub_strndup (data->sblock.volume_name, 14); + *label = grub_strndup (data->sblock.volume_name, + sizeof (data->sblock.volume_name)); else *label = NULL; @@ -900,7 +1102,7 @@ grub_ext2_uuid (grub_device_t device, char **uuid) /* Get mtime. */ static grub_err_t -grub_ext2_mtime (grub_device_t device, grub_int32_t *tm) +grub_ext2_mtime (grub_device_t device, grub_int64_t *tm) { struct grub_ext2_data *data; grub_disk_t disk = device->disk; @@ -926,21 +1128,23 @@ grub_ext2_mtime (grub_device_t device, grub_int32_t *tm) static struct grub_fs grub_ext2_fs = { .name = "ext2", - .dir = grub_ext2_dir, - .open = grub_ext2_open, - .read = grub_ext2_read, - .close = grub_ext2_close, - .label = grub_ext2_label, - .uuid = grub_ext2_uuid, - .mtime = grub_ext2_mtime, + .fs_dir = grub_ext2_dir, + .fs_open = grub_ext2_open, + .fs_read = grub_ext2_read, + .fs_close = grub_ext2_close, + .fs_label = grub_ext2_label, + .fs_uuid = grub_ext2_uuid, + .fs_mtime = grub_ext2_mtime, #ifdef GRUB_UTIL .reserved_first_sector = 1, + .blocklist_install = 1, #endif .next = 0 }; GRUB_MOD_INIT(ext2) { + grub_ext2_fs.mod = mod; grub_fs_register (&grub_ext2_fs); my_mod = mod; } diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c new file mode 100644 index 000000000..72b4aa1e6 --- /dev/null +++ b/grub-core/fs/f2fs.c @@ -0,0 +1,1377 @@ +/* + * f2fs.c - Flash-Friendly File System + * + * Written by Jaegeuk Kim + * + * Copyright (C) 2015 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* F2FS Magic Number. */ +#define F2FS_SUPER_MAGIC 0xf2f52010 + +#define CHECKSUM_OFFSET 4092 /* Must be aligned 4 bytes. */ +#define U32_CHECKSUM_OFFSET (CHECKSUM_OFFSET >> 2) +#define CRCPOLY_LE 0xedb88320 + +/* Byte-size offset. */ +#define F2FS_SUPER_OFFSET ((grub_disk_addr_t)1024) +#define F2FS_SUPER_OFFSET0 (F2FS_SUPER_OFFSET >> GRUB_DISK_SECTOR_BITS) +#define F2FS_SUPER_OFFSET1 ((F2FS_SUPER_OFFSET + F2FS_BLKSIZE) >> \ + GRUB_DISK_SECTOR_BITS) + +/* 9 bits for 512 bytes. */ +#define F2FS_MIN_LOG_SECTOR_SIZE 9 + +/* Support only 4KB block. */ +#define F2FS_BLK_BITS 12 +#define F2FS_BLKSIZE (1 << F2FS_BLK_BITS) +#define F2FS_BLK_SEC_BITS (F2FS_BLK_BITS - GRUB_DISK_SECTOR_BITS) + +#define VERSION_LEN 256 +#define F2FS_MAX_EXTENSION 64 + +#define CP_COMPACT_SUM_FLAG 0x00000004 +#define CP_UMOUNT_FLAG 0x00000001 + +#define MAX_ACTIVE_LOGS 16 +#define MAX_ACTIVE_NODE_LOGS 8 +#define MAX_ACTIVE_DATA_LOGS 8 +#define NR_CURSEG_DATA_TYPE 3 +#define NR_CURSEG_NODE_TYPE 3 +#define NR_CURSEG_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) + +#define ENTRIES_IN_SUM 512 +#define SUMMARY_SIZE 7 +#define SUM_FOOTER_SIZE 5 +#define JENTRY_SIZE (sizeof(struct grub_f2fs_nat_jent)) +#define SUM_ENTRIES_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) +#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE - SUM_ENTRIES_SIZE) +#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) / JENTRY_SIZE) +#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) % JENTRY_SIZE) + +#define NAT_ENTRY_SIZE (sizeof(struct grub_f2fs_nat_entry)) +#define NAT_ENTRY_PER_BLOCK (F2FS_BLKSIZE / NAT_ENTRY_SIZE) + +#define F2FS_NAME_LEN 255 +#define F2FS_SLOT_LEN 8 +#define NR_DENTRY_IN_BLOCK 214 +#define SIZE_OF_DIR_ENTRY 11 /* By byte. */ +#define BITS_PER_BYTE 8 +#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ + BITS_PER_BYTE) +#define SIZE_OF_RESERVED (F2FS_BLKSIZE - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) + +#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs. */ +#define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode. */ + +#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block. */ +#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block. */ +#define NODE_DIR1_BLOCK (DEF_ADDRS_PER_INODE + 1) +#define NODE_DIR2_BLOCK (DEF_ADDRS_PER_INODE + 2) +#define NODE_IND1_BLOCK (DEF_ADDRS_PER_INODE + 3) +#define NODE_IND2_BLOCK (DEF_ADDRS_PER_INODE + 4) +#define NODE_DIND_BLOCK (DEF_ADDRS_PER_INODE + 5) + +#define MAX_INLINE_DATA (4 * (DEF_ADDRS_PER_INODE - \ + F2FS_INLINE_XATTR_ADDRS - 1)) +#define NR_INLINE_DENTRY (MAX_INLINE_DATA * BITS_PER_BYTE / \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + BITS_PER_BYTE + 1)) +#define INLINE_DENTRY_BITMAP_SIZE ((NR_INLINE_DENTRY + BITS_PER_BYTE - 1) / \ + BITS_PER_BYTE) +#define INLINE_RESERVED_SIZE (MAX_INLINE_DATA - \ + ((SIZE_OF_DIR_ENTRY + F2FS_SLOT_LEN) * \ + NR_INLINE_DENTRY + \ + INLINE_DENTRY_BITMAP_SIZE)) +#define CURSEG_HOT_DATA 0 + +#define CKPT_FLAG_SET(ckpt, f) (ckpt)->ckpt_flags & \ + grub_cpu_to_le32_compile_time (f) + +#define F2FS_INLINE_XATTR 0x01 /* File inline xattr flag. */ +#define F2FS_INLINE_DATA 0x02 /* File inline data flag. */ +#define F2FS_INLINE_DENTRY 0x04 /* File inline dentry flag. */ +#define F2FS_DATA_EXIST 0x08 /* File inline data exist flag. */ +#define F2FS_INLINE_DOTS 0x10 /* File having implicit dot dentries. */ + +#define MAX_VOLUME_NAME 512 +#define MAX_NAT_BITMAP_SIZE 3900 + +enum FILE_TYPE +{ + F2FS_FT_UNKNOWN, + F2FS_FT_REG_FILE = 1, + F2FS_FT_DIR = 2, + F2FS_FT_SYMLINK = 7 +}; + +struct grub_f2fs_superblock +{ + grub_uint32_t magic; + grub_uint16_t dummy1[2]; + grub_uint32_t log_sectorsize; + grub_uint32_t log_sectors_per_block; + grub_uint32_t log_blocksize; + grub_uint32_t log_blocks_per_seg; + grub_uint32_t segs_per_sec; + grub_uint32_t secs_per_zone; + grub_uint32_t checksum_offset; + grub_uint8_t dummy2[40]; + grub_uint32_t cp_blkaddr; + grub_uint32_t sit_blkaddr; + grub_uint32_t nat_blkaddr; + grub_uint32_t ssa_blkaddr; + grub_uint32_t main_blkaddr; + grub_uint32_t root_ino; + grub_uint32_t node_ino; + grub_uint32_t meta_ino; + grub_uint8_t uuid[16]; + grub_uint16_t volume_name[MAX_VOLUME_NAME]; + grub_uint32_t extension_count; + grub_uint8_t extension_list[F2FS_MAX_EXTENSION][8]; + grub_uint32_t cp_payload; + grub_uint8_t version[VERSION_LEN]; + grub_uint8_t init_version[VERSION_LEN]; +} GRUB_PACKED; + +struct grub_f2fs_checkpoint +{ + grub_uint64_t checkpoint_ver; + grub_uint64_t user_block_count; + grub_uint64_t valid_block_count; + grub_uint32_t rsvd_segment_count; + grub_uint32_t overprov_segment_count; + grub_uint32_t free_segment_count; + grub_uint32_t cur_node_segno[MAX_ACTIVE_NODE_LOGS]; + grub_uint16_t cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]; + grub_uint32_t cur_data_segno[MAX_ACTIVE_DATA_LOGS]; + grub_uint16_t cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]; + grub_uint32_t ckpt_flags; + grub_uint32_t cp_pack_total_block_count; + grub_uint32_t cp_pack_start_sum; + grub_uint32_t valid_node_count; + grub_uint32_t valid_inode_count; + grub_uint32_t next_free_nid; + grub_uint32_t sit_ver_bitmap_bytesize; + grub_uint32_t nat_ver_bitmap_bytesize; + grub_uint32_t checksum_offset; + grub_uint64_t elapsed_time; + grub_uint8_t alloc_type[MAX_ACTIVE_LOGS]; + grub_uint8_t sit_nat_version_bitmap[MAX_NAT_BITMAP_SIZE]; + grub_uint32_t checksum; +} GRUB_PACKED; + +struct grub_f2fs_nat_entry { + grub_uint8_t version; + grub_uint32_t ino; + grub_uint32_t block_addr; +} GRUB_PACKED; + +struct grub_f2fs_nat_jent +{ + grub_uint32_t nid; + struct grub_f2fs_nat_entry ne; +} GRUB_PACKED; + +struct grub_f2fs_nat_journal { + grub_uint16_t n_nats; + struct grub_f2fs_nat_jent entries[NAT_JOURNAL_ENTRIES]; + grub_uint8_t reserved[NAT_JOURNAL_RESERVED]; +} GRUB_PACKED; + +struct grub_f2fs_nat_block { + struct grub_f2fs_nat_entry ne[NAT_ENTRY_PER_BLOCK]; +} GRUB_PACKED; + +struct grub_f2fs_dir_entry +{ + grub_uint32_t hash_code; + grub_uint32_t ino; + grub_uint16_t name_len; + grub_uint8_t file_type; +} GRUB_PACKED; + +struct grub_f2fs_inline_dentry +{ + grub_uint8_t dentry_bitmap[INLINE_DENTRY_BITMAP_SIZE]; + grub_uint8_t reserved[INLINE_RESERVED_SIZE]; + struct grub_f2fs_dir_entry dentry[NR_INLINE_DENTRY]; + grub_uint8_t filename[NR_INLINE_DENTRY][F2FS_SLOT_LEN]; +} GRUB_PACKED; + +struct grub_f2fs_dentry_block { + grub_uint8_t dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; + grub_uint8_t reserved[SIZE_OF_RESERVED]; + struct grub_f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; + grub_uint8_t filename[NR_DENTRY_IN_BLOCK][F2FS_SLOT_LEN]; +} GRUB_PACKED; + +struct grub_f2fs_inode +{ + grub_uint16_t i_mode; + grub_uint8_t i_advise; + grub_uint8_t i_inline; + grub_uint32_t i_uid; + grub_uint32_t i_gid; + grub_uint32_t i_links; + grub_uint64_t i_size; + grub_uint64_t i_blocks; + grub_uint64_t i_atime; + grub_uint64_t i_ctime; + grub_uint64_t i_mtime; + grub_uint32_t i_atime_nsec; + grub_uint32_t i_ctime_nsec; + grub_uint32_t i_mtime_nsec; + grub_uint32_t i_generation; + grub_uint32_t i_current_depth; + grub_uint32_t i_xattr_nid; + grub_uint32_t i_flags; + grub_uint32_t i_pino; + grub_uint32_t i_namelen; + grub_uint8_t i_name[F2FS_NAME_LEN]; + grub_uint8_t i_dir_level; + grub_uint8_t i_ext[12]; + grub_uint32_t i_addr[DEF_ADDRS_PER_INODE]; + grub_uint32_t i_nid[5]; +} GRUB_PACKED; + +struct grub_direct_node { + grub_uint32_t addr[ADDRS_PER_BLOCK]; +} GRUB_PACKED; + +struct grub_indirect_node { + grub_uint32_t nid[NIDS_PER_BLOCK]; +} GRUB_PACKED; + +struct grub_f2fs_node +{ + union + { + struct grub_f2fs_inode i; + struct grub_direct_node dn; + struct grub_indirect_node in; + /* Should occupy F2FS_BLKSIZE totally. */ + char buf[F2FS_BLKSIZE - 40]; + }; + grub_uint8_t dummy[40]; +} GRUB_PACKED; + +struct grub_fshelp_node +{ + struct grub_f2fs_data *data; + struct grub_f2fs_node inode; + grub_uint32_t ino; + int inode_read; +}; + +struct grub_f2fs_data +{ + struct grub_f2fs_superblock sblock; + struct grub_f2fs_checkpoint ckpt; + + grub_uint32_t root_ino; + grub_uint32_t blocks_per_seg; + grub_uint32_t cp_blkaddr; + grub_uint32_t nat_blkaddr; + + struct grub_f2fs_nat_journal nat_j; + char *nat_bitmap; + grub_uint32_t nat_bitmap_size; + + grub_disk_t disk; + struct grub_f2fs_node *inode; + struct grub_fshelp_node diropen; +}; + +struct grub_f2fs_dir_iter_ctx +{ + struct grub_f2fs_data *data; + grub_fshelp_iterate_dir_hook_t hook; + void *hook_data; + grub_uint8_t *bitmap; + grub_uint8_t (*filename)[F2FS_SLOT_LEN]; + struct grub_f2fs_dir_entry *dentry; + int max; +}; + +struct grub_f2fs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_f2fs_data *data; +}; + +static grub_dl_t my_mod; + +static int +grub_f2fs_test_bit_le (int nr, const grub_uint8_t *addr) +{ + return addr[nr >> 3] & (1 << (nr & 7)); +} + +static char * +get_inline_addr (struct grub_f2fs_inode *inode) +{ + return (char *) &inode->i_addr[1]; +} + +static grub_uint64_t +grub_f2fs_file_size (struct grub_f2fs_inode *inode) +{ + return grub_le_to_cpu64 (inode->i_size); +} + +static grub_uint32_t +start_cp_addr (struct grub_f2fs_data *data) +{ + struct grub_f2fs_checkpoint *ckpt = &data->ckpt; + grub_uint32_t start_addr = data->cp_blkaddr; + + if (!(ckpt->checkpoint_ver & grub_cpu_to_le64_compile_time(1))) + return start_addr + data->blocks_per_seg; + + return start_addr; +} + +static grub_uint32_t +start_sum_block (struct grub_f2fs_data *data) +{ + struct grub_f2fs_checkpoint *ckpt = &data->ckpt; + + return start_cp_addr (data) + grub_le_to_cpu32 (ckpt->cp_pack_start_sum); +} + +static grub_uint32_t +sum_blk_addr (struct grub_f2fs_data *data, int base, int type) +{ + struct grub_f2fs_checkpoint *ckpt = &data->ckpt; + + return start_cp_addr (data) + + grub_le_to_cpu32 (ckpt->cp_pack_total_block_count) - + (base + 1) + type; +} + +static void * +nat_bitmap_ptr (struct grub_f2fs_data *data, grub_uint32_t *nat_bitmap_size) +{ + struct grub_f2fs_checkpoint *ckpt = &data->ckpt; + grub_uint32_t offset; + *nat_bitmap_size = MAX_NAT_BITMAP_SIZE; + + if (grub_le_to_cpu32 (data->sblock.cp_payload) > 0) + return ckpt->sit_nat_version_bitmap; + + offset = grub_le_to_cpu32 (ckpt->sit_ver_bitmap_bytesize); + if (offset >= MAX_NAT_BITMAP_SIZE) + return NULL; + + *nat_bitmap_size = *nat_bitmap_size - offset; + + return ckpt->sit_nat_version_bitmap + offset; +} + +static grub_uint32_t +get_node_id (struct grub_f2fs_node *rn, int off, int inode_block) +{ + if (inode_block) + return grub_le_to_cpu32 (rn->i.i_nid[off - NODE_DIR1_BLOCK]); + + return grub_le_to_cpu32 (rn->in.nid[off]); +} + +static grub_err_t +grub_f2fs_block_read (struct grub_f2fs_data *data, grub_uint32_t blkaddr, + void *buf) +{ + return grub_disk_read (data->disk, + ((grub_disk_addr_t)blkaddr) << F2FS_BLK_SEC_BITS, + 0, F2FS_BLKSIZE, buf); +} + +/* CRC32 */ +static grub_uint32_t +grub_f2fs_cal_crc32 (const void *buf, const grub_uint32_t len) +{ + grub_uint32_t crc = F2FS_SUPER_MAGIC; + unsigned char *p = (unsigned char *)buf; + grub_uint32_t tmp = len; + int i; + + while (tmp--) + { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + + return crc; +} + +static int +grub_f2fs_crc_valid (grub_uint32_t blk_crc, void *buf, const grub_uint32_t len) +{ + grub_uint32_t cal_crc = 0; + + cal_crc = grub_f2fs_cal_crc32 (buf, len); + + return (cal_crc == blk_crc) ? 1 : 0; +} + +static int +grub_f2fs_test_bit (grub_uint32_t nr, const char *p, grub_uint32_t len) +{ + int mask; + grub_uint32_t shifted_nr = (nr >> 3); + + if (shifted_nr >= len) + return -1; + + p += shifted_nr; + mask = 1 << (7 - (nr & 0x07)); + + return mask & *p; +} + +static int +grub_f2fs_sanity_check_sb (struct grub_f2fs_superblock *sb) +{ + grub_uint32_t log_sectorsize, log_sectors_per_block; + + if (sb->magic != grub_cpu_to_le32_compile_time (F2FS_SUPER_MAGIC)) + return -1; + + if (sb->log_blocksize != grub_cpu_to_le32_compile_time (F2FS_BLK_BITS)) + return -1; + + log_sectorsize = grub_le_to_cpu32 (sb->log_sectorsize); + log_sectors_per_block = grub_le_to_cpu32 (sb->log_sectors_per_block); + + if (log_sectorsize > F2FS_BLK_BITS) + return -1; + + if (log_sectorsize < F2FS_MIN_LOG_SECTOR_SIZE) + return -1; + + if (log_sectors_per_block + log_sectorsize != F2FS_BLK_BITS) + return -1; + + return 0; +} + +static int +grub_f2fs_read_sb (struct grub_f2fs_data *data, grub_disk_addr_t offset) +{ + grub_disk_t disk = data->disk; + grub_err_t err; + + /* Read first super block. */ + err = grub_disk_read (disk, offset, 0, sizeof (data->sblock), &data->sblock); + if (err) + return -1; + + return grub_f2fs_sanity_check_sb (&data->sblock); +} + +static void * +validate_checkpoint (struct grub_f2fs_data *data, grub_uint32_t cp_addr, + grub_uint64_t *version) +{ + grub_uint32_t *cp_page_1, *cp_page_2; + struct grub_f2fs_checkpoint *cp_block; + grub_uint64_t cur_version = 0, pre_version = 0; + grub_uint32_t crc = 0; + grub_uint32_t crc_offset; + grub_err_t err; + + /* Read the 1st cp block in this CP pack. */ + cp_page_1 = grub_malloc (F2FS_BLKSIZE); + if (!cp_page_1) + return NULL; + + err = grub_f2fs_block_read (data, cp_addr, cp_page_1); + if (err) + goto invalid_cp1; + + cp_block = (struct grub_f2fs_checkpoint *)cp_page_1; + crc_offset = grub_le_to_cpu32 (cp_block->checksum_offset); + if (crc_offset != CHECKSUM_OFFSET) + goto invalid_cp1; + + crc = grub_le_to_cpu32 (*(cp_page_1 + U32_CHECKSUM_OFFSET)); + if (!grub_f2fs_crc_valid (crc, cp_block, crc_offset)) + goto invalid_cp1; + + pre_version = grub_le_to_cpu64 (cp_block->checkpoint_ver); + + /* Read the 2nd cp block in this CP pack. */ + cp_page_2 = grub_malloc (F2FS_BLKSIZE); + if (!cp_page_2) + goto invalid_cp1; + + cp_addr += grub_le_to_cpu32 (cp_block->cp_pack_total_block_count) - 1; + + err = grub_f2fs_block_read (data, cp_addr, cp_page_2); + if (err) + goto invalid_cp2; + + cp_block = (struct grub_f2fs_checkpoint *)cp_page_2; + crc_offset = grub_le_to_cpu32 (cp_block->checksum_offset); + if (crc_offset != CHECKSUM_OFFSET) + goto invalid_cp2; + + crc = grub_le_to_cpu32 (*(cp_page_2 + U32_CHECKSUM_OFFSET)); + if (!grub_f2fs_crc_valid (crc, cp_block, crc_offset)) + goto invalid_cp2; + + cur_version = grub_le_to_cpu64 (cp_block->checkpoint_ver); + if (cur_version == pre_version) + { + *version = cur_version; + grub_free (cp_page_2); + + return cp_page_1; + } + + invalid_cp2: + grub_free (cp_page_2); + + invalid_cp1: + grub_free (cp_page_1); + + return NULL; +} + +static grub_err_t +grub_f2fs_read_cp (struct grub_f2fs_data *data) +{ + void *cp1, *cp2, *cur_page; + grub_uint64_t cp1_version = 0, cp2_version = 0; + grub_uint64_t cp_start_blk_no; + + /* + * Finding out valid cp block involves read both + * sets (cp pack1 and cp pack 2). + */ + cp_start_blk_no = data->cp_blkaddr; + cp1 = validate_checkpoint (data, cp_start_blk_no, &cp1_version); + if (!cp1 && grub_errno) + return grub_errno; + + /* The second checkpoint pack should start at the next segment. */ + cp_start_blk_no += data->blocks_per_seg; + cp2 = validate_checkpoint (data, cp_start_blk_no, &cp2_version); + if (!cp2 && grub_errno) + { + grub_free (cp1); + return grub_errno; + } + + if (cp1 && cp2) + cur_page = (cp2_version > cp1_version) ? cp2 : cp1; + else if (cp1) + cur_page = cp1; + else if (cp2) + cur_page = cp2; + else + return grub_error (GRUB_ERR_BAD_FS, "no checkpoints"); + + grub_memcpy (&data->ckpt, cur_page, F2FS_BLKSIZE); + + grub_free (cp1); + grub_free (cp2); + + return 0; +} + +static grub_err_t +get_nat_journal (struct grub_f2fs_data *data) +{ + grub_uint32_t block; + char *buf; + grub_err_t err; + + buf = grub_malloc (F2FS_BLKSIZE); + if (!buf) + return grub_errno; + + if (CKPT_FLAG_SET(&data->ckpt, CP_COMPACT_SUM_FLAG)) + block = start_sum_block (data); + else if (CKPT_FLAG_SET (&data->ckpt, CP_UMOUNT_FLAG)) + block = sum_blk_addr (data, NR_CURSEG_TYPE, CURSEG_HOT_DATA); + else + block = sum_blk_addr (data, NR_CURSEG_DATA_TYPE, CURSEG_HOT_DATA); + + err = grub_f2fs_block_read (data, block, buf); + if (err) + goto fail; + + if (CKPT_FLAG_SET (&data->ckpt, CP_COMPACT_SUM_FLAG)) + grub_memcpy (&data->nat_j, buf, SUM_JOURNAL_SIZE); + else + grub_memcpy (&data->nat_j, buf + SUM_ENTRIES_SIZE, SUM_JOURNAL_SIZE); + + fail: + grub_free (buf); + + return err; +} + +static grub_err_t +get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid, + grub_uint32_t *blkaddr) +{ + grub_uint16_t n = grub_le_to_cpu16 (data->nat_j.n_nats); + grub_uint16_t i; + + if (n > NAT_JOURNAL_ENTRIES) + return grub_error (GRUB_ERR_BAD_FS, + "invalid number of nat journal entries"); + + for (i = 0; i < n; i++) + { + if (grub_le_to_cpu32 (data->nat_j.entries[i].nid) == nid) + { + *blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr); + break; + } + } + + return GRUB_ERR_NONE; +} + +static grub_uint32_t +get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid) +{ + struct grub_f2fs_nat_block *nat_block; + grub_uint32_t seg_off, block_off, entry_off, block_addr; + grub_uint32_t blkaddr = 0; + grub_err_t err; + int result_bit; + + err = get_blkaddr_from_nat_journal (data, nid, &blkaddr); + if (err != GRUB_ERR_NONE) + return 0; + + if (blkaddr) + return blkaddr; + + nat_block = grub_malloc (F2FS_BLKSIZE); + if (!nat_block) + return 0; + + block_off = nid / NAT_ENTRY_PER_BLOCK; + entry_off = nid % NAT_ENTRY_PER_BLOCK; + + seg_off = block_off / data->blocks_per_seg; + block_addr = data->nat_blkaddr + + ((seg_off * data->blocks_per_seg) << 1) + + (block_off & (data->blocks_per_seg - 1)); + + result_bit = grub_f2fs_test_bit (block_off, data->nat_bitmap, + data->nat_bitmap_size); + if (result_bit > 0) + block_addr += data->blocks_per_seg; + else if (result_bit == -1) + { + grub_free (nat_block); + return 0; + } + + err = grub_f2fs_block_read (data, block_addr, nat_block); + if (err) + { + grub_free (nat_block); + return 0; + } + + blkaddr = grub_le_to_cpu32 (nat_block->ne[entry_off].block_addr); + + grub_free (nat_block); + + return blkaddr; +} + +static int +grub_get_node_path (struct grub_f2fs_inode *inode, grub_uint32_t block, + grub_uint32_t offset[4], grub_uint32_t noffset[4]) +{ + grub_uint32_t direct_blks = ADDRS_PER_BLOCK; + grub_uint32_t dptrs_per_blk = NIDS_PER_BLOCK; + grub_uint32_t indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; + grub_uint32_t dindirect_blks = indirect_blks * NIDS_PER_BLOCK; + grub_uint32_t direct_index = DEF_ADDRS_PER_INODE; + int n = 0; + int level = -1; + + if (inode->i_inline & F2FS_INLINE_XATTR) + direct_index -= F2FS_INLINE_XATTR_ADDRS; + + noffset[0] = 0; + + if (block < direct_index) + { + offset[n] = block; + level = 0; + goto got; + } + + block -= direct_index; + if (block < direct_blks) + { + offset[n++] = NODE_DIR1_BLOCK; + noffset[n] = 1; + offset[n] = block; + level = 1; + goto got; + } + + block -= direct_blks; + if (block < direct_blks) + { + offset[n++] = NODE_DIR2_BLOCK; + noffset[n] = 2; + offset[n] = block; + level = 1; + goto got; + } + + block -= direct_blks; + if (block < indirect_blks) + { + offset[n++] = NODE_IND1_BLOCK; + noffset[n] = 3; + offset[n++] = block / direct_blks; + noffset[n] = 4 + offset[n - 1]; + offset[n] = block % direct_blks; + level = 2; + goto got; + } + + block -= indirect_blks; + if (block < indirect_blks) + { + offset[n++] = NODE_IND2_BLOCK; + noffset[n] = 4 + dptrs_per_blk; + offset[n++] = block / direct_blks; + noffset[n] = 5 + dptrs_per_blk + offset[n - 1]; + offset[n] = block % direct_blks; + level = 2; + goto got; + } + + block -= indirect_blks; + if (block < dindirect_blks) + { + offset[n++] = NODE_DIND_BLOCK; + noffset[n] = 5 + (dptrs_per_blk * 2); + offset[n++] = block / indirect_blks; + noffset[n] = 6 + (dptrs_per_blk * 2) + + offset[n - 1] * (dptrs_per_blk + 1); + offset[n++] = (block / direct_blks) % dptrs_per_blk; + noffset[n] = 7 + (dptrs_per_blk * 2) + + offset[n - 2] * (dptrs_per_blk + 1) + offset[n - 1]; + offset[n] = block % direct_blks; + level = 3; + goto got; + } + + got: + return level; +} + +static grub_err_t +grub_f2fs_read_node (struct grub_f2fs_data *data, + grub_uint32_t nid, struct grub_f2fs_node *np) +{ + grub_uint32_t blkaddr; + + blkaddr = get_node_blkaddr (data, nid); + if (!blkaddr) + return grub_errno; + + return grub_f2fs_block_read (data, blkaddr, np); +} + +static struct grub_f2fs_data * +grub_f2fs_mount (grub_disk_t disk) +{ + struct grub_f2fs_data *data; + grub_err_t err; + + data = grub_malloc (sizeof (*data)); + if (!data) + return NULL; + + data->disk = disk; + + if (grub_f2fs_read_sb (data, F2FS_SUPER_OFFSET0)) + { + if (grub_f2fs_read_sb (data, F2FS_SUPER_OFFSET1)) + { + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, + "not a F2FS filesystem (no superblock)"); + goto fail; + } + } + + data->root_ino = grub_le_to_cpu32 (data->sblock.root_ino); + data->cp_blkaddr = grub_le_to_cpu32 (data->sblock.cp_blkaddr); + data->nat_blkaddr = grub_le_to_cpu32 (data->sblock.nat_blkaddr); + data->blocks_per_seg = 1 << + grub_le_to_cpu32 (data->sblock.log_blocks_per_seg); + + err = grub_f2fs_read_cp (data); + if (err) + goto fail; + + data->nat_bitmap = nat_bitmap_ptr (data, &data->nat_bitmap_size); + if (data->nat_bitmap == NULL) + goto fail; + + err = get_nat_journal (data); + if (err) + goto fail; + + data->diropen.data = data; + data->diropen.ino = data->root_ino; + data->diropen.inode_read = 1; + data->inode = &data->diropen.inode; + + err = grub_f2fs_read_node (data, data->root_ino, data->inode); + if (err) + goto fail; + + return data; + + fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a F2FS filesystem"); + + grub_free (data); + + return NULL; +} + +/* Guarantee inline_data was handled by caller. */ +static grub_disk_addr_t +grub_f2fs_get_block (grub_fshelp_node_t node, grub_disk_addr_t block_ofs) +{ + struct grub_f2fs_data *data = node->data; + struct grub_f2fs_inode *inode = &node->inode.i; + grub_uint32_t offset[4], noffset[4], nids[4]; + struct grub_f2fs_node *node_block; + grub_uint32_t block_addr = -1; + int level, i; + + level = grub_get_node_path (inode, block_ofs, offset, noffset); + + if (level < 0) + return -1; + + if (level == 0) + return grub_le_to_cpu32 (inode->i_addr[offset[0]]); + + node_block = grub_malloc (F2FS_BLKSIZE); + if (!node_block) + return -1; + + nids[1] = get_node_id (&node->inode, offset[0], 1); + + /* Get indirect or direct nodes. */ + for (i = 1; i <= level; i++) + { + grub_f2fs_read_node (data, nids[i], node_block); + if (grub_errno) + goto fail; + + if (i < level) + nids[i + 1] = get_node_id (node_block, offset[i], 0); + } + + block_addr = grub_le_to_cpu32 (node_block->dn.addr[offset[level]]); + + fail: + grub_free (node_block); + + return block_addr; +} + +static grub_ssize_t +grub_f2fs_read_file (grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) +{ + struct grub_f2fs_inode *inode = &node->inode.i; + grub_off_t filesize = grub_f2fs_file_size (inode); + char *inline_addr = get_inline_addr (inode); + + if (inode->i_inline & F2FS_INLINE_DATA) + { + if (filesize > MAX_INLINE_DATA) + return -1; + + if (len > filesize - pos) + len = filesize - pos; + + grub_memcpy (buf, inline_addr + pos, len); + return len; + } + + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, + pos, len, buf, grub_f2fs_get_block, + filesize, + F2FS_BLK_SEC_BITS, 0); +} + +static char * +grub_f2fs_read_symlink (grub_fshelp_node_t node) +{ + char *symlink; + struct grub_fshelp_node *diro = node; + grub_uint64_t filesize; + grub_size_t sz; + + if (!diro->inode_read) + { + grub_f2fs_read_node (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + filesize = grub_f2fs_file_size(&diro->inode.i); + + if (grub_add (filesize, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); + if (!symlink) + return 0; + + grub_f2fs_read_file (diro, 0, 0, 0, filesize, symlink); + if (grub_errno) + { + grub_free (symlink); + return 0; + } + + symlink[filesize] = '\0'; + + return symlink; +} + +static int +grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) +{ + struct grub_fshelp_node *fdiro; + int i; + + for (i = 0; i < ctx->max;) + { + char *filename; + enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; + enum FILE_TYPE ftype; + int name_len; + int ret; + int sz; + + if (grub_f2fs_test_bit_le (i, ctx->bitmap) == 0) + { + i++; + continue; + } + + ftype = ctx->dentry[i].file_type; + name_len = grub_le_to_cpu16 (ctx->dentry[i].name_len); + + if (name_len >= F2FS_NAME_LEN) + return 0; + + if (grub_add (name_len, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory entry name length overflow")); + return 0; + } + filename = grub_malloc (sz); + if (!filename) + return 0; + + grub_memcpy (filename, ctx->filename[i], name_len); + filename[name_len] = 0; + + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!fdiro) + { + grub_free(filename); + return 0; + } + + if (ftype == F2FS_FT_DIR) + type = GRUB_FSHELP_DIR; + else if (ftype == F2FS_FT_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else if (ftype == F2FS_FT_REG_FILE) + type = GRUB_FSHELP_REG; + + fdiro->data = ctx->data; + fdiro->ino = grub_le_to_cpu32 (ctx->dentry[i].ino); + fdiro->inode_read = 0; + + ret = ctx->hook (filename, type, fdiro, ctx->hook_data); + grub_free(filename); + if (ret) + return 1; + + i += (name_len + F2FS_SLOT_LEN - 1) / F2FS_SLOT_LEN; + } + + return 0; +} + +static int +grub_f2fs_iterate_inline_dir (struct grub_f2fs_inode *dir, + struct grub_f2fs_dir_iter_ctx *ctx) +{ + struct grub_f2fs_inline_dentry *de_blk; + + de_blk = (struct grub_f2fs_inline_dentry *) get_inline_addr (dir); + + ctx->bitmap = de_blk->dentry_bitmap; + ctx->dentry = de_blk->dentry; + ctx->filename = de_blk->filename; + ctx->max = NR_INLINE_DENTRY; + + return grub_f2fs_check_dentries (ctx); +} + +static int +grub_f2fs_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; + struct grub_f2fs_inode *inode; + struct grub_f2fs_dir_iter_ctx ctx = { + .data = diro->data, + .hook = hook, + .hook_data = hook_data + }; + grub_off_t fpos = 0; + + if (!diro->inode_read) + { + grub_f2fs_read_node (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + inode = &diro->inode.i; + + if (inode->i_inline & F2FS_INLINE_DENTRY) + return grub_f2fs_iterate_inline_dir (inode, &ctx); + + while (fpos < grub_f2fs_file_size (inode)) + { + struct grub_f2fs_dentry_block *de_blk; + char *buf; + int ret; + + buf = grub_zalloc (F2FS_BLKSIZE); + if (!buf) + return 0; + + grub_f2fs_read_file (diro, 0, 0, fpos, F2FS_BLKSIZE, buf); + if (grub_errno) + { + grub_free (buf); + return 0; + } + + de_blk = (struct grub_f2fs_dentry_block *) buf; + + ctx.bitmap = de_blk->dentry_bitmap; + ctx.dentry = de_blk->dentry; + ctx.filename = de_blk->filename; + ctx.max = NR_DENTRY_IN_BLOCK; + + ret = grub_f2fs_check_dentries (&ctx); + grub_free (buf); + if (ret) + return 1; + + fpos += F2FS_BLKSIZE; + } + + return 0; +} + +static int +grub_f2fs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_f2fs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + if (!node->inode_read) + { + grub_f2fs_read_node (ctx->data, node->ino, &node->inode); + if (!grub_errno) + node->inode_read = 1; + grub_errno = GRUB_ERR_NONE; + } + if (node->inode_read) + { + info.mtimeset = 1; + info.mtime = grub_le_to_cpu64 (node->inode.i.i_mtime); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_f2fs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_f2fs_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + ctx.data = grub_f2fs_mount (device->disk); + if (!ctx.data) + goto fail; + + grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro, + grub_f2fs_iterate_dir, grub_f2fs_read_symlink, + GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_f2fs_iterate_dir (fdiro, grub_f2fs_dir_iter, &ctx); + + fail: + if (fdiro != &ctx.data->diropen) + grub_free (fdiro); + grub_free (ctx.data); + grub_dl_unref (my_mod); + + return grub_errno; +} + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_f2fs_open (struct grub_file *file, const char *name) +{ + struct grub_f2fs_data *data = NULL; + struct grub_fshelp_node *fdiro = 0; + struct grub_f2fs_inode *inode; + + grub_dl_ref (my_mod); + + data = grub_f2fs_mount (file->device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (name, &data->diropen, &fdiro, + grub_f2fs_iterate_dir, grub_f2fs_read_symlink, + GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + if (!fdiro->inode_read) + { + grub_f2fs_read_node (data, fdiro->ino, &fdiro->inode); + if (grub_errno) + goto fail; + } + + grub_memcpy (data->inode, &fdiro->inode, sizeof (*data->inode)); + grub_free (fdiro); + + inode = &(data->inode->i); + file->size = grub_f2fs_file_size (inode); + file->data = data; + file->offset = 0; + + if (inode->i_inline & F2FS_INLINE_DATA && file->size > MAX_INLINE_DATA) + grub_error (GRUB_ERR_BAD_FS, "corrupted inline_data: need fsck"); + + return 0; + + fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_ssize_t +grub_f2fs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_f2fs_data *data = (struct grub_f2fs_data *) file->data; + + return grub_f2fs_read_file (&data->diropen, + file->read_hook, file->read_hook_data, + file->offset, len, buf); +} + +static grub_err_t +grub_f2fs_close (grub_file_t file) +{ + struct grub_f2fs_data *data = (struct grub_f2fs_data *) file->data; + + grub_free (data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +static grub_uint8_t * +grub_f2fs_utf16_to_utf8 (grub_uint16_t *in_buf_le) +{ + grub_uint16_t in_buf[MAX_VOLUME_NAME]; + grub_uint8_t *out_buf; + int len = 0; + + out_buf = grub_malloc (MAX_VOLUME_NAME * GRUB_MAX_UTF8_PER_UTF16 + 1); + if (!out_buf) + return NULL; + + while (*in_buf_le != 0 && len < MAX_VOLUME_NAME) { + in_buf[len] = grub_le_to_cpu16 (in_buf_le[len]); + len++; + } + + *grub_utf16_to_utf8 (out_buf, in_buf, len) = '\0'; + + return out_buf; +} + +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif + +static grub_err_t +grub_f2fs_label (grub_device_t device, char **label) +{ + struct grub_f2fs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_f2fs_mount (disk); + if (data) + *label = (char *) grub_f2fs_utf16_to_utf8 (data->sblock.volume_name); + else + *label = NULL; + + grub_free (data); + grub_dl_unref (my_mod); + + return grub_errno; +} + +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif + +static grub_err_t +grub_f2fs_uuid (grub_device_t device, char **uuid) +{ + struct grub_f2fs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_f2fs_mount (disk); + if (data) + { + *uuid = + grub_xasprintf + ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + data->sblock.uuid[0], data->sblock.uuid[1], + data->sblock.uuid[2], data->sblock.uuid[3], + data->sblock.uuid[4], data->sblock.uuid[5], + data->sblock.uuid[6], data->sblock.uuid[7], + data->sblock.uuid[8], data->sblock.uuid[9], + data->sblock.uuid[10], data->sblock.uuid[11], + data->sblock.uuid[12], data->sblock.uuid[13], + data->sblock.uuid[14], data->sblock.uuid[15]); + } + else + *uuid = NULL; + + grub_free (data); + grub_dl_unref (my_mod); + + return grub_errno; +} + +static struct grub_fs grub_f2fs_fs = { + .name = "f2fs", + .fs_dir = grub_f2fs_dir, + .fs_open = grub_f2fs_open, + .fs_read = grub_f2fs_read, + .fs_close = grub_f2fs_close, + .fs_label = grub_f2fs_label, + .fs_uuid = grub_f2fs_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (f2fs) +{ + grub_f2fs_fs.mod = mod; + grub_fs_register (&grub_f2fs_fs); + my_mod = mod; +} + +GRUB_MOD_FINI (f2fs) +{ + grub_fs_unregister (&grub_f2fs_fs); +} diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c new file mode 100644 index 000000000..6e62b915d --- /dev/null +++ b/grub-core/fs/fat.c @@ -0,0 +1,1327 @@ +/* fat.c - FAT filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef MODE_EXFAT +#include +#else +#include +#endif +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +enum + { + GRUB_FAT_ATTR_READ_ONLY = 0x01, + GRUB_FAT_ATTR_HIDDEN = 0x02, + GRUB_FAT_ATTR_SYSTEM = 0x04, +#ifndef MODE_EXFAT + GRUB_FAT_ATTR_VOLUME_ID = 0x08, +#endif + GRUB_FAT_ATTR_DIRECTORY = 0x10, + GRUB_FAT_ATTR_ARCHIVE = 0x20, + +#ifndef MODE_EXFAT + GRUB_FAT_ATTR_LONG_NAME = (GRUB_FAT_ATTR_READ_ONLY + | GRUB_FAT_ATTR_HIDDEN + | GRUB_FAT_ATTR_SYSTEM + | GRUB_FAT_ATTR_VOLUME_ID), +#endif + GRUB_FAT_ATTR_VALID = (GRUB_FAT_ATTR_READ_ONLY + | GRUB_FAT_ATTR_HIDDEN + | GRUB_FAT_ATTR_SYSTEM + | GRUB_FAT_ATTR_DIRECTORY + | GRUB_FAT_ATTR_ARCHIVE +#ifndef MODE_EXFAT + | GRUB_FAT_ATTR_VOLUME_ID +#endif + ) + }; + +#ifdef MODE_EXFAT +typedef struct grub_exfat_bpb grub_current_fat_bpb_t; +#else +typedef struct grub_fat_bpb grub_current_fat_bpb_t; +#endif + +#ifdef MODE_EXFAT +enum + { + FLAG_CONTIGUOUS = 2 + }; +struct grub_fat_dir_entry +{ + grub_uint8_t entry_type; + union + { + grub_uint8_t placeholder[31]; + struct { + grub_uint8_t secondary_count; + grub_uint16_t checksum; + grub_uint16_t attr; + grub_uint16_t reserved1; + grub_uint32_t c_time; + grub_uint32_t m_time; + grub_uint32_t a_time; + grub_uint8_t c_time_tenth; + grub_uint8_t m_time_tenth; + grub_uint8_t a_time_tenth; + grub_uint8_t reserved2[9]; + } GRUB_PACKED file; + struct { + grub_uint8_t flags; + grub_uint8_t reserved1; + grub_uint8_t name_length; + grub_uint16_t name_hash; + grub_uint16_t reserved2; + grub_uint64_t valid_size; + grub_uint32_t reserved3; + grub_uint32_t first_cluster; + grub_uint64_t file_size; + } GRUB_PACKED stream_extension; + struct { + grub_uint8_t flags; + grub_uint16_t str[15]; + } GRUB_PACKED file_name; + struct { + grub_uint8_t character_count; + grub_uint16_t str[15]; + } GRUB_PACKED volume_label; + } GRUB_PACKED type_specific; +} GRUB_PACKED; + +struct grub_fat_dir_node +{ + grub_uint32_t attr; + grub_uint32_t first_cluster; + grub_uint64_t file_size; + grub_uint64_t valid_size; + int have_stream; + int is_contiguous; +}; + +typedef struct grub_fat_dir_node grub_fat_dir_node_t; + +#else +struct grub_fat_dir_entry +{ + grub_uint8_t name[11]; + grub_uint8_t attr; + grub_uint8_t nt_reserved; + grub_uint8_t c_time_tenth; + grub_uint16_t c_time; + grub_uint16_t c_date; + grub_uint16_t a_date; + grub_uint16_t first_cluster_high; + grub_uint16_t w_time; + grub_uint16_t w_date; + grub_uint16_t first_cluster_low; + grub_uint32_t file_size; +} GRUB_PACKED; + +struct grub_fat_long_name_entry +{ + grub_uint8_t id; + grub_uint16_t name1[5]; + grub_uint8_t attr; + grub_uint8_t reserved; + grub_uint8_t checksum; + grub_uint16_t name2[6]; + grub_uint16_t first_cluster; + grub_uint16_t name3[2]; +} GRUB_PACKED; + +typedef struct grub_fat_dir_entry grub_fat_dir_node_t; + +#endif + +struct grub_fat_data +{ + int logical_sector_bits; + grub_uint32_t num_sectors; + + grub_uint32_t fat_sector; + grub_uint32_t sectors_per_fat; + int fat_size; + + grub_uint32_t root_cluster; +#ifndef MODE_EXFAT + grub_uint32_t root_sector; + grub_uint32_t num_root_sectors; +#endif + + int cluster_bits; + grub_uint32_t cluster_eof_mark; + grub_uint32_t cluster_sector; + grub_uint32_t num_clusters; + + grub_uint32_t uuid; +}; + +struct grub_fshelp_node { + grub_disk_t disk; + struct grub_fat_data *data; + + grub_uint8_t attr; +#ifndef MODE_EXFAT + grub_uint32_t file_size; +#else + grub_uint64_t file_size; +#endif + grub_uint32_t file_cluster; + grub_uint32_t cur_cluster_num; + grub_uint32_t cur_cluster; + +#ifdef MODE_EXFAT + int is_contiguous; +#endif +}; + +static grub_dl_t my_mod; + +#ifndef MODE_EXFAT +static int +fat_log2 (unsigned x) +{ + int i; + + if (x == 0) + return -1; + + for (i = 0; (x & 1) == 0; i++) + x >>= 1; + + if (x != 1) + return -1; + + return i; +} +#endif + +static struct grub_fat_data * +grub_fat_mount (grub_disk_t disk) +{ + grub_current_fat_bpb_t bpb; + struct grub_fat_data *data = 0; + grub_uint32_t first_fat, magic; + + if (! disk) + goto fail; + + data = (struct grub_fat_data *) grub_malloc (sizeof (*data)); + if (! data) + goto fail; + + /* Read the BPB. */ + if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb)) + goto fail; + +#ifdef MODE_EXFAT + if (grub_memcmp ((const char *) bpb.oem_name, "EXFAT ", + sizeof (bpb.oem_name)) != 0) + goto fail; +#endif + + /* Get the sizes of logical sectors and clusters. */ +#ifdef MODE_EXFAT + data->logical_sector_bits = bpb.bytes_per_sector_shift; +#else + data->logical_sector_bits = + fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector)); +#endif + if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS + || data->logical_sector_bits >= 16) + goto fail; + data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS; + +#ifdef MODE_EXFAT + data->cluster_bits = bpb.sectors_per_cluster_shift; +#else + data->cluster_bits = fat_log2 (bpb.sectors_per_cluster); +#endif + if (data->cluster_bits < 0 || data->cluster_bits > 25) + goto fail; + data->cluster_bits += data->logical_sector_bits; + + /* Get information about FATs. */ +#ifdef MODE_EXFAT + data->fat_sector = (grub_le_to_cpu32 (bpb.num_reserved_sectors) + << data->logical_sector_bits); +#else + data->fat_sector = (grub_le_to_cpu16 (bpb.num_reserved_sectors) + << data->logical_sector_bits); +#endif + if (data->fat_sector == 0) + goto fail; + +#ifdef MODE_EXFAT + data->sectors_per_fat = (grub_le_to_cpu32 (bpb.sectors_per_fat) + << data->logical_sector_bits); +#else + data->sectors_per_fat = ((bpb.sectors_per_fat_16 + ? grub_le_to_cpu16 (bpb.sectors_per_fat_16) + : grub_le_to_cpu32 (bpb.version_specific.fat32.sectors_per_fat_32)) + << data->logical_sector_bits); +#endif + if (data->sectors_per_fat == 0) + goto fail; + + /* Get the number of sectors in this volume. */ +#ifdef MODE_EXFAT + data->num_sectors = ((grub_le_to_cpu64 (bpb.num_total_sectors)) + << data->logical_sector_bits); +#else + data->num_sectors = ((bpb.num_total_sectors_16 + ? grub_le_to_cpu16 (bpb.num_total_sectors_16) + : grub_le_to_cpu32 (bpb.num_total_sectors_32)) + << data->logical_sector_bits); +#endif + if (data->num_sectors == 0) + goto fail; + + /* Get information about the root directory. */ + if (bpb.num_fats == 0) + goto fail; + +#ifndef MODE_EXFAT + data->root_sector = data->fat_sector + bpb.num_fats * data->sectors_per_fat; + data->num_root_sectors + = ((((grub_uint32_t) grub_le_to_cpu16 (bpb.num_root_entries) + * sizeof (struct grub_fat_dir_entry) + + grub_le_to_cpu16 (bpb.bytes_per_sector) - 1) + >> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS)) + << (data->logical_sector_bits)); +#endif + +#ifdef MODE_EXFAT + data->cluster_sector = (grub_le_to_cpu32 (bpb.cluster_offset) + << data->logical_sector_bits); + data->num_clusters = (grub_le_to_cpu32 (bpb.cluster_count) + << data->logical_sector_bits); +#else + data->cluster_sector = data->root_sector + data->num_root_sectors; + data->num_clusters = (((data->num_sectors - data->cluster_sector) + >> data->cluster_bits) + + 2); +#endif + + if (data->num_clusters <= 2) + goto fail; + +#ifdef MODE_EXFAT + { + /* exFAT. */ + data->root_cluster = grub_le_to_cpu32 (bpb.root_cluster); + data->fat_size = 32; + data->cluster_eof_mark = 0xffffffff; + + if ((bpb.volume_flags & grub_cpu_to_le16_compile_time (0x1)) + && bpb.num_fats > 1) + data->fat_sector += data->sectors_per_fat; + } +#else + if (! bpb.sectors_per_fat_16) + { + /* FAT32. */ + grub_uint16_t flags = grub_le_to_cpu16 (bpb.version_specific.fat32.extended_flags); + + data->root_cluster = grub_le_to_cpu32 (bpb.version_specific.fat32.root_cluster); + data->fat_size = 32; + data->cluster_eof_mark = 0x0ffffff8; + + if (flags & 0x80) + { + /* Get an active FAT. */ + unsigned active_fat = flags & 0xf; + + if (active_fat > bpb.num_fats) + goto fail; + + data->fat_sector += active_fat * data->sectors_per_fat; + } + + if (bpb.num_root_entries != 0 || bpb.version_specific.fat32.fs_version != 0) + goto fail; + } + else + { + /* FAT12 or FAT16. */ + data->root_cluster = ~0U; + + if (data->num_clusters <= 4085 + 2) + { + /* FAT12. */ + data->fat_size = 12; + data->cluster_eof_mark = 0x0ff8; + } + else + { + /* FAT16. */ + data->fat_size = 16; + data->cluster_eof_mark = 0xfff8; + } + } +#endif + + /* More sanity checks. */ + if (data->num_sectors <= data->fat_sector) + goto fail; + + if (grub_disk_read (disk, + data->fat_sector, + 0, + sizeof (first_fat), + &first_fat)) + goto fail; + + first_fat = grub_le_to_cpu32 (first_fat); + + if (data->fat_size == 32) + { + first_fat &= 0x0fffffff; + magic = 0x0fffff00; + } + else if (data->fat_size == 16) + { + first_fat &= 0x0000ffff; + magic = 0xff00; + } + else + { + first_fat &= 0x00000fff; + magic = 0x0f00; + } + + /* Serial number. */ +#ifdef MODE_EXFAT + data->uuid = grub_le_to_cpu32 (bpb.num_serial); +#else + if (bpb.sectors_per_fat_16) + data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat12_or_fat16.num_serial); + else + data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat32.num_serial); +#endif + +#ifndef MODE_EXFAT + /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media + descriptor, even if it is a so-called superfloppy (e.g. an USB key). + The check may be too strict for this kind of stupid BIOSes, as + they overwrite the media descriptor. */ + if ((first_fat | 0x8) != (magic | bpb.media | 0x8)) + goto fail; +#else + (void) magic; +#endif + + return data; + + fail: + + grub_free (data); + grub_error (GRUB_ERR_BAD_FS, "not a FAT filesystem"); + return 0; +} + +static grub_ssize_t +grub_fat_read_data (grub_disk_t disk, grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t offset, grub_size_t len, char *buf) +{ + grub_size_t size; + grub_uint32_t logical_cluster; + unsigned logical_cluster_bits; + grub_ssize_t ret = 0; + unsigned long sector; + +#ifndef MODE_EXFAT + /* This is a special case. FAT12 and FAT16 doesn't have the root directory + in clusters. */ + if (node->file_cluster == ~0U) + { + size = (node->data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset; + if (size > len) + size = len; + + if (grub_disk_read (disk, node->data->root_sector, offset, size, buf)) + return -1; + + return size; + } +#endif + +#ifdef MODE_EXFAT + if (node->is_contiguous) + { + /* Read the data here. */ + sector = (node->data->cluster_sector + + ((node->file_cluster - 2) + << node->data->cluster_bits)); + + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + grub_disk_read (disk, sector + (offset >> GRUB_DISK_SECTOR_BITS), + offset & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); + disk->read_hook = 0; + if (grub_errno) + return -1; + + return len; + } +#endif + + /* Calculate the logical cluster number and offset. */ + logical_cluster_bits = (node->data->cluster_bits + + GRUB_DISK_SECTOR_BITS); + logical_cluster = offset >> logical_cluster_bits; + offset &= (1ULL << logical_cluster_bits) - 1; + + if (logical_cluster < node->cur_cluster_num) + { + node->cur_cluster_num = 0; + node->cur_cluster = node->file_cluster; + } + + while (len) + { + while (logical_cluster > node->cur_cluster_num) + { + /* Find next cluster. */ + grub_uint32_t next_cluster; + grub_uint32_t fat_offset; + + switch (node->data->fat_size) + { + case 32: + fat_offset = node->cur_cluster << 2; + break; + case 16: + fat_offset = node->cur_cluster << 1; + break; + default: + /* case 12: */ + fat_offset = node->cur_cluster + (node->cur_cluster >> 1); + break; + } + + /* Read the FAT. */ + if (grub_disk_read (disk, node->data->fat_sector, fat_offset, + (node->data->fat_size + 7) >> 3, + (char *) &next_cluster)) + return -1; + + next_cluster = grub_le_to_cpu32 (next_cluster); + switch (node->data->fat_size) + { + case 16: + next_cluster &= 0xFFFF; + break; + case 12: + if (node->cur_cluster & 1) + next_cluster >>= 4; + + next_cluster &= 0x0FFF; + break; + } + + grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n", + node->data->fat_size, next_cluster); + + /* Check the end. */ + if (next_cluster >= node->data->cluster_eof_mark) + return ret; + + if (next_cluster < 2 || next_cluster >= node->data->num_clusters) + { + grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u", + next_cluster); + return -1; + } + + node->cur_cluster = next_cluster; + node->cur_cluster_num++; + } + + /* Read the data here. */ + sector = (node->data->cluster_sector + + ((node->cur_cluster - 2) + << node->data->cluster_bits)); + size = (1 << logical_cluster_bits) - offset; + if (size > len) + size = len; + + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + grub_disk_read (disk, sector, offset, size, buf); + disk->read_hook = 0; + if (grub_errno) + return -1; + + len -= size; + buf += size; + ret += size; + logical_cluster++; + offset = 0; + } + + return ret; +} + +struct grub_fat_iterate_context +{ +#ifdef MODE_EXFAT + struct grub_fat_dir_node dir; + struct grub_fat_dir_entry entry; +#else + struct grub_fat_dir_entry dir; +#endif + char *filename; + grub_uint16_t *unibuf; + grub_ssize_t offset; +}; + +static grub_err_t +grub_fat_iterate_init (struct grub_fat_iterate_context *ctxt) +{ + ctxt->offset = -sizeof (struct grub_fat_dir_entry); + +#ifndef MODE_EXFAT + /* Allocate space enough to hold a long name. */ + ctxt->filename = grub_malloc (0x40 * 13 * GRUB_MAX_UTF8_PER_UTF16 + 1); + ctxt->unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2); +#else + ctxt->unibuf = grub_malloc (15 * 256 * 2); + ctxt->filename = grub_malloc (15 * 256 * GRUB_MAX_UTF8_PER_UTF16 + 1); +#endif + + if (! ctxt->filename || ! ctxt->unibuf) + { + grub_free (ctxt->filename); + grub_free (ctxt->unibuf); + return grub_errno; + } + return GRUB_ERR_NONE; +} + +static void +grub_fat_iterate_fini (struct grub_fat_iterate_context *ctxt) +{ + grub_free (ctxt->filename); + grub_free (ctxt->unibuf); +} + +#ifdef MODE_EXFAT +static grub_err_t +grub_fat_iterate_dir_next (grub_fshelp_node_t node, + struct grub_fat_iterate_context *ctxt) +{ + grub_memset (&ctxt->dir, 0, sizeof (ctxt->dir)); + while (1) + { + struct grub_fat_dir_entry *dir = &ctxt->entry; + + ctxt->offset += sizeof (*dir); + + if (grub_fat_read_data (node->disk, node, 0, 0, ctxt->offset, sizeof (*dir), + (char *) dir) + != sizeof (*dir)) + break; + + if (dir->entry_type == 0) + break; + if (!(dir->entry_type & 0x80)) + continue; + + if (dir->entry_type == 0x85) + { + unsigned i, nsec, slots = 0; + + nsec = dir->type_specific.file.secondary_count; + + ctxt->dir.attr = grub_cpu_to_le16 (dir->type_specific.file.attr); + ctxt->dir.have_stream = 0; + for (i = 0; i < nsec; i++) + { + struct grub_fat_dir_entry sec; + ctxt->offset += sizeof (sec); + if (grub_fat_read_data (node->disk, node, 0, 0, + ctxt->offset, sizeof (sec), (char *) &sec) + != sizeof (sec)) + break; + if (!(sec.entry_type & 0x80)) + continue; + if (!(sec.entry_type & 0x40)) + break; + switch (sec.entry_type) + { + case 0xc0: + ctxt->dir.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster); + ctxt->dir.valid_size + = grub_cpu_to_le64 (sec.type_specific.stream_extension.valid_size); + ctxt->dir.file_size + = grub_cpu_to_le64 (sec.type_specific.stream_extension.file_size); + ctxt->dir.have_stream = 1; + ctxt->dir.is_contiguous = !!(sec.type_specific.stream_extension.flags + & grub_cpu_to_le16_compile_time (FLAG_CONTIGUOUS)); + break; + case 0xc1: + { + int j; + for (j = 0; j < 15; j++) + ctxt->unibuf[slots * 15 + j] + = grub_le_to_cpu16 (sec.type_specific.file_name.str[j]); + slots++; + } + break; + default: + grub_dprintf ("exfat", "unknown secondary type 0x%02x\n", + sec.entry_type); + } + } + + if (i != nsec) + { + ctxt->offset -= sizeof (*dir); + continue; + } + + *grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename, ctxt->unibuf, + slots * 15) = '\0'; + + return 0; + } + /* Allocation bitmap. */ + if (dir->entry_type == 0x81) + continue; + /* Upcase table. */ + if (dir->entry_type == 0x82) + continue; + /* Volume label. */ + if (dir->entry_type == 0x83) + continue; + grub_dprintf ("exfat", "unknown primary type 0x%02x\n", + dir->entry_type); + } + return grub_errno ? : GRUB_ERR_EOF; +} + +/* + * Convert a timestamp in exFAT format to seconds since the UNIX epoch + * according to sections 7.4.8 and 7.4.9 in the exFAT specification. + * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification + */ +static int +grub_exfat_timestamp (grub_uint32_t field, grub_uint8_t msec, grub_int64_t *nix) { + struct grub_datetime datetime = { + .year = (field >> 25) + 1980, + .month = (field & 0x01E00000) >> 21, + .day = (field & 0x001F0000) >> 16, + .hour = (field & 0x0000F800) >> 11, + .minute = (field & 0x000007E0) >> 5, + .second = (field & 0x0000001F) * 2 + (msec >= 100 ? 1 : 0), + }; + + /* The conversion below allows seconds=60, so don't trust its validation. */ + if ((field & 0x1F) > 29) + return 0; + + /* Validate the 10-msec field even though it is rounded down to seconds. */ + if (msec > 199) + return 0; + + return grub_datetime2unixtime (&datetime, nix); +} + +#else + +static grub_err_t +grub_fat_iterate_dir_next (grub_fshelp_node_t node, + struct grub_fat_iterate_context *ctxt) +{ + char *filep = 0; + int checksum = -1; + int slot = -1, slots = -1; + + while (1) + { + unsigned i; + + /* Adjust the offset. */ + ctxt->offset += sizeof (ctxt->dir); + + /* Read a directory entry. */ + if (grub_fat_read_data (node->disk, node, 0, 0, + ctxt->offset, sizeof (ctxt->dir), + (char *) &ctxt->dir) + != sizeof (ctxt->dir) || ctxt->dir.name[0] == 0) + break; + + /* Handle long name entries. */ + if (ctxt->dir.attr == GRUB_FAT_ATTR_LONG_NAME) + { + struct grub_fat_long_name_entry *long_name + = (struct grub_fat_long_name_entry *) &ctxt->dir; + grub_uint8_t id = long_name->id; + + if (id & 0x40) + { + id &= 0x3f; + slots = slot = id; + checksum = long_name->checksum; + } + + if (id != slot || slot == 0 || checksum != long_name->checksum) + { + checksum = -1; + continue; + } + + slot--; + grub_memcpy (ctxt->unibuf + slot * 13, long_name->name1, 5 * 2); + grub_memcpy (ctxt->unibuf + slot * 13 + 5, long_name->name2, 6 * 2); + grub_memcpy (ctxt->unibuf + slot * 13 + 11, long_name->name3, 2 * 2); + continue; + } + + /* Check if this entry is valid. */ + if (ctxt->dir.name[0] == 0xe5 || (ctxt->dir.attr & ~GRUB_FAT_ATTR_VALID)) + continue; + + /* This is a workaround for Japanese. */ + if (ctxt->dir.name[0] == 0x05) + ctxt->dir.name[0] = 0xe5; + + if (checksum != -1 && slot == 0) + { + grub_uint8_t sum; + + for (sum = 0, i = 0; i < sizeof (ctxt->dir.name); i++) + sum = ((sum >> 1) | (sum << 7)) + ctxt->dir.name[i]; + + if (sum == checksum) + { + int u; + + for (u = 0; u < slots * 13; u++) + ctxt->unibuf[u] = grub_le_to_cpu16 (ctxt->unibuf[u]); + + *grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename, + ctxt->unibuf, + slots * 13) = '\0'; + + return GRUB_ERR_NONE; + } + + checksum = -1; + } + + /* Convert the 8.3 file name. */ + filep = ctxt->filename; + if (ctxt->dir.attr & GRUB_FAT_ATTR_VOLUME_ID) + { + for (i = 0; i < sizeof (ctxt->dir.name) && ctxt->dir.name[i]; i++) + *filep++ = ctxt->dir.name[i]; + while (i > 0 && ctxt->dir.name[i - 1] == ' ') + { + filep--; + i--; + } + } + else + { + for (i = 0; i < 8 && ctxt->dir.name[i]; i++) + *filep++ = grub_tolower (ctxt->dir.name[i]); + while (i > 0 && ctxt->dir.name[i - 1] == ' ') + { + filep--; + i--; + } + + /* XXX should we check that dir position is 0 or 1? */ + if (i > 2 || filep[0] != '.' || (i == 2 && filep[1] != '.')) + *filep++ = '.'; + + for (i = 8; i < 11 && ctxt->dir.name[i]; i++) + *filep++ = grub_tolower (ctxt->dir.name[i]); + while (i > 8 && ctxt->dir.name[i - 1] == ' ') + { + filep--; + i--; + } + + if (i == 8) + filep--; + } + *filep = '\0'; + return GRUB_ERR_NONE; + } + + return grub_errno ? : GRUB_ERR_EOF; +} + +/* + * Convert a date and time in FAT format to seconds since the UNIX epoch + * according to sections 11.3.5 and 11.3.6 in ECMA-107. + * https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf + */ +static int +grub_fat_timestamp (grub_uint16_t time, grub_uint16_t date, grub_int64_t *nix) { + struct grub_datetime datetime = { + .year = (date >> 9) + 1980, + .month = (date & 0x01E0) >> 5, + .day = (date & 0x001F), + .hour = (time >> 11), + .minute = (time & 0x07E0) >> 5, + .second = (time & 0x001F) * 2, + }; + + /* The conversion below allows seconds=60, so don't trust its validation. */ + if ((time & 0x1F) > 29) + return 0; + + return grub_datetime2unixtime (&datetime, nix); +} + +#endif + +static grub_err_t lookup_file (grub_fshelp_node_t node, + const char *name, + grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype) +{ + grub_err_t err; + struct grub_fat_iterate_context ctxt; + + err = grub_fat_iterate_init (&ctxt); + if (err) + return err; + + while (!(err = grub_fat_iterate_dir_next (node, &ctxt))) + { + +#ifdef MODE_EXFAT + if (!ctxt.dir.have_stream) + continue; +#else + if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID) + continue; +#endif + + if (grub_strcasecmp (name, ctxt.filename) == 0) + { + *foundnode = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!*foundnode) + return grub_errno; + (*foundnode)->attr = ctxt.dir.attr; +#ifdef MODE_EXFAT + (*foundnode)->file_size = ctxt.dir.file_size; + (*foundnode)->file_cluster = ctxt.dir.first_cluster; + (*foundnode)->is_contiguous = ctxt.dir.is_contiguous; +#else + (*foundnode)->file_size = grub_le_to_cpu32 (ctxt.dir.file_size); + (*foundnode)->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16) + | grub_le_to_cpu16 (ctxt.dir.first_cluster_low)); + /* If directory points to root, starting cluster is 0 */ + if (!(*foundnode)->file_cluster) + (*foundnode)->file_cluster = node->data->root_cluster; +#endif + (*foundnode)->cur_cluster_num = ~0U; + (*foundnode)->data = node->data; + (*foundnode)->disk = node->disk; + + *foundtype = ((*foundnode)->attr & GRUB_FAT_ATTR_DIRECTORY) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG; + + grub_fat_iterate_fini (&ctxt); + return GRUB_ERR_NONE; + } + } + + grub_fat_iterate_fini (&ctxt); + if (err == GRUB_ERR_EOF) + err = 0; + + return err; + +} + +static grub_err_t +grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, + void *hook_data) +{ + struct grub_fat_data *data = 0; + grub_disk_t disk = device->disk; + grub_fshelp_node_t found = NULL; + grub_err_t err; + struct grub_fat_iterate_context ctxt; + + grub_dl_ref (my_mod); + + data = grub_fat_mount (disk); + if (! data) + goto fail; + + struct grub_fshelp_node root = { + .data = data, + .disk = disk, + .attr = GRUB_FAT_ATTR_DIRECTORY, + .file_size = 0, + .file_cluster = data->root_cluster, + .cur_cluster_num = ~0U, + .cur_cluster = 0, +#ifdef MODE_EXFAT + .is_contiguous = 0, +#endif + }; + + err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, NULL, GRUB_FSHELP_DIR); + if (err) + goto fail; + + err = grub_fat_iterate_init (&ctxt); + if (err) + goto fail; + + while (!(err = grub_fat_iterate_dir_next (found, &ctxt))) + { + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + + info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY); + info.case_insensitive = 1; +#ifdef MODE_EXFAT + if (!ctxt.dir.have_stream) + continue; + info.mtimeset = grub_exfat_timestamp (grub_le_to_cpu32 (ctxt.entry.type_specific.file.m_time), + ctxt.entry.type_specific.file.m_time_tenth, + &info.mtime); +#else + if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID) + continue; + info.mtimeset = grub_fat_timestamp (grub_le_to_cpu16 (ctxt.dir.w_time), + grub_le_to_cpu16 (ctxt.dir.w_date), + &info.mtime); +#endif + + if (hook (ctxt.filename, &info, hook_data)) + break; + } + grub_fat_iterate_fini (&ctxt); + if (err == GRUB_ERR_EOF) + err = 0; + + fail: + if (found != &root) + grub_free (found); + + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_fat_open (grub_file_t file, const char *name) +{ + struct grub_fat_data *data = 0; + grub_fshelp_node_t found = NULL; + grub_err_t err; + grub_disk_t disk = file->device->disk; + + grub_dl_ref (my_mod); + + data = grub_fat_mount (disk); + if (! data) + goto fail; + + struct grub_fshelp_node root = { + .data = data, + .disk = disk, + .attr = GRUB_FAT_ATTR_DIRECTORY, + .file_size = 0, + .file_cluster = data->root_cluster, + .cur_cluster_num = ~0U, + .cur_cluster = 0, +#ifdef MODE_EXFAT + .is_contiguous = 0, +#endif + }; + + err = grub_fshelp_find_file_lookup (name, &root, &found, lookup_file, NULL, GRUB_FSHELP_REG); + if (err) + goto fail; + + file->data = found; + file->size = found->file_size; + + return GRUB_ERR_NONE; + + fail: + + if (found != &root) + grub_free (found); + + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_ssize_t +grub_fat_read (grub_file_t file, char *buf, grub_size_t len) +{ + return grub_fat_read_data (file->device->disk, file->data, + file->read_hook, file->read_hook_data, + file->offset, len, buf); +} + +static grub_err_t +grub_fat_close (grub_file_t file) +{ + grub_fshelp_node_t node = file->data; + + grub_free (node->data); + grub_free (node); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +#ifdef MODE_EXFAT +static grub_err_t +grub_fat_label (grub_device_t device, char **label) +{ + struct grub_fat_dir_entry dir; + grub_ssize_t offset = -sizeof(dir); + grub_disk_t disk = device->disk; + struct grub_fshelp_node root = { + .disk = disk, + .attr = GRUB_FAT_ATTR_DIRECTORY, + .file_size = 0, + .cur_cluster_num = ~0U, + .cur_cluster = 0, + .is_contiguous = 0, + }; + + root.data = grub_fat_mount (disk); + if (! root.data) + return grub_errno; + + root.file_cluster = root.data->root_cluster; + + *label = NULL; + + while (1) + { + offset += sizeof (dir); + + if (grub_fat_read_data (disk, &root, 0, 0, + offset, sizeof (dir), (char *) &dir) + != sizeof (dir)) + break; + + if (dir.entry_type == 0) + break; + if (!(dir.entry_type & 0x80)) + continue; + + /* Volume label. */ + if (dir.entry_type == 0x83) + { + grub_size_t chc; + grub_uint16_t t[ARRAY_SIZE (dir.type_specific.volume_label.str)]; + grub_size_t i; + *label = grub_malloc (ARRAY_SIZE (dir.type_specific.volume_label.str) + * GRUB_MAX_UTF8_PER_UTF16 + 1); + if (!*label) + { + grub_free (root.data); + return grub_errno; + } + chc = dir.type_specific.volume_label.character_count; + if (chc > ARRAY_SIZE (dir.type_specific.volume_label.str)) + chc = ARRAY_SIZE (dir.type_specific.volume_label.str); + for (i = 0; i < chc; i++) + t[i] = grub_le_to_cpu16 (dir.type_specific.volume_label.str[i]); + *grub_utf16_to_utf8 ((grub_uint8_t *) *label, t, chc) = '\0'; + } + } + + grub_free (root.data); + return grub_errno; +} + +#else + +static grub_err_t +grub_fat_label (grub_device_t device, char **label) +{ + grub_disk_t disk = device->disk; + grub_err_t err; + struct grub_fat_iterate_context ctxt; + struct grub_fshelp_node root = { + .disk = disk, + .attr = GRUB_FAT_ATTR_DIRECTORY, + .file_size = 0, + .cur_cluster_num = ~0U, + .cur_cluster = 0, + }; + + *label = 0; + + grub_dl_ref (my_mod); + + root.data = grub_fat_mount (disk); + if (! root.data) + goto fail; + + root.file_cluster = root.data->root_cluster; + + err = grub_fat_iterate_init (&ctxt); + if (err) + goto fail; + + while (!(err = grub_fat_iterate_dir_next (&root, &ctxt))) + if ((ctxt.dir.attr & ~GRUB_FAT_ATTR_ARCHIVE) == GRUB_FAT_ATTR_VOLUME_ID) + { + *label = grub_strdup (ctxt.filename); + break; + } + + grub_fat_iterate_fini (&ctxt); + + fail: + + grub_dl_unref (my_mod); + + grub_free (root.data); + + return grub_errno; +} + +#endif + +static grub_err_t +grub_fat_uuid (grub_device_t device, char **uuid) +{ + struct grub_fat_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_fat_mount (disk); + if (data) + { + char *ptr; + *uuid = grub_xasprintf ("%04x-%04x", + (grub_uint16_t) (data->uuid >> 16), + (grub_uint16_t) data->uuid); + for (ptr = *uuid; ptr && *ptr; ptr++) + *ptr = grub_toupper (*ptr); + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +#ifdef GRUB_UTIL +#ifndef MODE_EXFAT +grub_disk_addr_t +grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +#else +grub_disk_addr_t + grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +#endif +{ + grub_disk_addr_t ret; + struct grub_fat_data *data; + data = grub_fat_mount (disk); + if (!data) + return 0; + ret = data->cluster_sector; + + *sec_per_lcn = 1ULL << data->cluster_bits; + + grub_free (data); + return ret; +} +#endif + +static struct grub_fs grub_fat_fs = + { +#ifdef MODE_EXFAT + .name = "exfat", +#else + .name = "fat", +#endif + .fs_dir = grub_fat_dir, + .fs_open = grub_fat_open, + .fs_read = grub_fat_read, + .fs_close = grub_fat_close, + .fs_label = grub_fat_label, + .fs_uuid = grub_fat_uuid, +#ifdef GRUB_UTIL +#ifdef MODE_EXFAT + /* ExFAT BPB is 30 larger than FAT32 one. */ + .reserved_first_sector = 0, +#else + .reserved_first_sector = 1, +#endif + .blocklist_install = 1, +#endif + .next = 0 + }; + +#ifdef MODE_EXFAT +GRUB_MOD_INIT(exfat) +#else +GRUB_MOD_INIT(fat) +#endif +{ + COMPILE_TIME_ASSERT (sizeof (struct grub_fat_dir_entry) == 32); + grub_fat_fs.mod = mod; + grub_fs_register (&grub_fat_fs); + my_mod = mod; +} +#ifdef MODE_EXFAT +GRUB_MOD_FINI(exfat) +#else +GRUB_MOD_FINI(fat) +#endif +{ + grub_fs_unregister (&grub_fat_fs); +} + diff --git a/grub-core/fs/fshelp.c b/grub-core/fs/fshelp.c new file mode 100644 index 000000000..cb41934b4 --- /dev/null +++ b/grub-core/fs/fshelp.c @@ -0,0 +1,441 @@ +/* fshelp.c -- Filesystem helper functions */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef int (*iterate_dir_func) (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, + void *data); +typedef grub_err_t (*lookup_file_func) (grub_fshelp_node_t dir, + const char *name, + grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype); +typedef char *(*read_symlink_func) (grub_fshelp_node_t node); + +struct stack_element { + struct stack_element *parent; + grub_fshelp_node_t node; + enum grub_fshelp_filetype type; +}; + +/* Context for grub_fshelp_find_file. */ +struct grub_fshelp_find_file_ctx +{ + /* Inputs. */ + const char *path; + grub_fshelp_node_t rootnode; + + /* Global options. */ + int symlinknest; + + /* Current file being traversed and its parents. */ + struct stack_element *currnode; +}; + +/* Helper for find_file_iter. */ +static void +free_node (grub_fshelp_node_t node, struct grub_fshelp_find_file_ctx *ctx) +{ + if (node != ctx->rootnode) + grub_free (node); +} + +static void +pop_element (struct grub_fshelp_find_file_ctx *ctx) +{ + struct stack_element *el; + el = ctx->currnode; + ctx->currnode = el->parent; + free_node (el->node, ctx); + grub_free (el); +} + +static void +free_stack (struct grub_fshelp_find_file_ctx *ctx) +{ + while (ctx->currnode) + pop_element (ctx); +} + +static void +go_up_a_level (struct grub_fshelp_find_file_ctx *ctx) +{ + if (!ctx->currnode->parent) + return; + pop_element (ctx); +} + +static grub_err_t +push_node (struct grub_fshelp_find_file_ctx *ctx, grub_fshelp_node_t node, enum grub_fshelp_filetype filetype) +{ + struct stack_element *nst; + nst = grub_malloc (sizeof (*nst)); + if (!nst) + return grub_errno; + nst->node = node; + nst->type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE; + nst->parent = ctx->currnode; + ctx->currnode = nst; + return GRUB_ERR_NONE; +} + +static grub_err_t +go_to_root (struct grub_fshelp_find_file_ctx *ctx) +{ + free_stack (ctx); + return push_node (ctx, ctx->rootnode, GRUB_FSHELP_DIR); +} + +struct grub_fshelp_find_file_iter_ctx +{ + const char *name; + grub_fshelp_node_t *foundnode; + enum grub_fshelp_filetype *foundtype; +}; + +/* Helper for grub_fshelp_find_file. */ +static int +find_file_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_fshelp_find_file_iter_ctx *ctx = data; + + if (filetype == GRUB_FSHELP_UNKNOWN || + ((filetype & GRUB_FSHELP_CASE_INSENSITIVE) + ? grub_strcasecmp (ctx->name, filename) + : grub_strcmp (ctx->name, filename))) + { + grub_free (node); + return 0; + } + + /* The node is found, stop iterating over the nodes. */ + *ctx->foundnode = node; + *ctx->foundtype = filetype; + return 1; +} + +static grub_err_t +directory_find_file (grub_fshelp_node_t node, const char *name, grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype, iterate_dir_func iterate_dir) +{ + int found; + struct grub_fshelp_find_file_iter_ctx ctx = { + .foundnode = foundnode, + .foundtype = foundtype, + .name = name + }; + found = iterate_dir (node, find_file_iter, &ctx); + if (! found) + { + if (grub_errno) + return grub_errno; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +find_file (char *currpath, + iterate_dir_func iterate_dir, lookup_file_func lookup_file, + read_symlink_func read_symlink, + struct grub_fshelp_find_file_ctx *ctx) +{ + char *name, *next; + grub_err_t err; + for (name = currpath; ; name = next) + { + char c; + grub_fshelp_node_t foundnode = NULL; + enum grub_fshelp_filetype foundtype = 0; + + /* Remove all leading slashes. */ + while (*name == '/') + name++; + + /* Found the node! */ + if (! *name) + return 0; + + /* Extract the actual part from the pathname. */ + for (next = name; *next && *next != '/'; next++); + + /* At this point it is expected that the current node is a + directory, check if this is true. */ + if (ctx->currnode->type != GRUB_FSHELP_DIR) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + + /* Don't rely on fs providing actual . in the listing. */ + if (next - name == 1 && name[0] == '.') + continue; + + /* Don't rely on fs providing actual .. in the listing. */ + if (next - name == 2 && name[0] == '.' && name[1] == '.') + { + go_up_a_level (ctx); + continue; + } + + /* Iterate over the directory. */ + c = *next; + *next = '\0'; + if (lookup_file) + err = lookup_file (ctx->currnode->node, name, &foundnode, &foundtype); + else + err = directory_find_file (ctx->currnode->node, name, &foundnode, &foundtype, iterate_dir); + *next = c; + + if (err) + return err; + + if (!foundnode) + break; + + push_node (ctx, foundnode, foundtype); + + /* Read in the symlink and follow it. */ + if (ctx->currnode->type == GRUB_FSHELP_SYMLINK) + { + char *symlink; + + /* Test if the symlink does not loop. */ + if (++ctx->symlinknest == 8) + return grub_error (GRUB_ERR_SYMLINK_LOOP, + N_("too deep nesting of symlinks")); + + symlink = read_symlink (ctx->currnode->node); + + if (!symlink) + return grub_errno; + + /* The symlink is an absolute path, go back to the root inode. */ + if (symlink[0] == '/') + { + err = go_to_root (ctx); + if (err) + return err; + } + else + { + /* Get from symlink to containing directory. */ + go_up_a_level (ctx); + } + + + /* Lookup the node the symlink points to. */ + find_file (symlink, iterate_dir, lookup_file, read_symlink, ctx); + grub_free (symlink); + + if (grub_errno) + return grub_errno; + } + } + + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + ctx->path); +} + +static grub_err_t +grub_fshelp_find_file_real (const char *path, grub_fshelp_node_t rootnode, + grub_fshelp_node_t *foundnode, + iterate_dir_func iterate_dir, + lookup_file_func lookup_file, + read_symlink_func read_symlink, + enum grub_fshelp_filetype expecttype) +{ + struct grub_fshelp_find_file_ctx ctx = { + .path = path, + .rootnode = rootnode, + .symlinknest = 0, + .currnode = 0 + }; + grub_err_t err; + enum grub_fshelp_filetype foundtype; + char *duppath; + + if (!path || path[0] != '/') + { + return grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path); + } + + err = go_to_root (&ctx); + if (err) + return err; + + duppath = grub_strdup (path); + if (!duppath) + return grub_errno; + err = find_file (duppath, iterate_dir, lookup_file, read_symlink, &ctx); + grub_free (duppath); + if (err) + { + free_stack (&ctx); + return err; + } + + *foundnode = ctx.currnode->node; + foundtype = ctx.currnode->type; + /* Avoid the node being freed. */ + ctx.currnode->node = 0; + free_stack (&ctx); + + /* Check if the node that was found was of the expected type. */ + if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); + else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + + return 0; +} + +/* Lookup the node PATH. The node ROOTNODE describes the root of the + directory tree. The node found is returned in FOUNDNODE, which is + either a ROOTNODE or a new malloc'ed node. ITERATE_DIR is used to + iterate over all directory entries in the current node. + READ_SYMLINK is used to read the symlink if a node is a symlink. + EXPECTTYPE is the type node that is expected by the called, an + error is generated if the node is not of the expected type. */ +grub_err_t +grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode, + grub_fshelp_node_t *foundnode, + iterate_dir_func iterate_dir, + read_symlink_func read_symlink, + enum grub_fshelp_filetype expecttype) +{ + return grub_fshelp_find_file_real (path, rootnode, foundnode, + iterate_dir, NULL, + read_symlink, expecttype); + +} + +grub_err_t +grub_fshelp_find_file_lookup (const char *path, grub_fshelp_node_t rootnode, + grub_fshelp_node_t *foundnode, + lookup_file_func lookup_file, + read_symlink_func read_symlink, + enum grub_fshelp_filetype expecttype) +{ + return grub_fshelp_find_file_real (path, rootnode, foundnode, + NULL, lookup_file, + read_symlink, expecttype); + +} + +/* Read LEN bytes from the file NODE on disk DISK into the buffer BUF, + beginning with the block POS. READ_HOOK should be set before + reading a block from the file. READ_HOOK_DATA is passed through as + the DATA argument to READ_HOOK. GET_BLOCK is used to translate + file blocks to disk blocks. The file is FILESIZE bytes big and the + blocks have a size of LOG2BLOCKSIZE (in log2). */ +grub_ssize_t +grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf, + grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, + grub_disk_addr_t block), + grub_off_t filesize, int log2blocksize, + grub_disk_addr_t blocks_start) +{ + grub_disk_addr_t i, blockcnt; + int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS); + + /* + * Catch blatantly invalid log2blocksize. We could be a lot stricter, but + * this is the most permissive we can be before we start to see integer + * overflow/underflow issues. + */ + if (log2blocksize + GRUB_DISK_SECTOR_BITS >= 31) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("blocksize too large")); + return -1; + } + + if (pos > filesize) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read past the end of file")); + return -1; + } + + /* Adjust LEN so it we can't read past the end of the file. */ + if (pos + len > filesize) + len = filesize - pos; + + blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS); + + for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++) + { + grub_disk_addr_t blknr; + int blockoff = pos & (blocksize - 1); + int blockend = blocksize; + + int skipfirst = 0; + + blknr = get_block (node, i); + if (grub_errno) + return -1; + + blknr = blknr << log2blocksize; + + /* Last block. */ + if (i == blockcnt - 1) + { + blockend = (len + pos) & (blocksize - 1); + + /* The last portion is exactly blocksize. */ + if (! blockend) + blockend = blocksize; + } + + /* First block. */ + if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS))) + { + skipfirst = blockoff; + blockend -= skipfirst; + } + + /* If the block number is 0 this block is not stored on disk but + is zero filled instead. */ + if (blknr) + { + disk->read_hook = read_hook; + disk->read_hook_data = read_hook_data; + + grub_disk_read (disk, blknr + blocks_start, skipfirst, + blockend, buf); + disk->read_hook = 0; + if (grub_errno) + return -1; + } + else + grub_memset (buf, 0, blockend); + + buf += blocksize - skipfirst; + } + + return len; +} diff --git a/fs/hfs.c b/grub-core/fs/hfs.c similarity index 54% rename from fs/hfs.c rename to grub-core/fs/hfs.c index cef856326..ce7581dd5 100644 --- a/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_HFS_SBLOCK 2 #define GRUB_HFS_EMBED_HFSPLUS_SIG 0x482B @@ -62,7 +67,7 @@ struct grub_hfs_node grub_uint8_t level; grub_uint16_t reccnt; grub_uint16_t unused; -} __attribute__ ((packed)); +} GRUB_PACKED; /* The head of the B*-Tree. */ struct grub_hfs_treeheader @@ -78,7 +83,7 @@ struct grub_hfs_treeheader grub_uint32_t nodes; grub_uint32_t free_nodes; grub_uint8_t unused[76]; -} __attribute__ ((packed)); +} GRUB_PACKED; /* The state of a mounted HFS filesystem. */ struct grub_hfs_data @@ -109,7 +114,7 @@ struct grub_hfs_catalog_key /* Filename. */ grub_uint8_t str[31]; -} __attribute__ ((packed)); +} GRUB_PACKED; /* The key as used on disk in a extent overflow tree. Using this key the extents can be looked up using a fileid and logical start block @@ -122,7 +127,7 @@ struct grub_hfs_extent_key grub_uint8_t forktype; grub_uint32_t fileid; grub_uint16_t first_block; -} __attribute__ ((packed)); +} GRUB_PACKED; /* A directory record. This is used to find out the directory ID. */ struct grub_hfs_dirrec @@ -131,7 +136,9 @@ struct grub_hfs_dirrec grub_uint8_t type; grub_uint8_t unused[5]; grub_uint32_t dirid; -} __attribute__ ((packed)); + grub_uint32_t ctime; + grub_uint32_t mtime; +} GRUB_PACKED; /* Information about a file. */ struct grub_hfs_filerec @@ -142,27 +149,29 @@ struct grub_hfs_filerec grub_uint32_t fileid; grub_uint8_t unused2[2]; grub_uint32_t size; - grub_uint8_t unused3[44]; + grub_uint8_t unused3[18]; + grub_uint32_t mtime; + grub_uint8_t unused4[22]; /* The first 3 extents of the file. The other extents can be found in the extent overflow file. */ grub_hfs_datarecord_t extents; -} __attribute__ ((packed)); +} GRUB_PACKED; /* A record descriptor, both key and data, used to pass to call back functions. */ struct grub_hfs_record { void *key; - int keylen; + grub_size_t keylen; void *data; - int datalen; + grub_size_t datalen; }; static grub_dl_t my_mod; static int grub_hfs_find_node (struct grub_hfs_data *, char *, - grub_uint32_t, int, char *, int); + grub_uint32_t, int, char *, grub_size_t); /* Find block BLOCK of the file FILE in the mounted UFS filesystem DATA. The first 3 extents are described by DAT. If cache is set, @@ -236,24 +245,26 @@ grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat, POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_hfs_read_file (struct grub_hfs_data *data, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_uint32_t pos, grub_size_t len, char *buf) { - int i; - int blockcnt; + grub_off_t i; + grub_off_t blockcnt; - blockcnt = ((len + pos) - + data->blksz - 1) / data->blksz; + /* Files are at most 2G/4G - 1 bytes on hfs. Avoid 64-bit division. + Moreover len > 0 as checked in upper layer. */ + blockcnt = (len + pos - 1) / data->blksz + 1; for (i = pos / data->blksz; i < blockcnt; i++) { - int blknr; - int blockoff = pos % data->blksz; - int blockend = data->blksz; + grub_disk_addr_t blknr; + grub_off_t blockoff; + grub_off_t blockend = data->blksz; int skipfirst = 0; + blockoff = pos % data->blksz; + blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1); if (grub_errno) return -1; @@ -280,6 +291,7 @@ grub_hfs_read_file (struct grub_hfs_data *data, if (blknr) { data->disk->read_hook = read_hook; + data->disk->read_hook_data = read_hook_data; grub_disk_read (data->disk, blknr, skipfirst, blockend, buf); data->disk->read_hook = 0; @@ -319,7 +331,9 @@ grub_hfs_mount (grub_disk_t disk) goto fail; /* Check if this is a HFS filesystem. */ - if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC) + if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC + || data->sblock.blksz == 0 + || (data->sblock.blksz & grub_cpu_to_be32_compile_time (0xc00001ff))) { grub_error (GRUB_ERR_BAD_FS, "not an HFS filesystem"); goto fail; @@ -356,11 +370,16 @@ grub_hfs_mount (grub_disk_t disk) data->cat_root = grub_be_to_cpu32 (treehead.head.root_node); data->cat_size = grub_be_to_cpu16 (treehead.head.node_size); + if (data->cat_size == 0 + || data->blksz < data->cat_size + || data->blksz < data->ext_size) + goto fail; + /* Lookup the root directory node in the catalog tree using the volume name. */ - key.parent_dir = grub_cpu_to_be32 (1); + key.parent_dir = grub_cpu_to_be32_compile_time (1); key.strlen = data->sblock.volname[0]; - grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1)); + grub_strlcpy ((char *) key.str, (char *) (data->sblock.volname + 1), sizeof (key.str)); if (grub_hfs_find_node (data, (char *) &key, data->cat_root, 0, (char *) &dir, sizeof (dir)) == 0) @@ -386,8 +405,8 @@ grub_hfs_mount (grub_disk_t disk) /* Compare the K1 and K2 catalog file keys using HFS character ordering. */ static int -grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1, - struct grub_hfs_catalog_key *k2) +grub_hfs_cmp_catkeys (const struct grub_hfs_catalog_key *k1, + const struct grub_hfs_catalog_key *k2) { /* Taken from hfsutils 3.2.6 and converted to a readable form */ static const unsigned char hfs_charorder[256] = { @@ -630,8 +649,8 @@ grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1, /* Compare the K1 and K2 extent overflow file keys. */ static int -grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1, - struct grub_hfs_extent_key *k2) +grub_hfs_cmp_extkeys (const struct grub_hfs_extent_key *k1, + const struct grub_hfs_extent_key *k2) { int cmp = k1->forktype - k2->forktype; if (cmp == 0) @@ -650,22 +669,32 @@ grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1, static grub_err_t grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx, int this, int (*node_hook) (struct grub_hfs_node *hnd, - struct grub_hfs_record *)) + struct grub_hfs_record *, + void *hook_arg), + void *hook_arg) { - int nodesize = type == 0 ? data->cat_size : data->ext_size; + grub_size_t nodesize = type == 0 ? data->cat_size : data->ext_size; - union + union node_union { struct grub_hfs_node node; - char rawnode[nodesize]; - grub_uint16_t offsets[nodesize / 2]; - } node; + char rawnode[0]; + grub_uint16_t offsets[0]; + } *node; + + if (nodesize < sizeof (struct grub_hfs_node)) + nodesize = sizeof (struct grub_hfs_node); + + node = grub_malloc (nodesize); + if (!node) + return grub_errno; do { int i; struct grub_hfs_extent *dat; int blk; + grub_uint16_t reccnt; dat = (struct grub_hfs_extent *) (type == 0 ? (&data->sblock.catalog_recs) @@ -676,40 +705,101 @@ grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx, (type == 0) ? GRUB_HFS_CNID_CAT : GRUB_HFS_CNID_EXT, idx / (data->blksz / nodesize), 0); blk += (idx % (data->blksz / nodesize)); - if (grub_errno) - return grub_errno; - if (grub_disk_read (data->disk, blk, 0, - sizeof (node), &node)) - return grub_errno; + if (grub_errno || grub_disk_read (data->disk, blk, 0, + nodesize, node)) + { + grub_free (node); + return grub_errno; + } + + reccnt = grub_be_to_cpu16 (node->node.reccnt); + if (reccnt > (nodesize >> 1)) + reccnt = (nodesize >> 1); /* Iterate over all records in this node. */ - for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++) + for (i = 0; i < reccnt; i++) { int pos = (nodesize >> 1) - 1 - i; struct pointer { grub_uint8_t keylen; grub_uint8_t key; - } __attribute__ ((packed)) *pnt; - pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos]) - + node.rawnode); + } GRUB_PACKED *pnt; + grub_uint16_t off = grub_be_to_cpu16 (node->offsets[pos]); + if (off > nodesize - sizeof(*pnt)) + continue; + pnt = (struct pointer *) (off + node->rawnode); + if (nodesize < (grub_size_t) off + pnt->keylen + 1) + continue; struct grub_hfs_record rec = { &pnt->key, pnt->keylen, &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2, - nodesize - grub_be_to_cpu16 (node.offsets[pos]) - - pnt->keylen - 1 + nodesize - off - pnt->keylen - 1 }; - if (node_hook (&node.node, &rec)) - return 0; + if (node_hook (&node->node, &rec, hook_arg)) + { + grub_free (node); + return 0; + } } - idx = grub_be_to_cpu32 (node.node.next); + idx = grub_be_to_cpu32 (node->node.next); } while (idx && this); + grub_free (node); + return 0; +} + +struct grub_hfs_find_node_node_found_ctx +{ + int found; + int isleaf; + int done; + int type; + const char *key; + char *datar; + grub_size_t datalen; +}; + +static int +grub_hfs_find_node_node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec, + void *hook_arg) +{ + struct grub_hfs_find_node_node_found_ctx *ctx = hook_arg; + int cmp = 1; + + if (ctx->type == 0) + cmp = grub_hfs_cmp_catkeys (rec->key, (const void *) ctx->key); + else + cmp = grub_hfs_cmp_extkeys (rec->key, (const void *) ctx->key); + + /* If the key is smaller or equal to the current node, mark the + entry. In case of a non-leaf mode it will be used to lookup + the rest of the tree. */ + if (cmp <= 0) + ctx->found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data)); + else /* The key can not be found in the tree. */ + return 1; + + /* Check if this node is a leaf node. */ + if (hnd->type == GRUB_HFS_NODE_LEAF) + { + ctx->isleaf = 1; + + /* Found it!!!! */ + if (cmp == 0) + { + ctx->done = 1; + + grub_memcpy (ctx->datar, rec->data, + rec->datalen < ctx->datalen ? rec->datalen : ctx->datalen); + return 1; + } + } return 0; } @@ -721,67 +811,84 @@ grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx, the data in DATAR with a maximum length of DATALEN. */ static int grub_hfs_find_node (struct grub_hfs_data *data, char *key, - grub_uint32_t idx, int type, char *datar, int datalen) + grub_uint32_t idx, int type, char *datar, grub_size_t datalen) { - int found = -1; - int isleaf = 0; - int done = 0; - - auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *); - - int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec) + struct grub_hfs_find_node_node_found_ctx ctx = { - int cmp = 1; - - if (type == 0) - cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key); - else - cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key); - - /* If the key is smaller or equal to the current node, mark the - entry. In case of a non-leaf mode it will be used to lookup - the rest of the tree. */ - if (cmp <= 0) - { - grub_uint32_t *node = (grub_uint32_t *) rec->data; - found = grub_be_to_cpu32 (*node); - } - else /* The key can not be found in the tree. */ - return 1; - - /* Check if this node is a leaf node. */ - if (hnd->type == GRUB_HFS_NODE_LEAF) - { - isleaf = 1; - - /* Found it!!!! */ - if (cmp == 0) - { - done = 1; - - grub_memcpy (datar, rec->data, - rec->datalen < datalen ? rec->datalen : datalen); - return 1; - } - } - - return 0; - } + .found = -1, + .isleaf = 0, + .done = 0, + .type = type, + .key = key, + .datar = datar, + .datalen = datalen + }; do { - found = -1; + ctx.found = -1; - if (grub_hfs_iterate_records (data, type, idx, 0, node_found)) + if (grub_hfs_iterate_records (data, type, idx, 0, grub_hfs_find_node_node_found, &ctx)) return 0; - if (found == -1) + if (ctx.found == -1) return 0; - idx = found; - } while (! isleaf); + idx = ctx.found; + } while (! ctx.isleaf); - return done; + return ctx.done; +} + +struct grub_hfs_iterate_dir_node_found_ctx +{ + grub_uint32_t dir_be; + int found; + int isleaf; + grub_uint32_t next; + int (*hook) (struct grub_hfs_record *, void *hook_arg); + void *hook_arg; +}; + +static int +grub_hfs_iterate_dir_node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec, + void *hook_arg) +{ + struct grub_hfs_iterate_dir_node_found_ctx *ctx = hook_arg; + struct grub_hfs_catalog_key *ckey = rec->key; + + /* The lowest key possible with DIR as root directory. */ + const struct grub_hfs_catalog_key key = {0, ctx->dir_be, 0, ""}; + + if (grub_hfs_cmp_catkeys (rec->key, &key) <= 0) + ctx->found = grub_be_to_cpu32 (grub_get_unaligned32 (rec->data)); + + if (hnd->type == 0xFF && ckey->strlen > 0) + { + ctx->isleaf = 1; + ctx->next = grub_be_to_cpu32 (hnd->next); + + /* An entry was found. */ + if (ckey->parent_dir == ctx->dir_be) + return ctx->hook (rec, ctx->hook_arg); + } + + return 0; +} + +static int +grub_hfs_iterate_dir_it_dir (struct grub_hfs_node *hnd __attribute ((unused)), + struct grub_hfs_record *rec, + void *hook_arg) +{ + struct grub_hfs_catalog_key *ckey = rec->key; + struct grub_hfs_iterate_dir_node_found_ctx *ctx = hook_arg; + + /* Stop when the entries do not match anymore. */ + if (ckey->parent_dir != ctx->dir_be) + return 1; + + return ctx->hook (rec, ctx->hook_arg); } @@ -790,185 +897,375 @@ grub_hfs_find_node (struct grub_hfs_data *data, char *key, call HOOK. */ static grub_err_t grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx, - unsigned int dir, int (*hook) (struct grub_hfs_record *)) + grub_uint32_t dir, int (*hook) (struct grub_hfs_record *, void *hook_arg), + void *hook_arg) { - int found = -1; - int isleaf = 0; - int next = 0; - - /* The lowest key possible with DIR as root directory. */ - struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""}; - - auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *); - auto int it_dir (struct grub_hfs_node * __attribute ((unused)), - struct grub_hfs_record *); - - - int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec) - { - struct grub_hfs_catalog_key *ckey = rec->key; - - if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0) - found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data); - - if (hnd->type == 0xFF && ckey->strlen > 0) - { - isleaf = 1; - next = grub_be_to_cpu32 (hnd->next); - - /* An entry was found. */ - if (grub_be_to_cpu32 (ckey->parent_dir) == dir) - return hook (rec); - } - - return 0; - } - - int it_dir (struct grub_hfs_node *hnd __attribute ((unused)), - struct grub_hfs_record *rec) - { - struct grub_hfs_catalog_key *ckey = rec->key; - struct grub_hfs_catalog_key *origkey = &key; - - /* Stop when the entries do not match anymore. */ - if (grub_be_to_cpu32 (ckey->parent_dir) - != grub_be_to_cpu32 ((origkey)->parent_dir)) - return 1; - - return hook (rec); - } + struct grub_hfs_iterate_dir_node_found_ctx ctx = + { + .dir_be = grub_cpu_to_be32 (dir), + .found = -1, + .isleaf = 0, + .next = 0, + .hook = hook, + .hook_arg = hook_arg + }; do { - found = -1; + ctx.found = -1; - if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found)) + if (grub_hfs_iterate_records (data, 0, root_idx, 0, grub_hfs_iterate_dir_node_found, &ctx)) return grub_errno; - if (found == -1) + if (ctx.found == -1) return 0; - root_idx = found; - } while (! isleaf); + root_idx = ctx.found; + } while (! ctx.isleaf); /* If there was a matching record in this leaf node, continue the iteration until the last record was found. */ - grub_hfs_iterate_records (data, 0, next, 1, it_dir); + grub_hfs_iterate_records (data, 0, ctx.next, 1, grub_hfs_iterate_dir_it_dir, &ctx); return grub_errno; } +#define MAX_UTF8_PER_MAC_ROMAN 3 + +static const char macroman[0x80][MAX_UTF8_PER_MAC_ROMAN + 1] = + { + /* 80 */ "\xc3\x84", + /* 81 */ "\xc3\x85", + /* 82 */ "\xc3\x87", + /* 83 */ "\xc3\x89", + /* 84 */ "\xc3\x91", + /* 85 */ "\xc3\x96", + /* 86 */ "\xc3\x9c", + /* 87 */ "\xc3\xa1", + /* 88 */ "\xc3\xa0", + /* 89 */ "\xc3\xa2", + /* 8A */ "\xc3\xa4", + /* 8B */ "\xc3\xa3", + /* 8C */ "\xc3\xa5", + /* 8D */ "\xc3\xa7", + /* 8E */ "\xc3\xa9", + /* 8F */ "\xc3\xa8", + /* 90 */ "\xc3\xaa", + /* 91 */ "\xc3\xab", + /* 92 */ "\xc3\xad", + /* 93 */ "\xc3\xac", + /* 94 */ "\xc3\xae", + /* 95 */ "\xc3\xaf", + /* 96 */ "\xc3\xb1", + /* 97 */ "\xc3\xb3", + /* 98 */ "\xc3\xb2", + /* 99 */ "\xc3\xb4", + /* 9A */ "\xc3\xb6", + /* 9B */ "\xc3\xb5", + /* 9C */ "\xc3\xba", + /* 9D */ "\xc3\xb9", + /* 9E */ "\xc3\xbb", + /* 9F */ "\xc3\xbc", + /* A0 */ "\xe2\x80\xa0", + /* A1 */ "\xc2\xb0", + /* A2 */ "\xc2\xa2", + /* A3 */ "\xc2\xa3", + /* A4 */ "\xc2\xa7", + /* A5 */ "\xe2\x80\xa2", + /* A6 */ "\xc2\xb6", + /* A7 */ "\xc3\x9f", + /* A8 */ "\xc2\xae", + /* A9 */ "\xc2\xa9", + /* AA */ "\xe2\x84\xa2", + /* AB */ "\xc2\xb4", + /* AC */ "\xc2\xa8", + /* AD */ "\xe2\x89\xa0", + /* AE */ "\xc3\x86", + /* AF */ "\xc3\x98", + /* B0 */ "\xe2\x88\x9e", + /* B1 */ "\xc2\xb1", + /* B2 */ "\xe2\x89\xa4", + /* B3 */ "\xe2\x89\xa5", + /* B4 */ "\xc2\xa5", + /* B5 */ "\xc2\xb5", + /* B6 */ "\xe2\x88\x82", + /* B7 */ "\xe2\x88\x91", + /* B8 */ "\xe2\x88\x8f", + /* B9 */ "\xcf\x80", + /* BA */ "\xe2\x88\xab", + /* BB */ "\xc2\xaa", + /* BC */ "\xc2\xba", + /* BD */ "\xce\xa9", + /* BE */ "\xc3\xa6", + /* BF */ "\xc3\xb8", + /* C0 */ "\xc2\xbf", + /* C1 */ "\xc2\xa1", + /* C2 */ "\xc2\xac", + /* C3 */ "\xe2\x88\x9a", + /* C4 */ "\xc6\x92", + /* C5 */ "\xe2\x89\x88", + /* C6 */ "\xe2\x88\x86", + /* C7 */ "\xc2\xab", + /* C8 */ "\xc2\xbb", + /* C9 */ "\xe2\x80\xa6", + /* CA */ "\xc2\xa0", + /* CB */ "\xc3\x80", + /* CC */ "\xc3\x83", + /* CD */ "\xc3\x95", + /* CE */ "\xc5\x92", + /* CF */ "\xc5\x93", + /* D0 */ "\xe2\x80\x93", + /* D1 */ "\xe2\x80\x94", + /* D2 */ "\xe2\x80\x9c", + /* D3 */ "\xe2\x80\x9d", + /* D4 */ "\xe2\x80\x98", + /* D5 */ "\xe2\x80\x99", + /* D6 */ "\xc3\xb7", + /* D7 */ "\xe2\x97\x8a", + /* D8 */ "\xc3\xbf", + /* D9 */ "\xc5\xb8", + /* DA */ "\xe2\x81\x84", + /* DB */ "\xe2\x82\xac", + /* DC */ "\xe2\x80\xb9", + /* DD */ "\xe2\x80\xba", + /* DE */ "\xef\xac\x81", + /* DF */ "\xef\xac\x82", + /* E0 */ "\xe2\x80\xa1", + /* E1 */ "\xc2\xb7", + /* E2 */ "\xe2\x80\x9a", + /* E3 */ "\xe2\x80\x9e", + /* E4 */ "\xe2\x80\xb0", + /* E5 */ "\xc3\x82", + /* E6 */ "\xc3\x8a", + /* E7 */ "\xc3\x81", + /* E8 */ "\xc3\x8b", + /* E9 */ "\xc3\x88", + /* EA */ "\xc3\x8d", + /* EB */ "\xc3\x8e", + /* EC */ "\xc3\x8f", + /* ED */ "\xc3\x8c", + /* EE */ "\xc3\x93", + /* EF */ "\xc3\x94", + /* F0 */ "\xef\xa3\xbf", + /* F1 */ "\xc3\x92", + /* F2 */ "\xc3\x9a", + /* F3 */ "\xc3\x9b", + /* F4 */ "\xc3\x99", + /* F5 */ "\xc4\xb1", + /* F6 */ "\xcb\x86", + /* F7 */ "\xcb\x9c", + /* F8 */ "\xc2\xaf", + /* F9 */ "\xcb\x98", + /* FA */ "\xcb\x99", + /* FB */ "\xcb\x9a", + /* FC */ "\xc2\xb8", + /* FD */ "\xcb\x9d", + /* FE */ "\xcb\x9b", + /* FF */ "\xcb\x87", + }; + +static void +macroman_to_utf8 (char *to, const grub_uint8_t *from, grub_size_t len, + int translate_slash) +{ + char *optr = to; + const grub_uint8_t *iptr; + + for (iptr = from; iptr < from + len && *iptr; iptr++) + { + /* Translate '/' to ':' as per HFS spec. */ + if (*iptr == '/' && translate_slash) + { + *optr++ = ':'; + continue; + } + if (!(*iptr & 0x80)) + { + *optr++ = *iptr; + continue; + } + optr = grub_stpcpy (optr, macroman[*iptr & 0x7f]); + } + *optr = 0; +} + +static grub_ssize_t +utf8_to_macroman (grub_uint8_t *to, const char *from) +{ + grub_uint8_t *end = to + 31; + grub_uint8_t *optr = to; + const char *iptr = from; + + while (*iptr && optr < end) + { + int i, clen; + /* Translate ':' to '/' as per HFS spec. */ + if (*iptr == ':') + { + *optr++ = '/'; + iptr++; + continue; + } + if (!(*iptr & 0x80)) + { + *optr++ = *iptr++; + continue; + } + clen = 2; + if ((*iptr & 0xf0) == 0xe0) + clen++; + for (i = 0; i < 0x80; i++) + if (grub_memcmp (macroman[i], iptr, clen) == 0) + break; + if (i == 0x80) + break; + *optr++ = i | 0x80; + iptr += clen; + } + /* Too long or not encodable. */ + if (*iptr) + return -1; + return optr - to; +} + +union grub_hfs_anyrec { + struct grub_hfs_filerec frec; + struct grub_hfs_dirrec dir; +}; + +struct grub_fshelp_node +{ + struct grub_hfs_data *data; + union grub_hfs_anyrec fdrec; + grub_uint32_t inode; +}; + +static grub_err_t +lookup_file (grub_fshelp_node_t dir, + const char *name, + grub_fshelp_node_t *foundnode, + enum grub_fshelp_filetype *foundtype) +{ + struct grub_hfs_catalog_key key; + grub_ssize_t slen; + union grub_hfs_anyrec fdrec; + + key.parent_dir = grub_cpu_to_be32 (dir->inode); + slen = utf8_to_macroman (key.str, name); + if (slen < 0) + /* Not found */ + return GRUB_ERR_NONE; + key.strlen = slen; + + /* Lookup this node. */ + if (! grub_hfs_find_node (dir->data, (char *) &key, dir->data->cat_root, + 0, (char *) &fdrec.frec, sizeof (fdrec.frec))) + /* Not found */ + return GRUB_ERR_NONE; + + *foundnode = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!*foundnode) + return grub_errno; + + (*foundnode)->inode = grub_be_to_cpu32 (fdrec.dir.dirid); + (*foundnode)->fdrec = fdrec; + (*foundnode)->data = dir->data; + *foundtype = (fdrec.frec.type == GRUB_HFS_FILETYPE_DIR) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG; + return GRUB_ERR_NONE; +} /* Find a file or directory with the pathname PATH in the filesystem DATA. Return the file record in RETDATA when it is non-zero. Return the directory number in RETINODE when it is non-zero. */ static grub_err_t grub_hfs_find_dir (struct grub_hfs_data *data, const char *path, - struct grub_hfs_filerec *retdata, int *retinode) + grub_fshelp_node_t *found, + enum grub_fshelp_filetype exptype) { - int inode = data->rootdir; - char *next; - char *origpath; - union { - struct grub_hfs_filerec frec; - struct grub_hfs_dirrec dir; - } fdrec; - - fdrec.frec.type = GRUB_HFS_FILETYPE_DIR; - - if (path[0] != '/') - { - grub_error (GRUB_ERR_BAD_FILENAME, "bad filename"); - return 0; + struct grub_fshelp_node root = { + .data = data, + .inode = data->rootdir, + .fdrec = { + .frec = { + .type = GRUB_HFS_FILETYPE_DIR + } } + }; + grub_err_t err; - origpath = grub_strdup (path); - if (!origpath) - return grub_errno; + err = grub_fshelp_find_file_lookup (path, &root, found, lookup_file, NULL, exptype); - path = origpath; - while (*path == '/') - path++; - - while (path && grub_strlen (path)) + if (&root == *found) { - if (fdrec.frec.type != GRUB_HFS_FILETYPE_DIR) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - goto fail; - } - - /* Isolate a part of the path. */ - next = grub_strchr (path, '/'); - if (next) - { - while (*next == '/') - *(next++) = '\0'; - } - - struct grub_hfs_catalog_key key; - - key.parent_dir = grub_cpu_to_be32 (inode); - key.strlen = grub_strlen (path); - grub_strcpy ((char *) (key.str), path); - - /* Lookup this node. */ - if (! grub_hfs_find_node (data, (char *) &key, data->cat_root, - 0, (char *) &fdrec.frec, sizeof (fdrec.frec))) - { - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - goto fail; - } - - if (grub_errno) - goto fail; - - inode = grub_be_to_cpu32 (fdrec.dir.dirid); - path = next; + *found = grub_malloc (sizeof (root)); + if (!*found) + return grub_errno; + grub_memcpy (*found, &root, sizeof (root)); } - - if (retdata) - grub_memcpy (retdata, &fdrec.frec, sizeof (fdrec.frec)); - - if (retinode) - *retinode = inode; - - fail: - grub_free (origpath); - return grub_errno; + return err; } +struct grub_hfs_dir_hook_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +static int +grub_hfs_dir_hook (struct grub_hfs_record *rec, void *hook_arg) +{ + struct grub_hfs_dir_hook_ctx *ctx = hook_arg; + struct grub_hfs_dirrec *drec = rec->data; + struct grub_hfs_filerec *frec = rec->data; + struct grub_hfs_catalog_key *ckey = rec->key; + char fname[sizeof (ckey->str) * MAX_UTF8_PER_MAC_ROMAN + 1]; + struct grub_dirhook_info info; + grub_size_t len; + + grub_memset (fname, 0, sizeof (fname)); + + grub_memset (&info, 0, sizeof (info)); + + len = ckey->strlen; + if (len > sizeof (ckey->str)) + len = sizeof (ckey->str); + macroman_to_utf8 (fname, ckey->str, len, 1); + + info.case_insensitive = 1; + + if (drec->type == GRUB_HFS_FILETYPE_DIR) + { + info.dir = 1; + info.mtimeset = 1; + info.inodeset = 1; + info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800; + info.inode = grub_be_to_cpu32 (drec->dirid); + return ctx->hook (fname, &info, ctx->hook_data); + } + if (frec->type == GRUB_HFS_FILETYPE_FILE) + { + info.dir = 0; + info.mtimeset = 1; + info.inodeset = 1; + info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800; + info.inode = grub_be_to_cpu32 (frec->fileid); + return ctx->hook (fname, &info, ctx->hook_data); + } + + return 0; +} static grub_err_t -grub_hfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) +grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, + void *hook_data) { - int inode; - - auto int dir_hook (struct grub_hfs_record *rec); - - int dir_hook (struct grub_hfs_record *rec) - { - char fname[32] = { 0 }; - char *filetype = rec->data; - struct grub_hfs_catalog_key *ckey = rec->key; - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - - grub_strncpy (fname, (char *) (ckey->str), ckey->strlen); - - if (*filetype == GRUB_HFS_FILETYPE_DIR - || *filetype == GRUB_HFS_FILETYPE_FILE) - { - info.dir = (*filetype == GRUB_HFS_FILETYPE_DIR); - return hook (fname, &info); - } - return 0; - } - struct grub_hfs_data *data; - struct grub_hfs_filerec frec; + struct grub_hfs_dir_hook_ctx ctx = + { + .hook = hook, + .hook_data = hook_data + }; + grub_fshelp_node_t found = NULL; grub_dl_ref (my_mod); @@ -977,18 +1274,13 @@ grub_hfs_dir (grub_device_t device, const char *path, goto fail; /* First the directory ID for the directory. */ - if (grub_hfs_find_dir (data, path, &frec, &inode)) + if (grub_hfs_find_dir (data, path, &found, GRUB_FSHELP_DIR)) goto fail; - if (frec.type != GRUB_HFS_FILETYPE_DIR) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - goto fail; - } - - grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook); + grub_hfs_iterate_dir (data, data->cat_root, found->inode, grub_hfs_dir_hook, &ctx); fail: + grub_free (found); grub_free (data); grub_dl_unref (my_mod); @@ -1002,35 +1294,36 @@ static grub_err_t grub_hfs_open (struct grub_file *file, const char *name) { struct grub_hfs_data *data; - struct grub_hfs_filerec frec; + grub_fshelp_node_t found = NULL; grub_dl_ref (my_mod); data = grub_hfs_mount (file->device->disk); - if (grub_hfs_find_dir (data, name, &frec, 0)) + if (!data) { - grub_free (data); grub_dl_unref (my_mod); return grub_errno; } - if (frec.type != GRUB_HFS_FILETYPE_FILE) + if (grub_hfs_find_dir (data, name, &found, GRUB_FSHELP_REG)) { grub_free (data); - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file"); + grub_free (found); grub_dl_unref (my_mod); return grub_errno; } - grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t)); - file->size = grub_be_to_cpu32 (frec.size); - data->size = grub_be_to_cpu32 (frec.size); - data->fileid = grub_be_to_cpu32 (frec.fileid); + grub_memcpy (data->extents, found->fdrec.frec.extents, sizeof (grub_hfs_datarecord_t)); + file->size = grub_be_to_cpu32 (found->fdrec.frec.size); + data->size = grub_be_to_cpu32 (found->fdrec.frec.size); + data->fileid = grub_be_to_cpu32 (found->fdrec.frec.fileid); file->offset = 0; file->data = data; + grub_free (found); + return 0; } @@ -1040,7 +1333,8 @@ grub_hfs_read (grub_file_t file, char *buf, grub_size_t len) struct grub_hfs_data *data = (struct grub_hfs_data *) file->data; - return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf); + return grub_hfs_read_file (data, file->read_hook, file->read_hook_data, + file->offset, len, buf); } @@ -1063,8 +1357,15 @@ grub_hfs_label (grub_device_t device, char **label) data = grub_hfs_mount (device->disk); if (data) - *label = grub_strndup ((char *) (data->sblock.volname + 1), - *data->sblock.volname); + { + grub_size_t len = data->sblock.volname[0]; + if (len > sizeof (data->sblock.volname) - 1) + len = sizeof (data->sblock.volname) - 1; + *label = grub_calloc (MAX_UTF8_PER_MAC_ROMAN + 1, len); + if (*label) + macroman_to_utf8 (*label, data->sblock.volname + 1, + len + 1, 0); + } else *label = 0; @@ -1072,6 +1373,22 @@ grub_hfs_label (grub_device_t device, char **label) return grub_errno; } +static grub_err_t +grub_hfs_mtime (grub_device_t device, grub_int64_t *tm) +{ + struct grub_hfs_data *data; + + data = grub_hfs_mount (device->disk); + + if (data) + *tm = grub_be_to_cpu32 (data->sblock.mtime) - 2082844800; + else + *tm = 0; + + grub_free (data); + return grub_errno; +} + static grub_err_t grub_hfs_uuid (grub_device_t device, char **uuid) { @@ -1101,22 +1418,30 @@ grub_hfs_uuid (grub_device_t device, char **uuid) static struct grub_fs grub_hfs_fs = { .name = "hfs", - .dir = grub_hfs_dir, - .open = grub_hfs_open, - .read = grub_hfs_read, - .close = grub_hfs_close, - .label = grub_hfs_label, - .uuid = grub_hfs_uuid, + .fs_dir = grub_hfs_dir, + .fs_open = grub_hfs_open, + .fs_read = grub_hfs_read, + .fs_close = grub_hfs_close, + .fs_label = grub_hfs_label, + .fs_uuid = grub_hfs_uuid, + .fs_mtime = grub_hfs_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif .next = 0 }; GRUB_MOD_INIT(hfs) { - grub_fs_register (&grub_hfs_fs); + grub_hfs_fs.mod = mod; + if (!grub_is_lockdown ()) + grub_fs_register (&grub_hfs_fs); my_mod = mod; } GRUB_MOD_FINI(hfs) { - grub_fs_unregister (&grub_hfs_fs); + if (!grub_is_lockdown()) + grub_fs_unregister (&grub_hfs_fs); } diff --git a/fs/hfsplus.c b/grub-core/fs/hfsplus.c similarity index 50% rename from fs/hfsplus.c rename to grub-core/fs/hfsplus.c index bcb8e9584..3f203abcc 100644 --- a/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -19,6 +19,7 @@ /* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */ +#define grub_fshelp_node grub_hfsplus_file #include #include #include @@ -29,47 +30,10 @@ #include #include #include +#include +#include -#define GRUB_HFSPLUS_MAGIC 0x482B -#define GRUB_HFSPLUSX_MAGIC 0x4858 -#define GRUB_HFSPLUS_SBLOCK 2 - -/* A HFS+ extent. */ -struct grub_hfsplus_extent -{ - /* The first block of a file on disk. */ - grub_uint32_t start; - /* The amount of blocks described by this extent. */ - grub_uint32_t count; -} __attribute__ ((packed)); - -/* The descriptor of a fork. */ -struct grub_hfsplus_forkdata -{ - grub_uint64_t size; - grub_uint32_t clumpsize; - grub_uint32_t blocks; - struct grub_hfsplus_extent extents[8]; -} __attribute__ ((packed)); - -/* The HFS+ Volume Header. */ -struct grub_hfsplus_volheader -{ - grub_uint16_t magic; - grub_uint16_t version; - grub_uint32_t attributes; - grub_uint8_t unused1[12]; - grub_uint32_t utime; - grub_uint8_t unused2[16]; - grub_uint32_t blksize; - grub_uint8_t unused3[60]; - grub_uint64_t num_serial; - struct grub_hfsplus_forkdata allocations_file; - struct grub_hfsplus_forkdata extents_file; - struct grub_hfsplus_forkdata catalog_file; - struct grub_hfsplus_forkdata attrib_file; - struct grub_hfsplus_forkdata startup_file; -} __attribute__ ((packed)); +GRUB_MOD_LICENSE ("GPLv3+"); /* The type of node. */ enum grub_hfsplus_btnode_type @@ -80,16 +44,6 @@ enum grub_hfsplus_btnode_type GRUB_HFSPLUS_BTNODE_TYPE_MAP = 2, }; -struct grub_hfsplus_btnode -{ - grub_uint32_t next; - grub_uint32_t prev; - grub_int8_t type; - grub_uint8_t height; - grub_uint16_t count; - grub_uint16_t unused; -} __attribute__ ((packed)); - /* The header of a HFS+ B+ Tree. */ struct grub_hfsplus_btheader { @@ -107,42 +61,13 @@ struct grub_hfsplus_btheader grub_uint8_t btree_type; grub_uint8_t key_compare; grub_uint32_t attributes; -} __attribute__ ((packed)); - -/* The on disk layout of a catalog key. */ -struct grub_hfsplus_catkey -{ - grub_uint16_t keylen; - grub_uint32_t parent; - grub_uint16_t namelen; - grub_uint16_t name[30]; -} __attribute__ ((packed)); - -/* The on disk layout of an extent overflow file key. */ -struct grub_hfsplus_extkey -{ - grub_uint16_t keylen; - grub_uint8_t type; - grub_uint8_t unused; - grub_uint32_t fileid; - grub_uint32_t start; -} __attribute__ ((packed)); - -struct grub_hfsplus_key -{ - union - { - struct grub_hfsplus_extkey extkey; - struct grub_hfsplus_catkey catkey; - grub_uint16_t keylen; - }; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_hfsplus_catfile { grub_uint16_t type; grub_uint16_t flags; - grub_uint32_t reserved; + grub_uint32_t parentid; /* Thread only. */ grub_uint32_t fileid; grub_uint8_t unused1[4]; grub_uint32_t mtime; @@ -151,7 +76,7 @@ struct grub_hfsplus_catfile grub_uint8_t unused3[44]; struct grub_hfsplus_forkdata data; struct grub_hfsplus_forkdata resource; -} __attribute__ ((packed)); +} GRUB_PACKED; /* Filetype information as used in inodes. */ #define GRUB_HFSPLUS_FILEMODE_MASK 0170000 @@ -159,10 +84,20 @@ struct grub_hfsplus_catfile #define GRUB_HFSPLUS_FILEMODE_DIRECTORY 0040000 #define GRUB_HFSPLUS_FILEMODE_SYMLINK 0120000 +#define HFSPLUS_BTNODE_MINSZ (1 << 9) +#define HFSPLUS_BTNODE_MAXSZ (1 << 15) + +#define HFSPLUS_CATKEY_MIN_LEN 6 +#define HFSPLUS_CATKEY_MAX_LEN 516 + /* Some pre-defined file IDs. */ -#define GRUB_HFSPLUS_FILEID_ROOTDIR 2 -#define GRUB_HFSPLUS_FILEID_OVERFLOW 3 -#define GRUB_HFSPLUS_FILEID_CATALOG 4 +enum + { + GRUB_HFSPLUS_FILEID_ROOTDIR = 2, + GRUB_HFSPLUS_FILEID_OVERFLOW = 3, + GRUB_HFSPLUS_FILEID_CATALOG = 4, + GRUB_HFSPLUS_FILEID_ATTR = 8 + }; enum grub_hfsplus_filetype { @@ -175,107 +110,25 @@ enum grub_hfsplus_filetype #define GRUB_HFSPLUSX_BINARYCOMPARE 0xBC #define GRUB_HFSPLUSX_CASEFOLDING 0xCF -/* Internal representation of a catalog key. */ -struct grub_hfsplus_catkey_internal -{ - int parent; - char *name; -}; - -/* Internal representation of an extent overflow key. */ -struct grub_hfsplus_extkey_internal -{ - grub_uint32_t fileid; - grub_uint32_t start; -}; - -struct grub_hfsplus_key_internal -{ - union - { - struct grub_hfsplus_extkey_internal extkey; - struct grub_hfsplus_catkey_internal catkey; - }; -}; - - - -struct grub_fshelp_node -{ - struct grub_hfsplus_data *data; - struct grub_hfsplus_extent extents[8]; - grub_uint64_t size; - grub_uint32_t fileid; - grub_int32_t mtime; -}; - -struct grub_hfsplus_btree -{ - grub_uint32_t root; - int nodesize; - - /* Catalog file node. */ - struct grub_fshelp_node file; -}; - -/* Information about a "mounted" HFS+ filesystem. */ -struct grub_hfsplus_data -{ - struct grub_hfsplus_volheader volheader; - grub_disk_t disk; - - unsigned int log2blksize; - - struct grub_hfsplus_btree catalog_tree; - struct grub_hfsplus_btree extoverflow_tree; - - struct grub_fshelp_node dirroot; - struct grub_fshelp_node opened_file; - - /* This is the offset into the physical disk for an embedded HFS+ - filesystem (one inside a plain HFS wrapper). */ - int embedded_offset; - int case_sensitive; -}; - static grub_dl_t my_mod; -/* Return the offset of the record with the index INDEX, in the node - NODE which is part of the B+ tree BTREE. */ -static inline unsigned int -grub_hfsplus_btree_recoffset (struct grub_hfsplus_btree *btree, - struct grub_hfsplus_btnode *node, int index) -{ - char *cnode = (char *) node; - grub_uint16_t *recptr; - recptr = (grub_uint16_t *) (&cnode[btree->nodesize - - index * sizeof (grub_uint16_t) - 2]); - return grub_be_to_cpu16 (*recptr); -} - -/* Return a pointer to the record with the index INDEX, in the node - NODE which is part of the B+ tree BTREE. */ -static inline struct grub_hfsplus_key * -grub_hfsplus_btree_recptr (struct grub_hfsplus_btree *btree, - struct grub_hfsplus_btnode *node, int index) -{ - char *cnode = (char *) node; - unsigned int offset; - offset = grub_hfsplus_btree_recoffset (btree, node, index); - return (struct grub_hfsplus_key *) &cnode[offset]; -} +grub_err_t (*grub_hfsplus_open_compressed) (struct grub_fshelp_node *node); +grub_ssize_t (*grub_hfsplus_read_compressed) (struct grub_hfsplus_file *node, + grub_off_t pos, + grub_size_t len, + char *buf); /* Find the extent that points to FILEBLOCK. If it is not in one of the 8 extents described by EXTENT, return -1. In that case set FILEBLOCK to the next block. */ -static int +static grub_disk_addr_t grub_hfsplus_find_block (struct grub_hfsplus_extent *extent, - int *fileblock) + grub_disk_addr_t *fileblock) { int i; - grub_size_t blksleft = *fileblock; + grub_disk_addr_t blksleft = *fileblock; /* First lookup the file in the given extents. */ for (i = 0; i < 8; i++) @@ -286,16 +139,9 @@ grub_hfsplus_find_block (struct grub_hfsplus_extent *extent, } *fileblock = blksleft; - return -1; + return 0xffffffffffffffffULL; } -static grub_err_t -grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, - struct grub_hfsplus_key_internal *key, - int (*compare_keys) (struct grub_hfsplus_key *keya, - struct grub_hfsplus_key_internal *keyb), - struct grub_hfsplus_btnode **matchnode, int *keyoffset); - static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya, struct grub_hfsplus_key_internal *keyb); @@ -305,15 +151,16 @@ static grub_disk_addr_t grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_hfsplus_btnode *nnode = 0; - int blksleft = fileblock; - struct grub_hfsplus_extent *extents = &node->extents[0]; + grub_disk_addr_t blksleft = fileblock; + struct grub_hfsplus_extent *extents = node->compressed + ? &node->resource_extents[0] : &node->extents[0]; while (1) { struct grub_hfsplus_extkey *key; - struct grub_hfsplus_extkey_internal extoverflow; - int blk; - int ptr; + struct grub_hfsplus_key_internal extoverflow; + grub_disk_addr_t blk; + grub_off_t ptr; /* Try to find this block in the current set of extents. */ blk = grub_hfsplus_find_block (extents, &blksleft); @@ -323,10 +170,8 @@ grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_free (nnode); nnode = 0; - if (blk != -1) - return (blk - + (node->data->embedded_offset >> (node->data->log2blksize - - GRUB_DISK_SECTOR_BITS))); + if (blk != 0xffffffffffffffffULL) + return blk; /* For the extent overflow file, extra extents can't be found in the extent overflow file. If this happens, you found a @@ -338,16 +183,30 @@ grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) break; } - /* Set up the key to look for in the extent overflow file. */ - extoverflow.fileid = node->fileid; - extoverflow.start = fileblock - blksleft; + /* + * If the extent overflow tree isn't ready yet, we can't look + * in it. This can happen where the catalog file is corrupted. + */ + if (!node->data->extoverflow_tree_ready) + { + grub_error (GRUB_ERR_BAD_FS, + "attempted to read extent overflow tree before loading"); + break; + } + /* Set up the key to look for in the extent overflow file. */ + extoverflow.extkey.fileid = node->fileid; + extoverflow.extkey.type = 0; + extoverflow.extkey.start = fileblock - blksleft; + extoverflow.extkey.type = node->compressed ? 0xff : 0; if (grub_hfsplus_btree_search (&node->data->extoverflow_tree, - (struct grub_hfsplus_key_internal *) &extoverflow, - grub_hfsplus_cmp_extkey, &nnode, &ptr)) + &extoverflow, + grub_hfsplus_cmp_extkey, &nnode, &ptr) + || !nnode) { grub_error (GRUB_ERR_READ_ERROR, - "no block found for the file id 0x%x and the block offset 0x%x", + "no block found for the file id 0x%x and the block" + " offset 0x%" PRIuGRUB_UINT64_T, node->fileid, fileblock); break; } @@ -370,16 +229,17 @@ grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ -static grub_ssize_t +grub_ssize_t grub_hfsplus_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) { - return grub_fshelp_read_file (node->data->disk, node, read_hook, + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, pos, len, buf, grub_hfsplus_read_block, node->size, - node->data->log2blksize - GRUB_DISK_SECTOR_BITS); + node->data->log2blksize - GRUB_DISK_SECTOR_BITS, + node->data->embedded_offset); } static struct grub_hfsplus_data * @@ -399,6 +259,7 @@ grub_hfsplus_mount (grub_disk_t disk) return 0; data->disk = disk; + data->extoverflow_tree_ready = 0; /* Read the bootblock. */ grub_disk_read (disk, GRUB_HFSPLUS_SBLOCK, 0, sizeof (volheader), @@ -409,9 +270,9 @@ grub_hfsplus_mount (grub_disk_t disk) data->embedded_offset = 0; if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC) { - int extent_start; - int ablk_size; - int ablk_start; + grub_disk_addr_t extent_start; + grub_disk_addr_t ablk_size; + grub_disk_addr_t ablk_start; /* See if there's an embedded HFS+ filesystem. */ if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC) @@ -437,31 +298,46 @@ grub_hfsplus_mount (grub_disk_t disk) /* Make sure this is an HFS+ filesystem. XXX: Do we really support HFX? */ magic = grub_be_to_cpu16 (volheader.hfsplus.magic); - if ((magic != GRUB_HFSPLUS_MAGIC) && (magic != GRUB_HFSPLUSX_MAGIC)) + if (((magic != GRUB_HFSPLUS_MAGIC) && (magic != GRUB_HFSPLUSX_MAGIC)) + || volheader.hfsplus.blksize == 0 + || ((volheader.hfsplus.blksize & (volheader.hfsplus.blksize - 1)) != 0) + || grub_be_to_cpu32 (volheader.hfsplus.blksize) < GRUB_DISK_SECTOR_SIZE) { grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); goto fail; } grub_memcpy (&data->volheader, &volheader.hfsplus, - sizeof (volheader.hfsplus)); + sizeof (volheader.hfsplus)); - if (grub_fshelp_log2blksize (grub_be_to_cpu32 (data->volheader.blksize), - &data->log2blksize)) - goto fail; + for (data->log2blksize = 0; + (1U << data->log2blksize) < grub_be_to_cpu32 (data->volheader.blksize); + data->log2blksize++); /* Make a new node for the catalog tree. */ data->catalog_tree.file.data = data; data->catalog_tree.file.fileid = GRUB_HFSPLUS_FILEID_CATALOG; + data->catalog_tree.file.compressed = 0; grub_memcpy (&data->catalog_tree.file.extents, data->volheader.catalog_file.extents, sizeof data->volheader.catalog_file.extents); data->catalog_tree.file.size = grub_be_to_cpu64 (data->volheader.catalog_file.size); + data->attr_tree.file.data = data; + data->attr_tree.file.fileid = GRUB_HFSPLUS_FILEID_ATTR; + grub_memcpy (&data->attr_tree.file.extents, + data->volheader.attr_file.extents, + sizeof data->volheader.attr_file.extents); + + data->attr_tree.file.size = + grub_be_to_cpu64 (data->volheader.attr_file.size); + data->attr_tree.file.compressed = 0; + /* Make a new node for the extent overflow file. */ data->extoverflow_tree.file.data = data; data->extoverflow_tree.file.fileid = GRUB_HFSPLUS_FILEID_OVERFLOW; + data->extoverflow_tree.file.compressed = 0; grub_memcpy (&data->extoverflow_tree.file.extents, data->volheader.extents_file.extents, sizeof data->volheader.catalog_file.extents); @@ -470,7 +346,7 @@ grub_hfsplus_mount (grub_disk_t disk) grub_be_to_cpu64 (data->volheader.extents_file.size); /* Read the essential information about the trees. */ - if (grub_hfsplus_read_file (&data->catalog_tree.file, 0, + if (grub_hfsplus_read_file (&data->catalog_tree.file, 0, 0, sizeof (struct grub_hfsplus_btnode), sizeof (header), (char *) &header) <= 0) goto fail; @@ -480,20 +356,48 @@ grub_hfsplus_mount (grub_disk_t disk) data->case_sensitive = ((magic == GRUB_HFSPLUSX_MAGIC) && (header.key_compare == GRUB_HFSPLUSX_BINARYCOMPARE)); - if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, + if (data->catalog_tree.nodesize < 2) + { + grub_error (GRUB_ERR_BAD_FS, "invalid catalog node size"); + goto fail; + } + + if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, 0, sizeof (struct grub_hfsplus_btnode), sizeof (header), (char *) &header) <= 0) goto fail; data->extoverflow_tree.root = grub_be_to_cpu32 (header.root); - if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, 0, + if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, 0, 0, sizeof (node), (char *) &node) <= 0) goto fail; data->extoverflow_tree.root = grub_be_to_cpu32 (header.root); data->extoverflow_tree.nodesize = grub_be_to_cpu16 (header.nodesize); + if (data->extoverflow_tree.nodesize < 2) + { + grub_error (GRUB_ERR_BAD_FS, "invalid extents overflow node size"); + goto fail; + } + + data->extoverflow_tree_ready = 1; + + if (grub_hfsplus_read_file (&data->attr_tree.file, 0, 0, + sizeof (struct grub_hfsplus_btnode), + sizeof (header), (char *) &header) <= 0) + { + grub_errno = 0; + data->attr_tree.root = 0; + data->attr_tree.nodesize = 0; + } + else + { + data->attr_tree.root = grub_be_to_cpu32 (header.root); + data->attr_tree.nodesize = grub_be_to_cpu16 (header.nodesize); + } + data->dirroot.data = data; data->dirroot.fileid = GRUB_HFSPLUS_FILEID_ROOTDIR; @@ -501,7 +405,7 @@ grub_hfsplus_mount (grub_disk_t disk) fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); grub_free (data); @@ -516,37 +420,48 @@ grub_hfsplus_cmp_catkey (struct grub_hfsplus_key *keya, { struct grub_hfsplus_catkey *catkey_a = &keya->catkey; struct grub_hfsplus_catkey_internal *catkey_b = &keyb->catkey; - char *filename; - int i; int diff; + grub_size_t len; - diff = grub_be_to_cpu32 (catkey_a->parent) - catkey_b->parent; - if (diff) - return diff; + /* Safe unsigned comparison */ + grub_uint32_t aparent = grub_be_to_cpu32 (catkey_a->parent); + if (aparent > catkey_b->parent) + return 1; + if (aparent < catkey_b->parent) + return -1; - /* Change the filename in keya so the endianness is correct. */ - for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++) - catkey_a->name[i] = grub_be_to_cpu16 (catkey_a->name[i]); + len = grub_be_to_cpu16 (catkey_a->namelen); + if (len > catkey_b->namelen) + len = catkey_b->namelen; + /* Since it's big-endian memcmp gives the same result as manually comparing + uint16_t but may be faster. */ + diff = grub_memcmp (catkey_a->name, catkey_b->name, + len * sizeof (catkey_a->name[0])); + if (diff == 0) + diff = grub_be_to_cpu16 (catkey_a->namelen) - catkey_b->namelen; - filename = grub_malloc (grub_be_to_cpu16 (catkey_a->namelen) + 1); - - if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey_a->name, - grub_be_to_cpu16 (catkey_a->namelen))) - return -1; /* XXX: This error never occurs, but in case it happens - just skip this entry. */ - - diff = grub_strncmp (filename, catkey_b->name, - grub_be_to_cpu16 (catkey_a->namelen)); - - grub_free (filename); - - /* The endianness was changed to host format, change it back to - whatever it was. */ - for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++) - catkey_a->name[i] = grub_cpu_to_be16 (catkey_a->name[i]); return diff; } +/* Compare the on disk catalog key KEYA with the catalog key we are + looking for (KEYB). */ +static int +grub_hfsplus_cmp_catkey_id (struct grub_hfsplus_key *keya, + struct grub_hfsplus_key_internal *keyb) +{ + struct grub_hfsplus_catkey *catkey_a = &keya->catkey; + struct grub_hfsplus_catkey_internal *catkey_b = &keyb->catkey; + + /* Safe unsigned comparison */ + grub_uint32_t aparent = grub_be_to_cpu32 (catkey_a->parent); + if (aparent > catkey_b->parent) + return 1; + if (aparent < catkey_b->parent) + return -1; + + return 0; +} + /* Compare the on disk extent overflow key KEYA with the extent overflow key we are looking for (KEYB). */ static int @@ -555,15 +470,32 @@ grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya, { struct grub_hfsplus_extkey *extkey_a = &keya->extkey; struct grub_hfsplus_extkey_internal *extkey_b = &keyb->extkey; - int diff; + grub_uint32_t akey; - diff = grub_be_to_cpu32 (extkey_a->fileid) - extkey_b->fileid; + /* Safe unsigned comparison */ + akey = grub_be_to_cpu32 (extkey_a->fileid); + if (akey > extkey_b->fileid) + return 1; + if (akey < extkey_b->fileid) + return -1; - if (diff) - return diff; + if (extkey_a->type > extkey_b->type) + return 1; + if (extkey_a->type < extkey_b->type) + return -1; - diff = grub_be_to_cpu32 (extkey_a->start) - extkey_b->start; - return diff; + if (extkey_a->type > extkey_b->type) + return +1; + + if (extkey_a->type < extkey_b->type) + return -1; + + akey = grub_be_to_cpu32 (extkey_a->start); + if (akey > extkey_b->start) + return 1; + if (akey < extkey_b->start) + return -1; + return 0; } static char * @@ -571,12 +503,16 @@ grub_hfsplus_read_symlink (grub_fshelp_node_t node) { char *symlink; grub_ssize_t numread; + grub_size_t sz = node->size; - symlink = grub_malloc (node->size + 1); + if (grub_add (sz, 1, &sz)) + return NULL; + + symlink = grub_malloc (sz); if (!symlink) return 0; - numread = grub_hfsplus_read_file (node, 0, 0, node->size, symlink); + numread = grub_hfsplus_read_file (node, 0, 0, 0, node->size, symlink); if (numread != (grub_ssize_t) node->size) { grub_free (symlink); @@ -590,10 +526,13 @@ grub_hfsplus_read_symlink (grub_fshelp_node_t node) static int grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree, struct grub_hfsplus_btnode *first_node, - int first_rec, - int (*hook) (void *record)) + grub_disk_addr_t first_rec, + int (*hook) (void *record, void *hook_arg), + void *hook_arg) { - int rec; + grub_disk_addr_t rec; + grub_uint64_t saved_node = -1; + grub_uint64_t node_count = 0; for (;;) { @@ -602,15 +541,25 @@ grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree, /* Iterate over all records in this node. */ for (rec = first_rec; rec < grub_be_to_cpu16 (first_node->count); rec++) { - if (hook (grub_hfsplus_btree_recptr (btree, first_node, rec))) + if (hook (grub_hfsplus_btree_recptr (btree, first_node, rec), hook_arg)) return 1; } if (! first_node->next) break; - if (grub_hfsplus_read_file (&btree->file, 0, - (grub_be_to_cpu32 (first_node->next) + if (node_count && first_node->next == saved_node) + { + grub_error (GRUB_ERR_BAD_FS, "HFS+ btree loop"); + return 0; + } + if (!(node_count & (node_count - 1))) + saved_node = first_node->next; + node_count++; + + if (grub_hfsplus_read_file (&btree->file, 0, 0, + (((grub_disk_addr_t) + grub_be_to_cpu32 (first_node->next)) * btree->nodesize), btree->nodesize, cnode) <= 0) return 1; @@ -626,30 +575,54 @@ grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree, keys using the function COMPARE_KEYS. When a match is found, return the node in MATCHNODE and a pointer to the data in this node in KEYOFFSET. MATCHNODE should be freed by the caller. */ -static grub_err_t +grub_err_t grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, struct grub_hfsplus_key_internal *key, int (*compare_keys) (struct grub_hfsplus_key *keya, struct grub_hfsplus_key_internal *keyb), - struct grub_hfsplus_btnode **matchnode, int *keyoffset) + struct grub_hfsplus_btnode **matchnode, + grub_off_t *keyoffset) { grub_uint64_t currnode; char *node; struct grub_hfsplus_btnode *nodedesc; - int rec; + grub_disk_addr_t rec; + grub_uint64_t save_node; + grub_uint64_t node_count = 0; + + if (!btree->nodesize) + { + *matchnode = 0; + return 0; + } + + if (btree->nodesize < HFSPLUS_BTNODE_MINSZ || + btree->nodesize > HFSPLUS_BTNODE_MAXSZ) + return grub_error (GRUB_ERR_BAD_FS, "invalid HFS+ btree node size"); node = grub_malloc (btree->nodesize); if (! node) return grub_errno; currnode = btree->root; + save_node = currnode - 1; while (1) { int match = 0; + if (save_node == currnode) + { + grub_free (node); + return grub_error (GRUB_ERR_BAD_FS, "HFS+ btree loop"); + } + if (!(node_count & (node_count - 1))) + save_node = currnode; + node_count++; + /* Read a node. */ - if (grub_hfsplus_read_file (&btree->file, 0, - (long)currnode * (long)btree->nodesize, + if (grub_hfsplus_read_file (&btree->file, 0, 0, + (grub_disk_addr_t) currnode + * (grub_disk_addr_t) btree->nodesize, btree->nodesize, (char *) node) <= 0) { grub_free (node); @@ -678,7 +651,7 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, } else if (nodedesc->type == GRUB_HFSPLUS_BTNODE_TYPE_INDEX) { - grub_uint32_t *pointer; + void *pointer; /* The place where the key could have been found didn't contain the key. This means that the previous match @@ -690,10 +663,17 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, that we are looking for. The last match that is found will be used to locate the child which can contain the record. */ - pointer = (grub_uint32_t *) ((char *) currkey - + grub_be_to_cpu16 (currkey->keylen) - + 2); - currnode = grub_be_to_cpu32 (*pointer); + pointer = ((char *) currkey + + grub_be_to_cpu16 (currkey->keylen) + + 2); + + if ((char *) pointer > node + btree->nodesize - 2) + { + grub_free (node); + return grub_error (GRUB_ERR_BAD_FS, "HFS+ key beyond end of node"); + } + + currnode = grub_be_to_cpu32 (grub_get_unaligned32 (pointer)); match = 1; } } @@ -704,134 +684,198 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, { *matchnode = 0; grub_free (node); - return 1; + return 0; } } } -static int -grub_hfsplus_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) +struct list_nodes_ctx { - int ret = 0; + int ret; + grub_fshelp_node_t dir; + grub_fshelp_iterate_dir_hook_t hook; + void *hook_data; +}; - auto int list_nodes (void *record); - int list_nodes (void *record) +static int +list_nodes (void *record, void *hook_arg) +{ + struct grub_hfsplus_catkey *catkey; + char *filename; + int i; + struct grub_fshelp_node *node; + grub_uint16_t *keyname; + struct grub_hfsplus_catfile *fileinfo; + enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; + struct list_nodes_ctx *ctx = hook_arg; + + catkey = (struct grub_hfsplus_catkey *) record; + + if (grub_be_to_cpu16 (catkey->keylen) < HFSPLUS_CATKEY_MIN_LEN || + grub_be_to_cpu16 (catkey->keylen) > HFSPLUS_CATKEY_MAX_LEN) { - struct grub_hfsplus_catkey *catkey; - char *filename; - int i; - struct grub_fshelp_node *node; - struct grub_hfsplus_catfile *fileinfo; - enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; + grub_error (GRUB_ERR_BAD_FS, "catalog key length is out of range"); + return 1; + } - catkey = (struct grub_hfsplus_catkey *) record; + fileinfo = + (struct grub_hfsplus_catfile *) ((char *) record + + grub_be_to_cpu16 (catkey->keylen) + + 2 + (grub_be_to_cpu16(catkey->keylen) + % 2)); - fileinfo = - (struct grub_hfsplus_catfile *) ((char *) record - + grub_be_to_cpu16 (catkey->keylen) - + 2 + (grub_be_to_cpu16(catkey->keylen) - % 2)); + /* Stop iterating when the last directory entry is found. */ + if (grub_be_to_cpu32 (catkey->parent) != ctx->dir->fileid) + return 1; - /* Stop iterating when the last directory entry is found. */ - if (grub_be_to_cpu32 (catkey->parent) != dir->fileid) + /* Determine the type of the node that is found. */ + switch (fileinfo->type) + { + case grub_cpu_to_be16_compile_time (GRUB_HFSPLUS_FILETYPE_REG): + { + int mode = (grub_be_to_cpu16 (fileinfo->mode) + & GRUB_HFSPLUS_FILEMODE_MASK); + + if (mode == GRUB_HFSPLUS_FILEMODE_REG) + type = GRUB_FSHELP_REG; + else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else + type = GRUB_FSHELP_UNKNOWN; + break; + } + case grub_cpu_to_be16_compile_time (GRUB_HFSPLUS_FILETYPE_DIR): + type = GRUB_FSHELP_DIR; + break; + case grub_cpu_to_be16_compile_time (GRUB_HFSPLUS_FILETYPE_DIR_THREAD): + if (ctx->dir->fileid == 2) + return 0; + node = grub_malloc (sizeof (*node)); + if (!node) return 1; + node->data = ctx->dir->data; + node->mtime = 0; + node->size = 0; + node->fileid = grub_be_to_cpu32 (fileinfo->parentid); - /* Determine the type of the node that is found. */ - if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_REG) - { - int mode = (grub_be_to_cpu16 (fileinfo->mode) - & GRUB_HFSPLUS_FILEMODE_MASK); - - if (mode == GRUB_HFSPLUS_FILEMODE_REG) - type = GRUB_FSHELP_REG; - else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK) - type = GRUB_FSHELP_SYMLINK; - else - type = GRUB_FSHELP_UNKNOWN; - } - else if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_DIR) - type = GRUB_FSHELP_DIR; - - if (type == GRUB_FSHELP_UNKNOWN) - return 0; - - /* Make sure the byte order of the UTF16 string is correct. */ - for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++) - { - catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]); - - /* If the name is obviously invalid, skip this node. */ - if (catkey->name[i] == 0) - return 0; - } - - filename = grub_malloc (grub_be_to_cpu16 (catkey->namelen) + 1); - if (! filename) - return 0; - - if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey->name, - grub_be_to_cpu16 (catkey->namelen))) + ctx->ret = ctx->hook ("..", GRUB_FSHELP_DIR, node, ctx->hook_data); + return ctx->ret; + } + + if (type == GRUB_FSHELP_UNKNOWN) + return 0; + + filename = grub_calloc (grub_be_to_cpu16 (catkey->namelen), + GRUB_MAX_UTF8_PER_UTF16 + 1); + if (! filename) + return 0; + + keyname = grub_calloc (grub_be_to_cpu16 (catkey->namelen), sizeof (*keyname)); + if (!keyname) + { + grub_free (filename); + return 0; + } + + /* Make sure the byte order of the UTF16 string is correct. */ + for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++) + { + keyname[i] = grub_be_to_cpu16 (catkey->name[i]); + + if (keyname[i] == '/') + keyname[i] = ':'; + + /* If the name is obviously invalid, skip this node. */ + if (keyname[i] == 0) { + grub_free (keyname); grub_free (filename); return 0; } - - filename[grub_be_to_cpu16 (catkey->namelen)] = '\0'; - - /* Restore the byte order to what it was previously. */ - for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++) - catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]); - - /* hfs+ is case insensitive. */ - if (! dir->data->case_sensitive) - type |= GRUB_FSHELP_CASE_INSENSITIVE; - - /* Only accept valid nodes. */ - if (grub_strlen (filename) == grub_be_to_cpu16 (catkey->namelen)) - { - /* A valid node is found; setup the node and call the - callback function. */ - node = grub_malloc (sizeof (*node)); - node->data = dir->data; - - grub_memcpy (node->extents, fileinfo->data.extents, - sizeof (node->extents)); - node->mtime = grub_be_to_cpu32 (fileinfo->mtime) - 2082844800; - node->size = grub_be_to_cpu64 (fileinfo->data.size); - node->fileid = grub_be_to_cpu32 (fileinfo->fileid); - - ret = hook (filename, type, node); - } - - grub_free (filename); - - return ret; } + *grub_utf16_to_utf8 ((grub_uint8_t *) filename, keyname, + grub_be_to_cpu16 (catkey->namelen)) = '\0'; + + grub_free (keyname); + + /* hfs+ is case insensitive. */ + if (! ctx->dir->data->case_sensitive) + type |= GRUB_FSHELP_CASE_INSENSITIVE; + + /* A valid node is found; setup the node and call the + callback function. */ + node = grub_malloc (sizeof (*node)); + if (!node) + { + grub_free (filename); + return 1; + } + node->data = ctx->dir->data; + node->compressed = 0; + node->cbuf = 0; + node->compress_index = 0; + + grub_memcpy (node->extents, fileinfo->data.extents, + sizeof (node->extents)); + grub_memcpy (node->resource_extents, fileinfo->resource.extents, + sizeof (node->resource_extents)); + node->mtime = grub_be_to_cpu32 (fileinfo->mtime) - 2082844800; + node->size = grub_be_to_cpu64 (fileinfo->data.size); + node->resource_size = grub_be_to_cpu64 (fileinfo->resource.size); + node->fileid = grub_be_to_cpu32 (fileinfo->fileid); + + ctx->ret = ctx->hook (filename, type, node, ctx->hook_data); + + grub_free (filename); + + return ctx->ret; +} + +static int +grub_hfsplus_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + struct list_nodes_ctx ctx = + { + .ret = 0, + .dir = dir, + .hook = hook, + .hook_data = hook_data + }; + struct grub_hfsplus_key_internal intern; - struct grub_hfsplus_btnode *node; - int ptr; + struct grub_hfsplus_btnode *node = NULL; + grub_disk_addr_t ptr = 0; + + { + struct grub_fshelp_node *fsnode; + fsnode = grub_malloc (sizeof (*fsnode)); + if (!fsnode) + return 1; + *fsnode = *dir; + if (hook (".", GRUB_FSHELP_DIR, fsnode, hook_data)) + return 1; + } /* Create a key that points to the first entry in the directory. */ intern.catkey.parent = dir->fileid; - intern.catkey.name = ""; + intern.catkey.name = 0; + intern.catkey.namelen = 0; /* First lookup the first entry. */ if (grub_hfsplus_btree_search (&dir->data->catalog_tree, &intern, - grub_hfsplus_cmp_catkey, &node, &ptr)) + grub_hfsplus_cmp_catkey, &node, &ptr) + || !node) return 0; /* Iterate over all entries in this directory. */ grub_hfsplus_btree_iterate_node (&dir->data->catalog_tree, node, ptr, - list_nodes); + list_nodes, &ctx); grub_free (node); - return ret; + return ctx.ret; } /* Open a file named NAME and initialize FILE. */ @@ -853,6 +897,14 @@ grub_hfsplus_open (struct grub_file *file, const char *name) if (grub_errno) goto fail; + if (grub_hfsplus_open_compressed) + { + grub_err_t err; + err = grub_hfsplus_open_compressed (fdiro); + if (err) + goto fail; + } + file->size = fdiro->size; data->opened_file = *fdiro; grub_free (fdiro); @@ -876,14 +928,19 @@ grub_hfsplus_open (struct grub_file *file, const char *name) static grub_err_t grub_hfsplus_close (grub_file_t file) { - grub_free (file->data); + struct grub_hfsplus_data *data = + (struct grub_hfsplus_data *) file->data; + + grub_free (data->opened_file.cbuf); + grub_free (data->opened_file.compress_index); + + grub_free (data); grub_dl_unref (my_mod); return GRUB_ERR_NONE; } - /* Read LEN bytes data from FILE into BUF. */ static grub_ssize_t grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len) @@ -891,39 +948,52 @@ grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len) struct grub_hfsplus_data *data = (struct grub_hfsplus_data *) file->data; - int size = grub_hfsplus_read_file (&data->opened_file, file->read_hook, - file->offset, len, buf); + data->opened_file.file = file; - return size; + if (grub_hfsplus_read_compressed && data->opened_file.compressed) + return grub_hfsplus_read_compressed (&data->opened_file, + file->offset, len, buf); + + return grub_hfsplus_read_file (&data->opened_file, + file->read_hook, file->read_hook_data, + file->offset, len, buf); } +/* Context for grub_hfsplus_dir. */ +struct grub_hfsplus_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_hfsplus_dir. */ +static int +grub_hfsplus_dir_iter (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_hfsplus_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = node->mtime; + info.inodeset = 1; + info.inode = node->fileid; + info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} static grub_err_t grub_hfsplus_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { + struct grub_hfsplus_dir_ctx ctx = { hook, hook_data }; struct grub_hfsplus_data *data = 0; struct grub_fshelp_node *fdiro = 0; - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - info.mtimeset = 1; - info.mtime = node->mtime; - info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE); - grub_free (node); - return hook (filename, &info); - } - grub_dl_ref (my_mod); data = grub_hfsplus_mount (device->disk); @@ -938,7 +1008,7 @@ grub_hfsplus_dir (grub_device_t device, const char *path, goto fail; /* Iterate over all entries in this directory. */ - grub_hfsplus_iterate_dir (fdiro, iterate); + grub_hfsplus_iterate_dir (fdiro, grub_hfsplus_dir_iter, &ctx); fail: if (data && fdiro != &data->dirroot) @@ -952,18 +1022,94 @@ grub_hfsplus_dir (grub_device_t device, const char *path, static grub_err_t -grub_hfsplus_label (grub_device_t device __attribute__((unused)) - , char **label __attribute__((unused))) +grub_hfsplus_label (grub_device_t device, char **label) { - /* XXX: It's not documented how to read a label. */ - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "reading the label of a HFS+ " - "partition is not implemented"); + struct grub_hfsplus_data *data; + grub_disk_t disk = device->disk; + struct grub_hfsplus_catkey *catkey; + int i, label_len; + grub_uint16_t *label_name; + struct grub_hfsplus_key_internal intern; + struct grub_hfsplus_btnode *node = NULL; + grub_disk_addr_t ptr = 0; + + *label = 0; + + data = grub_hfsplus_mount (disk); + if (!data) + return grub_errno; + + /* Create a key that points to the label. */ + intern.catkey.parent = 1; + intern.catkey.name = 0; + intern.catkey.namelen = 0; + + /* First lookup the first entry. */ + if (grub_hfsplus_btree_search (&data->catalog_tree, &intern, + grub_hfsplus_cmp_catkey_id, &node, &ptr) + || !node) + { + grub_free (data); + return 0; + } + + catkey = (struct grub_hfsplus_catkey *) + grub_hfsplus_btree_recptr (&data->catalog_tree, node, ptr); + + label_len = grub_be_to_cpu16 (catkey->namelen); + + /* Ensure that the length is >= 0. */ + if (label_len < 0) + label_len = 0; + + /* Ensure label length is at most 255 Unicode characters. */ + if (label_len > 255) + label_len = 255; + + label_name = grub_calloc (label_len, sizeof (*label_name)); + if (!label_name) + { + grub_free (node); + grub_free (data); + return grub_errno; + } + + for (i = 0; i < label_len; i++) + { + label_name[i] = grub_be_to_cpu16 (catkey->name[i]); + + /* If the name is obviously invalid, skip this node. */ + if (label_name[i] == 0) + { + grub_free (label_name); + grub_free (node); + grub_free (data); + return 0; + } + } + + *label = grub_calloc (label_len, GRUB_MAX_UTF8_PER_UTF16 + 1); + if (! *label) + { + grub_free (label_name); + grub_free (node); + grub_free (data); + return grub_errno; + } + + *grub_utf16_to_utf8 ((grub_uint8_t *) (*label), label_name, + label_len) = '\0'; + + grub_free (label_name); + grub_free (node); + grub_free (data); + + return GRUB_ERR_NONE; } /* Get mtime. */ static grub_err_t -grub_hfsplus_mtime (grub_device_t device, grub_int32_t *tm) +grub_hfsplus_mtime (grub_device_t device, grub_int64_t *tm) { struct grub_hfsplus_data *data; grub_disk_t disk = device->disk; @@ -1014,21 +1160,23 @@ grub_hfsplus_uuid (grub_device_t device, char **uuid) static struct grub_fs grub_hfsplus_fs = { .name = "hfsplus", - .dir = grub_hfsplus_dir, - .open = grub_hfsplus_open, - .read = grub_hfsplus_read, - .close = grub_hfsplus_close, - .label = grub_hfsplus_label, - .mtime = grub_hfsplus_mtime, - .uuid = grub_hfsplus_uuid, + .fs_dir = grub_hfsplus_dir, + .fs_open = grub_hfsplus_open, + .fs_read = grub_hfsplus_read, + .fs_close = grub_hfsplus_close, + .fs_label = grub_hfsplus_label, + .fs_mtime = grub_hfsplus_mtime, + .fs_uuid = grub_hfsplus_uuid, #ifdef GRUB_UTIL .reserved_first_sector = 1, + .blocklist_install = 1, #endif .next = 0 }; GRUB_MOD_INIT(hfsplus) { + grub_hfsplus_fs.mod = mod; grub_fs_register (&grub_hfsplus_fs); my_mod = mod; } diff --git a/grub-core/fs/hfspluscomp.c b/grub-core/fs/hfspluscomp.c new file mode 100644 index 000000000..a80954ee6 --- /dev/null +++ b/grub-core/fs/hfspluscomp.c @@ -0,0 +1,322 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + +/* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* big-endian. */ +struct grub_hfsplus_compress_header1 +{ + grub_uint32_t header_size; + grub_uint32_t end_descriptor_offset; + grub_uint32_t total_compressed_size_including_seek_blocks_and_header2; + grub_uint32_t value_0x32; + grub_uint8_t unused[0xf0]; +} GRUB_PACKED; + +/* big-endian. */ +struct grub_hfsplus_compress_header2 +{ + grub_uint32_t total_compressed_size_including_seek_blocks; +} GRUB_PACKED; + +/* little-endian. */ +struct grub_hfsplus_compress_header3 +{ + grub_uint32_t num_chunks; +} GRUB_PACKED; + +/* little-endian. */ +struct grub_hfsplus_compress_block_descriptor +{ + grub_uint32_t offset; + grub_uint32_t size; +}; + +struct grub_hfsplus_compress_end_descriptor +{ + grub_uint8_t always_the_same[50]; +} GRUB_PACKED; + +struct grub_hfsplus_attr_header +{ + grub_uint8_t unused[3]; + grub_uint8_t type; + grub_uint32_t unknown[1]; + grub_uint64_t size; +} GRUB_PACKED; + +struct grub_hfsplus_compress_attr +{ + grub_uint32_t magic; + grub_uint32_t type; + grub_uint32_t uncompressed_inline_size; + grub_uint32_t always_0; +} GRUB_PACKED; + +enum + { + HFSPLUS_COMPRESSION_INLINE = 3, + HFSPLUS_COMPRESSION_RESOURCE = 4 + }; + +static int +grub_hfsplus_cmp_attrkey (struct grub_hfsplus_key *keya, + struct grub_hfsplus_key_internal *keyb) +{ + struct grub_hfsplus_attrkey *attrkey_a = &keya->attrkey; + struct grub_hfsplus_attrkey_internal *attrkey_b = &keyb->attrkey; + grub_uint32_t aparent = grub_be_to_cpu32 (attrkey_a->cnid); + grub_size_t len; + int diff; + + if (aparent > attrkey_b->cnid) + return 1; + if (aparent < attrkey_b->cnid) + return -1; + + len = grub_be_to_cpu16 (attrkey_a->namelen); + if (len > attrkey_b->namelen) + len = attrkey_b->namelen; + /* Since it's big-endian memcmp gives the same result as manually comparing + uint16_t but may be faster. */ + diff = grub_memcmp (attrkey_a->name, attrkey_b->name, + len * sizeof (attrkey_a->name[0])); + if (diff == 0) + diff = grub_be_to_cpu16 (attrkey_a->namelen) - attrkey_b->namelen; + return diff; +} + +#define HFSPLUS_COMPRESS_BLOCK_SIZE 65536 + +static grub_ssize_t +hfsplus_read_compressed_real (struct grub_hfsplus_file *node, + grub_off_t pos, grub_size_t len, char *buf) +{ + char *tmp_buf = 0; + grub_size_t len0 = len; + + if (node->compressed == 1) + { + grub_memcpy (buf, node->cbuf + pos, len); + if (grub_file_progress_hook && node->file) + grub_file_progress_hook (0, 0, len, NULL, node->file); + return len; + } + + while (len) + { + grub_uint32_t block = pos / HFSPLUS_COMPRESS_BLOCK_SIZE; + grub_size_t curlen = HFSPLUS_COMPRESS_BLOCK_SIZE + - (pos % HFSPLUS_COMPRESS_BLOCK_SIZE); + + if (curlen > len) + curlen = len; + + if (node->cbuf_block != block) + { + grub_uint32_t sz = grub_le_to_cpu32 (node->compress_index[block].size); + grub_size_t ts; + if (!tmp_buf) + tmp_buf = grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE); + if (!tmp_buf) + return -1; + if (grub_hfsplus_read_file (node, 0, 0, + grub_le_to_cpu32 (node->compress_index[block].start) + 0x104, + sz, tmp_buf) + != (grub_ssize_t) sz) + { + grub_free (tmp_buf); + return -1; + } + ts = HFSPLUS_COMPRESS_BLOCK_SIZE; + if (ts > node->size - (pos & ~(HFSPLUS_COMPRESS_BLOCK_SIZE))) + ts = node->size - (pos & ~(HFSPLUS_COMPRESS_BLOCK_SIZE)); + if (grub_zlib_decompress (tmp_buf, sz, 0, + node->cbuf, ts) != (grub_ssize_t) ts) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "premature end of compressed"); + + grub_free (tmp_buf); + return -1; + } + node->cbuf_block = block; + } + grub_memcpy (buf, node->cbuf + (pos % HFSPLUS_COMPRESS_BLOCK_SIZE), + curlen); + if (grub_file_progress_hook && node->file) + grub_file_progress_hook (0, 0, curlen, NULL, node->file); + buf += curlen; + pos += curlen; + len -= curlen; + } + grub_free (tmp_buf); + return len0; +} + +static grub_err_t +hfsplus_open_compressed_real (struct grub_hfsplus_file *node) +{ + grub_err_t err; + struct grub_hfsplus_btnode *attr_node; + grub_off_t attr_off; + struct grub_hfsplus_key_internal key; + struct grub_hfsplus_attr_header *attr_head; + struct grub_hfsplus_compress_attr *cmp_head; +#define c grub_cpu_to_be16_compile_time + const grub_uint16_t compress_attr_name[] = + { + c('c'), c('o'), c('m'), c('.'), c('a'), c('p'), c('p'), c('l'), c('e'), + c('.'), c('d'), c('e'), c('c'), c('m'), c('p'), c('f'), c('s') }; +#undef c + if (node->size) + return 0; + + key.attrkey.cnid = node->fileid; + key.attrkey.namelen = sizeof (compress_attr_name) / sizeof (compress_attr_name[0]); + key.attrkey.name = compress_attr_name; + + err = grub_hfsplus_btree_search (&node->data->attr_tree, &key, + grub_hfsplus_cmp_attrkey, + &attr_node, &attr_off); + if (err || !attr_node) + { + grub_errno = 0; + return 0; + } + + attr_head = (struct grub_hfsplus_attr_header *) + ((char *) grub_hfsplus_btree_recptr (&node->data->attr_tree, + attr_node, attr_off) + + sizeof (struct grub_hfsplus_attrkey) + sizeof (compress_attr_name)); + if (attr_head->type != 0x10 + || !(attr_head->size & grub_cpu_to_be64_compile_time(~0xfULL))) + { + grub_free (attr_node); + return 0; + } + cmp_head = (struct grub_hfsplus_compress_attr *) (attr_head + 1); + if (cmp_head->magic != grub_cpu_to_be32_compile_time (0x66706d63)) + { + grub_free (attr_node); + return 0; + } + node->size = grub_le_to_cpu32 (cmp_head->uncompressed_inline_size); + + if (cmp_head->type == grub_cpu_to_le32_compile_time (HFSPLUS_COMPRESSION_RESOURCE)) + { + grub_uint32_t index_size; + node->compressed = 2; + + if (grub_hfsplus_read_file (node, 0, 0, + 0x104, sizeof (index_size), + (char *) &index_size) + != 4) + { + node->compressed = 0; + grub_free (attr_node); + grub_errno = 0; + return 0; + } + node->compress_index_size = grub_le_to_cpu32 (index_size); + node->compress_index = grub_calloc (node->compress_index_size, + sizeof (node->compress_index[0])); + if (!node->compress_index) + { + node->compressed = 0; + grub_free (attr_node); + return grub_errno; + } + + /* + * The node->compress_index_size * sizeof (node->compress_index[0]) is safe here + * due to relevant checks done in grub_calloc() above. + */ + if (grub_hfsplus_read_file (node, 0, 0, + 0x104 + sizeof (index_size), + node->compress_index_size + * sizeof (node->compress_index[0]), + (char *) node->compress_index) + != (grub_ssize_t) (node->compress_index_size + * sizeof (node->compress_index[0]))) + { + node->compressed = 0; + grub_free (attr_node); + grub_free (node->compress_index); + grub_errno = 0; + return 0; + } + + node->cbuf_block = -1; + + node->cbuf = grub_malloc (HFSPLUS_COMPRESS_BLOCK_SIZE); + grub_free (attr_node); + if (!node->cbuf) + { + node->compressed = 0; + grub_free (node->compress_index); + return grub_errno; + } + return 0; + } + if (cmp_head->type != HFSPLUS_COMPRESSION_INLINE) + { + grub_free (attr_node); + return 0; + } + + node->cbuf = grub_malloc (node->size); + if (!node->cbuf) + return grub_errno; + + if (grub_zlib_decompress ((char *) (cmp_head + 1), + grub_cpu_to_be64 (attr_head->size) + - sizeof (*cmp_head), 0, + node->cbuf, node->size) + != (grub_ssize_t) node->size) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "premature end of compressed"); + return grub_errno; + } + node->compressed = 1; + return 0; +} + +GRUB_MOD_INIT(hfspluscomp) +{ + grub_hfsplus_open_compressed = hfsplus_open_compressed_real; + grub_hfsplus_read_compressed = hfsplus_read_compressed_real; +} + +GRUB_MOD_FINI(hfspluscomp) +{ + grub_hfsplus_open_compressed = 0; + grub_hfsplus_read_compressed = 0; +} diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c new file mode 100644 index 000000000..c73cb9ce0 --- /dev/null +++ b/grub-core/fs/iso9660.c @@ -0,0 +1,1271 @@ +/* iso9660.c - iso9660 implementation with extensions: + SUSP, Rock Ridge. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_ISO9660_FSTYPE_DIR 0040000 +#define GRUB_ISO9660_FSTYPE_REG 0100000 +#define GRUB_ISO9660_FSTYPE_SYMLINK 0120000 +#define GRUB_ISO9660_FSTYPE_MASK 0170000 + +#define GRUB_ISO9660_LOG2_BLKSZ 2 +#define GRUB_ISO9660_BLKSZ 2048 + +#define GRUB_ISO9660_RR_DOT 2 +#define GRUB_ISO9660_RR_DOTDOT 4 + +#define GRUB_ISO9660_VOLDESC_BOOT 0 +#define GRUB_ISO9660_VOLDESC_PRIMARY 1 +#define GRUB_ISO9660_VOLDESC_SUPP 2 +#define GRUB_ISO9660_VOLDESC_PART 3 +#define GRUB_ISO9660_VOLDESC_END 255 + +#define GRUB_ISO9660_SUSP_HEADER_SZ 4 +#define GRUB_ISO9660_MAX_CE_HOPS 100000 + +/* The head of a volume descriptor. */ +struct grub_iso9660_voldesc +{ + grub_uint8_t type; + grub_uint8_t magic[5]; + grub_uint8_t version; +} GRUB_PACKED; + +struct grub_iso9660_date2 +{ + grub_uint8_t year; + grub_uint8_t month; + grub_uint8_t day; + grub_uint8_t hour; + grub_uint8_t minute; + grub_uint8_t second; + grub_uint8_t offset; +} GRUB_PACKED; + +/* A directory entry. */ +struct grub_iso9660_dir +{ + grub_uint8_t len; + grub_uint8_t ext_sectors; + grub_uint32_t first_sector; + grub_uint32_t first_sector_be; + grub_uint32_t size; + grub_uint32_t size_be; + struct grub_iso9660_date2 mtime; + grub_uint8_t flags; + grub_uint8_t unused2[6]; +#define MAX_NAMELEN 255 + grub_uint8_t namelen; +} GRUB_PACKED; + +struct grub_iso9660_date +{ + grub_uint8_t year[4]; + grub_uint8_t month[2]; + grub_uint8_t day[2]; + grub_uint8_t hour[2]; + grub_uint8_t minute[2]; + grub_uint8_t second[2]; + grub_uint8_t hundredth[2]; + grub_uint8_t offset; +} GRUB_PACKED; + +/* The primary volume descriptor. Only little endian is used. */ +struct grub_iso9660_primary_voldesc +{ + struct grub_iso9660_voldesc voldesc; + grub_uint8_t unused1[33]; + grub_uint8_t volname[32]; + grub_uint8_t unused2[16]; + grub_uint8_t escape[32]; + grub_uint8_t unused3[12]; + grub_uint32_t path_table_size; + grub_uint8_t unused4[4]; + grub_uint32_t path_table; + grub_uint8_t unused5[12]; + struct grub_iso9660_dir rootdir; + grub_uint8_t unused6[624]; + struct grub_iso9660_date created; + struct grub_iso9660_date modified; +} GRUB_PACKED; + +/* A single entry in the path table. */ +struct grub_iso9660_path +{ + grub_uint8_t len; + grub_uint8_t sectors; + grub_uint32_t first_sector; + grub_uint16_t parentdir; + grub_uint8_t name[0]; +} GRUB_PACKED; + +/* An entry in the System Usage area of the directory entry. */ +struct grub_iso9660_susp_entry +{ + grub_uint8_t sig[2]; + grub_uint8_t len; + grub_uint8_t version; + grub_uint8_t data[0]; +} GRUB_PACKED; + +/* The CE entry. This is used to describe the next block where data + can be found. */ +struct grub_iso9660_susp_ce +{ + struct grub_iso9660_susp_entry entry; + grub_uint32_t blk; + grub_uint32_t blk_be; + grub_uint32_t off; + grub_uint32_t off_be; + grub_uint32_t len; + grub_uint32_t len_be; +} GRUB_PACKED; + +struct grub_iso9660_data +{ + struct grub_iso9660_primary_voldesc voldesc; + grub_disk_t disk; + int rockridge; + int susp_skip; + int joliet; + struct grub_fshelp_node *node; +}; + +struct grub_fshelp_node +{ + struct grub_iso9660_data *data; + grub_size_t have_dirents, alloc_dirents; + int have_symlink; + struct grub_iso9660_dir dirents[8]; + char symlink[0]; +}; + +enum + { + FLAG_TYPE_PLAIN = 0, + FLAG_TYPE_DIR = 2, + FLAG_TYPE = 3, + FLAG_MORE_EXTENTS = 0x80 + }; + +static grub_dl_t my_mod; + + +static grub_err_t +iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int64_t *nix) +{ + struct grub_datetime datetime; + + if (! i->year[0] && ! i->year[1] + && ! i->year[2] && ! i->year[3] + && ! i->month[0] && ! i->month[1] + && ! i->day[0] && ! i->day[1] + && ! i->hour[0] && ! i->hour[1] + && ! i->minute[0] && ! i->minute[1] + && ! i->second[0] && ! i->second[1] + && ! i->hundredth[0] && ! i->hundredth[1]) + return grub_error (GRUB_ERR_BAD_NUMBER, "empty date"); + datetime.year = (i->year[0] - '0') * 1000 + (i->year[1] - '0') * 100 + + (i->year[2] - '0') * 10 + (i->year[3] - '0'); + datetime.month = (i->month[0] - '0') * 10 + (i->month[1] - '0'); + datetime.day = (i->day[0] - '0') * 10 + (i->day[1] - '0'); + datetime.hour = (i->hour[0] - '0') * 10 + (i->hour[1] - '0'); + datetime.minute = (i->minute[0] - '0') * 10 + (i->minute[1] - '0'); + datetime.second = (i->second[0] - '0') * 10 + (i->second[1] - '0'); + + if (!grub_datetime2unixtime (&datetime, nix)) + return grub_error (GRUB_ERR_BAD_NUMBER, "incorrect date"); + *nix -= i->offset * 60 * 15; + return GRUB_ERR_NONE; +} + +static int +iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int64_t *nix) +{ + struct grub_datetime datetime; + + datetime.year = i->year + 1900; + datetime.month = i->month; + datetime.day = i->day; + datetime.hour = i->hour; + datetime.minute = i->minute; + datetime.second = i->second; + + if (!grub_datetime2unixtime (&datetime, nix)) + return 0; + *nix -= i->offset * 60 * 15; + return 1; +} + +static grub_err_t +read_node (grub_fshelp_node_t node, grub_off_t off, grub_size_t len, char *buf) +{ + grub_size_t i = 0; + + while (len > 0) + { + grub_size_t toread; + grub_err_t err; + while (i < node->have_dirents + && off >= grub_le_to_cpu32 (node->dirents[i].size)) + { + off -= grub_le_to_cpu32 (node->dirents[i].size); + i++; + } + if (i == node->have_dirents) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "read out of range"); + toread = grub_le_to_cpu32 (node->dirents[i].size); + if (toread > len) + toread = len; + err = grub_disk_read (node->data->disk, + ((grub_disk_addr_t) grub_le_to_cpu32 (node->dirents[i].first_sector)) << GRUB_ISO9660_LOG2_BLKSZ, + off, toread, buf); + if (err) + return err; + len -= toread; + off += toread; + buf += toread; + } + return GRUB_ERR_NONE; +} + +/* Iterate over the susp entries, starting with block SUA_BLOCK on the + offset SUA_POS with a size of SUA_SIZE bytes. Hook is called for + every entry. */ +static grub_err_t +grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off, + grub_ssize_t sua_size, + grub_err_t (*hook) + (struct grub_iso9660_susp_entry *entry, void *hook_arg), + void *hook_arg) +{ + char *sua; + struct grub_iso9660_susp_entry *entry; + grub_err_t err; + int ce_counter = 0; + grub_ssize_t ce_sua_size = 0; + grub_off_t ce_off; + grub_disk_addr_t ce_block; + + if (sua_size <= 0) + return GRUB_ERR_NONE; + + if (sua_size < GRUB_ISO9660_SUSP_HEADER_SZ) + return grub_error (GRUB_ERR_BAD_FS, "invalid susp entry size"); + + sua = grub_malloc (sua_size); + if (!sua) + return grub_errno; + + /* Load a part of the System Usage Area. */ + err = read_node (node, off, sua_size, sua); + if (err) + { + grub_free (sua); + return err; + } + + entry = (struct grub_iso9660_susp_entry *) sua; + + next_susp_area: + while (entry->len > 0) + { + /* Ensure the entry is within System Use Area. */ + if ((char *) entry + entry->len > (sua + sua_size)) + break; + + /* The last entry. */ + if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0) + break; + + /* Additional entries are stored elsewhere. */ + if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0) + { + struct grub_iso9660_susp_ce *ce; + + if (ce_sua_size > 0) + { + grub_free (sua); + return grub_error (GRUB_ERR_BAD_FS, + "more than one CE entry in SUSP area"); + } + + /* Buffer CE parameters for use after the end of this loop. */ + ce = (struct grub_iso9660_susp_ce *) entry; + ce_sua_size = grub_le_to_cpu32 (ce->len); + ce_off = grub_le_to_cpu32 (ce->off); + ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ; + } + else if (hook (entry, hook_arg)) + { + grub_free (sua); + return 0; + } + + entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len); + + if (((sua + sua_size) - (char *) entry) < GRUB_ISO9660_SUSP_HEADER_SZ) + break; + } + + if (ce_sua_size > 0) + { + /* Load the next System Use Area by buffered CE entry parameters. */ + if (++ce_counter > GRUB_ISO9660_MAX_CE_HOPS) + { + grub_free (sua); + return grub_error (GRUB_ERR_BAD_FS, "suspecting endless CE loop"); + } + if (ce_sua_size < GRUB_ISO9660_SUSP_HEADER_SZ) + { + grub_free (sua); + return grub_error (GRUB_ERR_BAD_FS, "invalid continuation area in CE entry"); + } + + grub_free (sua); + sua = grub_malloc (ce_sua_size); + if (!sua) + return grub_errno; + + err = grub_disk_read (node->data->disk, ce_block, ce_off, ce_sua_size, sua); + if (err) + { + grub_free (sua); + return err; + } + entry = (struct grub_iso9660_susp_entry *) sua; + sua_size = ce_sua_size; + ce_sua_size = 0; + + goto next_susp_area; + } + + grub_free (sua); + return 0; +} + +static char * +grub_iso9660_convert_string (grub_uint8_t *us, int len) +{ + char *p; + int i; + grub_uint16_t t[MAX_NAMELEN / 2 + 1]; + + p = grub_calloc (len, GRUB_MAX_UTF8_PER_UTF16 + 1); + if (! p) + return NULL; + + for (i=0; isig, "ER", 2) == 0) + { + data->rockridge = 1; + return 1; + } + return 0; +} + +static grub_err_t +set_rockridge (struct grub_iso9660_data *data) +{ + int sua_pos; + int sua_size; + char *sua; + struct grub_iso9660_dir rootdir; + struct grub_iso9660_susp_entry *entry; + + data->rockridge = 0; + + /* Read the system use area and test it to see if SUSP is + supported. */ + if (grub_disk_read (data->disk, + (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector) + << GRUB_ISO9660_LOG2_BLKSZ), 0, + sizeof (rootdir), (char *) &rootdir)) + return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + + sua_pos = (sizeof (rootdir) + rootdir.namelen + + (rootdir.namelen % 2) - 1); + sua_size = rootdir.len - sua_pos; + + if (!sua_size) + return GRUB_ERR_NONE; + + if (sua_size < GRUB_ISO9660_SUSP_HEADER_SZ) + return grub_error (GRUB_ERR_BAD_FS, "invalid rock ridge entry size"); + + sua = grub_malloc (sua_size); + if (! sua) + return grub_errno; + + if (grub_disk_read (data->disk, + (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector) + << GRUB_ISO9660_LOG2_BLKSZ), sua_pos, + sua_size, sua)) + { + grub_free (sua); + return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + } + + entry = (struct grub_iso9660_susp_entry *) sua; + + /* Test if the SUSP protocol is used on this filesystem. */ + if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0) + { + struct grub_fshelp_node rootnode; + + rootnode.data = data; + rootnode.alloc_dirents = ARRAY_SIZE (rootnode.dirents); + rootnode.have_dirents = 1; + rootnode.have_symlink = 0; + rootnode.dirents[0] = data->voldesc.rootdir; + + /* The size of SP (version 1) is fixed to 7. */ + if (sua_size < 7 || entry->len < 7) + { + grub_free (sua); + return grub_error (GRUB_ERR_BAD_FS, "corrupted rock ridge entry"); + } + + /* + * The 2nd data byte stored how many bytes are skipped every time + * to get to the SUA (System Usage Area). + */ + data->susp_skip = entry->data[2]; + entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len); + + /* Iterate over the entries in the SUA area to detect + extensions. */ + if (grub_iso9660_susp_iterate (&rootnode, + sua_pos, sua_size, susp_iterate_set_rockridge, + data)) + { + grub_free (sua); + return grub_errno; + } + } + grub_free (sua); + return GRUB_ERR_NONE; +} + +static struct grub_iso9660_data * +grub_iso9660_mount (grub_disk_t disk) +{ + struct grub_iso9660_data *data = 0; + struct grub_iso9660_primary_voldesc voldesc; + int block; + + data = grub_zalloc (sizeof (struct grub_iso9660_data)); + if (! data) + return 0; + + data->disk = disk; + + block = 16; + do + { + int copy_voldesc = 0; + + /* Read the superblock. */ + if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0, + sizeof (struct grub_iso9660_primary_voldesc), + (char *) &voldesc)) + { + grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + goto fail; + } + + if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0) + { + grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + goto fail; + } + + if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY) + copy_voldesc = 1; + else if (!data->rockridge + && (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP) + && (voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f) + && + ((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1. */ + (voldesc.escape[2] == 0x43) || /* UCS-2 Level 2. */ + (voldesc.escape[2] == 0x45))) /* UCS-2 Level 3. */ + { + copy_voldesc = 1; + data->joliet = 1; + } + + if (copy_voldesc) + { + grub_memcpy((char *) &data->voldesc, (char *) &voldesc, + sizeof (struct grub_iso9660_primary_voldesc)); + if (set_rockridge (data)) + goto fail; + } + + block++; + } while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END); + + return data; + + fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + + grub_free (data); + return 0; +} + + +static char * +grub_iso9660_read_symlink (grub_fshelp_node_t node) +{ + return node->have_symlink + ? grub_strdup (node->symlink + + (node->have_dirents) * sizeof (node->dirents[0]) + - sizeof (node->dirents)) : grub_strdup (""); +} + +static grub_off_t +get_node_size (grub_fshelp_node_t node) +{ + grub_off_t ret = 0; + grub_size_t i; + + for (i = 0; i < node->have_dirents; i++) + ret += grub_le_to_cpu32 (node->dirents[i].size); + return ret; +} + +struct iterate_dir_ctx +{ + char *filename; + int filename_alloc; + enum grub_fshelp_filetype type; + char *symlink; + int was_continue; +}; + + /* Extend the symlink. */ +static void +add_part (struct iterate_dir_ctx *ctx, + const char *part, + int len2) +{ + int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0; + grub_size_t sz; + char *new; + + if (grub_add (size, len2, &sz) || + grub_add (sz, 1, &sz)) + return; + + new = grub_realloc (ctx->symlink, sz); + if (!new) + { + grub_free (ctx->symlink); + ctx->symlink = NULL; + return; + } + ctx->symlink = new; + + grub_memcpy (ctx->symlink + size, part, len2); + ctx->symlink[size + len2] = 0; +} + +static grub_err_t +susp_iterate_dir (struct grub_iso9660_susp_entry *entry, + void *_ctx) +{ + struct iterate_dir_ctx *ctx = _ctx; + + /* The filename in the rock ridge entry. */ + if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0) + { + /* The flags are stored at the data position 0, here the + filename type is stored. */ + /* FIXME: Fix this slightly improper cast. */ + if (entry->data[0] & GRUB_ISO9660_RR_DOT) + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) "."; + } + else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT) + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) ".."; + } + else if (entry->len >= 5) + { + grub_size_t off = 0, csize = 1; + char *old; + grub_size_t sz; + + csize = entry->len - 5; + old = ctx->filename; + if (ctx->filename_alloc) + { + off = grub_strlen (ctx->filename); + if (grub_add (csize, off, &sz) || + grub_add (sz, 1, &sz)) + return GRUB_ERR_OUT_OF_RANGE; + ctx->filename = grub_realloc (ctx->filename, sz); + } + else + { + off = 0; + if (grub_add (csize, 1, &sz)) + return GRUB_ERR_OUT_OF_RANGE; + ctx->filename = grub_zalloc (sz); + } + if (!ctx->filename) + { + ctx->filename = old; + return grub_errno; + } + ctx->filename_alloc = 1; + grub_memcpy (ctx->filename + off, (char *) &entry->data[1], csize); + ctx->filename[off + csize] = '\0'; + } + } + /* The mode information (st_mode). */ + else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0) + { + /* At position 0 of the PX record the st_mode information is + stored (little-endian). */ + grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8)) + & GRUB_ISO9660_FSTYPE_MASK); + + switch (mode) + { + case GRUB_ISO9660_FSTYPE_DIR: + ctx->type = GRUB_FSHELP_DIR; + break; + case GRUB_ISO9660_FSTYPE_REG: + ctx->type = GRUB_FSHELP_REG; + break; + case GRUB_ISO9660_FSTYPE_SYMLINK: + ctx->type = GRUB_FSHELP_SYMLINK; + break; + default: + ctx->type = GRUB_FSHELP_UNKNOWN; + } + } + else if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0) + { + unsigned int pos = 1; + unsigned int csize; + + /* The symlink is not stored as a POSIX symlink, translate it. */ + while ((pos + GRUB_ISO9660_SUSP_HEADER_SZ + 1) < entry->len) + { + /* + * entry->len is GRUB_ISO9660_SUSP_HEADER_SZ + 1 (the FLAGS) + + * length of the "Component Area". The length of a component + * record is 2 (pos and pos + 1) plus the "Component Content", + * of which starts at pos + 2. entry->data[pos] is the + * "Component Flags"; entry->data[pos + 1] is the length + * of the component. + */ + csize = entry->data[pos + 1] + 2; + if (GRUB_ISO9660_SUSP_HEADER_SZ + 1 + csize > entry->len) + break; + + /* The current position is the `Component Flag'. */ + switch (entry->data[pos] & 30) + { + case 0: + { + /* The data on pos + 2 is the actual data, pos + 1 + is the length. Both are part of the `Component + Record'. */ + if (ctx->symlink && !ctx->was_continue) + { + add_part (ctx, "/", 1); + if (grub_errno) + return grub_errno; + } + + add_part (ctx, (char *) &entry->data[pos + 2], + entry->data[pos + 1]); + ctx->was_continue = (entry->data[pos] & 1); + break; + } + + case 2: + add_part (ctx, "./", 2); + break; + + case 4: + add_part (ctx, "../", 3); + break; + + case 8: + add_part (ctx, "/", 1); + break; + } + + /* Check if grub_realloc() failed in add_part(). */ + if (grub_errno) + return grub_errno; + + /* In pos + 1 the length of the `Component Record' is + stored. */ + pos += entry->data[pos + 1] + 2; + } + + /* Check if `grub_realloc' failed. */ + if (grub_errno) + return grub_errno; + } + + return 0; +} + +static int +grub_iso9660_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + struct grub_iso9660_dir dirent; + grub_off_t offset = 0; + grub_off_t len; + struct iterate_dir_ctx ctx; + + len = get_node_size (dir); + + for (; offset < len; offset += dirent.len) + { + ctx.symlink = 0; + ctx.was_continue = 0; + + if (read_node (dir, offset, sizeof (dirent), (char *) &dirent)) + return 0; + + /* The end of the block, skip to the next one. */ + if (!dirent.len) + { + offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ; + continue; + } + + { + char name[MAX_NAMELEN + 1]; + int nameoffset = offset + sizeof (dirent); + struct grub_fshelp_node *node; + int sua_off = (sizeof (dirent) + dirent.namelen + 1 + - (dirent.namelen % 2)); + int sua_size = dirent.len - sua_off; + + sua_off += offset + dir->data->susp_skip; + + ctx.filename = 0; + ctx.filename_alloc = 0; + ctx.type = GRUB_FSHELP_UNKNOWN; + + if (dir->data->rockridge + && grub_iso9660_susp_iterate (dir, sua_off, sua_size, + susp_iterate_dir, &ctx)) + return 0; + + /* Read the name. */ + if (read_node (dir, nameoffset, dirent.namelen, (char *) name)) + return 0; + + node = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!node) + return 0; + + node->alloc_dirents = ARRAY_SIZE (node->dirents); + node->have_dirents = 1; + + /* Setup a new node. */ + node->data = dir->data; + node->have_symlink = 0; + + /* If the filetype was not stored using rockridge, use + whatever is stored in the iso9660 filesystem. */ + if (ctx.type == GRUB_FSHELP_UNKNOWN) + { + if ((dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR) + ctx.type = GRUB_FSHELP_DIR; + else + ctx.type = GRUB_FSHELP_REG; + } + + /* . and .. */ + if (!ctx.filename && dirent.namelen == 1 && name[0] == 0) + ctx.filename = (char *) "."; + + if (!ctx.filename && dirent.namelen == 1 && name[0] == 1) + ctx.filename = (char *) ".."; + + /* The filename was not stored in a rock ridge entry. Read it + from the iso9660 filesystem. */ + if (!dir->data->joliet && !ctx.filename) + { + char *ptr; + name[dirent.namelen] = '\0'; + ctx.filename = grub_strrchr (name, ';'); + if (ctx.filename) + *ctx.filename = '\0'; + /* ISO9660 names are not case-preserving. */ + ctx.type |= GRUB_FSHELP_CASE_INSENSITIVE; + for (ptr = name; *ptr; ptr++) + *ptr = grub_tolower (*ptr); + if (ptr != name && *(ptr - 1) == '.') + *(ptr - 1) = 0; + ctx.filename = name; + } + + if (dir->data->joliet && !ctx.filename) + { + char *semicolon; + + ctx.filename = grub_iso9660_convert_string + ((grub_uint8_t *) name, dirent.namelen >> 1); + + semicolon = grub_strrchr (ctx.filename, ';'); + if (semicolon) + *semicolon = '\0'; + + ctx.filename_alloc = 1; + } + + node->dirents[0] = dirent; + while (dirent.flags & FLAG_MORE_EXTENTS) + { + offset += dirent.len; + + /* offset should within the dir's len. */ + if (offset > len) + { + if (ctx.filename_alloc) + grub_free (ctx.filename); + grub_free (node); + return 0; + } + + if (read_node (dir, offset, sizeof (dirent), (char *) &dirent)) + { + if (ctx.filename_alloc) + grub_free (ctx.filename); + grub_free (node); + return 0; + } + + /* + * It is either the end of block or zero-padded sector, + * skip to the next block. + */ + if (!dirent.len) + { + offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ; + dirent.flags |= FLAG_MORE_EXTENTS; + continue; + } + + if (node->have_dirents >= node->alloc_dirents) + { + struct grub_fshelp_node *new_node; + grub_size_t sz; + + if (grub_mul (node->alloc_dirents, 2, &node->alloc_dirents) || + grub_sub (node->alloc_dirents, ARRAY_SIZE (node->dirents), &sz) || + grub_mul (sz, sizeof (node->dirents[0]), &sz) || + grub_add (sz, sizeof (struct grub_fshelp_node), &sz)) + goto fail_0; + + new_node = grub_realloc (node, sz); + if (!new_node) + { + fail_0: + if (ctx.filename_alloc) + grub_free (ctx.filename); + grub_free (node); + return 0; + } + node = new_node; + } + node->dirents[node->have_dirents++] = dirent; + } + if (ctx.symlink) + { + if ((node->alloc_dirents - node->have_dirents) + * sizeof (node->dirents[0]) < grub_strlen (ctx.symlink) + 1) + { + struct grub_fshelp_node *new_node; + grub_size_t sz; + + if (grub_sub (node->alloc_dirents, ARRAY_SIZE (node->dirents), &sz) || + grub_mul (sz, sizeof (node->dirents[0]), &sz) || + grub_add (sz, sizeof (struct grub_fshelp_node) + 1, &sz) || + grub_add (sz, grub_strlen (ctx.symlink), &sz)) + goto fail_1; + + new_node = grub_realloc (node, sz); + if (!new_node) + { + fail_1: + if (ctx.filename_alloc) + grub_free (ctx.filename); + grub_free (node); + return 0; + } + node = new_node; + } + node->have_symlink = 1; + grub_strcpy (node->symlink + + node->have_dirents * sizeof (node->dirents[0]) + - sizeof (node->dirents), ctx.symlink); + grub_free (ctx.symlink); + ctx.symlink = 0; + ctx.was_continue = 0; + } + if (hook (ctx.filename, ctx.type, node, hook_data)) + { + if (ctx.filename_alloc) + grub_free (ctx.filename); + return 1; + } + if (ctx.filename_alloc) + grub_free (ctx.filename); + } + } + + return 0; +} + + + +/* Context for grub_iso9660_dir. */ +struct grub_iso9660_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_iso9660_dir. */ +static int +grub_iso9660_dir_iter (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_iso9660_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime); + + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_iso9660_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_iso9660_dir_ctx ctx = { hook, hook_data }; + struct grub_iso9660_data *data = 0; + struct grub_fshelp_node rootnode; + struct grub_fshelp_node *foundnode; + + grub_dl_ref (my_mod); + + data = grub_iso9660_mount (device->disk); + if (! data) + goto fail; + + rootnode.data = data; + rootnode.alloc_dirents = 0; + rootnode.have_dirents = 1; + rootnode.have_symlink = 0; + rootnode.dirents[0] = data->voldesc.rootdir; + + /* Use the fshelp function to traverse the path. */ + if (grub_fshelp_find_file (path, &rootnode, + &foundnode, + grub_iso9660_iterate_dir, + grub_iso9660_read_symlink, + GRUB_FSHELP_DIR)) + goto fail; + + /* List the files in the directory. */ + grub_iso9660_iterate_dir (foundnode, grub_iso9660_dir_iter, &ctx); + + if (foundnode != &rootnode) + grub_free (foundnode); + + fail: + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_iso9660_open (struct grub_file *file, const char *name) +{ + struct grub_iso9660_data *data; + struct grub_fshelp_node rootnode; + struct grub_fshelp_node *foundnode; + + grub_dl_ref (my_mod); + + data = grub_iso9660_mount (file->device->disk); + if (!data) + goto fail; + + rootnode.data = data; + rootnode.alloc_dirents = 0; + rootnode.have_dirents = 1; + rootnode.have_symlink = 0; + rootnode.dirents[0] = data->voldesc.rootdir; + + /* Use the fshelp function to traverse the path. */ + if (grub_fshelp_find_file (name, &rootnode, + &foundnode, + grub_iso9660_iterate_dir, + grub_iso9660_read_symlink, + GRUB_FSHELP_REG)) + goto fail; + + data->node = foundnode; + file->data = data; + file->size = get_node_size (foundnode); + file->offset = 0; + + return 0; + + fail: + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + + +static grub_ssize_t +grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_iso9660_data *data = + (struct grub_iso9660_data *) file->data; + grub_err_t err; + + /* XXX: The file is stored in as a single extent. */ + data->disk->read_hook = file->read_hook; + data->disk->read_hook_data = file->read_hook_data; + err = read_node (data->node, file->offset, len, buf); + data->disk->read_hook = NULL; + + if (err || grub_errno) + return -1; + + return len; +} + + +static grub_err_t +grub_iso9660_close (grub_file_t file) +{ + struct grub_iso9660_data *data = + (struct grub_iso9660_data *) file->data; + grub_free (data->node); + grub_free (data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_iso9660_label (grub_device_t device, char **label) +{ + struct grub_iso9660_data *data; + data = grub_iso9660_mount (device->disk); + + if (data) + { + if (data->joliet) + *label = grub_iso9660_convert_string (data->voldesc.volname, 16); + else + *label = grub_strndup ((char *) data->voldesc.volname, 32); + if (*label) + { + char *ptr; + for (ptr = *label; *ptr;ptr++); + ptr--; + while (ptr >= *label && *ptr == ' ') + *ptr-- = 0; + } + + grub_free (data); + } + else + *label = 0; + + return grub_errno; +} + + +static grub_err_t +grub_iso9660_uuid (grub_device_t device, char **uuid) +{ + struct grub_iso9660_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_iso9660_mount (disk); + if (data) + { + if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1] + && ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3] + && ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1] + && ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1] + && ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1] + && ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1] + && ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1] + && ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1]) + { + grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID"); + *uuid = NULL; + } + else + { + *uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c", + data->voldesc.modified.year[0], + data->voldesc.modified.year[1], + data->voldesc.modified.year[2], + data->voldesc.modified.year[3], + data->voldesc.modified.month[0], + data->voldesc.modified.month[1], + data->voldesc.modified.day[0], + data->voldesc.modified.day[1], + data->voldesc.modified.hour[0], + data->voldesc.modified.hour[1], + data->voldesc.modified.minute[0], + data->voldesc.modified.minute[1], + data->voldesc.modified.second[0], + data->voldesc.modified.second[1], + data->voldesc.modified.hundredth[0], + data->voldesc.modified.hundredth[1]); + } + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +/* Get writing time of filesystem. */ +static grub_err_t +grub_iso9660_mtime (grub_device_t device, grub_int64_t *timebuf) +{ + struct grub_iso9660_data *data; + grub_disk_t disk = device->disk; + grub_err_t err; + + grub_dl_ref (my_mod); + + data = grub_iso9660_mount (disk); + if (!data) + { + grub_dl_unref (my_mod); + return grub_errno; + } + err = iso9660_to_unixtime (&data->voldesc.modified, timebuf); + + grub_dl_unref (my_mod); + + grub_free (data); + + return err; +} + + + + +static struct grub_fs grub_iso9660_fs = + { + .name = "iso9660", + .fs_dir = grub_iso9660_dir, + .fs_open = grub_iso9660_open, + .fs_read = grub_iso9660_read, + .fs_close = grub_iso9660_close, + .fs_label = grub_iso9660_label, + .fs_uuid = grub_iso9660_uuid, + .fs_mtime = grub_iso9660_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(iso9660) +{ + grub_iso9660_fs.mod = mod; + grub_fs_register (&grub_iso9660_fs); + my_mod = mod; +} + +GRUB_MOD_FINI(iso9660) +{ + grub_fs_unregister (&grub_iso9660_fs); +} diff --git a/fs/jfs.c b/grub-core/fs/jfs.c similarity index 58% rename from fs/jfs.c rename to grub-core/fs/jfs.c index c9839a22f..03be9ef4c 100644 --- a/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_JFS_MAX_SYMLNK_CNT 8 #define GRUB_JFS_FILETYPE_MASK 0170000 @@ -38,6 +42,13 @@ #define GRUB_JFS_TREE_LEAF 2 +/* + * Define max entries stored in-line in an inode. + * https://jfs.sourceforge.net/project/pub/jfslayout.pdf + */ +#define GRUB_JFS_INODE_INLINE_ENTRIES 8 +#define GRUB_JFS_DIR_MAX_SLOTS 128 + struct grub_jfs_sblock { /* The magic for JFS. It should contain the string "JFS1". */ @@ -49,11 +60,13 @@ struct grub_jfs_sblock 4096 was tested. */ grub_uint32_t blksz; grub_uint16_t log2_blksz; - - grub_uint8_t unused[71]; - grub_uint8_t volname[11]; - grub_uint8_t unused2[32]; + grub_uint8_t unused[14]; + grub_uint32_t flags; + grub_uint8_t unused3[61]; + char volname[11]; + grub_uint8_t unused2[24]; grub_uint8_t uuid[16]; + char volname2[16]; }; struct grub_jfs_extent @@ -65,13 +78,16 @@ struct grub_jfs_extent /* The physical offset of the first block on the disk. */ grub_uint8_t blk1; grub_uint32_t blk2; -} __attribute__ ((packed)); +} GRUB_PACKED; + +#define GRUB_JFS_IAG_INODES_OFFSET 3072 +#define GRUB_JFS_IAG_INODES_COUNT 128 struct grub_jfs_iag { - grub_uint8_t unused[3072]; - struct grub_jfs_extent inodes[128]; -} __attribute__ ((packed)); + grub_uint8_t unused[GRUB_JFS_IAG_INODES_OFFSET]; + struct grub_jfs_extent inodes[GRUB_JFS_IAG_INODES_COUNT]; +} GRUB_PACKED; /* The head of the tree used to find extents. */ @@ -86,7 +102,7 @@ struct grub_jfs_treehead grub_uint16_t count; grub_uint16_t max; grub_uint8_t unused2[10]; -} __attribute__ ((packed)); +} GRUB_PACKED; /* A node in the extent tree. */ struct grub_jfs_tree_extent @@ -99,7 +115,7 @@ struct grub_jfs_tree_extent grub_uint32_t offset2; struct grub_jfs_extent extent; -} __attribute__ ((packed)); +} GRUB_PACKED; /* The tree of directory entries. */ struct grub_jfs_tree_dir @@ -120,7 +136,7 @@ struct grub_jfs_tree_dir /* The location of the sorted array of pointers to dirents. */ grub_uint8_t sindex; grub_uint8_t unused[10]; -} __attribute__ ((packed)); +} GRUB_PACKED; /* An internal node in the dirents tree. */ struct grub_jfs_internal_dirent @@ -129,7 +145,7 @@ struct grub_jfs_internal_dirent grub_uint8_t next; grub_uint8_t len; grub_uint16_t namepart[11]; -} __attribute__ ((packed)); +} GRUB_PACKED; /* A leaf node in the dirents tree. */ struct grub_jfs_leaf_dirent @@ -142,7 +158,7 @@ struct grub_jfs_leaf_dirent grub_uint8_t len; grub_uint16_t namepart[11]; grub_uint32_t index; -} __attribute__ ((packed)); +} GRUB_PACKED; /* A leaf in the dirents tree. This one is used if the previously dirent was not big enough to store the name. */ @@ -151,7 +167,13 @@ struct grub_jfs_leaf_next_dirent grub_uint8_t next; grub_uint8_t len; grub_uint16_t namepart[15]; -} __attribute__ ((packed)); +} GRUB_PACKED; + +struct grub_jfs_time +{ + grub_int32_t sec; + grub_int32_t nanosec; +} GRUB_PACKED; struct grub_jfs_inode { @@ -162,13 +184,16 @@ struct grub_jfs_inode grub_uint64_t size; grub_uint8_t unused2[20]; grub_uint32_t mode; - grub_uint8_t unused3[72]; + struct grub_jfs_time atime; + struct grub_jfs_time ctime; + struct grub_jfs_time mtime; + grub_uint8_t unused3[48]; grub_uint8_t unused4[96]; union { /* The tree describing the extents of the file. */ - struct __attribute__ ((packed)) + struct GRUB_PACKED { struct grub_jfs_treehead tree; struct grub_jfs_tree_extent extents[16]; @@ -186,18 +211,18 @@ struct grub_jfs_inode grub_uint8_t freecnt; grub_uint8_t freelist; grub_uint32_t idotdot; - grub_uint8_t sorted[8]; + grub_uint8_t sorted[GRUB_JFS_INODE_INLINE_ENTRIES]; } header; - struct grub_jfs_leaf_dirent dirents[8]; - } dir __attribute__ ((packed)); + struct grub_jfs_leaf_dirent dirents[GRUB_JFS_INODE_INLINE_ENTRIES]; + } GRUB_PACKED dir; /* Fast symlink. */ struct { grub_uint8_t unused[32]; - grub_uint8_t path[128]; + grub_uint8_t path[256]; } symlink; - } __attribute__ ((packed)); -} __attribute__ ((packed)); + } GRUB_PACKED; +} GRUB_PACKED; struct grub_jfs_data { @@ -205,9 +230,11 @@ struct grub_jfs_data grub_disk_t disk; struct grub_jfs_inode fileset; struct grub_jfs_inode currinode; + int caseins; int pos; int linknest; -} __attribute__ ((packed)); + int namecomponentlen; +} GRUB_PACKED; struct grub_jfs_diropen { @@ -217,92 +244,130 @@ struct grub_jfs_diropen struct grub_jfs_tree_dir header; struct grub_jfs_leaf_dirent dirent[0]; struct grub_jfs_leaf_next_dirent next_dirent[0]; - char sorted[0]; - } *dirpage __attribute__ ((packed)); + grub_uint8_t sorted[0]; + } GRUB_PACKED *dirpage; struct grub_jfs_data *data; struct grub_jfs_inode *inode; int count; - char *sorted; + grub_uint8_t *sorted; struct grub_jfs_leaf_dirent *leaf; struct grub_jfs_leaf_next_dirent *next_leaf; /* The filename and inode of the last read dirent. */ - char name[255]; + /* On-disk name is at most 255 UTF-16 codepoints. + Every UTF-16 codepoint is at most 4 UTF-8 bytes. + */ + char name[256 * GRUB_MAX_UTF8_PER_UTF16 + 1]; grub_uint32_t ino; -} __attribute__ ((packed)); +} GRUB_PACKED; static grub_dl_t my_mod; -static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino); +static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino); + +/* + * An extent's offset, physical and logical, is represented as a 40-bit value. + * This 40-bit value is split into two parts: + * - offset1: the most signficant 8 bits of the offset, + * - offset2: the least significant 32 bits of the offset. + * + * This function calculates and returns the 64-bit offset of an extent. + */ +static grub_uint64_t +get_ext_offset (grub_uint8_t offset1, grub_uint32_t offset2) +{ + return (((grub_uint64_t) offset1 << 32) | grub_le_to_cpu32 (offset2)); +} + +static grub_uint64_t +getblk (struct grub_jfs_treehead *treehead, + struct grub_jfs_tree_extent *extents, + int max_extents, + struct grub_jfs_data *data, + grub_uint64_t blk) +{ + int found = -1; + int i; + grub_uint64_t ext_offset, ext_blk; + + grub_errno = GRUB_ERR_NONE; + + for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2 && + i < max_extents; i++) + { + ext_offset = get_ext_offset (extents[i].offset1, extents[i].offset2); + ext_blk = get_ext_offset (extents[i].extent.blk1, extents[i].extent.blk2); + + if (treehead->flags & GRUB_JFS_TREE_LEAF) + { + /* Read the leafnode. */ + if (ext_offset <= blk + && ((grub_le_to_cpu16 (extents[i].extent.length)) + + (extents[i].extent.length2 << 16) + + ext_offset) > blk) + return (blk - ext_offset + ext_blk); + } + else + if (blk >= ext_offset) + found = i; + } + + if (found != -1) + { + grub_uint64_t ret = 0; + struct + { + struct grub_jfs_treehead treehead; + struct grub_jfs_tree_extent extents[254]; + } *tree; + + tree = grub_zalloc (sizeof (*tree)); + if (!tree) + return 0; + + if (!grub_disk_read (data->disk, + (grub_disk_addr_t) ext_blk + << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS), + 0, sizeof (*tree), (char *) tree)) + { + if (grub_memcmp (&tree->treehead, treehead, sizeof (struct grub_jfs_treehead)) || + grub_memcmp (&tree->extents, extents, 254 * sizeof (struct grub_jfs_tree_extent))) + ret = getblk (&tree->treehead, &tree->extents[0], 254, data, blk); + else + { + grub_error (GRUB_ERR_BAD_FS, "jfs: infinite recursion detected"); + ret = 0; + } + } + grub_free (tree); + return ret; + } + + grub_error (GRUB_ERR_READ_ERROR, "jfs: block %" PRIuGRUB_UINT64_T " not found", blk); + return 0; +} /* Get the block number for the block BLK in the node INODE in the mounted filesystem DATA. */ -static int +static grub_uint64_t grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode, - unsigned int blk) + grub_uint64_t blk) { - auto int getblk (struct grub_jfs_treehead *treehead, - struct grub_jfs_tree_extent *extents); - - int getblk (struct grub_jfs_treehead *treehead, - struct grub_jfs_tree_extent *extents) - { - int found = -1; - int i; - - for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++) - { - if (treehead->flags & GRUB_JFS_TREE_LEAF) - { - /* Read the leafnode. */ - if (grub_le_to_cpu32 (extents[i].offset2) <= blk - && ((grub_le_to_cpu16 (extents[i].extent.length)) - + (extents[i].extent.length2 << 8) - + grub_le_to_cpu32 (extents[i].offset2)) > blk) - return (blk - grub_le_to_cpu32 (extents[i].offset2) - + grub_le_to_cpu32 (extents[i].extent.blk2)); - } - else - if (blk >= grub_le_to_cpu32 (extents[i].offset2)) - found = i; - } - - if (found != -1) - { - struct - { - struct grub_jfs_treehead treehead; - struct grub_jfs_tree_extent extents[254]; - } tree; - - if (grub_disk_read (data->disk, - grub_le_to_cpu32 (extents[found].extent.blk2) - << (grub_le_to_cpu16 (data->sblock.log2_blksz) - - GRUB_DISK_SECTOR_BITS), 0, - sizeof (tree), (char *) &tree)) - return -1; - - return getblk (&tree.treehead, &tree.extents[0]); - } - - return -1; - } - - return getblk (&inode->file.tree, &inode->file.extents[0]); + return getblk (&inode->file.tree, &inode->file.extents[0], 16, data, blk); } static grub_err_t -grub_jfs_read_inode (struct grub_jfs_data *data, int ino, +grub_jfs_read_inode (struct grub_jfs_data *data, grub_uint32_t ino, struct grub_jfs_inode *inode) { - struct grub_jfs_iag iag; - int iagnum = ino / 4096; - int inoext = (ino % 4096) / 32; - int inonum = (ino % 4096) % 32; - grub_uint32_t iagblk; - grub_uint32_t inoblk; + struct grub_jfs_extent iag_inodes[GRUB_JFS_IAG_INODES_COUNT]; + grub_uint32_t iagnum = ino / 4096; + unsigned inoext = (ino % 4096) / 32; + unsigned inonum = (ino % 4096) % 32; + grub_uint64_t iagblk; + grub_uint64_t inoblk; iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1); if (grub_errno) @@ -311,11 +376,12 @@ grub_jfs_read_inode (struct grub_jfs_data *data, int ino, /* Read in the IAG. */ if (grub_disk_read (data->disk, iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz) - - GRUB_DISK_SECTOR_BITS), 0, - sizeof (struct grub_jfs_iag), &iag)) + - GRUB_DISK_SECTOR_BITS), + GRUB_JFS_IAG_INODES_OFFSET, + sizeof (iag_inodes), &iag_inodes)) return grub_errno; - inoblk = grub_le_to_cpu32 (iag.inodes[inoext].blk2); + inoblk = get_ext_offset (iag_inodes[inoext].blk1, iag_inodes[inoext].blk2); inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); inoblk += inonum; @@ -348,6 +414,15 @@ grub_jfs_mount (grub_disk_t disk) goto fail; } + if (data->sblock.blksz == 0 + || grub_le_to_cpu32 (data->sblock.blksz) + != (1U << grub_le_to_cpu16 (data->sblock.log2_blksz)) + || grub_le_to_cpu16 (data->sblock.log2_blksz) < GRUB_DISK_SECTOR_BITS) + { + grub_error (GRUB_ERR_BAD_FS, "not a JFS filesystem"); + goto fail; + } + data->disk = disk; data->pos = 0; data->linknest = 0; @@ -357,6 +432,16 @@ grub_jfs_mount (grub_disk_t disk) sizeof (struct grub_jfs_inode), &data->fileset)) goto fail; + if (data->sblock.flags & grub_cpu_to_le32_compile_time (0x00200000)) + data->namecomponentlen = 11; + else + data->namecomponentlen = 13; + + if (data->sblock.flags & grub_cpu_to_le32_compile_time (0x40000000)) + data->caseins = 1; + else + data->caseins = 0; + return data; fail: @@ -374,14 +459,14 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) { struct grub_jfs_internal_dirent *de; struct grub_jfs_diropen *diro; - int blk; + grub_disk_addr_t blk; de = (struct grub_jfs_internal_dirent *) inode->dir.dirents; if (!((grub_le_to_cpu32 (inode->mode) & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR)) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); return 0; } @@ -395,9 +480,16 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) /* Check if the entire tree is contained within the inode. */ if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF) { + if (inode->dir.header.count > GRUB_JFS_INODE_INLINE_ENTRIES) + { + grub_free (diro); + grub_error (GRUB_ERR_BAD_FS, N_("invalid JFS inode")); + return 0; + } + diro->leaf = inode->dir.dirents; diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de; - diro->sorted = (char *) (inode->dir.header.sorted); + diro->sorted = inode->dir.header.sorted; diro->count = inode->dir.header.count; return diro; @@ -410,7 +502,16 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) return 0; } - blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2); + if (inode->dir.header.sorted[0] >= GRUB_JFS_DIR_MAX_SLOTS) + { + grub_error (GRUB_ERR_BAD_FS, N_("invalid directory slot index")); + grub_free (diro->dirpage); + grub_free (diro); + return 0; + } + + blk = get_ext_offset (de[inode->dir.header.sorted[0]].ex.blk1, + de[inode->dir.header.sorted[0]].ex.blk2); blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); /* Read in the nodes until we are on the leaf node level. */ @@ -428,7 +529,7 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent; index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; - blk = (grub_le_to_cpu32 (de[index].ex.blk2) + blk = (get_ext_offset (de[index].ex.blk1, de[index].ex.blk2) << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS)); } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF)); @@ -451,6 +552,17 @@ grub_jfs_closedir (struct grub_jfs_diropen *diro) grub_free (diro); } +static void +le_to_cpu16_copy (grub_uint16_t *out, grub_uint16_t *in, grub_size_t len) +{ + while (len--) + *out++ = grub_le_to_cpu16 (*in++); +} + +#if __GNUC__ >= 9 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" +#endif /* Read in the next dirent from the directory described by DIRO. */ static grub_err_t @@ -461,21 +573,12 @@ grub_jfs_getent (struct grub_jfs_diropen *diro) struct grub_jfs_leaf_next_dirent *next_leaf; int len; int nextent; - grub_uint16_t filename[255]; - - auto void addstr (grub_uint16_t *uname, int ulen); - - /* Add the unicode string to the utf16 filename buffer. */ - void addstr (grub_uint16_t *name, int ulen) - { - while (ulen--) - filename[strpos++] = *(name++); - } + grub_uint16_t filename[256]; /* The last node, read in more. */ if (diro->index == diro->count) { - unsigned int next; + grub_disk_addr_t next; /* If the inode contains the entry tree or if this was the last node, there is nothing to read. */ @@ -499,7 +602,7 @@ grub_jfs_getent (struct grub_jfs_diropen *diro) diro->index = 0; } - leaf = &diro->leaf[(int) diro->sorted[diro->index]]; + leaf = &diro->leaf[diro->sorted[diro->index]]; next_leaf = &diro->next_leaf[diro->index]; len = leaf->len; @@ -509,17 +612,21 @@ grub_jfs_getent (struct grub_jfs_diropen *diro) return grub_jfs_getent (diro); } - addstr (leaf->namepart, len < 11 ? len : 11); + le_to_cpu16_copy (filename + strpos, leaf->namepart, len < diro->data->namecomponentlen ? len + : diro->data->namecomponentlen); + strpos += len < diro->data->namecomponentlen ? len + : diro->data->namecomponentlen; diro->ino = grub_le_to_cpu32 (leaf->inode); - len -= 11; + len -= diro->data->namecomponentlen; /* Move down to the leaf level. */ nextent = leaf->next; - if (leaf->next != 255) + if (leaf->next != 255 && len > 0) do { next_leaf = &diro->next_leaf[nextent]; - addstr (next_leaf->namepart, len < 15 ? len : 15 ); + le_to_cpu16_copy (filename + strpos, next_leaf->namepart, len < 15 ? len : 15); + strpos += len < 15 ? len : 15; len -= 15; nextent = next_leaf->next; @@ -533,28 +640,30 @@ grub_jfs_getent (struct grub_jfs_diropen *diro) return 0; } +#if __GNUC__ >= 9 +#pragma GCC diagnostic pop +#endif /* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_jfs_read_file (struct grub_jfs_data *data, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) { - int i; - int blockcnt; + grub_off_t i; + grub_off_t blockcnt; - blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1) - / grub_le_to_cpu32 (data->sblock.blksz)); + blockcnt = (len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1) + >> grub_le_to_cpu16 (data->sblock.log2_blksz); - for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++) + for (i = pos >> grub_le_to_cpu16 (data->sblock.log2_blksz); i < blockcnt; i++) { - int blknr; - int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz); - int blockend = grub_le_to_cpu32 (data->sblock.blksz); + grub_disk_addr_t blknr; + grub_uint32_t blockoff = pos & (grub_le_to_cpu32 (data->sblock.blksz) - 1); + grub_uint32_t blockend = grub_le_to_cpu32 (data->sblock.blksz); - int skipfirst = 0; + grub_uint64_t skipfirst = 0; blknr = grub_jfs_blkno (data, &data->currinode, i); if (grub_errno) @@ -563,20 +672,21 @@ grub_jfs_read_file (struct grub_jfs_data *data, /* Last block. */ if (i == blockcnt - 1) { - blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz); + blockend = (len + pos) & (grub_le_to_cpu32 (data->sblock.blksz) - 1); if (!blockend) blockend = grub_le_to_cpu32 (data->sblock.blksz); } /* First block. */ - if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz))) + if (i == (pos >> grub_le_to_cpu16 (data->sblock.log2_blksz))) { skipfirst = blockoff; blockend -= skipfirst; } data->disk->read_hook = read_hook; + data->disk->read_hook_data = read_hook_data; grub_disk_read (data->disk, blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS), @@ -596,112 +706,98 @@ grub_jfs_read_file (struct grub_jfs_data *data, /* Find the file with the pathname PATH on the filesystem described by DATA. */ static grub_err_t -grub_jfs_find_file (struct grub_jfs_data *data, const char *path) +grub_jfs_find_file (struct grub_jfs_data *data, const char *path, + grub_uint32_t start_ino) { - char fpath[grub_strlen (path)]; - char *name = fpath; - char *next; - unsigned int pos = 0; - struct grub_jfs_diropen *diro; + const char *name; + const char *next = path; + struct grub_jfs_diropen *diro = NULL; - grub_strncpy (fpath, path, grub_strlen (path) + 1); - - if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode)) + if (grub_jfs_read_inode (data, start_ino, &data->currinode)) return grub_errno; - /* Skip the first slashes. */ - while (*name == '/') + while (1) { - name++; - if (!*name) - return 0; - } - - /* Extract the actual part from the pathname. */ - next = grub_strchr (name, '/'); - if (next) - { - while (*next == '/') - { - next[0] = '\0'; - next++; - } - } - diro = grub_jfs_opendir (data, &data->currinode); - if (!diro) - return grub_errno; - - for (;;) - { - if (grub_strlen (name) == 0) + name = next; + while (*name == '/') + name++; + if (name[0] == 0) return GRUB_ERR_NONE; + for (next = name; *next && *next != '/'; next++); - if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE) - break; + if (name[0] == '.' && name + 1 == next) + continue; - /* Check if the current direntry matches the current part of the - pathname. */ - if (!grub_strcmp (name, diro->name)) + if (name[0] == '.' && name[1] == '.' && name + 2 == next) { - int ino = diro->ino; - int dirino = grub_le_to_cpu32 (data->currinode.inode); - - grub_jfs_closedir (diro); - diro = 0; + grub_uint32_t ino = grub_le_to_cpu32 (data->currinode.dir.header.idotdot); if (grub_jfs_read_inode (data, ino, &data->currinode)) - break; - - /* Check if this is a symlink. */ - if ((grub_le_to_cpu32 (data->currinode.mode) - & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK) - { - grub_jfs_lookup_symlink (data, dirino); - if (grub_errno) - return grub_errno; - } - - if (!next) - return 0; - - pos = 0; - - name = next; - next = grub_strchr (name, '/'); - if (next) - { - next[0] = '\0'; - next++; - } - - /* Open this directory for reading dirents. */ - diro = grub_jfs_opendir (data, &data->currinode); - if (!diro) return grub_errno; continue; } - } - grub_jfs_closedir (diro); - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); - return grub_errno; + diro = grub_jfs_opendir (data, &data->currinode); + if (!diro) + return grub_errno; + + for (;;) + { + if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE) + { + grub_jfs_closedir (diro); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path); + } + + /* Check if the current direntry matches the current part of the + pathname. */ + if ((data->caseins ? grub_strncasecmp (name, diro->name, next - name) == 0 + : grub_strncmp (name, diro->name, next - name) == 0) && !diro->name[next - name]) + { + grub_uint32_t ino = diro->ino; + grub_uint32_t dirino = grub_le_to_cpu32 (data->currinode.inode); + + grub_jfs_closedir (diro); + + if (grub_jfs_read_inode (data, ino, &data->currinode)) + break; + + /* Check if this is a symlink. */ + if ((grub_le_to_cpu32 (data->currinode.mode) + & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK) + { + grub_jfs_lookup_symlink (data, dirino); + if (grub_errno) + return grub_errno; + } + + break; + } + } + } } static grub_err_t -grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino) +grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino) { - int size = grub_le_to_cpu64 (data->currinode.size); - char symlink[size + 1]; + grub_size_t size = grub_le_to_cpu64 (data->currinode.size); + char *symlink; if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT) - return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks"); + return grub_error (GRUB_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks")); - if (size <= 128) - grub_strncpy (symlink, (char *) (data->currinode.symlink.path), 128); - else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0) + symlink = grub_malloc (size + 1); + if (!symlink) return grub_errno; + if (size <= sizeof (data->currinode.symlink.path)) + grub_memcpy (symlink, (char *) (data->currinode.symlink.path), size); + else if (grub_jfs_read_file (data, 0, 0, 0, size, symlink) < 0) + { + grub_free (symlink); + return grub_errno; + } symlink[size] = '\0'; @@ -709,13 +805,9 @@ grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino) if (symlink[0] == '/') ino = 2; - /* Now load in the old inode. */ - if (grub_jfs_read_inode (data, ino, &data->currinode)) - return grub_errno; + grub_jfs_find_file (data, symlink, ino); - grub_jfs_find_file (data, symlink); - if (grub_errno) - grub_error (grub_errno, "cannot follow symlink `%s'", symlink); + grub_free (symlink); return grub_errno; } @@ -723,8 +815,7 @@ grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino) static grub_err_t grub_jfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { struct grub_jfs_data *data = 0; struct grub_jfs_diropen *diro = 0; @@ -735,7 +826,7 @@ grub_jfs_dir (grub_device_t device, const char *path, if (!data) goto fail; - if (grub_jfs_find_file (data, path)) + if (grub_jfs_find_file (data, path, GRUB_JFS_AGGR_INODE)) goto fail; diro = grub_jfs_opendir (data, &data->currinode); @@ -754,7 +845,9 @@ grub_jfs_dir (grub_device_t device, const char *path, info.dir = (grub_le_to_cpu32 (inode.mode) & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR; - if (hook (diro->name, &info)) + info.mtimeset = 1; + info.mtime = grub_le_to_cpu32 (inode.mtime.sec); + if (hook (diro->name, &info, hook_data)) goto fail; } @@ -784,7 +877,7 @@ grub_jfs_open (struct grub_file *file, const char *name) if (!data) goto fail; - grub_jfs_find_file (data, name); + grub_jfs_find_file (data, name, GRUB_JFS_AGGR_INODE); if (grub_errno) goto fail; @@ -792,7 +885,7 @@ grub_jfs_open (struct grub_file *file, const char *name) if (! ((grub_le_to_cpu32 (data->currinode.mode) & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG)) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file"); + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); goto fail; } @@ -817,7 +910,8 @@ grub_jfs_read (grub_file_t file, char *buf, grub_size_t len) struct grub_jfs_data *data = (struct grub_jfs_data *) file->data; - return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf); + return grub_jfs_read_file (data, file->read_hook, file->read_hook_data, + file->offset, len, buf); } @@ -870,10 +964,25 @@ grub_jfs_label (grub_device_t device, char **label) data = grub_jfs_mount (device->disk); if (data) - *label = grub_strndup ((char *) (data->sblock.volname), 11); + { + if (data->sblock.volname2[0] < ' ') + { + char *ptr; + ptr = data->sblock.volname + sizeof (data->sblock.volname) - 1; + while (ptr >= data->sblock.volname && *ptr == ' ') + ptr--; + *label = grub_strndup (data->sblock.volname, + ptr - data->sblock.volname + 1); + } + else + *label = grub_strndup (data->sblock.volname2, + sizeof (data->sblock.volname2)); + } else *label = 0; + grub_free (data); + return grub_errno; } @@ -881,22 +990,31 @@ grub_jfs_label (grub_device_t device, char **label) static struct grub_fs grub_jfs_fs = { .name = "jfs", - .dir = grub_jfs_dir, - .open = grub_jfs_open, - .read = grub_jfs_read, - .close = grub_jfs_close, - .label = grub_jfs_label, - .uuid = grub_jfs_uuid, + .fs_dir = grub_jfs_dir, + .fs_open = grub_jfs_open, + .fs_read = grub_jfs_read, + .fs_close = grub_jfs_close, + .fs_label = grub_jfs_label, + .fs_uuid = grub_jfs_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif .next = 0 }; GRUB_MOD_INIT(jfs) { - grub_fs_register (&grub_jfs_fs); + if (!grub_is_lockdown ()) + { + grub_jfs_fs.mod = mod; + grub_fs_register (&grub_jfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(jfs) { - grub_fs_unregister (&grub_jfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_jfs_fs); } diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c new file mode 100644 index 000000000..4440fcca8 --- /dev/null +++ b/grub-core/fs/minix.c @@ -0,0 +1,766 @@ +/* minix.c - The minix filesystem, version 1 and 2. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#ifdef MODE_MINIX3 +#define GRUB_MINIX_MAGIC 0x4D5A +#elif defined(MODE_MINIX2) +#define GRUB_MINIX_MAGIC 0x2468 +#define GRUB_MINIX_MAGIC_30 0x2478 +#else +#define GRUB_MINIX_MAGIC 0x137F +#define GRUB_MINIX_MAGIC_30 0x138F +#endif + +#define EXT2_MAGIC 0xEF53 + +#define GRUB_MINIX_INODE_DIR_BLOCKS 7 +#define GRUB_MINIX_LOG2_BSIZE 1 +#define GRUB_MINIX_ROOT_INODE 1 +#define GRUB_MINIX_MAX_SYMLNK_CNT 8 +#define GRUB_MINIX_SBLOCK 2 + +#define GRUB_MINIX_IFDIR 0040000U +#define GRUB_MINIX_IFLNK 0120000U + +#ifdef MODE_BIGENDIAN +#define grub_cpu_to_minix16_compile_time grub_cpu_to_be16_compile_time +#define grub_minix_to_cpu16 grub_be_to_cpu16 +#define grub_minix_to_cpu32 grub_be_to_cpu32 +#else +#define grub_cpu_to_minix16_compile_time grub_cpu_to_le16_compile_time +#define grub_minix_to_cpu16 grub_le_to_cpu16 +#define grub_minix_to_cpu32 grub_le_to_cpu32 +#endif + +#if defined(MODE_MINIX2) || defined(MODE_MINIX3) +typedef grub_uint32_t grub_minix_uintn_t; +#define grub_minix_to_cpu_n grub_minix_to_cpu32 +#else +typedef grub_uint16_t grub_minix_uintn_t; +#define grub_minix_to_cpu_n grub_minix_to_cpu16 +#endif + +#ifdef MODE_MINIX3 +typedef grub_uint32_t grub_minix_ino_t; +#define grub_minix_to_cpu_ino grub_minix_to_cpu32 +#else +typedef grub_uint16_t grub_minix_ino_t; +#define grub_minix_to_cpu_ino grub_minix_to_cpu16 +#endif + +#define GRUB_MINIX_INODE_SIZE(data) (grub_minix_to_cpu32 (data->inode.size)) +#define GRUB_MINIX_INODE_MODE(data) (grub_minix_to_cpu16 (data->inode.mode)) +#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_to_cpu_n \ + (data->inode.dir_zones[blk])) +#define GRUB_MINIX_INODE_INDIR_ZONE(data) (grub_minix_to_cpu_n \ + (data->inode.indir_zone)) +#define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_to_cpu_n \ + (data->inode.double_indir_zone)) + + +#ifdef MODE_MINIX3 +struct grub_minix_sblock +{ + grub_uint32_t inode_cnt; + grub_uint16_t zone_cnt; + grub_uint16_t inode_bmap_size; + grub_uint16_t zone_bmap_size; + grub_uint16_t first_data_zone; + grub_uint16_t log2_zone_size; + grub_uint16_t pad; + grub_uint32_t max_file_size; + grub_uint32_t zones; + grub_uint16_t magic; + + grub_uint16_t pad2; + grub_uint16_t block_size; + grub_uint8_t disk_version; +}; +#else +struct grub_minix_sblock +{ + grub_uint16_t inode_cnt; + grub_uint16_t zone_cnt; + grub_uint16_t inode_bmap_size; + grub_uint16_t zone_bmap_size; + grub_uint16_t first_data_zone; + grub_uint16_t log2_zone_size; + grub_uint32_t max_file_size; + grub_uint16_t magic; +}; +#endif + +#if defined(MODE_MINIX3) || defined(MODE_MINIX2) +struct grub_minix_inode +{ + grub_uint16_t mode; + grub_uint16_t nlinks; + grub_uint16_t uid; + grub_uint16_t gid; + grub_uint32_t size; + grub_uint32_t atime; + grub_uint32_t mtime; + grub_uint32_t ctime; + grub_uint32_t dir_zones[7]; + grub_uint32_t indir_zone; + grub_uint32_t double_indir_zone; + grub_uint32_t triple_indir_zone; +}; +#else +struct grub_minix_inode +{ + grub_uint16_t mode; + grub_uint16_t uid; + grub_uint32_t size; + grub_uint32_t mtime; + grub_uint8_t gid; + grub_uint8_t nlinks; + grub_uint16_t dir_zones[7]; + grub_uint16_t indir_zone; + grub_uint16_t double_indir_zone; +}; + +#endif + +#if defined(MODE_MINIX3) +#define MAX_MINIX_FILENAME_SIZE 60 +#else +#define MAX_MINIX_FILENAME_SIZE 30 +#endif + +/* Information about a "mounted" minix filesystem. */ +struct grub_minix_data +{ + struct grub_minix_sblock sblock; + struct grub_minix_inode inode; + grub_uint32_t block_per_zone; + grub_minix_ino_t ino; + int linknest; + grub_disk_t disk; + int filename_size; + grub_size_t block_size; +}; + +static grub_dl_t my_mod; + +static grub_err_t grub_minix_find_file (struct grub_minix_data *data, + const char *path); + +#ifdef MODE_MINIX3 +static inline grub_disk_addr_t +grub_minix_zone2sect (struct grub_minix_data *data, grub_minix_uintn_t zone) +{ + return ((grub_disk_addr_t) zone) * data->block_size; +} +#else +static inline grub_disk_addr_t +grub_minix_zone2sect (struct grub_minix_data *data, grub_minix_uintn_t zone) +{ + int log2_zonesz = (GRUB_MINIX_LOG2_BSIZE + + grub_minix_to_cpu16 (data->sblock.log2_zone_size)); + return (((grub_disk_addr_t) zone) << log2_zonesz); +} +#endif + + + /* Read the block pointer in ZONE, on the offset NUM. */ +static grub_minix_uintn_t +grub_get_indir (struct grub_minix_data *data, + grub_minix_uintn_t zone, + grub_minix_uintn_t num) +{ + grub_minix_uintn_t indirn; + grub_disk_read (data->disk, + grub_minix_zone2sect(data, zone), + sizeof (grub_minix_uintn_t) * num, + sizeof (grub_minix_uintn_t), (char *) &indirn); + return grub_minix_to_cpu_n (indirn); +} + +static grub_minix_uintn_t +grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk) +{ + grub_minix_uintn_t indir; + + /* Direct block. */ + if (blk < GRUB_MINIX_INODE_DIR_BLOCKS) + return GRUB_MINIX_INODE_DIR_ZONES (data, blk); + + /* Indirect block. */ + blk -= GRUB_MINIX_INODE_DIR_BLOCKS; + if (blk < data->block_per_zone) + { + indir = grub_get_indir (data, GRUB_MINIX_INODE_INDIR_ZONE (data), blk); + return indir; + } + + /* Double indirect block. */ + blk -= data->block_per_zone; + if (blk < (grub_uint64_t) data->block_per_zone * (grub_uint64_t) data->block_per_zone) + { + indir = grub_get_indir (data, GRUB_MINIX_INODE_DINDIR_ZONE (data), + blk / data->block_per_zone); + + indir = grub_get_indir (data, indir, blk % data->block_per_zone); + + return indir; + } + +#if defined (MODE_MINIX3) || defined (MODE_MINIX2) + blk -= data->block_per_zone * data->block_per_zone; + if (blk < ((grub_uint64_t) data->block_per_zone * (grub_uint64_t) data->block_per_zone + * (grub_uint64_t) data->block_per_zone)) + { + indir = grub_get_indir (data, grub_minix_to_cpu_n (data->inode.triple_indir_zone), + (blk / data->block_per_zone) / data->block_per_zone); + indir = grub_get_indir (data, indir, (blk / data->block_per_zone) % data->block_per_zone); + indir = grub_get_indir (data, indir, blk % data->block_per_zone); + + return indir; + } +#endif + + /* This should never happen. */ + grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size"); + + return 0; +} + + +/* Read LEN bytes from the file described by DATA starting with byte + POS. Return the amount of read bytes in READ. */ +static grub_ssize_t +grub_minix_read_file (struct grub_minix_data *data, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) +{ + grub_uint32_t i; + grub_uint32_t blockcnt; + grub_uint32_t posblock; + grub_uint32_t blockoff; + + if (pos > GRUB_MINIX_INODE_SIZE (data)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read past the end of file")); + return -1; + } + + /* Adjust len so it we can't read past the end of the file. */ + if (len + pos > GRUB_MINIX_INODE_SIZE (data)) + len = GRUB_MINIX_INODE_SIZE (data) - pos; + if (len == 0) + return 0; + + /* Files are at most 2G/4G - 1 bytes on minixfs. Avoid 64-bit division. */ + blockcnt = ((grub_uint32_t) ((len + pos - 1) + >> GRUB_DISK_SECTOR_BITS)) / data->block_size + 1; + posblock = (((grub_uint32_t) pos) + / (data->block_size << GRUB_DISK_SECTOR_BITS)); + blockoff = (((grub_uint32_t) pos) + % (data->block_size << GRUB_DISK_SECTOR_BITS)); + + for (i = posblock; i < blockcnt; i++) + { + grub_minix_uintn_t blknr; + grub_uint64_t blockend = data->block_size << GRUB_DISK_SECTOR_BITS; + grub_off_t skipfirst = 0; + + blknr = grub_minix_get_file_block (data, i); + if (grub_errno) + return -1; + + /* Last block. */ + if (i == blockcnt - 1) + { + /* len + pos < 4G (checked above), so it doesn't overflow. */ + blockend = (((grub_uint32_t) (len + pos)) + % (data->block_size << GRUB_DISK_SECTOR_BITS)); + + if (!blockend) + blockend = data->block_size << GRUB_DISK_SECTOR_BITS; + } + + /* First block. */ + if (i == posblock) + { + skipfirst = blockoff; + blockend -= skipfirst; + } + + data->disk->read_hook = read_hook; + data->disk->read_hook_data = read_hook_data; + grub_disk_read (data->disk, + grub_minix_zone2sect(data, blknr), + skipfirst, blockend, buf); + data->disk->read_hook = 0; + if (grub_errno) + return -1; + + buf += (data->block_size << GRUB_DISK_SECTOR_BITS) - skipfirst; + } + + return len; +} + + +/* Read inode INO from the mounted filesystem described by DATA. This + inode is used by default now. */ +static grub_err_t +grub_minix_read_inode (struct grub_minix_data *data, grub_minix_ino_t ino) +{ + struct grub_minix_sblock *sblock = &data->sblock; + + /* Block in which the inode is stored. */ + grub_disk_addr_t block; + data->ino = ino; + + /* The first inode in minix is inode 1. */ + ino--; + block = grub_minix_zone2sect (data, + 2 + grub_minix_to_cpu16 (sblock->inode_bmap_size) + + grub_minix_to_cpu16 (sblock->zone_bmap_size)); + block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode)); + int offs = (ino % (GRUB_DISK_SECTOR_SIZE + / sizeof (struct grub_minix_inode)) + * sizeof (struct grub_minix_inode)); + + grub_disk_read (data->disk, block, offs, + sizeof (struct grub_minix_inode), &data->inode); + + return GRUB_ERR_NONE; +} + + +/* Lookup the symlink the current inode points to. INO is the inode + number of the directory the symlink is relative to. */ +static grub_err_t +grub_minix_lookup_symlink (struct grub_minix_data *data, grub_minix_ino_t ino) +{ + char *symlink; + grub_size_t sz = GRUB_MINIX_INODE_SIZE (data); + + if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT) + return grub_error (GRUB_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks")); + + symlink = grub_malloc (sz + 1); + if (!symlink) + return grub_errno; + if (grub_minix_read_file (data, 0, 0, 0, sz, symlink) < 0) + goto fail; + + symlink[sz] = '\0'; + + /* The symlink is an absolute path, go back to the root inode. */ + if (symlink[0] == '/') + ino = GRUB_MINIX_ROOT_INODE; + + /* Now load in the old inode. */ + if (grub_minix_read_inode (data, ino)) + goto fail; + + grub_minix_find_file (data, symlink); + + fail: + grub_free(symlink); + return grub_errno; +} + + +/* Find the file with the pathname PATH on the filesystem described by + DATA. */ +static grub_err_t +grub_minix_find_file (struct grub_minix_data *data, const char *path) +{ + const char *name; + const char *next = path; + unsigned int pos = 0; + grub_minix_ino_t dirino; + + while (1) + { + name = next; + /* Skip the first slash. */ + while (*name == '/') + name++; + if (!*name) + return GRUB_ERR_NONE; + + if ((GRUB_MINIX_INODE_MODE (data) + & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + + /* Extract the actual part from the pathname. */ + for (next = name; *next && *next != '/'; next++); + + for (pos = 0; ; ) + { + grub_minix_ino_t ino; + char filename[MAX_MINIX_FILENAME_SIZE + 1]; + + if (pos >= GRUB_MINIX_INODE_SIZE (data)) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path); + return grub_errno; + } + + if (grub_minix_read_file (data, 0, 0, pos, sizeof (ino), + (char *) &ino) < 0) + return grub_errno; + if (grub_minix_read_file (data, 0, 0, pos + sizeof (ino), + data->filename_size, (char *) filename)< 0) + return grub_errno; + + pos += sizeof (ino) + data->filename_size; + + filename[data->filename_size] = '\0'; + + /* Check if the current direntry matches the current part of the + pathname. */ + if (grub_strncmp (name, filename, next - name) == 0 + && filename[next - name] == '\0') + { + dirino = data->ino; + grub_minix_read_inode (data, grub_minix_to_cpu_ino (ino)); + + /* Follow the symlink. */ + if ((GRUB_MINIX_INODE_MODE (data) + & GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK) + { + grub_minix_lookup_symlink (data, dirino); + if (grub_errno) + return grub_errno; + } + + break; + } + } + } +} + + +/* Mount the filesystem on the disk DISK. */ +static struct grub_minix_data * +grub_minix_mount (grub_disk_t disk) +{ + struct grub_minix_data *data = NULL; + grub_uint16_t ext2_marker; + + grub_disk_read (disk, 2, 56, sizeof (ext2_marker), &ext2_marker); + if (grub_errno != GRUB_ERR_NONE) + goto fail; + + /* + * The ext2 filesystems can sometimes be mistakenly identified as MINIX, e.g. + * due to the number of free ext2 inodes being written to the same location + * where the MINIX superblock magic is found. Avoid such situations by + * skipping any filesystems that have the ext2 superblock magic. + */ + if (ext2_marker == grub_cpu_to_le16_compile_time (EXT2_MAGIC)) + goto fail; + + data = grub_malloc (sizeof (struct grub_minix_data)); + if (!data) + return 0; + + /* Read the superblock. */ + grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0, + sizeof (struct grub_minix_sblock),&data->sblock); + if (grub_errno) + goto fail; + + if (data->sblock.magic == grub_cpu_to_minix16_compile_time (GRUB_MINIX_MAGIC)) + { +#if !defined(MODE_MINIX3) + data->filename_size = 14; +#else + data->filename_size = 60; +#endif + } +#if !defined(MODE_MINIX3) + else if (data->sblock.magic + == grub_cpu_to_minix16_compile_time (GRUB_MINIX_MAGIC_30)) + data->filename_size = 30; +#endif + else + goto fail; + + /* 20 means 1G zones. We could support up to 31 but already 1G isn't + supported by anything else. */ + if (grub_minix_to_cpu16 (data->sblock.log2_zone_size) >= 20) + goto fail; + + data->disk = disk; + data->linknest = 0; +#ifdef MODE_MINIX3 + /* These tests are endian-independent. No need to byteswap. */ + if (data->sblock.block_size == 0xffff) + data->block_size = 2; + else + { + if ((data->sblock.block_size == grub_cpu_to_minix16_compile_time (0x200)) + || (data->sblock.block_size == 0) + || (data->sblock.block_size & grub_cpu_to_minix16_compile_time (0x1ff))) + goto fail; + data->block_size = grub_minix_to_cpu16 (data->sblock.block_size) + >> GRUB_DISK_SECTOR_BITS; + } +#else + data->block_size = 2; +#endif + + data->block_per_zone = (((grub_uint64_t) data->block_size << \ + (GRUB_DISK_SECTOR_BITS + grub_minix_to_cpu16 (data->sblock.log2_zone_size))) + / sizeof (grub_minix_uintn_t)); + if (!data->block_per_zone) + goto fail; + + return data; + + fail: + grub_free (data); +#if defined(MODE_MINIX3) + grub_error (GRUB_ERR_BAD_FS, "not a minix3 filesystem"); +#elif defined(MODE_MINIX2) + grub_error (GRUB_ERR_BAD_FS, "not a minix2 filesystem"); +#else + grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem"); +#endif + return 0; +} + +static grub_err_t +grub_minix_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_minix_data *data = 0; + unsigned int pos = 0; + + data = grub_minix_mount (device->disk); + if (!data) + return grub_errno; + + grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE); + if (grub_errno) + goto fail; + + grub_minix_find_file (data, path); + if (grub_errno) + goto fail; + + if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + goto fail; + } + + while (pos < GRUB_MINIX_INODE_SIZE (data)) + { + grub_minix_ino_t ino; + char filename[MAX_MINIX_FILENAME_SIZE + 1]; + grub_minix_ino_t dirino = data->ino; + struct grub_dirhook_info info; + grub_memset (&info, 0, sizeof (info)); + + + if (grub_minix_read_file (data, 0, 0, pos, sizeof (ino), + (char *) &ino) < 0) + return grub_errno; + + if (grub_minix_read_file (data, 0, 0, pos + sizeof (ino), + data->filename_size, + (char *) filename) < 0) + return grub_errno; + filename[data->filename_size] = '\0'; + if (!ino) + { + pos += sizeof (ino) + data->filename_size; + continue; + } + + grub_minix_read_inode (data, grub_minix_to_cpu_ino (ino)); + info.dir = ((GRUB_MINIX_INODE_MODE (data) + & GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR); + info.mtimeset = 1; + info.mtime = grub_minix_to_cpu32 (data->inode.mtime); + + if (hook (filename, &info, hook_data) ? 1 : 0) + break; + + /* Load the old inode back in. */ + grub_minix_read_inode (data, dirino); + + pos += sizeof (ino) + data->filename_size; + } + + fail: + grub_free (data); + return grub_errno; +} + + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_minix_open (struct grub_file *file, const char *name) +{ + struct grub_minix_data *data; + data = grub_minix_mount (file->device->disk); + if (!data) + return grub_errno; + + /* Open the inode op the root directory. */ + grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE); + if (grub_errno) + { + grub_free (data); + return grub_errno; + } + + if (!name || name[0] != '/') + { + grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), name); + return grub_errno; + } + + /* Traverse the directory tree to the node that should be + opened. */ + grub_minix_find_file (data, name); + if (grub_errno) + { + grub_free (data); + return grub_errno; + } + + file->data = data; + file->size = GRUB_MINIX_INODE_SIZE (data); + + return GRUB_ERR_NONE; +} + + +static grub_ssize_t +grub_minix_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_minix_data *data = + (struct grub_minix_data *) file->data; + + return grub_minix_read_file (data, file->read_hook, file->read_hook_data, + file->offset, len, buf); +} + + +static grub_err_t +grub_minix_close (grub_file_t file) +{ + grub_free (file->data); + + return GRUB_ERR_NONE; +} + + + +static struct grub_fs grub_minix_fs = + { +#ifdef MODE_BIGENDIAN +#if defined(MODE_MINIX3) + .name = "minix3_be", +#elif defined(MODE_MINIX2) + .name = "minix2_be", +#else + .name = "minix_be", +#endif +#else +#if defined(MODE_MINIX3) + .name = "minix3", +#elif defined(MODE_MINIX2) + .name = "minix2", +#else + .name = "minix", +#endif +#endif + .fs_dir = grub_minix_dir, + .fs_open = grub_minix_open, + .fs_read = grub_minix_read, + .fs_close = grub_minix_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif + .next = 0 + }; + +#ifdef MODE_BIGENDIAN +#if defined(MODE_MINIX3) +GRUB_MOD_INIT(minix3_be) +#elif defined(MODE_MINIX2) +GRUB_MOD_INIT(minix2_be) +#else +GRUB_MOD_INIT(minix_be) +#endif +#else +#if defined(MODE_MINIX3) +GRUB_MOD_INIT(minix3) +#elif defined(MODE_MINIX2) +GRUB_MOD_INIT(minix2) +#else +GRUB_MOD_INIT(minix) +#endif +#endif +{ + if (!grub_is_lockdown ()) + { + grub_minix_fs.mod = mod; + grub_fs_register (&grub_minix_fs); + } + my_mod = mod; +} + +#ifdef MODE_BIGENDIAN +#if defined(MODE_MINIX3) +GRUB_MOD_FINI(minix3_be) +#elif defined(MODE_MINIX2) +GRUB_MOD_FINI(minix2_be) +#else +GRUB_MOD_FINI(minix_be) +#endif +#else +#if defined(MODE_MINIX3) +GRUB_MOD_FINI(minix3) +#elif defined(MODE_MINIX2) +GRUB_MOD_FINI(minix2) +#else +GRUB_MOD_FINI(minix) +#endif +#endif +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_minix_fs); +} diff --git a/grub-core/fs/minix2.c b/grub-core/fs/minix2.c new file mode 100644 index 000000000..0fcd4b139 --- /dev/null +++ b/grub-core/fs/minix2.c @@ -0,0 +1,2 @@ +#define MODE_MINIX2 1 +#include "minix.c" diff --git a/grub-core/fs/minix2_be.c b/grub-core/fs/minix2_be.c new file mode 100644 index 000000000..cb786df1c --- /dev/null +++ b/grub-core/fs/minix2_be.c @@ -0,0 +1,3 @@ +#define MODE_MINIX2 1 +#define MODE_BIGENDIAN 1 +#include "minix.c" diff --git a/grub-core/fs/minix3.c b/grub-core/fs/minix3.c new file mode 100644 index 000000000..58a21d2b5 --- /dev/null +++ b/grub-core/fs/minix3.c @@ -0,0 +1,2 @@ +#define MODE_MINIX3 1 +#include "minix.c" diff --git a/grub-core/fs/minix3_be.c b/grub-core/fs/minix3_be.c new file mode 100644 index 000000000..d0305e4ff --- /dev/null +++ b/grub-core/fs/minix3_be.c @@ -0,0 +1,3 @@ +#define MODE_MINIX3 1 +#define MODE_BIGENDIAN 1 +#include "minix.c" diff --git a/grub-core/fs/minix_be.c b/grub-core/fs/minix_be.c new file mode 100644 index 000000000..fade347d6 --- /dev/null +++ b/grub-core/fs/minix_be.c @@ -0,0 +1,2 @@ +#define MODE_BIGENDIAN 1 +#include "minix.c" diff --git a/grub-core/fs/newc.c b/grub-core/fs/newc.c new file mode 100644 index 000000000..43b7f8b64 --- /dev/null +++ b/grub-core/fs/newc.c @@ -0,0 +1,74 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#define ALIGN_CPIO(x) (ALIGN_UP ((x), 4)) +#define MAGIC "070701" +#define MAGIC2 "070702" +struct head +{ + char magic[6]; + char ino[8]; + char mode[8]; + char uid[8]; + char gid[8]; + char nlink[8]; + char mtime[8]; + char filesize[8]; + char devmajor[8]; + char devminor[8]; + char rdevmajor[8]; + char rdevminor[8]; + char namesize[8]; + char check[8]; +} GRUB_PACKED; + +static inline unsigned long long +read_number (const char *str, grub_size_t size) +{ + unsigned long long ret = 0; + while (size-- && grub_isxdigit (*str)) + { + char dig = *str++; + if (dig >= '0' && dig <= '9') + dig &= 0xf; + else if (dig >= 'a' && dig <= 'f') + dig -= 'a' - 10; + else + dig -= 'A' - 10; + ret = (ret << 4) | (dig); + } + return ret; +} + +#define FSNAME "newc" + +#include "cpio_common.c" + +GRUB_MOD_INIT (newc) +{ + grub_cpio_fs.mod = mod; + grub_fs_register (&grub_cpio_fs); +} + +GRUB_MOD_FINI (newc) +{ + grub_fs_unregister (&grub_cpio_fs); +} diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c new file mode 100644 index 000000000..26e6077ff --- /dev/null +++ b/grub-core/fs/nilfs2.c @@ -0,0 +1,1247 @@ +/* + * nilfs2.c - New Implementation of Log filesystem + * + * Written by Jiro SEKIBA + * + * Copyright (C) 2003,2004,2005,2007,2008,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 . + */ + + +/* Filetype information as used in inodes. */ +#define FILETYPE_INO_MASK 0170000 +#define FILETYPE_INO_REG 0100000 +#define FILETYPE_INO_DIRECTORY 0040000 +#define FILETYPE_INO_SYMLINK 0120000 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define NILFS_INODE_BMAP_SIZE 7 + +#define NILFS_SUPORT_REV 2 + +/* Magic value used to identify an nilfs2 filesystem. */ +#define NILFS2_SUPER_MAGIC 0x3434 +/* nilfs btree node flag. */ +#define NILFS_BTREE_NODE_ROOT 0x01 + +/* nilfs btree node level. */ +#define NILFS_BTREE_LEVEL_DATA 0 +#define NILFS_BTREE_LEVEL_NODE_MIN (NILFS_BTREE_LEVEL_DATA + 1) + +/* nilfs 1st super block posission from beginning of the partition + in 512 block size */ +#define NILFS_1ST_SUPER_BLOCK 2 +/* nilfs 2nd super block posission from beginning of the partition + in 512 block size */ +#define NILFS_2ND_SUPER_BLOCK(devsize) (((devsize >> 3) - 1) << 3) + +#define LOG_INODE_SIZE 7 +struct grub_nilfs2_inode +{ + grub_uint64_t i_blocks; + grub_uint64_t i_size; + grub_uint64_t i_ctime; + grub_uint64_t i_mtime; + grub_uint32_t i_ctime_nsec; + grub_uint32_t i_mtime_nsec; + grub_uint32_t i_uid; + grub_uint32_t i_gid; + grub_uint16_t i_mode; + grub_uint16_t i_links_count; + grub_uint32_t i_flags; + grub_uint64_t i_bmap[NILFS_INODE_BMAP_SIZE]; +#define i_device_code i_bmap[0] + grub_uint64_t i_xattr; + grub_uint32_t i_generation; + grub_uint32_t i_pad; +}; + +struct grub_nilfs2_super_root +{ + grub_uint32_t sr_sum; + grub_uint16_t sr_bytes; + grub_uint16_t sr_flags; + grub_uint64_t sr_nongc_ctime; + struct grub_nilfs2_inode sr_dat; + struct grub_nilfs2_inode sr_cpfile; + struct grub_nilfs2_inode sr_sufile; +}; + +struct grub_nilfs2_super_block +{ + grub_uint32_t s_rev_level; + grub_uint16_t s_minor_rev_level; + grub_uint16_t s_magic; + grub_uint16_t s_bytes; + grub_uint16_t s_flags; + grub_uint32_t s_crc_seed; + grub_uint32_t s_sum; + grub_uint32_t s_log_block_size; + grub_uint64_t s_nsegments; + grub_uint64_t s_dev_size; + grub_uint64_t s_first_data_block; + grub_uint32_t s_blocks_per_segment; + grub_uint32_t s_r_segments_percentage; + grub_uint64_t s_last_cno; + grub_uint64_t s_last_pseg; + grub_uint64_t s_last_seq; + grub_uint64_t s_free_blocks_count; + grub_uint64_t s_ctime; + grub_uint64_t s_mtime; + grub_uint64_t s_wtime; + grub_uint16_t s_mnt_count; + grub_uint16_t s_max_mnt_count; + grub_uint16_t s_state; + grub_uint16_t s_errors; + grub_uint64_t s_lastcheck; + grub_uint32_t s_checkinterval; + grub_uint32_t s_creator_os; + grub_uint16_t s_def_resuid; + grub_uint16_t s_def_resgid; + grub_uint32_t s_first_ino; + grub_uint16_t s_inode_size; + grub_uint16_t s_dat_entry_size; + grub_uint16_t s_checkpoint_size; + grub_uint16_t s_segment_usage_size; + grub_uint8_t s_uuid[16]; + char s_volume_name[80]; + grub_uint32_t s_c_interval; + grub_uint32_t s_c_block_max; + grub_uint32_t s_reserved[192]; +}; + +struct grub_nilfs2_dir_entry +{ + grub_uint64_t inode; + grub_uint16_t rec_len; +#define MAX_NAMELEN 255 + grub_uint8_t name_len; + grub_uint8_t file_type; +#if 0 /* followed by file name. */ + char name[NILFS_NAME_LEN]; + char pad; +#endif +} GRUB_PACKED; + +enum +{ + NILFS_FT_UNKNOWN, + NILFS_FT_REG_FILE, + NILFS_FT_DIR, + NILFS_FT_CHRDEV, + NILFS_FT_BLKDEV, + NILFS_FT_FIFO, + NILFS_FT_SOCK, + NILFS_FT_SYMLINK, + NILFS_FT_MAX +}; + +struct grub_nilfs2_finfo +{ + grub_uint64_t fi_ino; + grub_uint64_t fi_cno; + grub_uint32_t fi_nblocks; + grub_uint32_t fi_ndatablk; +}; + +struct grub_nilfs2_binfo_v +{ + grub_uint64_t bi_vblocknr; + grub_uint64_t bi_blkoff; +}; + +struct grub_nilfs2_binfo_dat +{ + grub_uint64_t bi_blkoff; + grub_uint8_t bi_level; + grub_uint8_t bi_pad[7]; +}; + +union grub_nilfs2_binfo +{ + struct grub_nilfs2_binfo_v bi_v; + struct grub_nilfs2_binfo_dat bi_dat; +}; + +struct grub_nilfs2_segment_summary +{ + grub_uint32_t ss_datasum; + grub_uint32_t ss_sumsum; + grub_uint32_t ss_magic; + grub_uint16_t ss_bytes; + grub_uint16_t ss_flags; + grub_uint64_t ss_seq; + grub_uint64_t ss_create; + grub_uint64_t ss_next; + grub_uint32_t ss_nblocks; + grub_uint32_t ss_nfinfo; + grub_uint32_t ss_sumbytes; + grub_uint32_t ss_pad; +}; + +struct grub_nilfs2_btree_node +{ + grub_uint8_t bn_flags; + grub_uint8_t bn_level; + grub_uint16_t bn_nchildren; + grub_uint32_t bn_pad; + grub_uint64_t keys[0]; +}; + +struct grub_nilfs2_palloc_group_desc +{ + grub_uint32_t pg_nfrees; +}; + +#define LOG_SIZE_GROUP_DESC 2 + +#define LOG_NILFS_DAT_ENTRY_SIZE 5 +struct grub_nilfs2_dat_entry +{ + grub_uint64_t de_blocknr; + grub_uint64_t de_start; + grub_uint64_t de_end; + grub_uint64_t de_rsv; +}; + +struct grub_nilfs2_snapshot_list +{ + grub_uint64_t ssl_next; + grub_uint64_t ssl_prev; +}; + +struct grub_nilfs2_cpfile_header +{ + grub_uint64_t ch_ncheckpoints; + grub_uint64_t ch_nsnapshots; + struct grub_nilfs2_snapshot_list ch_snapshot_list; +}; + +struct grub_nilfs2_checkpoint +{ + grub_uint32_t cp_flags; + grub_uint32_t cp_checkpoints_count; + struct grub_nilfs2_snapshot_list cp_snapshot_list; + grub_uint64_t cp_cno; + grub_uint64_t cp_create; + grub_uint64_t cp_nblk_inc; + grub_uint64_t cp_inodes_count; + grub_uint64_t cp_blocks_count; + struct grub_nilfs2_inode cp_ifile_inode; +}; + + +#define NILFS_BMAP_LARGE 0x1 +#define NILFS_BMAP_SIZE (NILFS_INODE_BMAP_SIZE * sizeof(grub_uint64_t)) + +/* nilfs extra padding for nonroot btree node. */ +#define NILFS_BTREE_NODE_EXTRA_PAD_SIZE (sizeof(grub_uint64_t)) +#define NILFS_BTREE_ROOT_SIZE NILFS_BMAP_SIZE +#define NILFS_BTREE_ROOT_NCHILDREN_MAX \ + ((NILFS_BTREE_ROOT_SIZE - sizeof(struct nilfs_btree_node)) / \ + (sizeof(grub_uint64_t) + sizeof(grub_uint64_t)) ) + + +struct grub_fshelp_node +{ + struct grub_nilfs2_data *data; + struct grub_nilfs2_inode inode; + grub_uint64_t ino; + int inode_read; +}; + +struct grub_nilfs2_data +{ + struct grub_nilfs2_super_block sblock; + struct grub_nilfs2_super_root sroot; + struct grub_nilfs2_inode ifile; + grub_disk_t disk; + struct grub_nilfs2_inode *inode; + struct grub_fshelp_node diropen; +}; + +/* Log2 size of nilfs2 block in 512 blocks. */ +#define LOG2_NILFS2_BLOCK_SIZE(data) \ + (grub_le_to_cpu32 (data->sblock.s_log_block_size) + 1) + +/* Log2 size of nilfs2 block in bytes. */ +#define LOG2_BLOCK_SIZE(data) \ + (grub_le_to_cpu32 (data->sblock.s_log_block_size) + 10) + +/* The size of an nilfs2 block in bytes. */ +#define NILFS2_BLOCK_SIZE(data) (1 << LOG2_BLOCK_SIZE (data)) + +static grub_uint64_t +grub_nilfs2_dat_translate (struct grub_nilfs2_data *data, grub_uint64_t key); +static grub_dl_t my_mod; + + + +static inline unsigned long +grub_nilfs2_log_palloc_entries_per_group (struct grub_nilfs2_data *data) +{ + return LOG2_BLOCK_SIZE (data) + 3; +} + +static inline grub_uint64_t +grub_nilfs2_palloc_group (struct grub_nilfs2_data *data, + grub_uint64_t nr, grub_uint64_t * offset) +{ + *offset = nr & ((1 << grub_nilfs2_log_palloc_entries_per_group (data)) - 1); + return nr >> grub_nilfs2_log_palloc_entries_per_group (data); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_log_groups_per_desc_block (struct grub_nilfs2_data *data) +{ + return LOG2_BLOCK_SIZE (data) - LOG_SIZE_GROUP_DESC; + + COMPILE_TIME_ASSERT (sizeof (struct grub_nilfs2_palloc_group_desc) + == (1 << LOG_SIZE_GROUP_DESC)); +} + +static inline grub_uint32_t +grub_nilfs2_log_entries_per_block_log (struct grub_nilfs2_data *data, + unsigned long log_entry_size) +{ + return LOG2_BLOCK_SIZE (data) - log_entry_size; +} + + +static inline grub_uint32_t +grub_nilfs2_blocks_per_group_log (struct grub_nilfs2_data *data, + unsigned long log_entry_size) +{ + return (1 << (grub_nilfs2_log_palloc_entries_per_group (data) + - grub_nilfs2_log_entries_per_block_log (data, + log_entry_size))) + 1; +} + +static inline grub_uint32_t +grub_nilfs2_blocks_per_desc_block_log (struct grub_nilfs2_data *data, + unsigned long log_entry_size) +{ + return(grub_nilfs2_blocks_per_group_log (data, log_entry_size) + << grub_nilfs2_palloc_log_groups_per_desc_block (data)) + 1; +} + +static inline grub_uint32_t +grub_nilfs2_palloc_desc_block_offset_log (struct grub_nilfs2_data *data, + unsigned long group, + unsigned long log_entry_size) +{ + grub_uint32_t desc_block = + group >> grub_nilfs2_palloc_log_groups_per_desc_block (data); + return desc_block * grub_nilfs2_blocks_per_desc_block_log (data, + log_entry_size); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_bitmap_block_offset (struct grub_nilfs2_data *data, + unsigned long group, + unsigned long log_entry_size) +{ + unsigned long desc_offset = group + & ((1 << grub_nilfs2_palloc_log_groups_per_desc_block (data)) - 1); + + return grub_nilfs2_palloc_desc_block_offset_log (data, group, log_entry_size) + + 1 + + desc_offset * grub_nilfs2_blocks_per_group_log (data, log_entry_size); +} + +static inline grub_uint32_t +grub_nilfs2_palloc_entry_offset_log (struct grub_nilfs2_data *data, + grub_uint64_t nr, + unsigned long log_entry_size) +{ + unsigned long group; + grub_uint64_t group_offset; + + group = grub_nilfs2_palloc_group (data, nr, &group_offset); + + return grub_nilfs2_palloc_bitmap_block_offset (data, group, + log_entry_size) + 1 + + (group_offset >> grub_nilfs2_log_entries_per_block_log (data, + log_entry_size)); + +} + +static inline struct grub_nilfs2_btree_node * +grub_nilfs2_btree_get_root (struct grub_nilfs2_inode *inode) +{ + return (struct grub_nilfs2_btree_node *) &inode->i_bmap[0]; +} + +static inline int +grub_nilfs2_btree_get_level (struct grub_nilfs2_btree_node *node) +{ + return node->bn_level; +} + +static inline grub_uint64_t * +grub_nilfs2_btree_node_dkeys (struct grub_nilfs2_btree_node *node) +{ + return (node->keys + + ((node->bn_flags & NILFS_BTREE_NODE_ROOT) ? + 0 : (NILFS_BTREE_NODE_EXTRA_PAD_SIZE / sizeof (grub_uint64_t)))); +} + +static inline grub_uint64_t +grub_nilfs2_btree_node_get_key (struct grub_nilfs2_btree_node *node, + int index) +{ + return grub_le_to_cpu64 (*(grub_nilfs2_btree_node_dkeys (node) + index)); +} + +static inline int +grub_nilfs2_btree_node_nchildren_max (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node) +{ + int node_children_max = ((NILFS2_BLOCK_SIZE (data) - + sizeof (struct grub_nilfs2_btree_node) - + NILFS_BTREE_NODE_EXTRA_PAD_SIZE) / + (sizeof (grub_uint64_t) + sizeof (grub_uint64_t))); + + return (node->bn_flags & NILFS_BTREE_NODE_ROOT) ? 3 : node_children_max; +} + +static inline int +grub_nilfs2_btree_node_lookup (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node, + grub_uint64_t key, int *indexp) +{ + grub_uint64_t nkey; + int index = 0, low, high, s; + + low = 0; + + high = grub_le_to_cpu16 (node->bn_nchildren) - 1; + if (high >= grub_nilfs2_btree_node_nchildren_max (data, node)) + { + grub_error (GRUB_ERR_BAD_FS, "too many children"); + *indexp = index; + return 0; + } + + s = 0; + while (low <= high) + { + index = (low + high) / 2; + nkey = grub_nilfs2_btree_node_get_key (node, index); + if (nkey == key) + { + *indexp = index; + return 1; + } + else if (nkey < key) + { + low = index + 1; + s = -1; + } + else + { + high = index - 1; + s = 1; + } + } + + if (node->bn_level > NILFS_BTREE_LEVEL_NODE_MIN) + { + if (s > 0 && index > 0) + index--; + } + else if (s < 0) + index++; + + *indexp = index; + return s == 0; +} + +static inline grub_uint64_t * +grub_nilfs2_btree_node_dptrs (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node) +{ + return (grub_uint64_t *) (grub_nilfs2_btree_node_dkeys (node) + + grub_nilfs2_btree_node_nchildren_max (data, + node)); +} + +static inline grub_uint64_t +grub_nilfs2_btree_node_get_ptr (struct grub_nilfs2_data *data, + struct grub_nilfs2_btree_node *node, + int index) +{ + return + grub_le_to_cpu64 (*(grub_nilfs2_btree_node_dptrs (data, node) + index)); +} + +static inline int +grub_nilfs2_btree_get_nonroot_node (struct grub_nilfs2_data *data, + grub_uint64_t ptr, void *block) +{ + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + return grub_disk_read (disk, ptr * nilfs2_block_count, 0, + NILFS2_BLOCK_SIZE (data), block); +} + +static grub_uint64_t +grub_nilfs2_btree_lookup (struct grub_nilfs2_data *data, + struct grub_nilfs2_inode *inode, + grub_uint64_t key, int need_translate) +{ + struct grub_nilfs2_btree_node *node; + void *block; + grub_uint64_t ptr; + int level, found = 0, index; + + block = grub_malloc (NILFS2_BLOCK_SIZE (data)); + if (!block) + return -1; + + node = grub_nilfs2_btree_get_root (inode); + level = grub_nilfs2_btree_get_level (node); + + found = grub_nilfs2_btree_node_lookup (data, node, key, &index); + + if (grub_errno != GRUB_ERR_NONE) + goto fail; + + ptr = grub_nilfs2_btree_node_get_ptr (data, node, index); + if (need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + + for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) + { + grub_nilfs2_btree_get_nonroot_node (data, ptr, block); + if (grub_errno) + { + goto fail; + } + node = (struct grub_nilfs2_btree_node *) block; + + if (node->bn_level != level) + { + grub_error (GRUB_ERR_BAD_FS, "btree level mismatch\n"); + goto fail; + } + + if (!found) + found = grub_nilfs2_btree_node_lookup (data, node, key, &index); + else + index = 0; + + if (index < grub_nilfs2_btree_node_nchildren_max (data, node) && + grub_errno == GRUB_ERR_NONE) + { + ptr = grub_nilfs2_btree_node_get_ptr (data, node, index); + if (need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + } + else + { + grub_error (GRUB_ERR_BAD_FS, "btree corruption\n"); + goto fail; + } + } + + grub_free (block); + + if (!found) + return -1; + + return ptr; + fail: + grub_free (block); + return -1; +} + +static inline grub_uint64_t +grub_nilfs2_direct_lookup (struct grub_nilfs2_inode *inode, grub_uint64_t key) +{ + if (1 + key > 6) + { + grub_error (GRUB_ERR_BAD_FS, "key is too large"); + return 0xffffffffffffffff; + } + return grub_le_to_cpu64 (inode->i_bmap[1 + key]); +} + +static inline grub_uint64_t +grub_nilfs2_bmap_lookup (struct grub_nilfs2_data *data, + struct grub_nilfs2_inode *inode, + grub_uint64_t key, int need_translate) +{ + struct grub_nilfs2_btree_node *root = grub_nilfs2_btree_get_root (inode); + if (root->bn_flags & NILFS_BMAP_LARGE) + return grub_nilfs2_btree_lookup (data, inode, key, need_translate); + else + { + grub_uint64_t ptr; + ptr = grub_nilfs2_direct_lookup (inode, key); + if (ptr != ((grub_uint64_t) 0xffffffffffffffff) && need_translate) + ptr = grub_nilfs2_dat_translate (data, ptr); + return ptr; + } +} + +static grub_uint64_t +grub_nilfs2_dat_translate (struct grub_nilfs2_data *data, grub_uint64_t key) +{ + struct grub_nilfs2_dat_entry entry; + grub_disk_t disk = data->disk; + grub_uint64_t pptr; + grub_uint64_t blockno, offset; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + blockno = grub_nilfs2_palloc_entry_offset_log (data, key, + LOG_NILFS_DAT_ENTRY_SIZE); + + offset = ((key * sizeof (struct grub_nilfs2_dat_entry)) + & ((1 << LOG2_BLOCK_SIZE (data)) - 1)); + + pptr = grub_nilfs2_bmap_lookup (data, &data->sroot.sr_dat, blockno, 0); + if (pptr == (grub_uint64_t) - 1) + { + grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + return -1; + } + + grub_disk_read (disk, pptr * nilfs2_block_count, offset, + sizeof (struct grub_nilfs2_dat_entry), &entry); + + return grub_le_to_cpu64 (entry.de_blocknr); +} + + +static grub_disk_addr_t +grub_nilfs2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) +{ + struct grub_nilfs2_data *data = node->data; + struct grub_nilfs2_inode *inode = &node->inode; + grub_uint64_t pptr = -1; + + pptr = grub_nilfs2_bmap_lookup (data, inode, fileblock, 1); + if (pptr == (grub_uint64_t) - 1) + { + grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + return -1; + } + + return pptr; +} + +/* Read LEN bytes from the file described by DATA starting with byte + POS. Return the amount of read bytes in READ. */ +static grub_ssize_t +grub_nilfs2_read_file (grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) +{ + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, + pos, len, buf, grub_nilfs2_read_block, + grub_le_to_cpu64 (node->inode.i_size), + LOG2_NILFS2_BLOCK_SIZE (node->data), 0); + +} + +static grub_err_t +grub_nilfs2_read_checkpoint (struct grub_nilfs2_data *data, + grub_uint64_t cpno, + struct grub_nilfs2_checkpoint *cpp) +{ + grub_uint64_t blockno; + grub_uint64_t offset; + grub_uint64_t pptr; + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + /* Assume sizeof(struct grub_nilfs2_cpfile_header) < + sizeof(struct grub_nilfs2_checkpoint). + */ + blockno = grub_divmod64 (cpno, NILFS2_BLOCK_SIZE (data) / + sizeof (struct grub_nilfs2_checkpoint), &offset); + + pptr = grub_nilfs2_bmap_lookup (data, &data->sroot.sr_cpfile, blockno, 1); + if (pptr == (grub_uint64_t) - 1) + { + return grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + } + + return grub_disk_read (disk, pptr * nilfs2_block_count, + offset * sizeof (struct grub_nilfs2_checkpoint), + sizeof (struct grub_nilfs2_checkpoint), cpp); +} + +static inline grub_err_t +grub_nilfs2_read_last_checkpoint (struct grub_nilfs2_data *data, + struct grub_nilfs2_checkpoint *cpp) +{ + return grub_nilfs2_read_checkpoint (data, + grub_le_to_cpu64 (data-> + sblock.s_last_cno), + cpp); +} + +/* Read the inode INO for the file described by DATA into INODE. */ +static grub_err_t +grub_nilfs2_read_inode (struct grub_nilfs2_data *data, + grub_uint64_t ino, struct grub_nilfs2_inode *inodep) +{ + grub_uint64_t blockno; + grub_uint64_t offset; + grub_uint64_t pptr; + grub_disk_t disk = data->disk; + unsigned int nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + blockno = grub_nilfs2_palloc_entry_offset_log (data, ino, + LOG_INODE_SIZE); + + offset = ((sizeof (struct grub_nilfs2_inode) * ino) + & ((1 << LOG2_BLOCK_SIZE (data)) - 1)); + pptr = grub_nilfs2_bmap_lookup (data, &data->ifile, blockno, 1); + if (pptr == (grub_uint64_t) - 1) + { + return grub_error (GRUB_ERR_BAD_FS, "btree lookup failure"); + } + + return grub_disk_read (disk, pptr * nilfs2_block_count, offset, + sizeof (struct grub_nilfs2_inode), inodep); +} + +static int +grub_nilfs2_valid_sb (struct grub_nilfs2_super_block *sbp) +{ + if (grub_le_to_cpu16 (sbp->s_magic) != NILFS2_SUPER_MAGIC) + return 0; + + if (grub_le_to_cpu32 (sbp->s_rev_level) != NILFS_SUPORT_REV) + return 0; + + /* 20 already means 1GiB blocks. We don't want to deal with blocks overflowing int32. */ + if (grub_le_to_cpu32 (sbp->s_log_block_size) > 20) + return 0; + + return 1; +} + +static grub_err_t +grub_nilfs2_load_sb (struct grub_nilfs2_data *data) +{ + grub_disk_t disk = data->disk; + struct grub_nilfs2_super_block sb2; + grub_uint64_t partition_size; + int valid[2]; + int swp = 0; + grub_err_t err; + + /* Read first super block. */ + err = grub_disk_read (disk, NILFS_1ST_SUPER_BLOCK, 0, + sizeof (struct grub_nilfs2_super_block), &data->sblock); + if (err) + return err; + /* Make sure if 1st super block is valid. */ + valid[0] = grub_nilfs2_valid_sb (&data->sblock); + + if (valid[0]) + partition_size = (grub_le_to_cpu64 (data->sblock.s_dev_size) + >> GRUB_DISK_SECTOR_BITS); + else + partition_size = grub_disk_native_sectors (disk); + if (partition_size != GRUB_DISK_SIZE_UNKNOWN) + { + /* Read second super block. */ + err = grub_disk_read (disk, NILFS_2ND_SUPER_BLOCK (partition_size), 0, + sizeof (struct grub_nilfs2_super_block), &sb2); + if (err) + { + valid[1] = 0; + grub_errno = GRUB_ERR_NONE; + } + else + /* Make sure if 2nd super block is valid. */ + valid[1] = grub_nilfs2_valid_sb (&sb2); + } + else + /* 2nd super block may not exist, so it's invalid. */ + valid[1] = 0; + + if (!valid[0] && !valid[1]) + return grub_error (GRUB_ERR_BAD_FS, "not a nilfs2 filesystem"); + + swp = valid[1] && (!valid[0] || + grub_le_to_cpu64 (data->sblock.s_last_cno) < + grub_le_to_cpu64 (sb2.s_last_cno)); + + /* swap if first super block is invalid or older than second one. */ + if (swp) + grub_memcpy (&data->sblock, &sb2, + sizeof (struct grub_nilfs2_super_block)); + + return GRUB_ERR_NONE; +} + +static struct grub_nilfs2_data * +grub_nilfs2_mount (grub_disk_t disk) +{ + struct grub_nilfs2_data *data; + struct grub_nilfs2_segment_summary ss; + struct grub_nilfs2_checkpoint last_checkpoint; + grub_uint64_t last_pseg; + grub_uint32_t nblocks; + unsigned int nilfs2_block_count; + + data = grub_malloc (sizeof (struct grub_nilfs2_data)); + if (!data) + return 0; + + data->disk = disk; + + /* Read the superblock. */ + grub_nilfs2_load_sb (data); + if (grub_errno) + goto fail; + + nilfs2_block_count = (1 << LOG2_NILFS2_BLOCK_SIZE (data)); + + /* Read the last segment summary. */ + last_pseg = grub_le_to_cpu64 (data->sblock.s_last_pseg); + grub_disk_read (disk, last_pseg * nilfs2_block_count, 0, + sizeof (struct grub_nilfs2_segment_summary), &ss); + + if (grub_errno) + goto fail; + + /* Read the super root block. */ + nblocks = grub_le_to_cpu32 (ss.ss_nblocks); + grub_disk_read (disk, (last_pseg + (nblocks - 1)) * nilfs2_block_count, 0, + sizeof (struct grub_nilfs2_super_root), &data->sroot); + + if (grub_errno) + goto fail; + + grub_nilfs2_read_last_checkpoint (data, &last_checkpoint); + + if (grub_errno) + goto fail; + + grub_memcpy (&data->ifile, &last_checkpoint.cp_ifile_inode, + sizeof (struct grub_nilfs2_inode)); + + data->diropen.data = data; + data->diropen.ino = 2; + data->diropen.inode_read = 1; + data->inode = &data->diropen.inode; + + grub_nilfs2_read_inode (data, 2, data->inode); + + return data; + +fail: + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a nilfs2 filesystem"); + + grub_free (data); + return 0; +} + +static char * +grub_nilfs2_read_symlink (grub_fshelp_node_t node) +{ + char *symlink; + struct grub_fshelp_node *diro = node; + + if (!diro->inode_read) + { + grub_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + symlink = grub_malloc (grub_le_to_cpu64 (diro->inode.i_size) + 1); + if (!symlink) + return 0; + + grub_nilfs2_read_file (diro, 0, 0, 0, + grub_le_to_cpu64 (diro->inode.i_size), symlink); + if (grub_errno) + { + grub_free (symlink); + return 0; + } + + symlink[grub_le_to_cpu64 (diro->inode.i_size)] = '\0'; + return symlink; +} + +static int +grub_nilfs2_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_off_t fpos = 0; + struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; + + if (!diro->inode_read) + { + grub_nilfs2_read_inode (diro->data, diro->ino, &diro->inode); + if (grub_errno) + return 0; + } + + /* Iterate files. */ + while (fpos < grub_le_to_cpu64 (diro->inode.i_size)) + { + struct grub_nilfs2_dir_entry dirent; + + grub_nilfs2_read_file (diro, 0, 0, fpos, + sizeof (struct grub_nilfs2_dir_entry), + (char *) &dirent); + if (grub_errno) + return 0; + + if (dirent.rec_len == 0) + return 0; + + if (dirent.name_len != 0) + { + char filename[MAX_NAMELEN + 1]; + struct grub_fshelp_node *fdiro; + enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN; + + grub_nilfs2_read_file (diro, 0, 0, + fpos + sizeof (struct grub_nilfs2_dir_entry), + dirent.name_len, filename); + if (grub_errno) + return 0; + + fdiro = grub_malloc (sizeof (struct grub_fshelp_node)); + if (!fdiro) + return 0; + + fdiro->data = diro->data; + fdiro->ino = grub_le_to_cpu64 (dirent.inode); + + filename[dirent.name_len] = '\0'; + + if (dirent.file_type != NILFS_FT_UNKNOWN) + { + fdiro->inode_read = 0; + + if (dirent.file_type == NILFS_FT_DIR) + type = GRUB_FSHELP_DIR; + else if (dirent.file_type == NILFS_FT_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else if (dirent.file_type == NILFS_FT_REG_FILE) + type = GRUB_FSHELP_REG; + } + else + { + /* The filetype can not be read from the dirent, read + the inode to get more information. */ + grub_nilfs2_read_inode (diro->data, + grub_le_to_cpu64 (dirent.inode), + &fdiro->inode); + if (grub_errno) + { + grub_free (fdiro); + return 0; + } + + fdiro->inode_read = 1; + + if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) + type = GRUB_FSHELP_DIR; + else if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + else if ((grub_le_to_cpu16 (fdiro->inode.i_mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_REG) + type = GRUB_FSHELP_REG; + } + + if (hook (filename, type, fdiro, hook_data)) + return 1; + } + + fpos += grub_le_to_cpu16 (dirent.rec_len); + } + + return 0; +} + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_nilfs2_open (struct grub_file *file, const char *name) +{ + struct grub_nilfs2_data *data = NULL; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (file->device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (name, &data->diropen, &fdiro, + grub_nilfs2_iterate_dir, grub_nilfs2_read_symlink, + GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + if (!fdiro->inode_read) + { + grub_nilfs2_read_inode (data, fdiro->ino, &fdiro->inode); + if (grub_errno) + goto fail; + } + + grub_memcpy (data->inode, &fdiro->inode, sizeof (struct grub_nilfs2_inode)); + grub_free (fdiro); + + file->size = grub_le_to_cpu64 (data->inode->i_size); + file->data = data; + file->offset = 0; + + return 0; + +fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_close (grub_file_t file) +{ + grub_free (file->data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +/* Read LEN bytes data from FILE into BUF. */ +static grub_ssize_t +grub_nilfs2_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_nilfs2_data *data = (struct grub_nilfs2_data *) file->data; + + return grub_nilfs2_read_file (&data->diropen, + file->read_hook, file->read_hook_data, + file->offset, len, buf); +} + +/* Context for grub_nilfs2_dir. */ +struct grub_nilfs2_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_nilfs2_data *data; +}; + +/* Helper for grub_nilfs2_dir. */ +static int +grub_nilfs2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_nilfs2_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + if (!node->inode_read) + { + grub_nilfs2_read_inode (ctx->data, node->ino, &node->inode); + if (!grub_errno) + node->inode_read = 1; + grub_errno = GRUB_ERR_NONE; + } + if (node->inode_read) + { + info.mtimeset = 1; + info.mtime = grub_le_to_cpu64 (node->inode.i_mtime); + } + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_nilfs2_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_nilfs2_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + ctx.data = grub_nilfs2_mount (device->disk); + if (!ctx.data) + goto fail; + + grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro, + grub_nilfs2_iterate_dir, grub_nilfs2_read_symlink, + GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_nilfs2_iterate_dir (fdiro, grub_nilfs2_dir_iter, &ctx); + +fail: + if (fdiro != &ctx.data->diropen) + grub_free (fdiro); + grub_free (ctx.data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_label (grub_device_t device, char **label) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (data) + *label = grub_strndup (data->sblock.s_volume_name, + sizeof (data->sblock.s_volume_name)); + else + *label = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_nilfs2_uuid (grub_device_t device, char **uuid) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (data) + { + *uuid = + grub_xasprintf + ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + data->sblock.s_uuid[0], data->sblock.s_uuid[1], + data->sblock.s_uuid[2], data->sblock.s_uuid[3], + data->sblock.s_uuid[4], data->sblock.s_uuid[5], + data->sblock.s_uuid[6], data->sblock.s_uuid[7], + data->sblock.s_uuid[8], data->sblock.s_uuid[9], + data->sblock.s_uuid[10], data->sblock.s_uuid[11], + data->sblock.s_uuid[12], data->sblock.s_uuid[13], + data->sblock.s_uuid[14], data->sblock.s_uuid[15]); + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +/* Get mtime. */ +static grub_err_t +grub_nilfs2_mtime (grub_device_t device, grub_int64_t * tm) +{ + struct grub_nilfs2_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_nilfs2_mount (disk); + if (!data) + *tm = 0; + else + *tm = (grub_int32_t) grub_le_to_cpu64 (data->sblock.s_wtime); + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + + + +static struct grub_fs grub_nilfs2_fs = { + .name = "nilfs2", + .fs_dir = grub_nilfs2_dir, + .fs_open = grub_nilfs2_open, + .fs_read = grub_nilfs2_read, + .fs_close = grub_nilfs2_close, + .fs_label = grub_nilfs2_label, + .fs_uuid = grub_nilfs2_uuid, + .fs_mtime = grub_nilfs2_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (nilfs2) +{ + COMPILE_TIME_ASSERT ((1 << LOG_NILFS_DAT_ENTRY_SIZE) + == sizeof (struct + grub_nilfs2_dat_entry)); + COMPILE_TIME_ASSERT (1 << LOG_INODE_SIZE + == sizeof (struct grub_nilfs2_inode)); + if (!grub_is_lockdown ()) + { + grub_nilfs2_fs.mod = mod; + grub_fs_register (&grub_nilfs2_fs); + } + my_mod = mod; +} + +GRUB_MOD_FINI (nilfs2) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_nilfs2_fs); +} diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c new file mode 100644 index 000000000..b3117bf92 --- /dev/null +++ b/grub-core/fs/ntfs.c @@ -0,0 +1,1557 @@ +/* ntfs.c - NTFS filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#define grub_fshelp_node grub_ntfs_file + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +#define grub_fshelp_node grub_ntfs_file + +static inline grub_uint16_t +u16at (void *ptr, grub_size_t ofs) +{ + return grub_le_to_cpu16 (grub_get_unaligned16 ((char *) ptr + ofs)); +} + +static inline grub_uint32_t +u32at (void *ptr, grub_size_t ofs) +{ + return grub_le_to_cpu32 (grub_get_unaligned32 ((char *) ptr + ofs)); +} + +static inline grub_uint64_t +u64at (void *ptr, grub_size_t ofs) +{ + return grub_le_to_cpu64 (grub_get_unaligned64 ((char *) ptr + ofs)); +} + +static grub_uint16_t +first_attr_off (void *mft_buf_ptr) +{ + return u16at (mft_buf_ptr, 0x14); +} + +static grub_uint16_t +res_attr_data_off (void *res_attr_ptr) +{ + return u16at (res_attr_ptr, 0x14); +} + +static grub_uint32_t +res_attr_data_len (void *res_attr_ptr) +{ + return u32at (res_attr_ptr, 0x10); +} + +/* + * Check if the attribute is valid and doesn't exceed the allocated region. + * This accounts for resident and non-resident data. + * + * This is based off the documentation from the linux-ntfs project: + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html + */ +static bool +validate_attribute (grub_uint8_t *attr, void *end) +{ + grub_size_t attr_size = 0; + grub_size_t min_size = 0; + grub_size_t spare = (grub_uint8_t *) end - attr; + /* + * Just used as a temporary variable to try and deal with cases where someone + * tries to overlap fields. + */ + grub_size_t curr = 0; + + /* Need verify we can entirely read the attributes header. */ + if (attr + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + goto fail; + + /* + * So, the rest of this code uses a 16bit int for the attribute length but + * from reading the all the documentation I could find it says this field is + * actually 32bit. But let's be consistent with the rest of the code. + * + * https://elixir.bootlin.com/linux/v6.10.7/source/fs/ntfs3/ntfs.h#L370 + */ + attr_size = u16at (attr, GRUB_NTFS_ATTRIBUTE_LENGTH); + + if (attr_size > spare) + goto fail; + + /* Not an error case, just reached the end of the attributes. */ + if (attr_size == 0) + return false; + + /* + * Extra validation by trying to calculate a minimum possible size for this + * attribute. +8 from the size of the resident data struct which is the + * minimum that can be added. + */ + min_size = GRUB_NTFS_ATTRIBUTE_HEADER_SIZE + 8; + + if (min_size > attr_size) + goto fail; + + /* Is the data is resident (0) or not (1). */ + if (attr[GRUB_NTFS_ATTRIBUTE_RESIDENT] == 0) + { + /* Read the offset and size of the attribute. */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_RES_OFFSET); + curr += u32at (attr, GRUB_NTFS_ATTRIBUTE_RES_LENGTH); + if (curr > min_size) + min_size = curr; + } + else + { + /* + * If the data is non-resident, the minimum size is 64 which is where + * the data runs start. We already have a minimum size of 24. So, just + * adding 40 to get to the real value. + */ + min_size += 40; + if (min_size > attr_size) + goto fail; + /* If the compression unit size is > 0, +8 bytes*/ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_COMPRESSION_UNIT_SIZE) > 0) + min_size += 8; + + /* + * Need to consider the data runs now. Each member of the run has byte + * that describes the size of the data length and offset. Each being + * 4 bits in the byte. + */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_DATA_RUNS); + + if (curr + 1 > min_size) + min_size = curr + 1; + + if (min_size > attr_size) + goto fail; + + /* + * Each attribute can store multiple data runs which are stored + * continuously in the attribute. They exist as one header byte + * with up to 14 bytes following it depending on the lengths. + * We stop when we hit a header that is just a NUL byte. + * + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/data_runs.html + */ + while (attr[curr] != 0) + { + /* + * We stop when we hit a header that is just a NUL byte. The data + * run header is stored as a single byte where the top 4 bits refer + * to the number of bytes used to store the total length of the + * data run, and the number of bytes used to store the offset. + * These directly follow the header byte, so we use them to update + * the minimum size. + */ + min_size += (attr[curr] & 0x7) + ((attr[curr] >> 4) & 0x7); + curr += min_size; + min_size++; + if (min_size > attr_size) + goto fail; + } + } + + /* Name offset, doing this after data residence checks. */ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET) != 0) + { + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET); + /* + * Multiple the name length by 2 as its UTF-16. Can be zero if this in an + * unamed attribute. + */ + curr += attr[GRUB_NTFS_ATTRIBUTE_NAME_LENGTH] * 2; + if (curr > min_size) + min_size = curr; + } + + /* Padded to 8 bytes. */ + if (min_size % 8 != 0) + min_size += 8 - (min_size % 8); + + /* + * At this point min_size should be exactly attr_size but being flexible + * here to avoid any issues. + */ + if (min_size > attr_size) + goto fail; + + return true; + + fail: + grub_dprintf ("ntfs", "spare=%" PRIuGRUB_SIZE " min_size=%" PRIuGRUB_SIZE " attr_size=%" PRIuGRUB_SIZE "\n", + spare, min_size, attr_size); + return false; +} + +/* Return the next attribute if it exists, otherwise return NULL. */ +static grub_uint8_t * +next_attribute (grub_uint8_t *curr_attribute, void *end) +{ + grub_uint8_t *next = curr_attribute; + + /* + * Need to verify we aren't exceeding the end of the buffer by reading the + * header for the current attribute + */ + if (curr_attribute + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + return NULL; + + next += u16at (curr_attribute, 4); + if (validate_attribute (next, end) == false) + return NULL; + + return next; +} + + +grub_ntfscomp_func_t grub_ntfscomp_func; + +static grub_err_t +fixup (grub_uint8_t *buf, grub_size_t len, const grub_uint8_t *magic) +{ + grub_uint16_t ss; + grub_uint8_t *pu; + grub_uint16_t us; + + COMPILE_TIME_ASSERT ((1 << GRUB_NTFS_BLK_SHR) == GRUB_DISK_SECTOR_SIZE); + + if (grub_memcmp (buf, magic, 4)) + return grub_error (GRUB_ERR_BAD_FS, "%s label not found", magic); + + ss = u16at (buf, 6) - 1; + if (ss != len) + return grub_error (GRUB_ERR_BAD_FS, "size not match"); + pu = buf + u16at (buf, 4); + us = u16at (pu, 0); + buf -= 2; + while (ss > 0) + { + buf += GRUB_DISK_SECTOR_SIZE; + pu += 2; + if (u16at (buf, 0) != us) + return grub_error (GRUB_ERR_BAD_FS, "fixup signature not match"); + buf[0] = pu[0]; + buf[1] = pu[1]; + ss--; + } + + return 0; +} + +static grub_err_t read_mft (struct grub_ntfs_data *data, grub_uint8_t *buf, + grub_uint64_t mftno); +static grub_err_t read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, + grub_disk_addr_t ofs, grub_size_t len, + int cached, + grub_disk_read_hook_t read_hook, + void *read_hook_data); + +static grub_err_t read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, + grub_uint8_t *dest, + grub_disk_addr_t ofs, grub_size_t len, + int cached, + grub_disk_read_hook_t read_hook, + void *read_hook_data); + +static grub_err_t +init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) +{ + at->mft = mft; + at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; + at->attr_nxt = mft->buf + first_attr_off (mft->buf); + at->end = mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + + if (at->attr_nxt > at->end) + return grub_error (GRUB_ERR_BAD_FS, "attributes start outside the MFT"); + + at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; + + return GRUB_ERR_NONE; +} + +static void +free_attr (struct grub_ntfs_attr *at) +{ + grub_free (at->emft_buf); + grub_free (at->edat_buf); + grub_free (at->sbuf); +} + +static grub_uint8_t * +find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) +{ + grub_uint8_t *mft_end; + + if (at->flags & GRUB_NTFS_AF_ALST) + { + retry: + while (at->attr_nxt) + { + at->attr_cur = at->attr_nxt; + at->attr_nxt = next_attribute (at->attr_cur, at->attr_end); + if ((*at->attr_cur == attr) || (attr == 0)) + { + grub_uint8_t *new_pos, *end; + + if (at->flags & GRUB_NTFS_AF_MMFT) + { + if ((grub_disk_read + (at->mft->data->disk, u32at (at->attr_cur, 0x10), 0, + 512, at->emft_buf)) + || + (grub_disk_read + (at->mft->data->disk, u32at (at->attr_cur, 0x14), 0, + 512, at->emft_buf + 512))) + return NULL; + + if (fixup (at->emft_buf, at->mft->data->mft_size, + (const grub_uint8_t *) "FILE")) + return NULL; + } + else + { + if (read_mft (at->mft->data, at->emft_buf, + u32at (at->attr_cur, 0x10))) + return NULL; + } + + /* + * Only time emft_bufs is defined is in this function, with this + * size. + */ + grub_size_t emft_buf_size = + at->mft->data->mft_size << GRUB_NTFS_BLK_SHR; + + /* + * Needs to be enough space for the successful case to even + * bother. + */ + if (first_attr_off (at->emft_buf) >= (emft_buf_size - 0x18 - 2)) + { + grub_error (GRUB_ERR_BAD_FS, + "can\'t find 0x%X in attribute list", + (unsigned char) *at->attr_cur); + return NULL; + } + + new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; + end = &at->emft_buf[emft_buf_size]; + + while (new_pos && *new_pos != 0xFF) + { + if ((*new_pos == *at->attr_cur) + && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18))) + { + return new_pos; + } + new_pos = next_attribute (new_pos, end); + } + grub_error (GRUB_ERR_BAD_FS, + "can\'t find 0x%X in attribute list", + (unsigned char) *at->attr_cur); + return NULL; + } + } + return NULL; + } + at->attr_cur = at->attr_nxt; + mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + while (at->attr_cur >= at->mft->buf && at->attr_cur < mft_end && *at->attr_cur != 0xFF) + { + at->attr_nxt = next_attribute (at->attr_cur, at->end); + if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST) + at->attr_end = at->attr_cur; + if ((*at->attr_cur == attr) || (attr == 0)) + return at->attr_cur; + at->attr_cur = at->attr_nxt; + } + if (at->attr_end) + { + grub_uint8_t *pa, *pa_end; + + at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + if (at->emft_buf == NULL) + return NULL; + + pa = at->attr_end; + if (pa[8]) + { + grub_uint32_t n; + + n = ((u32at (pa, 0x30) + GRUB_DISK_SECTOR_SIZE - 1) + & (~(GRUB_DISK_SECTOR_SIZE - 1))); + at->attr_cur = at->attr_end; + at->edat_buf = grub_malloc (n); + if (!at->edat_buf) + return NULL; + if (read_data (at, pa, at->edat_buf, 0, n, 0, 0, 0)) + { + grub_error (GRUB_ERR_BAD_FS, + "fail to read non-resident attribute list"); + return NULL; + } + at->attr_nxt = at->edat_buf; + at->attr_end = at->edat_buf + u32at (pa, 0x30); + pa_end = at->edat_buf + n; + } + else + { + at->attr_nxt = at->attr_end + res_attr_data_off (pa); + at->attr_end = at->attr_end + u32at (pa, 4); + pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + } + at->flags |= GRUB_NTFS_AF_ALST; + + /* From this point on pa_end is the end of the buffer */ + at->end = pa_end; + + if (validate_attribute (at->attr_nxt, pa_end) == false) + return NULL; + + while (at->attr_nxt) + { + if ((*at->attr_nxt == attr) || (attr == 0)) + break; + at->attr_nxt = next_attribute (at->attr_nxt, pa_end); + } + + if (at->attr_nxt >= at->attr_end || at->attr_nxt == NULL) + return NULL; + + if ((at->flags & GRUB_NTFS_AF_MMFT) && (attr == GRUB_NTFS_AT_DATA)) + { + at->flags |= GRUB_NTFS_AF_GPOS; + at->attr_cur = at->attr_nxt; + pa = at->attr_cur; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + + grub_set_unaligned32 ((char *) pa + 0x10, + grub_cpu_to_le32 (at->mft->data->mft_start)); + grub_set_unaligned32 ((char *) pa + 0x14, + grub_cpu_to_le32 (at->mft->data->mft_start + + 1)); + pa = at->attr_nxt + u16at (pa, 4); + + if (validate_attribute (pa, pa_end) == true) + pa = NULL; + + while (pa) + { + if (*pa != attr) + break; + + if ((pa >= pa_end) || (pa_end - pa < 0x18)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list"); + return NULL; + } + + if (read_attr + (at, pa + 0x10, + u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), + at->mft->data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0)) + return NULL; + pa = next_attribute (pa, pa_end); + } + at->attr_nxt = at->attr_cur; + at->flags &= ~GRUB_NTFS_AF_GPOS; + } + goto retry; + } + return NULL; +} + +static grub_uint8_t * +locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, + grub_uint8_t attr) +{ + grub_uint8_t *pa; + + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; + + pa = find_attr (at, attr); + if (pa == NULL) + return NULL; + if ((at->flags & GRUB_NTFS_AF_ALST) == 0) + { + while (1) + { + pa = find_attr (at, attr); + if (pa == NULL) + break; + if (at->flags & GRUB_NTFS_AF_ALST) + return pa; + } + grub_errno = GRUB_ERR_NONE; + free_attr (at); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; + pa = find_attr (at, attr); + } + return pa; +} + +static grub_disk_addr_t +read_run_data (const grub_uint8_t *run, int nn, int sig) +{ + grub_uint64_t r = 0; + + if (sig && nn && (run[nn - 1] & 0x80)) + r = -1; + + grub_memcpy (&r, run, nn); + + return grub_le_to_cpu64 (r); +} + +grub_err_t +grub_ntfs_read_run_list (struct grub_ntfs_rlst * ctx) +{ + grub_uint8_t c1, c2; + grub_disk_addr_t val; + grub_uint8_t *run; + + run = ctx->cur_run; +retry: + c1 = ((*run) & 0x7); + c2 = ((*run) >> 4) & 0x7; + run++; + if (!c1) + { + if ((ctx->attr) && (ctx->attr->flags & GRUB_NTFS_AF_ALST)) + { + grub_disk_read_hook_t save_hook; + + save_hook = ctx->comp.disk->read_hook; + ctx->comp.disk->read_hook = 0; + run = find_attr (ctx->attr, *ctx->attr->attr_cur); + ctx->comp.disk->read_hook = save_hook; + if (run) + { + if (run[8] == 0) + return grub_error (GRUB_ERR_BAD_FS, + "$DATA should be non-resident"); + + run += u16at (run, 0x20); + ctx->curr_lcn = 0; + goto retry; + } + } + return grub_error (GRUB_ERR_BAD_FS, "run list overflow"); + } + ctx->curr_vcn = ctx->next_vcn; + ctx->next_vcn += read_run_data (run, c1, 0); /* length of current VCN */ + run += c1; + val = read_run_data (run, c2, 1); /* offset to previous LCN */ + run += c2; + ctx->curr_lcn += val; + if (val == 0) + ctx->flags |= GRUB_NTFS_RF_BLNK; + else + ctx->flags &= ~GRUB_NTFS_RF_BLNK; + ctx->cur_run = run; + return 0; +} + +static grub_disk_addr_t +grub_ntfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t block) +{ + struct grub_ntfs_rlst *ctx; + + ctx = (struct grub_ntfs_rlst *) node; + if (block >= ctx->next_vcn) + { + if (grub_ntfs_read_run_list (ctx)) + return -1; + return ctx->curr_lcn; + } + else + return (ctx->flags & GRUB_NTFS_RF_BLNK) ? 0 : (block - + ctx->curr_vcn + ctx->curr_lcn); +} + +static grub_err_t +read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest, + grub_disk_addr_t ofs, grub_size_t len, int cached, + grub_disk_read_hook_t read_hook, void *read_hook_data) +{ + struct grub_ntfs_rlst cc, *ctx; + + if (len == 0) + return 0; + + grub_memset (&cc, 0, sizeof (cc)); + ctx = &cc; + ctx->attr = at; + ctx->comp.log_spc = at->mft->data->log_spc; + ctx->comp.disk = at->mft->data->disk; + + if (read_hook == grub_file_progress_hook) + ctx->file = read_hook_data; + + if (pa[8] == 0) + { + if (ofs + len > res_attr_data_len (pa)) + return grub_error (GRUB_ERR_BAD_FS, "read out of range"); + + if (res_attr_data_len (pa) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large"); + + if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + if (res_attr_data_off (pa) + res_attr_data_len (pa) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa) + return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range"); + + grub_memcpy (dest, pa + res_attr_data_off (pa) + ofs, len); + return 0; + } + + ctx->cur_run = pa + u16at (pa, 0x20); + + ctx->next_vcn = u32at (pa, 0x10); + ctx->curr_lcn = 0; + + if ((pa[0xC] & GRUB_NTFS_FLAG_COMPRESSED) + && !(at->flags & GRUB_NTFS_AF_GPOS)) + { + if (!cached) + return grub_error (GRUB_ERR_BAD_FS, "attribute can\'t be compressed"); + + return (grub_ntfscomp_func) ? grub_ntfscomp_func (dest, ofs, len, ctx) + : grub_error (GRUB_ERR_BAD_FS, N_("module `%s' isn't loaded"), + "ntfscomp"); + } + + ctx->target_vcn = ofs >> (GRUB_NTFS_BLK_SHR + ctx->comp.log_spc); + while (ctx->next_vcn <= ctx->target_vcn) + { + if (grub_ntfs_read_run_list (ctx)) + return grub_errno; + } + + if (at->flags & GRUB_NTFS_AF_GPOS) + { + grub_disk_addr_t st0, st1; + grub_uint64_t m; + + m = (ofs >> GRUB_NTFS_BLK_SHR) & ((1 << ctx->comp.log_spc) - 1); + + st0 = + ((ctx->target_vcn - ctx->curr_vcn + ctx->curr_lcn) << ctx->comp.log_spc) + m; + st1 = st0 + 1; + if (st1 == + (ctx->next_vcn - ctx->curr_vcn + ctx->curr_lcn) << ctx->comp.log_spc) + { + if (grub_ntfs_read_run_list (ctx)) + return grub_errno; + st1 = ctx->curr_lcn << ctx->comp.log_spc; + } + grub_set_unaligned32 (dest, grub_cpu_to_le32 (st0)); + grub_set_unaligned32 (dest + 4, grub_cpu_to_le32 (st1)); + return 0; + } + + grub_fshelp_read_file (ctx->comp.disk, (grub_fshelp_node_t) ctx, + read_hook, read_hook_data, ofs, len, + (char *) dest, + grub_ntfs_read_block, ofs + len, + ctx->comp.log_spc, 0); + return grub_errno; +} + +static grub_err_t +read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, grub_disk_addr_t ofs, + grub_size_t len, int cached, + grub_disk_read_hook_t read_hook, void *read_hook_data) +{ + grub_uint8_t *save_cur; + grub_uint8_t attr; + grub_uint8_t *pp; + grub_err_t ret; + + save_cur = at->attr_cur; + at->attr_nxt = at->attr_cur; + attr = *at->attr_nxt; + if (at->flags & GRUB_NTFS_AF_ALST) + { + grub_uint8_t *pa; + grub_disk_addr_t vcn; + + /* If compression is possible make sure that we include possible + compressed block size. */ + if (GRUB_NTFS_LOG_COM_SEC >= at->mft->data->log_spc) + vcn = ((ofs >> GRUB_NTFS_COM_LOG_LEN) + << (GRUB_NTFS_LOG_COM_SEC - at->mft->data->log_spc)) & ~0xFULL; + else + vcn = ofs >> (at->mft->data->log_spc + GRUB_NTFS_BLK_SHR); + pa = at->attr_nxt + u16at (at->attr_nxt, 4); + if (validate_attribute (pa, at->attr_end) == false) + pa = NULL; + + while (pa) + { + if (*pa != attr) + break; + if (u32at (pa, 8) > vcn) + break; + at->attr_nxt = pa; + pa = next_attribute (pa, at->attr_end); + } + } + pp = find_attr (at, attr); + if (pp) + ret = read_data (at, pp, dest, ofs, len, cached, + read_hook, read_hook_data); + else + ret = + (grub_errno) ? grub_errno : grub_error (GRUB_ERR_BAD_FS, + "attribute not found"); + at->attr_cur = save_cur; + return ret; +} + +static grub_err_t +read_mft (struct grub_ntfs_data *data, grub_uint8_t *buf, grub_uint64_t mftno) +{ + if (read_attr + (&data->mmft.attr, buf, mftno * ((grub_disk_addr_t) data->mft_size << GRUB_NTFS_BLK_SHR), + data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0)) + return grub_error (GRUB_ERR_BAD_FS, "read MFT 0x%llx fails", (unsigned long long) mftno); + return fixup (buf, data->mft_size, (const grub_uint8_t *) "FILE"); +} + +static grub_err_t +init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) +{ + unsigned short flag; + + mft->inode_read = 1; + + mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + if (mft->buf == NULL) + return grub_errno; + + if (read_mft (mft->data, mft->buf, mftno)) + return grub_errno; + + flag = u16at (mft->buf, 0x16); + if ((flag & 1) == 0) + return grub_error (GRUB_ERR_BAD_FS, "MFT 0x%llx is not in use", + (unsigned long long) mftno); + + if ((flag & 2) == 0) + { + grub_uint8_t *pa; + + pa = locate_attr (&mft->attr, mft, GRUB_NTFS_AT_DATA); + if (pa == NULL) + return grub_error (GRUB_ERR_BAD_FS, "no $DATA in MFT 0x%llx", + (unsigned long long) mftno); + + if (!pa[8]) + mft->size = res_attr_data_len (pa); + else + mft->size = u64at (pa, 0x30); + + if ((mft->attr.flags & GRUB_NTFS_AF_ALST) == 0) + mft->attr.attr_end = 0; /* Don't jump to attribute list */ + } + else + return init_attr (&mft->attr, mft); + + return 0; +} + +static void +free_file (struct grub_ntfs_file *mft) +{ + free_attr (&mft->attr); + grub_free (mft->buf); +} + +static char * +get_utf8 (grub_uint8_t *in, grub_size_t len) +{ + grub_uint8_t *buf; + grub_uint16_t *tmp; + grub_size_t i; + + buf = grub_calloc (len, GRUB_MAX_UTF8_PER_UTF16 + 1); + tmp = grub_calloc (len, sizeof (tmp[0])); + if (!buf || !tmp) + { + grub_free (buf); + grub_free (tmp); + return NULL; + } + for (i = 0; i < len; i++) + tmp[i] = grub_le_to_cpu16 (grub_get_unaligned16 (in + 2 * i)); + *grub_utf16_to_utf8 (buf, tmp, len) = '\0'; + grub_free (tmp); + return (char *) buf; +} + +static int +list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos, grub_uint8_t *end_pos, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_uint8_t *np; + int ns; + + while (1) + { + grub_uint8_t namespace; + char *ustr; + + if ((pos >= end_pos) || (end_pos - pos < 0x52)) + break; + + if (pos[0xC] & 2) /* end signature */ + break; + + np = pos + 0x50; + ns = *(np++); + namespace = *(np++); + + if (2 * ns > end_pos - pos - 0x52) + break; + + /* + * Ignore files in DOS namespace, as they will reappear as Win32 + * names. + */ + if ((ns) && (namespace != 2)) + { + enum grub_fshelp_filetype type; + struct grub_ntfs_file *fdiro; + grub_uint32_t attr; + + attr = u32at (pos, 0x48); + if (attr & GRUB_NTFS_ATTR_REPARSE) + type = GRUB_FSHELP_SYMLINK; + else if (attr & GRUB_NTFS_ATTR_DIRECTORY) + type = GRUB_FSHELP_DIR; + else + type = GRUB_FSHELP_REG; + + fdiro = grub_zalloc (sizeof (struct grub_ntfs_file)); + if (!fdiro) + return 0; + + fdiro->data = diro->data; + fdiro->ino = u64at (pos, 0) & 0xffffffffffffULL; + fdiro->mtime = u64at (pos, 0x20); + + ustr = get_utf8 (np, ns); + if (ustr == NULL) + { + grub_free (fdiro); + return 0; + } + if (namespace) + type |= GRUB_FSHELP_CASE_INSENSITIVE; + + if (hook (ustr, type, fdiro, hook_data)) + { + grub_free (ustr); + return 1; + } + + grub_free (ustr); + } + pos += u16at (pos, 8); + } + return 0; +} + +struct symlink_descriptor +{ + grub_uint32_t type; + grub_uint32_t total_len; + grub_uint16_t off1; + grub_uint16_t len1; + grub_uint16_t off2; + grub_uint16_t len2; +} GRUB_PACKED; + +static char * +grub_ntfs_read_symlink (grub_fshelp_node_t node) +{ + struct grub_ntfs_file *mft; + struct symlink_descriptor symdesc; + grub_err_t err; + grub_uint8_t *buf16 = NULL; + char *buf, *end; + grub_size_t len; + grub_uint8_t *pa; + grub_size_t off; + + mft = (struct grub_ntfs_file *) node; + + mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + if (mft->buf == NULL) + return NULL; + + if (read_mft (mft->data, mft->buf, mft->ino)) + goto fail; + + pa = locate_attr (&mft->attr, mft, GRUB_NTFS_AT_SYMLINK); + if (pa == NULL) + { + grub_error (GRUB_ERR_BAD_FS, "no $SYMLINK in MFT 0x%llx", + (unsigned long long) mft->ino); + goto fail; + } + + err = read_attr (&mft->attr, (grub_uint8_t *) &symdesc, 0, + sizeof (struct symlink_descriptor), 1, 0, 0); + if (err) + goto fail; + + switch (grub_cpu_to_le32 (symdesc.type)) + { + case 0xa000000c: + off = (sizeof (struct symlink_descriptor) + 4 + + grub_cpu_to_le32 (symdesc.off1)); + len = grub_cpu_to_le32 (symdesc.len1); + break; + case 0xa0000003: + off = (sizeof (struct symlink_descriptor) + + grub_cpu_to_le32 (symdesc.off1)); + len = grub_cpu_to_le32 (symdesc.len1); + break; + default: + grub_error (GRUB_ERR_BAD_FS, "symlink type invalid (%x)", + grub_cpu_to_le32 (symdesc.type)); + goto fail; + } + + buf16 = grub_malloc (len); + if (!buf16) + goto fail; + + err = read_attr (&mft->attr, buf16, off, len, 1, 0, 0); + if (err) + goto fail; + + buf = get_utf8 (buf16, len / 2); + if (!buf) + goto fail; + + grub_free (mft->buf); + grub_free (buf16); + + for (end = buf; *end; end++) + if (*end == '\\') + *end = '/'; + + /* Split the sequence to avoid GCC thinking that this is a trigraph. */ + if (grub_memcmp (buf, "/?" "?/", 4) == 0 && buf[5] == ':' && buf[6] == '/' + && grub_isalpha (buf[4])) + { + grub_memmove (buf, buf + 6, end - buf + 1 - 6); + end -= 6; + } + return buf; + + fail: + grub_free (mft->buf); + grub_free (buf16); + return NULL; +} + +static int +grub_ntfs_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_uint8_t *bitmap; + struct grub_ntfs_attr attr, *at; + grub_uint8_t *cur_pos, *indx, *bmp; + int ret = 0; + grub_size_t bitmap_len; + struct grub_ntfs_file *mft; + + mft = (struct grub_ntfs_file *) dir; + + if (!mft->inode_read) + { + if (init_file (mft, mft->ino)) + return 0; + } + + indx = NULL; + bmp = NULL; + + at = &attr; + if (init_attr (at, mft) != GRUB_ERR_NONE) + return 0; + + while (1) + { + cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ROOT); + if (cur_pos == NULL) + { + grub_error (GRUB_ERR_BAD_FS, "no $INDEX_ROOT"); + goto done; + } + + /* Resident, Namelen=4, Offset=0x18, Flags=0x00, Name="$I30" */ + if ((u32at (cur_pos, 8) != 0x180400) || + (u32at (cur_pos, 0x18) != 0x490024) || + (u32at (cur_pos, 0x1C) != 0x300033)) + continue; + cur_pos += res_attr_data_off (cur_pos); + if (*cur_pos != 0x30) /* Not filename index */ + continue; + break; + } + + cur_pos += 0x10; /* Skip index root */ + ret = list_file (mft, cur_pos + u16at (cur_pos, 0), + at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), + hook, hook_data); + if (ret) + goto done; + + bitmap = NULL; + bitmap_len = 0; + free_attr (at); + /* No need to check errors here, as it will already be fine */ + init_attr (at, mft); + + while ((cur_pos = find_attr (at, GRUB_NTFS_AT_BITMAP)) != NULL) + { + int ofs; + + ofs = cur_pos[0xA]; + /* Namelen=4, Name="$I30" */ + if ((cur_pos[9] == 4) && + (u32at (cur_pos, ofs) == 0x490024) && + (u32at (cur_pos, ofs + 4) == 0x300033)) + { + int is_resident = (cur_pos[8] == 0); + + bitmap_len = ((is_resident) ? res_attr_data_len (cur_pos) : + u32at (cur_pos, 0x28)); + + bmp = grub_malloc (bitmap_len); + if (bmp == NULL) + goto done; + + if (is_resident) + { + if (bitmap_len > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap too large"); + goto done; + } + + if (cur_pos >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + + if (res_attr_data_off (cur_pos) + res_attr_data_len (cur_pos) > + (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) cur_pos) + { + grub_error (GRUB_ERR_BAD_FS, "resident bitmap out of range"); + goto done; + } + + grub_memcpy (bmp, cur_pos + res_attr_data_off (cur_pos), + bitmap_len); + } + else + { + if (read_data (at, cur_pos, bmp, 0, bitmap_len, 0, 0, 0)) + { + grub_error (GRUB_ERR_BAD_FS, + "fails to read non-resident $BITMAP"); + goto done; + } + bitmap_len = u32at (cur_pos, 0x30); + } + + bitmap = bmp; + break; + } + } + + free_attr (at); + cur_pos = locate_attr (at, mft, GRUB_NTFS_AT_INDEX_ALLOCATION); + while (cur_pos != NULL) + { + /* Non-resident, Namelen=4, Offset=0x40, Flags=0, Name="$I30" */ + if ((u32at (cur_pos, 8) == 0x400401) && + (u32at (cur_pos, 0x40) == 0x490024) && + (u32at (cur_pos, 0x44) == 0x300033)) + break; + cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ALLOCATION); + } + + if ((!cur_pos) && (bitmap)) + { + grub_error (GRUB_ERR_BAD_FS, "$BITMAP without $INDEX_ALLOCATION"); + goto done; + } + + if (bitmap) + { + grub_disk_addr_t i; + grub_uint8_t v; + + indx = grub_malloc (mft->data->idx_size << GRUB_NTFS_BLK_SHR); + if (indx == NULL) + goto done; + + v = 1; + for (i = 0; i < (grub_disk_addr_t)bitmap_len * 8; i++) + { + if (*bitmap & v) + { + if ((read_attr + (at, indx, i * (mft->data->idx_size << GRUB_NTFS_BLK_SHR), + (mft->data->idx_size << GRUB_NTFS_BLK_SHR), 0, 0, 0)) + || (fixup (indx, mft->data->idx_size, + (const grub_uint8_t *) "INDX"))) + goto done; + ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], + indx + (mft->data->idx_size << GRUB_NTFS_BLK_SHR), + hook, hook_data); + if (ret) + goto done; + } + v <<= 1; + if (!v) + { + v = 1; + bitmap++; + } + } + } + +done: + free_attr (at); + grub_free (indx); + grub_free (bmp); + + return ret; +} + +static struct grub_ntfs_data * +grub_ntfs_mount (grub_disk_t disk) +{ + struct grub_ntfs_bpb bpb; + struct grub_ntfs_data *data = 0; + grub_uint32_t spc; + + if (!disk) + goto fail; + + data = (struct grub_ntfs_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->disk = disk; + + /* Read the BPB. */ + if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb)) + goto fail; + + if (grub_memcmp ((char *) &bpb.oem_name, "NTFS", 4) != 0 + || bpb.sectors_per_cluster == 0 + || (bpb.sectors_per_cluster & (bpb.sectors_per_cluster - 1)) != 0 + || bpb.bytes_per_sector == 0 + || (bpb.bytes_per_sector & (bpb.bytes_per_sector - 1)) != 0) + goto fail; + + spc = (((grub_uint32_t) bpb.sectors_per_cluster + * (grub_uint32_t) grub_le_to_cpu16 (bpb.bytes_per_sector)) + >> GRUB_NTFS_BLK_SHR); + if (spc == 0) + goto fail; + + for (data->log_spc = 0; (1U << data->log_spc) < spc; data->log_spc++); + + if (bpb.clusters_per_mft > 0) + data->mft_size = ((grub_disk_addr_t) bpb.clusters_per_mft) << data->log_spc; + else if (-bpb.clusters_per_mft < GRUB_NTFS_BLK_SHR || -bpb.clusters_per_mft >= 31) + goto fail; + else + data->mft_size = 1ULL << (-bpb.clusters_per_mft - GRUB_NTFS_BLK_SHR); + + if (bpb.clusters_per_index > 0) + data->idx_size = (((grub_disk_addr_t) bpb.clusters_per_index) + << data->log_spc); + else if (-bpb.clusters_per_index < GRUB_NTFS_BLK_SHR || -bpb.clusters_per_index >= 31) + goto fail; + else + data->idx_size = 1ULL << (-bpb.clusters_per_index - GRUB_NTFS_BLK_SHR); + + data->mft_start = grub_le_to_cpu64 (bpb.mft_lcn) << data->log_spc; + + if ((data->mft_size > GRUB_NTFS_MAX_MFT) || (data->idx_size > GRUB_NTFS_MAX_IDX)) + goto fail; + + data->mmft.data = data; + data->cmft.data = data; + + data->mmft.buf = grub_malloc (data->mft_size << GRUB_NTFS_BLK_SHR); + if (!data->mmft.buf) + goto fail; + + if (grub_disk_read + (disk, data->mft_start, 0, data->mft_size << GRUB_NTFS_BLK_SHR, data->mmft.buf)) + goto fail; + + data->uuid = grub_le_to_cpu64 (bpb.num_serial); + + if (fixup (data->mmft.buf, data->mft_size, (const grub_uint8_t *) "FILE")) + goto fail; + + if (!locate_attr (&data->mmft.attr, &data->mmft, GRUB_NTFS_AT_DATA)) + goto fail; + + if (init_file (&data->cmft, GRUB_NTFS_FILE_ROOT)) + goto fail; + + return data; + +fail: + grub_error (GRUB_ERR_BAD_FS, "not an ntfs filesystem"); + + if (data) + { + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + return 0; +} + +/* Context for grub_ntfs_dir. */ +struct grub_ntfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_ntfs_dir. */ +static int +grub_ntfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_ntfs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = grub_divmod64 (node->mtime, 10000000, 0) + - 86400ULL * 365 * (1970 - 1601) + - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_ntfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_ntfs_dir_ctx ctx = { hook, hook_data }; + struct grub_ntfs_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_ntfs_mount (device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (path, &data->cmft, &fdiro, grub_ntfs_iterate_dir, + grub_ntfs_read_symlink, GRUB_FSHELP_DIR); + + if (grub_errno) + goto fail; + + grub_ntfs_iterate_dir (fdiro, grub_ntfs_dir_iter, &ctx); + +fail: + if ((fdiro) && (fdiro != &data->cmft)) + { + free_file (fdiro); + grub_free (fdiro); + } + if (data) + { + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_ntfs_open (grub_file_t file, const char *name) +{ + struct grub_ntfs_data *data = 0; + struct grub_fshelp_node *mft = 0; + + grub_dl_ref (my_mod); + + data = grub_ntfs_mount (file->device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file (name, &data->cmft, &mft, grub_ntfs_iterate_dir, + grub_ntfs_read_symlink, GRUB_FSHELP_REG); + + if (grub_errno) + goto fail; + + if (mft != &data->cmft) + { + free_file (&data->cmft); + grub_memcpy (&data->cmft, mft, sizeof (*mft)); + grub_free (mft); + if (!data->cmft.inode_read) + { + if (init_file (&data->cmft, data->cmft.ino)) + goto fail; + } + } + + file->size = data->cmft.size; + file->data = data; + file->offset = 0; + + return 0; + +fail: + if (data) + { + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_ssize_t +grub_ntfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_ntfs_file *mft; + + mft = &((struct grub_ntfs_data *) file->data)->cmft; + if (file->read_hook) + mft->attr.save_pos = 1; + + read_attr (&mft->attr, (grub_uint8_t *) buf, file->offset, len, 1, + file->read_hook, file->read_hook_data); + return (grub_errno) ? -1 : (grub_ssize_t) len; +} + +static grub_err_t +grub_ntfs_close (grub_file_t file) +{ + struct grub_ntfs_data *data; + + data = file->data; + + if (data) + { + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_ntfs_label (grub_device_t device, char **label) +{ + struct grub_ntfs_data *data = 0; + struct grub_fshelp_node *mft = 0; + grub_uint8_t *pa; + grub_err_t err; + + grub_dl_ref (my_mod); + + *label = 0; + + data = grub_ntfs_mount (device->disk); + if (!data) + goto fail; + + grub_fshelp_find_file ("/$Volume", &data->cmft, &mft, grub_ntfs_iterate_dir, + 0, GRUB_FSHELP_REG); + + if (grub_errno) + goto fail; + + if (!mft->inode_read) + { + mft->buf = grub_malloc (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + if (mft->buf == NULL) + goto fail; + + if (read_mft (mft->data, mft->buf, mft->ino)) + goto fail; + } + + err = init_attr (&mft->attr, mft); + if (err != GRUB_ERR_NONE) + return err; + + pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); + + if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa < 0x16) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + goto fail; + } + + if ((pa) && (pa[8] == 0) && (res_attr_data_len (pa))) + { + int len; + + len = res_attr_data_len (pa) / 2; + pa += res_attr_data_off (pa); + if (mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR) - pa >= 2 * len) + *label = get_utf8 (pa, len); + else + grub_error (GRUB_ERR_BAD_FS, "can\'t parse volume label"); + } + +fail: + if ((mft) && (mft != &data->cmft)) + { + free_file (mft); + grub_free (mft); + } + if (data) + { + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_ntfs_uuid (grub_device_t device, char **uuid) +{ + struct grub_ntfs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_ntfs_mount (disk); + if (data) + { + char *ptr; + *uuid = grub_xasprintf ("%016llx", (unsigned long long) data->uuid); + if (*uuid) + for (ptr = *uuid; *ptr; ptr++) + *ptr = grub_toupper (*ptr); + free_file (&data->mmft); + free_file (&data->cmft); + grub_free (data); + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static struct grub_fs grub_ntfs_fs = + { + .name = "ntfs", + .fs_dir = grub_ntfs_dir, + .fs_open = grub_ntfs_open, + .fs_read = grub_ntfs_read, + .fs_close = grub_ntfs_close, + .fs_label = grub_ntfs_label, + .fs_uuid = grub_ntfs_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (ntfs) +{ + if (!grub_is_lockdown ()) + { + grub_ntfs_fs.mod = mod; + grub_fs_register (&grub_ntfs_fs); + } + my_mod = mod; +} + +GRUB_MOD_FINI (ntfs) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ntfs_fs); +} diff --git a/fs/ntfscomp.c b/grub-core/fs/ntfscomp.c similarity index 51% rename from fs/ntfscomp.c rename to grub-core/fs/ntfscomp.c index c29979edc..b68bf5e40 100644 --- a/fs/ntfscomp.c +++ b/grub-core/fs/ntfscomp.c @@ -21,43 +21,46 @@ #include #include #include -#include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); static grub_err_t decomp_nextvcn (struct grub_ntfs_comp *cc) { if (cc->comp_head >= cc->comp_tail) - return grub_error (GRUB_ERR_BAD_FS, "compression block overflown"); + return grub_error (GRUB_ERR_BAD_FS, "compression block overflow"); if (grub_disk_read (cc->disk, - (cc->comp_table[cc->comp_head][1] - - (cc->comp_table[cc->comp_head][0] - cc->cbuf_vcn)) * cc->spc, 0, - cc->spc << BLK_SHR, cc->cbuf)) + (cc->comp_table[cc->comp_head].next_lcn - + (cc->comp_table[cc->comp_head].next_vcn - cc->cbuf_vcn)) << cc->log_spc, + 0, + 1 << (cc->log_spc + GRUB_NTFS_BLK_SHR), cc->cbuf)) return grub_errno; cc->cbuf_vcn++; - if ((cc->cbuf_vcn >= cc->comp_table[cc->comp_head][0])) + if ((cc->cbuf_vcn >= cc->comp_table[cc->comp_head].next_vcn)) cc->comp_head++; cc->cbuf_ofs = 0; return 0; } static grub_err_t -decomp_getch (struct grub_ntfs_comp *cc, unsigned char *res) +decomp_getch (struct grub_ntfs_comp *cc, grub_uint8_t *res) { - if (cc->cbuf_ofs >= (cc->spc << BLK_SHR)) + if (cc->cbuf_ofs >= (1U << (cc->log_spc + GRUB_NTFS_BLK_SHR))) { if (decomp_nextvcn (cc)) return grub_errno; } - *res = (unsigned char) cc->cbuf[cc->cbuf_ofs++]; + *res = cc->cbuf[cc->cbuf_ofs++]; return 0; } static grub_err_t decomp_get16 (struct grub_ntfs_comp *cc, grub_uint16_t * res) { - unsigned char c1 = 0, c2 = 0; + grub_uint8_t c1 = 0, c2 = 0; if ((decomp_getch (cc, &c1)) || (decomp_getch (cc, &c2))) return grub_errno; @@ -67,7 +70,7 @@ decomp_get16 (struct grub_ntfs_comp *cc, grub_uint16_t * res) /* Decompress a block (4096 bytes) */ static grub_err_t -decomp_block (struct grub_ntfs_comp *cc, char *dest) +decomp_block (struct grub_ntfs_comp *cc, grub_uint8_t *dest) { grub_uint16_t flg, cnt; @@ -79,13 +82,13 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) { if (flg & 0x8000) { - unsigned char tag; + grub_uint8_t tag; grub_uint32_t bits, copied; bits = copied = tag = 0; while (cnt > 0) { - if (copied > COM_LEN) + if (copied > GRUB_NTFS_COM_LEN) return grub_error (GRUB_ERR_BAD_FS, "compression block too large"); @@ -102,7 +105,7 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) if (tag & 1) { grub_uint32_t i, len, delta, code, lmask, dshift; - grub_uint16_t word; + grub_uint16_t word = 0; if (decomp_get16 (cc, &word)) return grub_errno; @@ -134,7 +137,7 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) } else { - unsigned char ch = 0; + grub_uint8_t ch = 0; if (decomp_getch (cc, &ch)) return grub_errno; @@ -148,7 +151,7 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) } else { - if (cnt != COM_LEN) + if (cnt != GRUB_NTFS_COM_LEN) return grub_error (GRUB_ERR_BAD_FS, "invalid compression block size"); } @@ -158,7 +161,7 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) { int n; - n = (cc->spc << BLK_SHR) - cc->cbuf_ofs; + n = (1 << (cc->log_spc + GRUB_NTFS_BLK_SHR)) - cc->cbuf_ofs; if (n > cnt) n = cnt; if ((dest) && (n)) @@ -175,22 +178,23 @@ decomp_block (struct grub_ntfs_comp *cc, char *dest) } static grub_err_t -read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) +read_block (struct grub_ntfs_rlst *ctx, grub_uint8_t *buf, grub_size_t num) { - int cpb = COM_SEC / ctx->comp.spc; + int log_cpb = GRUB_NTFS_LOG_COM_SEC - ctx->comp.log_spc; while (num) { - int nn; + grub_size_t nn; if ((ctx->target_vcn & 0xF) == 0) { - if (ctx->comp.comp_head != ctx->comp.comp_tail) + if (ctx->comp.comp_head != ctx->comp.comp_tail + && !(ctx->flags & GRUB_NTFS_RF_BLNK)) return grub_error (GRUB_ERR_BAD_FS, "invalid compression block"); ctx->comp.comp_head = ctx->comp.comp_tail = 0; ctx->comp.cbuf_vcn = ctx->target_vcn; - ctx->comp.cbuf_ofs = (ctx->comp.spc << BLK_SHR); + ctx->comp.cbuf_ofs = (1 << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR)); if (ctx->target_vcn >= ctx->next_vcn) { if (grub_ntfs_read_run_list (ctx)) @@ -198,10 +202,10 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) } while (ctx->target_vcn + 16 > ctx->next_vcn) { - if (ctx->flags & RF_BLNK) + if (ctx->flags & GRUB_NTFS_RF_BLNK) break; - ctx->comp.comp_table[ctx->comp.comp_tail][0] = ctx->next_vcn; - ctx->comp.comp_table[ctx->comp.comp_tail][1] = + ctx->comp.comp_table[ctx->comp.comp_tail].next_vcn = ctx->next_vcn; + ctx->comp.comp_table[ctx->comp.comp_tail].next_lcn = ctx->curr_lcn + ctx->next_vcn - ctx->curr_vcn; ctx->comp.comp_tail++; if (grub_ntfs_read_run_list (ctx)) @@ -209,20 +213,23 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) } } - nn = (16 - (unsigned) (ctx->target_vcn & 0xF)) / cpb; + nn = (16 - (unsigned) (ctx->target_vcn & 0xF)) >> log_cpb; if (nn > num) nn = num; num -= nn; - if (ctx->flags & RF_BLNK) + if (ctx->flags & GRUB_NTFS_RF_BLNK) { - ctx->target_vcn += nn * cpb; + ctx->target_vcn += nn << log_cpb; if (ctx->comp.comp_tail == 0) { if (buf) { - grub_memset (buf, 0, nn * COM_LEN); - buf += nn * COM_LEN; + grub_memset (buf, 0, nn * GRUB_NTFS_COM_LEN); + buf += nn * GRUB_NTFS_COM_LEN; + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, nn * GRUB_NTFS_COM_LEN, NULL, + ctx->file); } } else @@ -232,20 +239,23 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) if (decomp_block (&ctx->comp, buf)) return grub_errno; if (buf) - buf += COM_LEN; + buf += GRUB_NTFS_COM_LEN; + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, GRUB_NTFS_COM_LEN, NULL, + ctx->file); nn--; } } } else { - nn *= cpb; + nn <<= log_cpb; while ((ctx->comp.comp_head < ctx->comp.comp_tail) && (nn)) { - int tt; + grub_disk_addr_t tt; tt = - ctx->comp.comp_table[ctx->comp.comp_head][0] - + ctx->comp.comp_table[ctx->comp.comp_head].next_vcn - ctx->target_vcn; if (tt > nn) tt = nn; @@ -254,16 +264,21 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) { if (grub_disk_read (ctx->comp.disk, - (ctx->comp.comp_table[ctx->comp.comp_head][1] - - (ctx->comp.comp_table[ctx->comp.comp_head][0] - - ctx->target_vcn)) * ctx->comp.spc, 0, - tt * (ctx->comp.spc << BLK_SHR), buf)) + (ctx->comp.comp_table[ctx->comp.comp_head].next_lcn - + (ctx->comp.comp_table[ctx->comp.comp_head].next_vcn - + ctx->target_vcn)) << ctx->comp.log_spc, 0, + tt << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR), buf)) return grub_errno; - buf += tt * (ctx->comp.spc << BLK_SHR); + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, + tt << (ctx->comp.log_spc + + GRUB_NTFS_BLK_SHR), NULL, + ctx->file); + buf += tt << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR); } nn -= tt; if (ctx->target_vcn >= - ctx->comp.comp_table[ctx->comp.comp_head][0]) + ctx->comp.comp_table[ctx->comp.comp_head].next_vcn) ctx->comp.comp_head++; } if (nn) @@ -273,10 +288,15 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) if (grub_disk_read (ctx->comp.disk, (ctx->target_vcn - ctx->curr_vcn + - ctx->curr_lcn) * ctx->comp.spc, 0, - nn * (ctx->comp.spc << BLK_SHR), buf)) + ctx->curr_lcn) << ctx->comp.log_spc, 0, + nn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR), buf)) return grub_errno; - buf += nn * (ctx->comp.spc << BLK_SHR); + buf += nn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR); + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, + nn << (ctx->comp.log_spc + + GRUB_NTFS_BLK_SHR), NULL, + ctx->file); } ctx->target_vcn += nn; } @@ -286,74 +306,130 @@ read_block (struct grub_ntfs_rlst *ctx, char *buf, int num) } static grub_err_t -ntfscomp (struct grub_ntfs_attr *at, char *dest, grub_uint32_t ofs, - grub_uint32_t len, struct grub_ntfs_rlst *ctx, grub_uint32_t vcn) +ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs, + grub_size_t len, struct grub_ntfs_rlst *ctx) { grub_err_t ret; + grub_disk_addr_t vcn; + int log_sz; + + if (ctx->attr->sbuf) + { + if ((ofs & (~(GRUB_NTFS_COM_LEN - 1))) == ctx->attr->save_pos) + { + grub_disk_addr_t n; + + n = GRUB_NTFS_COM_LEN - (ofs - ctx->attr->save_pos); + if (n > len) + n = len; + + grub_memcpy (dest, ctx->attr->sbuf + ofs - ctx->attr->save_pos, n); + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, n, NULL, ctx->file); + if (n == len) + return 0; + + dest += n; + len -= n; + ofs += n; + } + } + else + { + ctx->attr->sbuf = grub_malloc (GRUB_NTFS_COM_LEN); + if (ctx->attr->sbuf == NULL) + return grub_errno; + ctx->attr->save_pos = 1; + } + + vcn = ctx->target_vcn = (ofs >> GRUB_NTFS_COM_LOG_LEN) * (GRUB_NTFS_COM_SEC >> ctx->comp.log_spc); + ctx->target_vcn &= ~0xFULL; + while (ctx->next_vcn <= ctx->target_vcn) + { + if (grub_ntfs_read_run_list (ctx)) + return grub_errno; + } ctx->comp.comp_head = ctx->comp.comp_tail = 0; - ctx->comp.cbuf = grub_malloc ((ctx->comp.spc) << BLK_SHR); + if (grub_add (ctx->comp.log_spc, GRUB_NTFS_BLK_SHR, &log_sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("compression buffer size overflow")); + return 0; + } + ctx->comp.cbuf = grub_malloc (1 << log_sz); if (!ctx->comp.cbuf) return 0; ret = 0; //ctx->comp.disk->read_hook = read_hook; + //ctx->comp.disk->read_hook_data = read_hook_data; if ((vcn > ctx->target_vcn) && (read_block - (ctx, NULL, ((vcn - ctx->target_vcn) * ctx->comp.spc) / COM_SEC))) + (ctx, NULL, (vcn - ctx->target_vcn) >> (GRUB_NTFS_LOG_COM_SEC - ctx->comp.log_spc)))) { ret = grub_errno; goto quit; } - if (ofs % COM_LEN) + if (ofs % GRUB_NTFS_COM_LEN) { grub_uint32_t t, n, o; + void *file = ctx->file; - t = ctx->target_vcn * (ctx->comp.spc << BLK_SHR); - if (read_block (ctx, at->sbuf, 1)) + ctx->file = 0; + + t = ctx->target_vcn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR); + if (read_block (ctx, ctx->attr->sbuf, 1)) { ret = grub_errno; goto quit; } - at->save_pos = t; + ctx->file = file; - o = ofs % COM_LEN; - n = COM_LEN - o; + ctx->attr->save_pos = t; + + o = ofs % GRUB_NTFS_COM_LEN; + n = GRUB_NTFS_COM_LEN - o; if (n > len) n = len; - grub_memcpy (dest, &at->sbuf[o], n); + grub_memcpy (dest, &ctx->attr->sbuf[o], n); + if (grub_file_progress_hook && ctx->file) + grub_file_progress_hook (0, 0, n, NULL, ctx->file); if (n == len) goto quit; dest += n; len -= n; } - if (read_block (ctx, dest, len / COM_LEN)) + if (read_block (ctx, dest, len / GRUB_NTFS_COM_LEN)) { ret = grub_errno; goto quit; } - dest += (len / COM_LEN) * COM_LEN; - len = len % COM_LEN; + dest += (len / GRUB_NTFS_COM_LEN) * GRUB_NTFS_COM_LEN; + len = len % GRUB_NTFS_COM_LEN; if (len) { grub_uint32_t t; + void *file = ctx->file; - t = ctx->target_vcn * (ctx->comp.spc << BLK_SHR); - if (read_block (ctx, at->sbuf, 1)) + ctx->file = 0; + t = ctx->target_vcn << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR); + if (read_block (ctx, ctx->attr->sbuf, 1)) { ret = grub_errno; goto quit; } - at->save_pos = t; + ctx->attr->save_pos = t; - grub_memcpy (dest, at->sbuf, len); + grub_memcpy (dest, ctx->attr->sbuf, len); + if (grub_file_progress_hook && file) + grub_file_progress_hook (0, 0, len, NULL, file); } quit: diff --git a/grub-core/fs/odc.c b/grub-core/fs/odc.c new file mode 100644 index 000000000..8e4e8aeac --- /dev/null +++ b/grub-core/fs/odc.c @@ -0,0 +1,62 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#define ALIGN_CPIO(x) x + +#define MAGIC "070707" +struct head +{ + char magic[6]; + char dev[6]; + char ino[6]; + char mode[6]; + char uid[6]; + char gid[6]; + char nlink[6]; + char rdev[6]; + char mtime[11]; + char namesize[6]; + char filesize[11]; +} GRUB_PACKED; + +static inline unsigned long long +read_number (const char *str, grub_size_t size) +{ + unsigned long long ret = 0; + while (size-- && *str >= '0' && *str <= '7') + ret = (ret << 3) | (*str++ & 0xf); + return ret; +} + +#define FSNAME "odc" + +#include "cpio_common.c" + +GRUB_MOD_INIT (odc) +{ + grub_cpio_fs.mod = mod; + grub_fs_register (&grub_cpio_fs); +} + +GRUB_MOD_FINI (odc) +{ + grub_fs_unregister (&grub_cpio_fs); +} diff --git a/grub-core/fs/proc.c b/grub-core/fs/proc.c new file mode 100644 index 000000000..bcde43349 --- /dev/null +++ b/grub-core/fs/proc.c @@ -0,0 +1,204 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_procfs_entry *grub_procfs_entries; + +static int +grub_procdev_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + return hook ("proc", hook_data); +} + +static grub_err_t +grub_procdev_open (const char *name, grub_disk_t disk) +{ + if (grub_strcmp (name, "proc")) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a procfs disk"); + + disk->total_sectors = 0; + disk->id = 0; + + disk->data = 0; + + return GRUB_ERR_NONE; +} + +static void +grub_procdev_close (grub_disk_t disk __attribute((unused))) +{ +} + +static grub_err_t +grub_procdev_read (grub_disk_t disk __attribute((unused)), + grub_disk_addr_t sector __attribute((unused)), + grub_size_t size __attribute((unused)), + char *buf __attribute((unused))) +{ + return GRUB_ERR_OUT_OF_RANGE; +} + +static grub_err_t +grub_procdev_write (grub_disk_t disk __attribute ((unused)), + grub_disk_addr_t sector __attribute ((unused)), + grub_size_t size __attribute ((unused)), + const char *buf __attribute ((unused))) +{ + return GRUB_ERR_OUT_OF_RANGE; +} + +struct grub_archelp_data +{ + struct grub_procfs_entry *entry, *next_entry; +}; + +static void +grub_procfs_rewind (struct grub_archelp_data *data) +{ + data->entry = NULL; + data->next_entry = grub_procfs_entries; +} + +static grub_err_t +grub_procfs_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_uint32_t *mode) +{ + data->entry = data->next_entry; + if (!data->entry) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + data->next_entry = data->entry->next; + *mode = GRUB_ARCHELP_ATTR_FILE | GRUB_ARCHELP_ATTR_NOTIME; + *name = grub_strdup (data->entry->name); + *mtime = 0; + if (!*name) + return grub_errno; + return GRUB_ERR_NONE; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_procfs_find_file, + .rewind = grub_procfs_rewind + }; + +static grub_ssize_t +grub_procfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + char *data = file->data; + + grub_memcpy (buf, data + file->offset, len); + + return len; +} + +static grub_err_t +grub_procfs_close (grub_file_t file) +{ + char *data; + + data = file->data; + grub_free (data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_procfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data data; + + /* Check if the disk is our dummy disk. */ + if (grub_strcmp (device->disk->name, "proc")) + return grub_error (GRUB_ERR_BAD_FS, "not a procfs"); + + grub_procfs_rewind (&data); + + return grub_archelp_dir (&data, &arcops, + path, hook, hook_data); +} + +static grub_err_t +grub_procfs_open (struct grub_file *file, const char *path) +{ + grub_err_t err; + struct grub_archelp_data data; + grub_size_t sz; + + grub_procfs_rewind (&data); + + err = grub_archelp_open (&data, &arcops, path); + if (err) + return err; + file->data = data.entry->get_contents (&sz); + if (!file->data) + return grub_errno; + file->size = sz; + return GRUB_ERR_NONE; +} + +static struct grub_disk_dev grub_procfs_dev = { + .name = "proc", + .id = GRUB_DISK_DEVICE_PROCFS_ID, + .disk_iterate = grub_procdev_iterate, + .disk_open = grub_procdev_open, + .disk_close = grub_procdev_close, + .disk_read = grub_procdev_read, + .disk_write = grub_procdev_write, + .next = 0 +}; + +static struct grub_fs grub_procfs_fs = + { + .name = "procfs", + .fs_dir = grub_procfs_dir, + .fs_open = grub_procfs_open, + .fs_read = grub_procfs_read, + .fs_close = grub_procfs_close, + .next = 0 + }; + +GRUB_MOD_INIT (procfs) +{ + grub_procfs_fs.mod = mod; + grub_disk_dev_register (&grub_procfs_dev); + grub_fs_register (&grub_procfs_fs); +} + +GRUB_MOD_FINI (procfs) +{ + grub_disk_dev_unregister (&grub_procfs_dev); + grub_fs_unregister (&grub_procfs_fs); +} diff --git a/fs/reiserfs.c b/grub-core/fs/reiserfs.c similarity index 64% rename from fs/reiserfs.c rename to grub-core/fs/reiserfs.c index c3db52f60..5d3c85950 100644 --- a/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -38,6 +38,10 @@ #include #include #include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); #define MIN(a, b) \ ({ typeof (a) _a = (a); \ @@ -55,14 +59,12 @@ #define REISERFS_MAGIC_DESC_BLOCK "ReIsErLB" /* If the 3rd bit of an item state is set, then it's visible. */ #define GRUB_REISERFS_VISIBLE_MASK ((grub_uint16_t) 0x04) -#define REISERFS_MAX_LABEL_LENGTH 16 -#define REISERFS_LABEL_OFFSET 0x64 #define S_IFLNK 0xA000 static grub_dl_t my_mod; -#define assert(boolean) real_assert (boolean, __FILE__, __LINE__) +#define assert(boolean) real_assert (boolean, GRUB_FILE, __LINE__) static inline void real_assert (int boolean, const char *file, const int line) { @@ -107,14 +109,15 @@ struct grub_reiserfs_superblock grub_uint32_t inode_generation; grub_uint8_t unused[4]; grub_uint16_t uuid[8]; -} __attribute__ ((packed)); + char label[16]; +} GRUB_PACKED; struct grub_reiserfs_journal_header { grub_uint32_t last_flush_uid; grub_uint32_t unflushed_offset; grub_uint32_t mount_id; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_description_block { @@ -122,14 +125,14 @@ struct grub_reiserfs_description_block grub_uint32_t len; grub_uint32_t mount_id; grub_uint32_t real_blocks[0]; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_commit_block { grub_uint32_t id; grub_uint32_t len; grub_uint32_t real_blocks[0]; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_stat_item_v1 { @@ -143,7 +146,7 @@ struct grub_reiserfs_stat_item_v1 grub_uint32_t ctime; grub_uint32_t rdev; grub_uint32_t first_direct_byte; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_stat_item_v2 { @@ -158,7 +161,7 @@ struct grub_reiserfs_stat_item_v2 grub_uint32_t ctime; grub_uint32_t blocks; grub_uint32_t first_direct_byte; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_key { @@ -170,13 +173,13 @@ struct grub_reiserfs_key { grub_uint32_t offset; grub_uint32_t type; - } v1 __attribute__ ((packed)); + } GRUB_PACKED v1; struct { grub_uint64_t offset_type; - } v2 __attribute__ ((packed)); + } GRUB_PACKED v2; } u; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_item_header { @@ -185,11 +188,11 @@ struct grub_reiserfs_item_header { grub_uint16_t free_space; grub_uint16_t entry_count; - } u __attribute__ ((packed)); + } GRUB_PACKED u; grub_uint16_t item_size; grub_uint16_t item_location; grub_uint16_t version; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_block_header { @@ -198,14 +201,14 @@ struct grub_reiserfs_block_header grub_uint16_t free_space; grub_uint16_t reserved; struct grub_reiserfs_key block_right_delimiting_key; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_disk_child { grub_uint32_t block_number; grub_uint16_t size; grub_uint16_t reserved; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_reiserfs_directory_header { @@ -214,7 +217,7 @@ struct grub_reiserfs_directory_header grub_uint32_t object_id; grub_uint16_t location; grub_uint16_t state; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_fshelp_node { @@ -222,6 +225,8 @@ struct grub_fshelp_node grub_uint32_t block_number; /* 0 if node is not found. */ grub_uint16_t block_position; grub_uint64_t next_offset; + grub_int32_t mtime; + grub_off_t size; enum grub_reiserfs_item_type type; /* To know how to read the header. */ struct grub_reiserfs_item_header header; }; @@ -233,6 +238,12 @@ struct grub_reiserfs_data grub_disk_t disk; }; +static grub_ssize_t +grub_reiserfs_read_real (struct grub_fshelp_node *node, + grub_off_t off, char *buf, grub_size_t len, + grub_disk_read_hook_t read_hook, + void *read_hook_data); + /* Internal-only functions. Not to be used outside of this file. */ /* Return the type of given v2 key. */ @@ -355,7 +366,7 @@ grub_reiserfs_set_key_offset (struct grub_reiserfs_key *key, key->u.v1.offset = grub_cpu_to_le32 (value); else key->u.v2.offset_type \ - = ((key->u.v2.offset_type & grub_cpu_to_le64 (15ULL << 60)) + = ((key->u.v2.offset_type & grub_cpu_to_le64_compile_time (15ULL << 60)) | grub_cpu_to_le64 (value & (~0ULL >> 4))); } @@ -402,7 +413,7 @@ grub_reiserfs_set_key_type (struct grub_reiserfs_key *key, key->u.v1.type = grub_cpu_to_le32 (type); else key->u.v2.offset_type - = ((key->u.v2.offset_type & grub_cpu_to_le64 (~0ULL >> 4)) + = ((key->u.v2.offset_type & grub_cpu_to_le64_compile_time (~0ULL >> 4)) | grub_cpu_to_le64 ((grub_uint64_t) type << 60)); assert (grub_reiserfs_get_key_type (key) == grub_type); @@ -465,7 +476,7 @@ grub_reiserfs_compare_keys (const struct grub_reiserfs_key *key1, static grub_err_t grub_reiserfs_get_item (struct grub_reiserfs_data *data, const struct grub_reiserfs_key *key, - struct grub_fshelp_node *item) + struct grub_fshelp_node *item, int exact) { grub_uint32_t block_number; struct grub_reiserfs_block_header *block_header = 0; @@ -475,23 +486,25 @@ grub_reiserfs_get_item (struct grub_reiserfs_data *data, grub_uint16_t previous_level = ~0; struct grub_reiserfs_item_header *item_headers = 0; +#if 0 if (! data) { - grub_error (GRUB_ERR_TEST_FAILURE, "data is NULL"); + grub_error (GRUB_ERR_BAD_FS, "data is NULL"); goto fail; } if (! key) { - grub_error (GRUB_ERR_TEST_FAILURE, "key is NULL"); + grub_error (GRUB_ERR_BAD_FS, "key is NULL"); goto fail; } if (! item) { - grub_error (GRUB_ERR_TEST_FAILURE, "item is NULL"); + grub_error (GRUB_ERR_BAD_FS, "item is NULL"); goto fail; } +#endif block_size = grub_le_to_cpu16 (data->superblock.block_size); block_number = grub_le_to_cpu32 (data->superblock.root_block); @@ -518,7 +531,7 @@ grub_reiserfs_get_item (struct grub_reiserfs_data *data, if (current_level >= previous_level) { grub_dprintf ("reiserfs_tree", "level loop detected, aborting\n"); - grub_error (GRUB_ERR_FILE_READ_ERROR, "level loop"); + grub_error (GRUB_ERR_BAD_FS, "level loop"); goto fail; } previous_level = current_level; @@ -576,30 +589,47 @@ grub_reiserfs_get_item (struct grub_reiserfs_data *data, item_headers = (struct grub_reiserfs_item_header *) (block_header + 1); for (i = 0; - i < item_count - && (grub_reiserfs_compare_keys (key, &(item_headers[i].key)) - != 0); + i < item_count; i++) - { -#ifdef GRUB_REISERFS_DEBUG - if (key->directory_id == item_headers[i].key.directory_id && \ - key->object_id == item_headers[i].key.object_id) - grub_printf("C"); - else - grub_printf(" "); - grub_printf(" %03d/%03d ", i + 1, item_count); - grub_reiserfs_print_key (&(item_headers[i].key)); -#endif - } - if (i < item_count) - block_key = &(item_headers[i].key); + { + int val; + val = grub_reiserfs_compare_keys (key, &(item_headers[i].key)); + if (val == 0) + { + block_key = &(item_headers[i].key); + break; + } + if (val < 0 && exact) + break; + if (val < 0) + { + if (i == 0) + { + grub_error (GRUB_ERR_READ_ERROR, "unexpected btree node"); + goto fail; + } + i--; + block_key = &(item_headers[i].key); + break; + } + } + if (!exact && i == item_count) + { + if (i == 0) + { + grub_error (GRUB_ERR_READ_ERROR, "unexpected btree node"); + goto fail; + } + i--; + block_key = &(item_headers[i].key); + } } } while (current_level > 1); item->data = data; - if (i == item_count || grub_reiserfs_compare_keys (key, block_key)) + if (!block_key) { item->block_number = 0; item->block_position = 0; @@ -637,43 +667,22 @@ static char * grub_reiserfs_read_symlink (grub_fshelp_node_t node) { char *symlink_buffer = 0; - grub_uint16_t block_size; - grub_disk_addr_t block; - grub_off_t offset; - grub_size_t len; - struct grub_fshelp_node found; - struct grub_reiserfs_key key; - - grub_memcpy (&key, &(node->header.key), sizeof (key)); - grub_reiserfs_set_key_offset (&key, 1); - grub_reiserfs_set_key_type (&key, GRUB_REISERFS_DIRECT, - grub_reiserfs_get_key_version (&key)); - - if (grub_reiserfs_get_item (node->data, &key, &found) != GRUB_ERR_NONE) - goto fail; - - if (found.block_number == 0) - goto fail; - - block_size = grub_le_to_cpu16 (node->data->superblock.block_size); - len = grub_le_to_cpu16 (found.header.item_size); - block = found.block_number * (block_size >> GRUB_DISK_SECTOR_BITS); - offset = grub_le_to_cpu16 (found.header.item_location); + grub_size_t len = node->size; + grub_ssize_t ret; symlink_buffer = grub_malloc (len + 1); if (! symlink_buffer) - goto fail; + return 0; - grub_disk_read (node->data->disk, block, offset, len, symlink_buffer); - if (grub_errno) - goto fail; + ret = grub_reiserfs_read_real (node, 0, symlink_buffer, len, 0, 0); + if (ret < 0) + { + grub_free (symlink_buffer); + return 0; + } - symlink_buffer[len] = 0; + symlink_buffer[ret] = 0; return symlink_buffer; - - fail: - grub_free (symlink_buffer); - return 0; } /* Fill the mounted filesystem structure and return it. */ @@ -709,10 +718,8 @@ grub_reiserfs_mount (grub_disk_t disk) /* Call HOOK for each file in directory ITEM. */ static int grub_reiserfs_iterate_dir (grub_fshelp_node_t item, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) + grub_fshelp_iterate_dir_hook_t hook, + void *hook_data) { struct grub_reiserfs_data *data = item->data; struct grub_reiserfs_block_header *block_header = 0; @@ -723,12 +730,11 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, if (item->type != GRUB_REISERFS_DIRECTORY) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, - "grub_reiserfs_iterate_dir called on a non-directory item"); + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); goto fail; } block_size = grub_le_to_cpu16 (data->superblock.block_size); - block_header = grub_malloc (block_size); + block_header = grub_malloc (block_size + 1); if (! block_header) goto fail; block_number = item->block_number; @@ -749,10 +755,12 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, if (grub_errno) goto fail; + ((char *) block_header)[block_size] = 0; + #if 0 if (grub_le_to_cpu16 (block_header->level) != 1) { - grub_error (GRUB_ERR_TEST_FAILURE, + grub_error (GRUB_ERR_BAD_FS, "reiserfs: block %d is not a leaf block", block_number); goto fail; @@ -772,178 +780,203 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, = &directory_headers[entry_number]; grub_uint16_t entry_state = grub_le_to_cpu16 (directory_header->state); + grub_fshelp_node_t entry_item; + struct grub_reiserfs_key entry_key; + enum grub_fshelp_filetype entry_type; + char *entry_name; + char *entry_name_end = 0; + char c; - if (entry_state & GRUB_REISERFS_VISIBLE_MASK) - { - grub_fshelp_node_t entry_item; - struct grub_reiserfs_key entry_key; - enum grub_reiserfs_item_type entry_type; - char *entry_name; + if (!(entry_state & GRUB_REISERFS_VISIBLE_MASK)) + continue; - entry_name = (((char *) directory_headers) - + grub_le_to_cpu16 (directory_header->location)); - entry_key.directory_id = directory_header->directory_id; - entry_key.object_id = directory_header->object_id; - entry_key.u.v2.offset_type = 0; - grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_DIRECTORY, - 2); - grub_reiserfs_set_key_offset (&entry_key, 1); + entry_name = (((char *) directory_headers) + + grub_le_to_cpu16 (directory_header->location)); + if (entry_number == 0) + { + entry_name_end = (char *) block_header + + grub_le_to_cpu16 (item_headers[block_position].item_location) + + grub_le_to_cpu16 (item_headers[block_position].item_size); + } + else + { + entry_name_end = (((char *) directory_headers) + + grub_le_to_cpu16 (directory_headers[entry_number - 1].location)); + } + if (entry_name_end < entry_name || entry_name_end > (char *) block_header + block_size) + { + entry_name_end = (char *) block_header + block_size; + } - entry_item = grub_malloc (sizeof (*entry_item)); - if (! entry_item) - goto fail; + entry_key.directory_id = directory_header->directory_id; + entry_key.object_id = directory_header->object_id; + entry_key.u.v2.offset_type = 0; + grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_DIRECTORY, + 2); + grub_reiserfs_set_key_offset (&entry_key, 1); - if (grub_reiserfs_get_item (data, &entry_key, entry_item) - != GRUB_ERR_NONE) - { - grub_free (entry_item); - goto fail; - } + entry_item = grub_malloc (sizeof (*entry_item)); + if (! entry_item) + goto fail; - if (entry_item->type == GRUB_REISERFS_DIRECTORY) - entry_type = GRUB_FSHELP_DIR; - else - { - grub_uint32_t entry_block_number; - /* Order is very important here. - First set the offset to 0 using current key version. - Then change the key type, which affects key version - detection. */ - grub_reiserfs_set_key_offset (&entry_key, 0); - grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_STAT, - 2); - if (grub_reiserfs_get_item (data, &entry_key, entry_item) - != GRUB_ERR_NONE) - { - grub_free (entry_item); - goto fail; - } + if (grub_reiserfs_get_item (data, &entry_key, entry_item, 1) + != GRUB_ERR_NONE) + { + grub_free (entry_item); + goto fail; + } - if (entry_item->block_number != 0) - { - grub_uint16_t entry_version; - entry_version - = grub_le_to_cpu16 (entry_item->header.version); - entry_block_number = entry_item->block_number; + if (entry_item->type == GRUB_REISERFS_DIRECTORY) + entry_type = GRUB_FSHELP_DIR; + else + { + grub_uint32_t entry_block_number; + /* Order is very important here. + First set the offset to 0 using current key version. + Then change the key type, which affects key version + detection. */ + grub_reiserfs_set_key_offset (&entry_key, 0); + grub_reiserfs_set_key_type (&entry_key, GRUB_REISERFS_STAT, + 2); + if (grub_reiserfs_get_item (data, &entry_key, entry_item, 1) + != GRUB_ERR_NONE) + { + grub_free (entry_item); + goto fail; + } + + if (entry_item->block_number != 0) + { + grub_uint16_t entry_version; + entry_version + = grub_le_to_cpu16 (entry_item->header.version); + entry_block_number = entry_item->block_number; +#if 0 + grub_dprintf ("reiserfs", + "version %04x block %08x (%08x) position %08x\n", + entry_version, entry_block_number, + ((grub_disk_addr_t) entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE, + grub_le_to_cpu16 (entry_item->header.item_location)); +#endif + if (entry_version == 0) /* Version 1 stat item. */ + { + struct grub_reiserfs_stat_item_v1 entry_v1_stat; + grub_disk_read (data->disk, + entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), + grub_le_to_cpu16 (entry_item->header.item_location), + sizeof (entry_v1_stat), + (char *) &entry_v1_stat); + if (grub_errno) + goto fail; #if 0 grub_dprintf ("reiserfs", - "version %04x block %08x (%08x) position %08x\n", - entry_version, entry_block_number, - ((grub_disk_addr_t) entry_block_number * block_size) / GRUB_DISK_SECTOR_SIZE, - grub_le_to_cpu16 (entry_item->header.item_location)); + "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", + grub_le_to_cpu16 (entry_v1_stat.mode), + grub_le_to_cpu16 (entry_v1_stat.hardlink_count), + grub_le_to_cpu16 (entry_v1_stat.uid), + grub_le_to_cpu16 (entry_v1_stat.gid), + grub_le_to_cpu32 (entry_v1_stat.size), + grub_le_to_cpu32 (entry_v1_stat.atime), + grub_le_to_cpu32 (entry_v1_stat.mtime), + grub_le_to_cpu32 (entry_v1_stat.ctime), + grub_le_to_cpu32 (entry_v1_stat.rdev), + grub_le_to_cpu32 (entry_v1_stat.first_direct_byte)); + grub_dprintf ("reiserfs", + "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", + entry_v1_stat.mode, + entry_v1_stat.hardlink_count, + entry_v1_stat.uid, + entry_v1_stat.gid, + entry_v1_stat.size, + entry_v1_stat.atime, + entry_v1_stat.mtime, + entry_v1_stat.ctime, + entry_v1_stat.rdev, + entry_v1_stat.first_direct_byte); #endif - if (entry_version == 0) /* Version 1 stat item. */ - { - struct grub_reiserfs_stat_item_v1 entry_v1_stat; - grub_disk_read (data->disk, - entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), - grub_le_to_cpu16 (entry_item->header.item_location), - sizeof (entry_v1_stat), - (char *) &entry_v1_stat); - if (grub_errno) - goto fail; + entry_item->mtime = grub_le_to_cpu32 (entry_v1_stat.mtime); + if ((grub_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK) + == S_IFLNK) + entry_type = GRUB_FSHELP_SYMLINK; + else + entry_type = GRUB_FSHELP_REG; + entry_item->size = (grub_off_t) grub_le_to_cpu32 (entry_v1_stat.size); + } + else + { + struct grub_reiserfs_stat_item_v2 entry_v2_stat; + grub_disk_read (data->disk, + entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), + grub_le_to_cpu16 (entry_item->header.item_location), + sizeof (entry_v2_stat), + (char *) &entry_v2_stat); + if (grub_errno) + goto fail; #if 0 - grub_dprintf ("reiserfs", - "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", - grub_le_to_cpu16 (entry_v1_stat.mode), - grub_le_to_cpu16 (entry_v1_stat.hardlink_count), - grub_le_to_cpu16 (entry_v1_stat.uid), - grub_le_to_cpu16 (entry_v1_stat.gid), - grub_le_to_cpu32 (entry_v1_stat.size), - grub_le_to_cpu32 (entry_v1_stat.atime), - grub_le_to_cpu32 (entry_v1_stat.mtime), - grub_le_to_cpu32 (entry_v1_stat.ctime), - grub_le_to_cpu32 (entry_v1_stat.rdev), - grub_le_to_cpu32 (entry_v1_stat.first_direct_byte)); - grub_dprintf ("reiserfs", - "%04x %04x %04x %04x %08x %08x | %08x %08x %08x %08x\n", - entry_v1_stat.mode, - entry_v1_stat.hardlink_count, - entry_v1_stat.uid, - entry_v1_stat.gid, - entry_v1_stat.size, - entry_v1_stat.atime, - entry_v1_stat.mtime, - entry_v1_stat.ctime, - entry_v1_stat.rdev, - entry_v1_stat.first_direct_byte); + grub_dprintf ("reiserfs", + "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", + grub_le_to_cpu16 (entry_v2_stat.mode), + grub_le_to_cpu16 (entry_v2_stat.reserved), + grub_le_to_cpu32 (entry_v2_stat.hardlink_count), + (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) >> 32), + (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) && 0xFFFFFFFF), + grub_le_to_cpu32 (entry_v2_stat.uid), + grub_le_to_cpu32 (entry_v2_stat.gid), + grub_le_to_cpu32 (entry_v2_stat.atime), + grub_le_to_cpu32 (entry_v2_stat.mtime), + grub_le_to_cpu32 (entry_v2_stat.ctime), + grub_le_to_cpu32 (entry_v2_stat.blocks), + grub_le_to_cpu32 (entry_v2_stat.first_direct_byte)); + grub_dprintf ("reiserfs", + "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", + entry_v2_stat.mode, + entry_v2_stat.reserved, + entry_v2_stat.hardlink_count, + (unsigned int) (entry_v2_stat.size >> 32), + (unsigned int) (entry_v2_stat.size && 0xFFFFFFFF), + entry_v2_stat.uid, + entry_v2_stat.gid, + entry_v2_stat.atime, + entry_v2_stat.mtime, + entry_v2_stat.ctime, + entry_v2_stat.blocks, + entry_v2_stat.first_direct_byte); #endif - if ((grub_le_to_cpu16 (entry_v1_stat.mode) & S_IFLNK) - == S_IFLNK) - entry_type = GRUB_FSHELP_SYMLINK; - else - entry_type = GRUB_FSHELP_REG; - } - else - { - struct grub_reiserfs_stat_item_v2 entry_v2_stat; - grub_disk_read (data->disk, - entry_block_number * (block_size >> GRUB_DISK_SECTOR_BITS), - grub_le_to_cpu16 (entry_item->header.item_location), - sizeof (entry_v2_stat), - (char *) &entry_v2_stat); - if (grub_errno) - goto fail; -#if 0 - grub_dprintf ("reiserfs", - "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", - grub_le_to_cpu16 (entry_v2_stat.mode), - grub_le_to_cpu16 (entry_v2_stat.reserved), - grub_le_to_cpu32 (entry_v2_stat.hardlink_count), - (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) >> 32), - (unsigned int) (grub_le_to_cpu64 (entry_v2_stat.size) && 0xFFFFFFFF), - grub_le_to_cpu32 (entry_v2_stat.uid), - grub_le_to_cpu32 (entry_v2_stat.gid), - grub_le_to_cpu32 (entry_v2_stat.atime), - grub_le_to_cpu32 (entry_v2_stat.mtime), - grub_le_to_cpu32 (entry_v2_stat.ctime), - grub_le_to_cpu32 (entry_v2_stat.blocks), - grub_le_to_cpu32 (entry_v2_stat.first_direct_byte)); - grub_dprintf ("reiserfs", - "%04x %04x %08x %08x%08x | %08x %08x %08x %08x | %08x %08x %08x\n", - entry_v2_stat.mode, - entry_v2_stat.reserved, - entry_v2_stat.hardlink_count, - (unsigned int) (entry_v2_stat.size >> 32), - (unsigned int) (entry_v2_stat.size && 0xFFFFFFFF), - entry_v2_stat.uid, - entry_v2_stat.gid, - entry_v2_stat.atime, - entry_v2_stat.mtime, - entry_v2_stat.ctime, - entry_v2_stat.blocks, - entry_v2_stat.first_direct_byte); -#endif - if ((grub_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK) - == S_IFLNK) - entry_type = GRUB_FSHELP_SYMLINK; - else - entry_type = GRUB_FSHELP_REG; - } - } - else - { - /* Pseudo file ".." never has stat block. */ - if (grub_strcmp (entry_name, "..")) - grub_dprintf ("reiserfs", - "Warning : %s has no stat block !\n", - entry_name); - grub_free (entry_item); - continue; - } - } - if (hook (entry_name, entry_type, entry_item)) - { - grub_dprintf ("reiserfs", "Found : %s, type=%d\n", - entry_name, entry_type); - ret = 1; - goto found; - } + entry_item->mtime = grub_le_to_cpu32 (entry_v2_stat.mtime); + entry_item->size = (grub_off_t) grub_le_to_cpu64 (entry_v2_stat.size); + if ((grub_le_to_cpu16 (entry_v2_stat.mode) & S_IFLNK) + == S_IFLNK) + entry_type = GRUB_FSHELP_SYMLINK; + else + entry_type = GRUB_FSHELP_REG; + } + } + else + { + /* Pseudo file ".." never has stat block. */ + if (entry_name_end == entry_name + 2 && grub_memcmp (entry_name, "..", 2) != 0) + grub_dprintf ("reiserfs", + "Warning : %s has no stat block !\n", + entry_name); + grub_free (entry_item); + goto next; + } + } - *entry_name = 0; /* Make sure next entry name (which is just - before this one in disk order) stops before - the current one. */ - } + c = *entry_name_end; + *entry_name_end = 0; + if (hook (entry_name, entry_type, entry_item, hook_data)) + { + *entry_name_end = c; + grub_dprintf ("reiserfs", "Found : %s, type=%d\n", + entry_name, entry_type); + ret = 1; + goto found; + } + *entry_name_end = c; + + next: + ; } if (next_offset == 0) @@ -952,7 +985,7 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item, grub_reiserfs_set_key_offset (&(item_headers[block_position].key), next_offset); if (grub_reiserfs_get_item (data, &(item_headers[block_position].key), - &directory_item) != GRUB_ERR_NONE) + &directory_item, 1) != GRUB_ERR_NONE) goto fail; block_number = directory_item.block_number; block_position = directory_item.block_position; @@ -979,22 +1012,19 @@ static grub_err_t grub_reiserfs_open (struct grub_file *file, const char *name) { struct grub_reiserfs_data *data = 0; - struct grub_fshelp_node root, *found = 0, info; + struct grub_fshelp_node root, *found = 0; struct grub_reiserfs_key key; - grub_uint32_t block_number; - grub_uint16_t entry_version, block_size, entry_location; grub_dl_ref (my_mod); data = grub_reiserfs_mount (file->device->disk); if (! data) goto fail; - block_size = grub_le_to_cpu16 (data->superblock.block_size); - key.directory_id = grub_cpu_to_le32 (1); - key.object_id = grub_cpu_to_le32 (2); + key.directory_id = grub_cpu_to_le32_compile_time (1); + key.object_id = grub_cpu_to_le32_compile_time (2); key.u.v2.offset_type = 0; grub_reiserfs_set_key_type (&key, GRUB_REISERFS_DIRECTORY, 2); grub_reiserfs_set_key_offset (&key, 1); - if (grub_reiserfs_get_item (data, &key, &root) != GRUB_ERR_NONE) + if (grub_reiserfs_get_item (data, &key, &root, 1) != GRUB_ERR_NONE) goto fail; if (root.block_number == 0) { @@ -1006,46 +1036,8 @@ grub_reiserfs_open (struct grub_file *file, const char *name) grub_reiserfs_read_symlink, GRUB_FSHELP_REG); if (grub_errno) goto fail; - key.directory_id = found->header.key.directory_id; - key.object_id = found->header.key.object_id; - grub_reiserfs_set_key_type (&key, GRUB_REISERFS_STAT, 2); - grub_reiserfs_set_key_offset (&key, 0); - if (grub_reiserfs_get_item (data, &key, &info) != GRUB_ERR_NONE) - goto fail; - if (info.block_number == 0) - { - grub_error (GRUB_ERR_BAD_FS, "unable to find searched item"); - goto fail; - } - entry_version = grub_le_to_cpu16 (info.header.version); - entry_location = grub_le_to_cpu16 (info.header.item_location); - block_number = info.block_number; - if (entry_version == 0) /* Version 1 stat item. */ - { - struct grub_reiserfs_stat_item_v1 entry_v1_stat; - grub_disk_read (data->disk, - block_number * (block_size >> GRUB_DISK_SECTOR_BITS), - entry_location - + (((grub_off_t) block_number * block_size) - & (GRUB_DISK_SECTOR_SIZE - 1)), - sizeof (entry_v1_stat), &entry_v1_stat); - if (grub_errno) - goto fail; - file->size = (grub_off_t) grub_le_to_cpu64 (entry_v1_stat.size); - } - else - { - struct grub_reiserfs_stat_item_v2 entry_v2_stat; - grub_disk_read (data->disk, - block_number * (block_size >> GRUB_DISK_SECTOR_BITS), - entry_location - + (((grub_off_t) block_number * block_size) - & (GRUB_DISK_SECTOR_SIZE - 1)), - sizeof (entry_v2_stat), &entry_v2_stat); - if (grub_errno) - goto fail; - file->size = (grub_off_t) grub_le_to_cpu64 (entry_v2_stat.size); - } + file->size = found->size; + grub_dprintf ("reiserfs", "file size : %d (%08x%08x)\n", (unsigned int) file->size, (unsigned int) (file->size >> 32), (unsigned int) file->size); @@ -1055,18 +1047,20 @@ grub_reiserfs_open (struct grub_file *file, const char *name) fail: assert (grub_errno != GRUB_ERR_NONE); - grub_free (found); + if (found != &root) + grub_free (found); grub_free (data); grub_dl_unref (my_mod); return grub_errno; } static grub_ssize_t -grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) +grub_reiserfs_read_real (struct grub_fshelp_node *node, + grub_off_t off, char *buf, grub_size_t len, + grub_disk_read_hook_t read_hook, void *read_hook_data) { unsigned int indirect_block, indirect_block_count; struct grub_reiserfs_key key; - struct grub_fshelp_node *node = file->data; struct grub_reiserfs_data *data = node->data; struct grub_fshelp_node found; grub_uint16_t block_size = grub_le_to_cpu16 (data->superblock.block_size); @@ -1081,20 +1075,36 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) key.object_id = node->header.key.object_id; key.u.v2.offset_type = 0; grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2); - initial_position = file->offset; + initial_position = off; current_position = 0; - final_position = MIN (len + initial_position, file->size); + final_position = MIN (len + initial_position, node->size); grub_dprintf ("reiserfs", "Reading from %lld to %lld (%lld instead of requested %ld)\n", (unsigned long long) initial_position, (unsigned long long) final_position, (unsigned long long) (final_position - initial_position), (unsigned long) len); + + grub_reiserfs_set_key_offset (&key, initial_position + 1); + + if (grub_reiserfs_get_item (data, &key, &found, 0) != GRUB_ERR_NONE) + goto fail; + + if (found.block_number == 0) + { + grub_error (GRUB_ERR_READ_ERROR, "offset %lld not found", + (unsigned long long) initial_position); + goto fail; + } + + current_key_offset = grub_reiserfs_get_key_offset (&found.header.key); + current_position = current_key_offset - 1; + while (current_position < final_position) { grub_reiserfs_set_key_offset (&key, current_key_offset); - if (grub_reiserfs_get_item (data, &key, &found) != GRUB_ERR_NONE) + if (grub_reiserfs_get_item (data, &key, &found, 1) != GRUB_ERR_NONE) goto fail; if (found.block_number == 0) goto fail; @@ -1102,7 +1112,7 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) switch (found.type) { case GRUB_REISERFS_DIRECT: - block = found.block_number * (block_size >> GRUB_DISK_SECTOR_BITS); + block = ((grub_disk_addr_t) found.block_number) * (block_size >> GRUB_DISK_SECTOR_BITS); grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); if (initial_position < current_position + item_size) { @@ -1113,7 +1123,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) "Reading direct block %u from %u to %u...\n", (unsigned) block, (unsigned) offset, (unsigned) (offset + length)); - found.data->disk->read_hook = file->read_hook; + found.data->disk->read_hook = read_hook; + found.data->disk->read_hook_data = read_hook_data; grub_disk_read (found.data->disk, block, offset @@ -1139,7 +1150,8 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) item_size, indirect_block_ptr); if (grub_errno) goto fail; - found.data->disk->read_hook = file->read_hook; + found.data->disk->read_hook = read_hook; + found.data->disk->read_hook_data = read_hook_data; for (indirect_block = 0; indirect_block < indirect_block_count && current_position < final_position; @@ -1238,7 +1250,14 @@ grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) fail: grub_free (indirect_block_ptr); - return 0; + return -1; +} + +static grub_ssize_t +grub_reiserfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + return grub_reiserfs_read_real (file->data, file->offset, buf, len, + file->read_hook, file->read_hook_data); } /* Close the file FILE. */ @@ -1254,40 +1273,50 @@ grub_reiserfs_close (grub_file_t file) return GRUB_ERR_NONE; } +/* Context for grub_reiserfs_dir. */ +struct grub_reiserfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_reiserfs_dir. */ +static int +grub_reiserfs_dir_iter (const char *filename, + enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_reiserfs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = node->mtime; + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + /* Call HOOK with each file under DIR. */ static grub_err_t grub_reiserfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { + struct grub_reiserfs_dir_ctx ctx = { hook, hook_data }; struct grub_reiserfs_data *data = 0; struct grub_fshelp_node root, *found; struct grub_reiserfs_key root_key; - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } grub_dl_ref (my_mod); data = grub_reiserfs_mount (device->disk); if (! data) goto fail; - root_key.directory_id = grub_cpu_to_le32 (1); - root_key.object_id = grub_cpu_to_le32 (2); + root_key.directory_id = grub_cpu_to_le32_compile_time (1); + root_key.object_id = grub_cpu_to_le32_compile_time (2); root_key.u.v2.offset_type = 0; grub_reiserfs_set_key_type (&root_key, GRUB_REISERFS_DIRECTORY, 2); grub_reiserfs_set_key_offset (&root_key, 1); - if (grub_reiserfs_get_item (data, &root_key, &root) != GRUB_ERR_NONE) + if (grub_reiserfs_get_item (data, &root_key, &root, 1) != GRUB_ERR_NONE) goto fail; if (root.block_number == 0) { @@ -1298,7 +1327,7 @@ grub_reiserfs_dir (grub_device_t device, const char *path, grub_reiserfs_read_symlink, GRUB_FSHELP_DIR); if (grub_errno) goto fail; - grub_reiserfs_iterate_dir (found, iterate); + grub_reiserfs_iterate_dir (found, grub_reiserfs_dir_iter, &ctx); grub_free (data); grub_dl_unref (my_mod); return GRUB_ERR_NONE; @@ -1315,14 +1344,24 @@ grub_reiserfs_dir (grub_device_t device, const char *path, static grub_err_t grub_reiserfs_label (grub_device_t device, char **label) { - *label = grub_malloc (REISERFS_MAX_LABEL_LENGTH); - if (*label) + struct grub_reiserfs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_reiserfs_mount (disk); + if (data) { - grub_disk_read (device->disk, - REISERFS_SUPER_BLOCK_OFFSET / GRUB_DISK_SECTOR_SIZE, - REISERFS_LABEL_OFFSET, REISERFS_MAX_LABEL_LENGTH, - *label); + *label = grub_strndup (data->superblock.label, + sizeof (data->superblock.label)); } + else + *label = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + return grub_errno; } @@ -1334,21 +1373,25 @@ grub_reiserfs_uuid (grub_device_t device, char **uuid) grub_dl_ref (my_mod); + *uuid = NULL; data = grub_reiserfs_mount (disk); if (data) { - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->superblock.uuid[0]), - grub_be_to_cpu16 (data->superblock.uuid[1]), - grub_be_to_cpu16 (data->superblock.uuid[2]), - grub_be_to_cpu16 (data->superblock.uuid[3]), - grub_be_to_cpu16 (data->superblock.uuid[4]), - grub_be_to_cpu16 (data->superblock.uuid[5]), - grub_be_to_cpu16 (data->superblock.uuid[6]), - grub_be_to_cpu16 (data->superblock.uuid[7])); + unsigned i; + for (i = 0; i < ARRAY_SIZE (data->superblock.uuid); i++) + if (data->superblock.uuid[i]) + break; + if (i < ARRAY_SIZE (data->superblock.uuid)) + *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->superblock.uuid[0]), + grub_be_to_cpu16 (data->superblock.uuid[1]), + grub_be_to_cpu16 (data->superblock.uuid[2]), + grub_be_to_cpu16 (data->superblock.uuid[3]), + grub_be_to_cpu16 (data->superblock.uuid[4]), + grub_be_to_cpu16 (data->superblock.uuid[5]), + grub_be_to_cpu16 (data->superblock.uuid[6]), + grub_be_to_cpu16 (data->superblock.uuid[7])); } - else - *uuid = NULL; grub_dl_unref (my_mod); @@ -1360,22 +1403,31 @@ grub_reiserfs_uuid (grub_device_t device, char **uuid) static struct grub_fs grub_reiserfs_fs = { .name = "reiserfs", - .dir = grub_reiserfs_dir, - .open = grub_reiserfs_open, - .read = grub_reiserfs_read, - .close = grub_reiserfs_close, - .label = grub_reiserfs_label, - .uuid = grub_reiserfs_uuid, + .fs_dir = grub_reiserfs_dir, + .fs_open = grub_reiserfs_open, + .fs_read = grub_reiserfs_read, + .fs_close = grub_reiserfs_close, + .fs_label = grub_reiserfs_label, + .fs_uuid = grub_reiserfs_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif .next = 0 }; GRUB_MOD_INIT(reiserfs) { - grub_fs_register (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + { + grub_reiserfs_fs.mod = mod; + grub_fs_register (&grub_reiserfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(reiserfs) { - grub_fs_unregister (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_reiserfs_fs); } diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c new file mode 100644 index 000000000..eafab03b2 --- /dev/null +++ b/grub-core/fs/romfs.c @@ -0,0 +1,490 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_romfs_superblock +{ + char magic[8]; +#define GRUB_ROMFS_MAGIC "-rom1fs-" + grub_uint32_t total_size; + grub_uint32_t chksum; + char label[0]; +}; + +struct grub_romfs_file_header +{ + grub_uint32_t next_file; + grub_uint32_t spec; + grub_uint32_t size; + grub_uint32_t chksum; + char name[0]; +}; + +struct grub_romfs_data +{ + grub_disk_addr_t first_file; + grub_disk_t disk; +}; + +struct grub_fshelp_node +{ + grub_disk_addr_t addr; + struct grub_romfs_data *data; + grub_disk_addr_t data_addr; + /* Not filled for root. */ + struct grub_romfs_file_header file; +}; + +#define GRUB_ROMFS_ALIGN 16 +#define GRUB_ROMFS_TYPE_MASK 7 +#define GRUB_ROMFS_TYPE_HARDLINK 0 +#define GRUB_ROMFS_TYPE_DIRECTORY 1 +#define GRUB_ROMFS_TYPE_REGULAR 2 +#define GRUB_ROMFS_TYPE_SYMLINK 3 + +static grub_err_t +do_checksum (void *in, grub_size_t insize) +{ + grub_uint32_t *a = in; + grub_size_t sz = insize / 4; + grub_uint32_t *b = a + sz; + grub_uint32_t csum = 0; + + while (a < b) + csum += grub_be_to_cpu32 (*a++); + if (csum) + return grub_error (GRUB_ERR_BAD_FS, "invalid checksum"); + return GRUB_ERR_NONE; +} + +static struct grub_romfs_data * +grub_romfs_mount (grub_device_t dev) +{ + union { + struct grub_romfs_superblock sb; + char d[512]; + } sb; + grub_err_t err; + char *ptr; + grub_disk_addr_t sec = 0; + struct grub_romfs_data *data; + if (!dev->disk) + { + grub_error (GRUB_ERR_BAD_FS, "not a disk"); + return NULL; + } + err = grub_disk_read (dev->disk, 0, 0, sizeof (sb), &sb); + if (err == GRUB_ERR_OUT_OF_RANGE) + err = grub_errno = GRUB_ERR_BAD_FS; + if (err) + return NULL; + if (grub_be_to_cpu32 (sb.sb.total_size) < sizeof (sb)) + { + grub_error (GRUB_ERR_BAD_FS, "too short filesystem"); + return NULL; + } + if (grub_memcmp (sb.sb.magic, GRUB_ROMFS_MAGIC, + sizeof (sb.sb.magic)) != 0) + { + grub_error (GRUB_ERR_BAD_FS, "not romfs"); + return NULL; + } + err = do_checksum (&sb, sizeof (sb) < grub_be_to_cpu32 (sb.sb.total_size) ? + sizeof (sb) : grub_be_to_cpu32 (sb.sb.total_size)); + if (err) + { + grub_error (GRUB_ERR_BAD_FS, "checksum incorrect"); + return NULL; + } + for (ptr = sb.sb.label; (void *) ptr < (void *) (&sb + 1) + && ptr - sb.d < (grub_ssize_t) grub_be_to_cpu32 (sb.sb.total_size); ptr++) + if (!*ptr) + break; + while ((void *) ptr == &sb + 1) + { + sec++; + err = grub_disk_read (dev->disk, sec, 0, sizeof (sb), &sb); + if (err == GRUB_ERR_OUT_OF_RANGE) + err = grub_errno = GRUB_ERR_BAD_FS; + if (err) + return NULL; + for (ptr = sb.d; (void *) ptr < (void *) (&sb + 1) + && (ptr - sb.d + (sec << GRUB_DISK_SECTOR_BITS) + < grub_be_to_cpu32 (sb.sb.total_size)); + ptr++) + if (!*ptr) + break; + } + data = grub_malloc (sizeof (*data)); + if (!data) + return NULL; + data->first_file = ALIGN_UP (ptr + 1 - sb.d, GRUB_ROMFS_ALIGN) + + (sec << GRUB_DISK_SECTOR_BITS); + data->disk = dev->disk; + return data; +} + +static char * +grub_romfs_read_symlink (grub_fshelp_node_t node) +{ + char *ret; + grub_err_t err; + ret = grub_malloc (grub_be_to_cpu32 (node->file.size) + 1); + if (!ret) + return NULL; + err = grub_disk_read (node->data->disk, + (node->data_addr) >> GRUB_DISK_SECTOR_BITS, + (node->data_addr) & (GRUB_DISK_SECTOR_SIZE - 1), + grub_be_to_cpu32 (node->file.size), ret); + if (err) + { + grub_free (ret); + return NULL; + } + ret[grub_be_to_cpu32 (node->file.size)] = 0; + return ret; +} + +static int +grub_romfs_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_disk_addr_t caddr; + struct grub_romfs_file_header hdr; + unsigned nptr; + unsigned i, j; + grub_size_t a = 0; + grub_properly_aligned_t *name = NULL; + + for (caddr = dir->data_addr; caddr; + caddr = grub_be_to_cpu32 (hdr.next_file) & ~(GRUB_ROMFS_ALIGN - 1)) + { + grub_disk_addr_t naddr = caddr + sizeof (hdr); + grub_uint32_t csum = 0; + enum grub_fshelp_filetype filetype = GRUB_FSHELP_UNKNOWN; + struct grub_fshelp_node *node = NULL; + grub_err_t err; + + err = grub_disk_read (dir->data->disk, caddr >> GRUB_DISK_SECTOR_BITS, + caddr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (hdr), &hdr); + if (err) + { + grub_free (name); + return 1; + } + for (nptr = 0; ; nptr++, naddr += 16) + { + if (a <= nptr) + { + grub_properly_aligned_t *on; + a = 2 * (nptr + 1); + on = name; + name = grub_realloc (name, a * 16); + if (!name) + { + grub_free (on); + return 1; + } + } + COMPILE_TIME_ASSERT (16 % sizeof (name[0]) == 0); + err = grub_disk_read (dir->data->disk, naddr >> GRUB_DISK_SECTOR_BITS, + naddr & (GRUB_DISK_SECTOR_SIZE - 1), + 16, name + (16 / sizeof (name[0])) * nptr); + if (err) + return 1; + for (j = 0; j < 16; j++) + if (!((char *) name)[16 * nptr + j]) + break; + if (j != 16) + break; + } + for (i = 0; i < sizeof (hdr) / sizeof (grub_uint32_t); i++) + csum += grub_be_to_cpu32 (((grub_uint32_t *) &hdr)[i]); + for (i = 0; i < (nptr + 1) * 4; i++) + csum += grub_be_to_cpu32 (((grub_uint32_t *) name)[i]); + if (csum != 0) + { + grub_error (GRUB_ERR_BAD_FS, "invalid checksum"); + grub_free (name); + return 1; + } + node = grub_malloc (sizeof (*node)); + if (!node) + return 1; + node->addr = caddr; + node->data_addr = caddr + (nptr + 1) * 16 + sizeof (hdr); + node->data = dir->data; + node->file = hdr; + switch (grub_be_to_cpu32 (hdr.next_file) & GRUB_ROMFS_TYPE_MASK) + { + case GRUB_ROMFS_TYPE_REGULAR: + filetype = GRUB_FSHELP_REG; + break; + case GRUB_ROMFS_TYPE_SYMLINK: + filetype = GRUB_FSHELP_SYMLINK; + break; + case GRUB_ROMFS_TYPE_DIRECTORY: + node->data_addr = grub_be_to_cpu32 (hdr.spec); + filetype = GRUB_FSHELP_DIR; + break; + case GRUB_ROMFS_TYPE_HARDLINK: + { + grub_disk_addr_t laddr; + node->addr = laddr = grub_be_to_cpu32 (hdr.spec); + err = grub_disk_read (dir->data->disk, + laddr >> GRUB_DISK_SECTOR_BITS, + laddr & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (node->file), &node->file); + if (err) + return 1; + if ((grub_be_to_cpu32 (node->file.next_file) & GRUB_ROMFS_TYPE_MASK) + == GRUB_ROMFS_TYPE_REGULAR + || (grub_be_to_cpu32 (node->file.next_file) + & GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_SYMLINK) + { + laddr += sizeof (hdr); + while (1) + { + char buf[16]; + err = grub_disk_read (dir->data->disk, + laddr >> GRUB_DISK_SECTOR_BITS, + laddr & (GRUB_DISK_SECTOR_SIZE - 1), + 16, buf); + if (err) + return 1; + for (i = 0; i < 16; i++) + if (!buf[i]) + break; + if (i != 16) + break; + laddr += 16; + } + node->data_addr = laddr + 16; + } + if ((grub_be_to_cpu32 (node->file.next_file) + & GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_REGULAR) + filetype = GRUB_FSHELP_REG; + if ((grub_be_to_cpu32 (node->file.next_file) + & GRUB_ROMFS_TYPE_MASK) == GRUB_ROMFS_TYPE_SYMLINK) + filetype = GRUB_FSHELP_SYMLINK; + if ((grub_be_to_cpu32 (node->file.next_file) & GRUB_ROMFS_TYPE_MASK) + == GRUB_ROMFS_TYPE_DIRECTORY) + { + node->data_addr = grub_be_to_cpu32 (node->file.spec); + filetype = GRUB_FSHELP_DIR; + } + + break; + } + } + + if (hook ((char *) name, filetype, node, hook_data)) + { + grub_free (name); + return 1; + } + } + grub_free (name); + return 0; +} + +/* Context for grub_romfs_dir. */ +struct grub_romfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_romfs_dir. */ +static int +grub_romfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_romfs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_romfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_romfs_dir_ctx ctx = { hook, hook_data }; + struct grub_romfs_data *data = 0; + struct grub_fshelp_node *fdiro = 0, start; + + data = grub_romfs_mount (device); + if (! data) + goto fail; + + start.addr = data->first_file; + start.data_addr = data->first_file; + start.data = data; + grub_fshelp_find_file (path, &start, &fdiro, grub_romfs_iterate_dir, + grub_romfs_read_symlink, GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_romfs_iterate_dir (fdiro, grub_romfs_dir_iter, &ctx); + + fail: + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_romfs_open (struct grub_file *file, const char *name) +{ + struct grub_romfs_data *data = 0; + struct grub_fshelp_node *fdiro = 0, start; + + data = grub_romfs_mount (file->device); + if (! data) + goto fail; + + start.addr = data->first_file; + start.data_addr = data->first_file; + start.data = data; + + grub_fshelp_find_file (name, &start, &fdiro, grub_romfs_iterate_dir, + grub_romfs_read_symlink, GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + file->size = grub_be_to_cpu32 (fdiro->file.size); + file->data = fdiro; + return GRUB_ERR_NONE; + + fail: + grub_free (data); + + return grub_errno; +} + +static grub_ssize_t +grub_romfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_fshelp_node *data = file->data; + + /* XXX: The file is stored in as a single extent. */ + data->data->disk->read_hook = file->read_hook; + data->data->disk->read_hook_data = file->read_hook_data; + grub_disk_read (data->data->disk, + (data->data_addr + file->offset) >> GRUB_DISK_SECTOR_BITS, + (data->data_addr + file->offset) & (GRUB_DISK_SECTOR_SIZE - 1), + len, buf); + data->data->disk->read_hook = NULL; + + if (grub_errno) + return -1; + + return len; +} + +static grub_err_t +grub_romfs_close (grub_file_t file) +{ + struct grub_fshelp_node *data = file->data; + + grub_free (data->data); + grub_free (data); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_romfs_label (grub_device_t device, char **label) +{ + struct grub_romfs_data *data; + grub_err_t err; + + *label = NULL; + + data = grub_romfs_mount (device); + if (!data) + return grub_errno; + *label = grub_malloc (data->first_file + 1 + - sizeof (struct grub_romfs_superblock)); + if (!*label) + { + grub_free (data); + return grub_errno; + } + err = grub_disk_read (device->disk, 0, sizeof (struct grub_romfs_superblock), + data->first_file + - sizeof (struct grub_romfs_superblock), + *label); + if (err) + { + grub_free (data); + grub_free (*label); + *label = NULL; + return err; + } + (*label)[data->first_file - sizeof (struct grub_romfs_superblock)] = 0; + grub_free (data); + return GRUB_ERR_NONE; +} + + +static struct grub_fs grub_romfs_fs = + { + .name = "romfs", + .fs_dir = grub_romfs_dir, + .fs_open = grub_romfs_open, + .fs_read = grub_romfs_read, + .fs_close = grub_romfs_close, + .fs_label = grub_romfs_label, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(romfs) +{ + if (!grub_is_lockdown ()) + { + grub_romfs_fs.mod = mod; + grub_fs_register (&grub_romfs_fs); + } +} + +GRUB_MOD_FINI(romfs) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_romfs_fs); +} diff --git a/fs/sfs.c b/grub-core/fs/sfs.c similarity index 52% rename from fs/sfs.c rename to grub-core/fs/sfs.c index 68f8b3a6e..bad4ae8d1 100644 --- a/fs/sfs.c +++ b/grub-core/fs/sfs.c @@ -25,6 +25,11 @@ #include #include #include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); /* The common header for a block. */ struct grub_sfs_bheader @@ -32,20 +37,27 @@ struct grub_sfs_bheader grub_uint8_t magic[4]; grub_uint32_t chksum; grub_uint32_t ipointtomyself; -} __attribute__ ((packed)); +} GRUB_PACKED; /* The sfs rootblock. */ struct grub_sfs_rblock { struct grub_sfs_bheader header; grub_uint32_t version; - grub_uint8_t unused1[36]; + grub_uint32_t createtime; + grub_uint8_t flags; + grub_uint8_t unused1[31]; grub_uint32_t blocksize; grub_uint8_t unused2[40]; grub_uint8_t unused3[8]; grub_uint32_t rootobject; grub_uint32_t btree; -} __attribute__ ((packed)); +} GRUB_PACKED; + +enum + { + FLAGS_CASE_SENSITIVE = 0x80 + }; /* A SFS object container. */ struct grub_sfs_obj @@ -59,18 +71,18 @@ struct grub_sfs_obj { grub_uint32_t first_block; grub_uint32_t size; - } file __attribute__ ((packed)); + } GRUB_PACKED file; struct { grub_uint32_t hashtable; grub_uint32_t dir_objc; - } dir __attribute__ ((packed)); + } GRUB_PACKED dir; } file_dir; - grub_uint8_t unused3[4]; + grub_uint32_t mtime; grub_uint8_t type; grub_uint8_t filename[1]; grub_uint8_t comment[1]; -} __attribute__ ((packed)); +} GRUB_PACKED; #define GRUB_SFS_TYPE_DELETED 32 #define GRUB_SFS_TYPE_SYMLINK 64 @@ -85,13 +97,13 @@ struct grub_sfs_objc grub_uint32_t prev; /* The amount of objects depends on the blocksize. */ struct grub_sfs_obj objects[1]; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_sfs_btree_node { grub_uint32_t key; grub_uint32_t data; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_sfs_btree_extent { @@ -99,7 +111,7 @@ struct grub_sfs_btree_extent grub_uint32_t next; grub_uint32_t prev; grub_uint16_t size; -} __attribute__ ((packed)); +} GRUB_PACKED; struct grub_sfs_btree { @@ -110,15 +122,27 @@ struct grub_sfs_btree /* Normally this can be kind of node, but just extents are supported. */ struct grub_sfs_btree_node node[1]; -} __attribute__ ((packed)); +} GRUB_PACKED; +struct cache_entry +{ + grub_uint32_t off; + grub_uint32_t block; +}; + struct grub_fshelp_node { struct grub_sfs_data *data; - int block; - int size; + grub_uint32_t block; + grub_uint32_t size; + grub_uint32_t mtime; + grub_uint32_t cache_off; + grub_uint32_t next_extent; + grub_size_t cache_allocated; + grub_size_t cache_size; + struct cache_entry *cache; }; /* Information about a "mounted" sfs filesystem. */ @@ -128,8 +152,10 @@ struct grub_sfs_data struct grub_fshelp_node diropen; grub_disk_t disk; - /* Blocksize in sectors. */ - unsigned int blocksize; + /* Log of blocksize in sectors. */ + int log_blocksize; + + int fshelp_flags; /* Label of the filesystem. */ char *label; @@ -143,17 +169,17 @@ static grub_dl_t my_mod; in NEXTEXT. */ static grub_err_t grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block, - int *size, int *nextext) + grub_uint32_t *size, grub_uint32_t *nextext) { char *treeblock; struct grub_sfs_btree *tree; int i; - int next; - int prev; + grub_uint32_t next; + grub_size_t blocksize = GRUB_DISK_SECTOR_SIZE << data->log_blocksize; - treeblock = grub_malloc (data->blocksize); - if (!block) - return 0; + treeblock = grub_malloc (blocksize); + if (!treeblock) + return grub_errno; next = grub_be_to_cpu32 (data->rblock.btree); tree = (struct grub_sfs_btree *) treeblock; @@ -161,16 +187,21 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block, /* Handle this level in the btree. */ do { - prev = 0; - - grub_disk_read (data->disk, next, 0, data->blocksize, treeblock); + grub_uint16_t nnodes; + grub_disk_read (data->disk, + ((grub_disk_addr_t) next) << data->log_blocksize, + 0, blocksize, treeblock); if (grub_errno) { grub_free (treeblock); return grub_errno; } - for (i = grub_be_to_cpu16 (tree->nodes) - 1; i >= 0; i--) + nnodes = grub_be_to_cpu16 (tree->nodes); + if (nnodes * (grub_uint32_t) (tree)->nodesize > blocksize) + break; + + for (i = (int) nnodes - 1; i >= 0; i--) { #define EXTNODE(tree, index) \ @@ -212,27 +243,105 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block, static grub_disk_addr_t grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { - int blk = node->block; - int size = 0; - int next = 0; + grub_uint32_t blk; + grub_uint32_t size = 0; + grub_uint32_t next = 0; + grub_disk_addr_t off; + struct grub_sfs_data *data = node->data; + + /* In case of the first block we don't have to lookup the + extent, the minimum size is always 1. */ + if (fileblock == 0) + return node->block; + + if (!node->cache) + { + grub_size_t cache_size; + /* Assume half-max extents (32768 sectors). */ + cache_size = ((node->size >> (data->log_blocksize + GRUB_DISK_SECTOR_BITS + + 15)) + + 3); + if (cache_size < 8) + cache_size = 8; + + node->cache_off = 0; + node->next_extent = node->block; + node->cache_size = 0; + + node->cache = grub_calloc (cache_size, sizeof (node->cache[0])); + if (!node->cache) + { + grub_errno = 0; + node->cache_allocated = 0; + } + else + { + node->cache_allocated = cache_size; + node->cache[0].off = 0; + node->cache[0].block = node->block; + } + } + + if (fileblock < node->cache_off) + { + unsigned int i = 0; + int j, lg; + for (lg = 0; node->cache_size >> lg; lg++); + + for (j = lg - 1; j >= 0; j--) + if ((i | (1 << j)) < node->cache_size + && node->cache[(i | (1 << j))].off <= fileblock) + i |= (1 << j); + return node->cache[i].block + fileblock - node->cache[i].off; + } + + off = node->cache_off; + blk = node->next_extent; while (blk) { grub_err_t err; - /* In case of the first block we don't have to lookup the - extent, the minimum size is always 1. */ - if (fileblock == 0) - return blk; - err = grub_sfs_read_extent (node->data, blk, &size, &next); if (err) return 0; - if (fileblock < (unsigned int) size) - return fileblock + blk; + if (node->cache && node->cache_size >= node->cache_allocated) + { + struct cache_entry *e = node->cache; + grub_size_t sz; - fileblock -= size; + if (grub_mul (node->cache_allocated, 2 * sizeof (e[0]), &sz)) + goto fail; + + e = grub_realloc (node->cache, sz); + if (!e) + { + fail: + grub_errno = 0; + grub_free (node->cache); + node->cache = 0; + } + else + { + node->cache_allocated *= 2; + node->cache = e; + } + } + + if (node->cache) + { + node->cache_off = off + size; + node->next_extent = next; + node->cache[node->cache_size].off = off; + node->cache[node->cache_size].block = blk; + node->cache_size++; + } + + if (fileblock - off < size) + return fileblock - off + blk; + + off += size; blk = next; } @@ -248,13 +357,13 @@ grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_sfs_read_file (grub_fshelp_node_t node, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) { - return grub_fshelp_read_file (node->data->disk, node, read_hook, + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, pos, len, buf, grub_sfs_read_block, - node->size, 0); + node->size, node->data->log_blocksize, 0); } @@ -264,7 +373,8 @@ grub_sfs_mount (grub_disk_t disk) struct grub_sfs_data *data; struct grub_sfs_objc *rootobjc; char *rootobjc_data = 0; - unsigned int blk; + grub_uint32_t blk; + unsigned int max_len; data = grub_malloc (sizeof (*data)); if (!data) @@ -277,20 +387,31 @@ grub_sfs_mount (grub_disk_t disk) goto fail; /* Make sure this is a sfs filesystem. */ - if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4)) + if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4) + || data->rblock.blocksize == 0 + || (data->rblock.blocksize & (data->rblock.blocksize - 1)) != 0 + || (data->rblock.blocksize & grub_cpu_to_be32_compile_time (0xf00001ff))) { grub_error (GRUB_ERR_BAD_FS, "not a SFS filesystem"); goto fail; } - data->blocksize = grub_be_to_cpu32 (data->rblock.blocksize); - rootobjc_data = grub_malloc (data->blocksize); + for (data->log_blocksize = 9; + (1U << data->log_blocksize) < grub_be_to_cpu32 (data->rblock.blocksize); + data->log_blocksize++); + data->log_blocksize -= GRUB_DISK_SECTOR_BITS; + if (data->rblock.flags & FLAGS_CASE_SENSITIVE) + data->fshelp_flags = 0; + else + data->fshelp_flags = GRUB_FSHELP_CASE_INSENSITIVE; + rootobjc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize); if (! rootobjc_data) goto fail; /* Read the root object container. */ - grub_disk_read (disk, grub_be_to_cpu32 (data->rblock.rootobject), 0, - data->blocksize, rootobjc_data); + grub_disk_read (disk, ((grub_disk_addr_t) grub_be_to_cpu32 (data->rblock.rootobject)) + << data->log_blocksize, 0, + GRUB_DISK_SECTOR_SIZE << data->log_blocksize, rootobjc_data); if (grub_errno) goto fail; @@ -300,9 +421,20 @@ grub_sfs_mount (grub_disk_t disk) data->diropen.size = 0; data->diropen.block = blk; data->diropen.data = data; + data->diropen.cache = 0; data->disk = disk; - data->label = grub_strdup ((char *) (rootobjc->objects[0].filename)); + /* We only read 1 block of data, so truncate the name if needed. */ + max_len = ((GRUB_DISK_SECTOR_SIZE << data->log_blocksize) + - 24 /* offsetof (struct grub_sfs_objc, objects) */ + - 25); /* offsetof (struct grub_sfs_obj, filename) */ + data->label = grub_zalloc (max_len + 1); + if (data->label == NULL) + goto fail; + + grub_strncpy (data->label, (char *) rootobjc->objects[0].filename, max_len); + + grub_free (rootobjc_data); return data; fail: @@ -322,11 +454,13 @@ grub_sfs_read_symlink (grub_fshelp_node_t node) char *symlink; char *block; - block = grub_malloc (data->blocksize); + block = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize); if (!block) return 0; - grub_disk_read (data->disk, node->block, 0, data->blocksize, block); + grub_disk_read (data->disk, ((grub_disk_addr_t) node->block) + << data->log_blocksize, + 0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize, block); if (grub_errno) { grub_free (block); @@ -335,46 +469,76 @@ grub_sfs_read_symlink (grub_fshelp_node_t node) /* This is just a wild guess, but it always worked for me. How the SLNK block looks like is not documented in the SFS docs. */ - symlink = grub_strdup (&block[24]); - grub_free (block); + symlink = grub_malloc (((GRUB_DISK_SECTOR_SIZE << data->log_blocksize) + - 24) * GRUB_MAX_UTF8_PER_LATIN1 + 1); if (!symlink) - return 0; - + { + grub_free (block); + return 0; + } + *grub_latin1_to_utf8 ((grub_uint8_t *) symlink, (grub_uint8_t *) &block[24], + (GRUB_DISK_SECTOR_SIZE << data->log_blocksize) - 24) = '\0'; + grub_free (block); return symlink; } +/* Helper for grub_sfs_iterate_dir. */ +static int +grub_sfs_create_node (struct grub_fshelp_node **node, + struct grub_sfs_data *data, + const char *name, + grub_uint32_t block, grub_uint32_t size, int type, + grub_uint32_t mtime, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_size_t len = grub_strlen (name); + grub_uint8_t *name_u8; + int ret; + grub_size_t sz; + + if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) || + grub_add (sz, 1, &sz)) + return 1; + + *node = grub_malloc (sizeof (**node)); + if (!*node) + return 1; + name_u8 = grub_malloc (sz); + if (!name_u8) + { + grub_free (*node); + return 1; + } + + (*node)->data = data; + (*node)->size = size; + (*node)->block = block; + (*node)->mtime = mtime; + (*node)->cache = 0; + (*node)->cache_off = 0; + (*node)->next_extent = block; + (*node)->cache_size = 0; + (*node)->cache_allocated = 0; + + *grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0'; + + ret = hook ((char *) name_u8, type | data->fshelp_flags, *node, hook_data); + grub_free (name_u8); + return ret; +} + static int grub_sfs_iterate_dir (grub_fshelp_node_t dir, - int NESTED_FUNC_ATTR - (*hook) (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node)) + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) { struct grub_fshelp_node *node = 0; struct grub_sfs_data *data = dir->data; char *objc_data; struct grub_sfs_objc *objc; unsigned int next = dir->block; - int pos; + grub_uint32_t pos; - auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block, - int size, int type); - - int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name, int block, - int size, int type) - { - node = grub_malloc (sizeof (*node)); - if (!node) - return 1; - - node->data = data; - node->size = size; - node->block = block; - - return hook (name, type, node); - } - - objc_data = grub_malloc (data->blocksize); + objc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize); if (!objc_data) goto fail; @@ -382,7 +546,9 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir, every block. */ while (next) { - grub_disk_read (data->disk, next, 0, data->blocksize, objc_data); + grub_disk_read (data->disk, ((grub_disk_addr_t) next) + << data->log_blocksize, 0, + GRUB_DISK_SECTOR_SIZE << data->log_blocksize, objc_data); if (grub_errno) goto fail; @@ -391,14 +557,15 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir, pos = (char *) &objc->objects[0] - (char *) objc; /* Iterate over all entries in this block. */ - while (pos + sizeof (struct grub_sfs_obj) < data->blocksize) + while (pos + sizeof (struct grub_sfs_obj) + < (1U << (GRUB_DISK_SECTOR_BITS + data->log_blocksize))) { struct grub_sfs_obj *obj; obj = (struct grub_sfs_obj *) ((char *) objc + pos); - char *filename = (char *) (obj->filename); - int len; + const char *filename = (const char *) obj->filename; + grub_size_t len; enum grub_fshelp_filetype type; - unsigned int block; + grub_uint32_t block; /* The filename and comment dynamically increase the size of the object. */ @@ -409,7 +576,7 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir, /* Round up to a multiple of two bytes. */ pos = ((pos + 1) >> 1) << 1; - if (grub_strlen (filename) == 0) + if (filename[0] == 0) continue; /* First check if the file was not deleted. */ @@ -427,9 +594,10 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir, else block = grub_be_to_cpu32 (obj->file_dir.file.first_block); - if (grub_sfs_create_node (filename, block, + if (grub_sfs_create_node (&node, data, filename, block, grub_be_to_cpu32 (obj->file_dir.file.size), - type)) + type, grub_be_to_cpu32 (obj->mtime), + hook, hook_data)) { grub_free (objc_data); return 1; @@ -488,7 +656,11 @@ grub_sfs_open (struct grub_file *file, const char *name) static grub_err_t grub_sfs_close (grub_file_t file) { - grub_free (file->data); + struct grub_sfs_data *data = (struct grub_sfs_data *) file->data; + + grub_free (data->diropen.cache); + grub_free (data->label); + grub_free (data); grub_dl_unref (my_mod); @@ -502,36 +674,44 @@ grub_sfs_read (grub_file_t file, char *buf, grub_size_t len) { struct grub_sfs_data *data = (struct grub_sfs_data *) file->data; - int size = grub_sfs_read_file (&data->diropen, file->read_hook, - file->offset, len, buf); - - return size; + return grub_sfs_read_file (&data->diropen, + file->read_hook, file->read_hook_data, + file->offset, len, buf); } +/* Context for grub_sfs_dir. */ +struct grub_sfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_sfs_dir. */ +static int +grub_sfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_sfs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2; + info.mtimeset = 1; + grub_free (node->cache); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + static grub_err_t grub_sfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { + struct grub_sfs_dir_ctx ctx = { hook, hook_data }; struct grub_sfs_data *data = 0; struct grub_fshelp_node *fdiro = 0; - auto int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node); - - int NESTED_FUNC_ATTR iterate (const char *filename, - enum grub_fshelp_filetype filetype, - grub_fshelp_node_t node) - { - struct grub_dirhook_info info; - grub_memset (&info, 0, sizeof (info)); - info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); - grub_free (node); - return hook (filename, &info); - } - grub_dl_ref (my_mod); data = grub_sfs_mount (device->disk); @@ -543,7 +723,7 @@ grub_sfs_dir (grub_device_t device, const char *path, if (grub_errno) goto fail; - grub_sfs_iterate_dir (fdiro, iterate); + grub_sfs_iterate_dir (fdiro, grub_sfs_dir_iter, &ctx); fail: if (data && fdiro != &data->diropen) @@ -566,8 +746,20 @@ grub_sfs_label (grub_device_t device, char **label) data = grub_sfs_mount (disk); if (data) - *label = data->label; + { + grub_size_t sz, len = grub_strlen (data->label); + if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) || + grub_add (sz, 1, &sz)) + return GRUB_ERR_OUT_OF_RANGE; + + *label = grub_malloc (sz); + if (*label) + *grub_latin1_to_utf8 ((grub_uint8_t *) *label, + (const grub_uint8_t *) data->label, + len) = '\0'; + grub_free (data->label); + } grub_free (data); return grub_errno; @@ -577,21 +769,30 @@ grub_sfs_label (grub_device_t device, char **label) static struct grub_fs grub_sfs_fs = { .name = "sfs", - .dir = grub_sfs_dir, - .open = grub_sfs_open, - .read = grub_sfs_read, - .close = grub_sfs_close, - .label = grub_sfs_label, + .fs_dir = grub_sfs_dir, + .fs_open = grub_sfs_open, + .fs_read = grub_sfs_read, + .fs_close = grub_sfs_close, + .fs_label = grub_sfs_label, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 1, +#endif .next = 0 }; GRUB_MOD_INIT(sfs) { - grub_fs_register (&grub_sfs_fs); + if (!grub_is_lockdown ()) + { + grub_sfs_fs.mod = mod; + grub_fs_register (&grub_sfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(sfs) { - grub_fs_unregister (&grub_sfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_sfs_fs); } diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c new file mode 100644 index 000000000..cf2bca822 --- /dev/null +++ b/grub-core/fs/squash4.c @@ -0,0 +1,1061 @@ +/* squash4.c - SquashFS */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xz.h" +#include "xz_stream.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + object format Pointed by + superblock RAW Fixed offset (0) + data RAW ? Fixed offset (60) + inode table Chunk superblock + dir table Chunk superblock + fragment table Chunk unk1 + unk1 RAW, Chunk superblock + unk2 RAW superblock + UID/GID Chunk exttblptr + exttblptr RAW superblock + + UID/GID table is the array ot uint32_t + unk1 contains pointer to fragment table followed by some chunk. + unk2 containts one uint64_t +*/ + +struct grub_squash_super +{ + grub_uint32_t magic; +#define SQUASH_MAGIC 0x73717368 + grub_uint32_t dummy1; + grub_uint32_t creation_time; + grub_uint32_t block_size; + grub_uint32_t dummy2; + grub_uint16_t compression; + grub_uint16_t dummy3; + grub_uint64_t dummy4; + grub_uint16_t root_ino_offset; + grub_uint32_t root_ino_chunk; + grub_uint16_t dummy5; + grub_uint64_t total_size; + grub_uint64_t exttbloffset; + grub_uint64_t dummy6; + grub_uint64_t inodeoffset; + grub_uint64_t diroffset; + grub_uint64_t unk1offset; + grub_uint64_t unk2offset; +} GRUB_PACKED; + +/* Chunk-based */ +struct grub_squash_inode +{ + /* Same values as direlem types. */ + grub_uint16_t type; + grub_uint16_t dummy[3]; + grub_uint32_t mtime; + grub_uint32_t dummy2; + union + { + struct { + grub_uint32_t chunk; + grub_uint32_t fragment; + grub_uint32_t offset; + grub_uint32_t size; + grub_uint32_t block_size[0]; + } GRUB_PACKED file; + struct { + grub_uint64_t chunk; + grub_uint64_t size; + grub_uint32_t dummy1[3]; + grub_uint32_t fragment; + grub_uint32_t offset; + grub_uint32_t dummy3; + grub_uint32_t block_size[0]; + } GRUB_PACKED long_file; + struct { + grub_uint32_t chunk; + grub_uint32_t dummy; + grub_uint16_t size; + grub_uint16_t offset; + } GRUB_PACKED dir; + struct { + grub_uint32_t dummy1; + grub_uint32_t size; + grub_uint32_t chunk; + grub_uint32_t dummy2; + grub_uint16_t dummy3; + grub_uint16_t offset; + } GRUB_PACKED long_dir; + struct { + grub_uint32_t dummy; + grub_uint32_t namelen; + char name[0]; + } GRUB_PACKED symlink; + } GRUB_PACKED; +} GRUB_PACKED; + +struct grub_squash_cache_inode +{ + struct grub_squash_inode ino; + grub_disk_addr_t ino_chunk; + grub_uint16_t ino_offset; + grub_uint32_t *block_sizes; + grub_disk_addr_t *cumulated_block_sizes; +}; + +/* Chunk-based. */ +struct grub_squash_dirent_header +{ + /* Actually the value is the number of elements - 1. */ + grub_uint32_t nelems; + grub_uint32_t ino_chunk; + grub_uint32_t dummy; +} GRUB_PACKED; + +struct grub_squash_dirent +{ + grub_uint16_t ino_offset; + grub_uint16_t dummy; + grub_uint16_t type; + /* Actually the value is the length of name - 1. */ + grub_uint16_t namelen; + char name[0]; +} GRUB_PACKED; + +enum + { + SQUASH_TYPE_DIR = 1, + SQUASH_TYPE_REGULAR = 2, + SQUASH_TYPE_SYMLINK = 3, + SQUASH_TYPE_LONG_DIR = 8, + SQUASH_TYPE_LONG_REGULAR = 9, + }; + + +struct grub_squash_frag_desc +{ + grub_uint64_t offset; + grub_uint32_t size; + grub_uint32_t dummy; +} GRUB_PACKED; + +enum + { + SQUASH_CHUNK_FLAGS = 0x8000, + SQUASH_CHUNK_UNCOMPRESSED = 0x8000 + }; + +enum + { + SQUASH_BLOCK_FLAGS = 0x1000000, + SQUASH_BLOCK_UNCOMPRESSED = 0x1000000 + }; + +enum + { + COMPRESSION_ZLIB = 1, + COMPRESSION_LZO = 3, + COMPRESSION_XZ = 4, + }; + + +#define SQUASH_CHUNK_SIZE 0x2000 +#define XZBUFSIZ 0x2000 + +struct grub_squash_data +{ + grub_disk_t disk; + struct grub_squash_super sb; + struct grub_squash_cache_inode ino; + grub_uint64_t fragments; + int log2_blksz; + grub_size_t blksz; + grub_ssize_t (*decompress) (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize, + struct grub_squash_data *data); + struct xz_dec *xzdec; + char *xzbuf; +}; + +struct grub_fshelp_node +{ + struct grub_squash_data *data; + struct grub_squash_inode ino; + grub_size_t stsize; + struct + { + grub_disk_addr_t ino_chunk; + grub_uint16_t ino_offset; + } stack[1]; +}; + +static grub_err_t +read_chunk (struct grub_squash_data *data, void *buf, grub_size_t len, + grub_uint64_t chunk_start, grub_off_t offset) +{ + while (len > 0) + { + grub_uint64_t csize; + grub_uint16_t d; + grub_err_t err; + while (1) + { + err = grub_disk_read (data->disk, + chunk_start >> GRUB_DISK_SECTOR_BITS, + chunk_start & (GRUB_DISK_SECTOR_SIZE - 1), + sizeof (d), &d); + if (err) + return err; + if (offset < SQUASH_CHUNK_SIZE) + break; + offset -= SQUASH_CHUNK_SIZE; + chunk_start += 2 + (grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS); + } + + csize = SQUASH_CHUNK_SIZE - offset; + if (csize > len) + csize = len; + + if (grub_le_to_cpu16 (d) & SQUASH_CHUNK_UNCOMPRESSED) + { + grub_disk_addr_t a = chunk_start + 2 + offset; + err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS), + a & (GRUB_DISK_SECTOR_SIZE - 1), + csize, buf); + if (err) + return err; + } + else + { + char *tmp; + grub_size_t bsize = grub_le_to_cpu16 (d) & ~SQUASH_CHUNK_FLAGS; + grub_disk_addr_t a = chunk_start + 2; + tmp = grub_malloc (bsize); + if (!tmp) + return grub_errno; + /* FIXME: buffer uncompressed data. */ + err = grub_disk_read (data->disk, (a >> GRUB_DISK_SECTOR_BITS), + a & (GRUB_DISK_SECTOR_SIZE - 1), + bsize, tmp); + if (err) + { + grub_free (tmp); + return err; + } + + if (data->decompress (tmp, bsize, offset, + buf, csize, data) < 0) + { + grub_free (tmp); + return grub_errno; + } + grub_free (tmp); + } + len -= csize; + offset += csize; + buf = (char *) buf + csize; + } + return GRUB_ERR_NONE; +} + +static grub_ssize_t +zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize, + struct grub_squash_data *data __attribute__ ((unused))) +{ + return grub_zlib_decompress (inbuf, insize, off, outbuf, outsize); +} + +static grub_ssize_t +lzo_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t len, struct grub_squash_data *data) +{ + lzo_uint usize = data->blksz; + grub_uint8_t *udata; + + if (usize < 8192) + usize = 8192; + + udata = grub_malloc (usize); + if (!udata) + return -1; + + if (lzo1x_decompress_safe ((grub_uint8_t *) inbuf, + insize, udata, &usize, NULL) != LZO_E_OK) + { + grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk"); + grub_free (udata); + return -1; + } + grub_memcpy (outbuf, udata + off, len); + grub_free (udata); + return len; +} + +static grub_ssize_t +xz_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t len, struct grub_squash_data *data) +{ + grub_size_t ret = 0; + grub_off_t pos = 0; + struct xz_buf buf; + + xz_dec_reset (data->xzdec); + buf.in = (grub_uint8_t *) inbuf; + buf.in_pos = 0; + buf.in_size = insize; + buf.out = (grub_uint8_t *) data->xzbuf; + buf.out_pos = 0; + buf.out_size = XZBUFSIZ; + + while (len) + { + enum xz_ret xzret; + + buf.out_pos = 0; + + xzret = xz_dec_run (data->xzdec, &buf); + + if (xzret != XZ_OK && xzret != XZ_STREAM_END) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "invalid xz chunk"); + return -1; + } + if (pos + buf.out_pos >= off) + { + grub_ssize_t outoff = pos - off; + grub_size_t l; + if (outoff >= 0) + { + l = buf.out_pos; + if (l > len) + l = len; + grub_memcpy (outbuf + outoff, buf.out, l); + } + else + { + outoff = -outoff; + l = buf.out_pos - outoff; + if (l > len) + l = len; + grub_memcpy (outbuf, buf.out + outoff, l); + } + ret += l; + len -= l; + } + pos += buf.out_pos; + if (xzret == XZ_STREAM_END) + break; + } + return ret; +} + +static struct grub_squash_data * +squash_mount (grub_disk_t disk) +{ + struct grub_squash_super sb; + grub_err_t err; + struct grub_squash_data *data; + grub_uint64_t frag; + + err = grub_disk_read (disk, 0, 0, sizeof (sb), &sb); + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a squash4"); + if (err) + return NULL; + if (sb.magic != grub_cpu_to_le32_compile_time (SQUASH_MAGIC) + || sb.block_size == 0 + || ((sb.block_size - 1) & sb.block_size)) + { + grub_error (GRUB_ERR_BAD_FS, "not squash4"); + return NULL; + } + + err = grub_disk_read (disk, + grub_le_to_cpu64 (sb.unk1offset) + >> GRUB_DISK_SECTOR_BITS, + grub_le_to_cpu64 (sb.unk1offset) + & (GRUB_DISK_SECTOR_SIZE - 1), sizeof (frag), &frag); + if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + grub_error (GRUB_ERR_BAD_FS, "not a squash4"); + if (err) + return NULL; + + data = grub_zalloc (sizeof (*data)); + if (!data) + return NULL; + data->sb = sb; + data->disk = disk; + data->fragments = grub_le_to_cpu64 (frag); + + switch (sb.compression) + { + case grub_cpu_to_le16_compile_time (COMPRESSION_ZLIB): + data->decompress = zlib_decompress; + break; + case grub_cpu_to_le16_compile_time (COMPRESSION_LZO): + data->decompress = lzo_decompress; + break; + case grub_cpu_to_le16_compile_time (COMPRESSION_XZ): + data->decompress = xz_decompress; + data->xzbuf = grub_malloc (XZBUFSIZ); + if (!data->xzbuf) + { + grub_free (data); + return NULL; + } + data->xzdec = xz_dec_init (1 << 16); + if (!data->xzdec) + { + grub_free (data->xzbuf); + grub_free (data); + return NULL; + } + break; + default: + grub_free (data); + grub_error (GRUB_ERR_BAD_FS, "unsupported compression %d", + grub_le_to_cpu16 (sb.compression)); + return NULL; + } + + data->blksz = grub_le_to_cpu32 (data->sb.block_size); + for (data->log2_blksz = 0; + (1U << data->log2_blksz) < data->blksz; + data->log2_blksz++); + + return data; +} + +static char * +grub_squash_read_symlink (grub_fshelp_node_t node) +{ + char *ret; + grub_err_t err; + grub_uint32_t sz; + + if (grub_add (grub_le_to_cpu32 (node->ino.symlink.namelen), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink name length overflow")); + return NULL; + } + + ret = grub_malloc (sz); + if (!ret) + return NULL; + + err = read_chunk (node->data, ret, + grub_le_to_cpu32 (node->ino.symlink.namelen), + grub_le_to_cpu64 (node->data->sb.inodeoffset) + + node->stack[node->stsize - 1].ino_chunk, + node->stack[node->stsize - 1].ino_offset + + (node->ino.symlink.name - (char *) &node->ino)); + if (err) + { + grub_free (ret); + return NULL; + } + ret[grub_le_to_cpu32 (node->ino.symlink.namelen)] = 0; + return ret; +} + +static int +grub_squash_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_uint32_t off; + grub_uint32_t endoff; + grub_uint64_t chunk; + unsigned i; + + /* FIXME: why - 3 ? */ + switch (dir->ino.type) + { + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_DIR): + off = grub_le_to_cpu16 (dir->ino.dir.offset); + endoff = grub_le_to_cpu16 (dir->ino.dir.size) + off - 3; + chunk = grub_le_to_cpu32 (dir->ino.dir.chunk); + break; + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_DIR): + off = grub_le_to_cpu16 (dir->ino.long_dir.offset); + endoff = grub_le_to_cpu16 (dir->ino.long_dir.size) + off - 3; + chunk = grub_le_to_cpu32 (dir->ino.long_dir.chunk); + break; + default: + grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", + grub_le_to_cpu16 (dir->ino.type)); + return 0; + } + + { + grub_fshelp_node_t node; + grub_size_t sz; + + if (grub_mul (dir->stsize, sizeof (dir->stack[0]), &sz) || + grub_add (sz, sizeof (*node), &sz)) + return 0; + + node = grub_malloc (sz); + if (!node) + return 0; + grub_memcpy (node, dir, sz); + if (hook (".", GRUB_FSHELP_DIR, node, hook_data)) + return 1; + + if (dir->stsize != 1) + { + grub_err_t err; + + if (grub_mul (dir->stsize, sizeof (dir->stack[0]), &sz) || + grub_add (sz, sizeof (*node), &sz)) + return 0; + + node = grub_malloc (sz); + if (!node) + return 0; + + grub_memcpy (node, dir, sz); + + node->stsize--; + err = read_chunk (dir->data, &node->ino, sizeof (node->ino), + grub_le_to_cpu64 (dir->data->sb.inodeoffset) + + node->stack[node->stsize - 1].ino_chunk, + node->stack[node->stsize - 1].ino_offset); + if (err) + { + grub_free (node); + return 0; + } + + if (hook ("..", GRUB_FSHELP_DIR, node, hook_data)) + return 1; + } + } + + while (off < endoff) + { + struct grub_squash_dirent_header dh; + grub_err_t err; + + err = read_chunk (dir->data, &dh, sizeof (dh), + grub_le_to_cpu64 (dir->data->sb.diroffset) + + chunk, off); + if (err) + return 0; + off += sizeof (dh); + for (i = 0; i < (unsigned) grub_le_to_cpu32 (dh.nelems) + 1; i++) + { + char *buf; + int r; + struct grub_fshelp_node *node; + enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG; + struct grub_squash_dirent di; + struct grub_squash_inode ino; + grub_size_t sz; + grub_uint16_t nlen; + + err = read_chunk (dir->data, &di, sizeof (di), + grub_le_to_cpu64 (dir->data->sb.diroffset) + + chunk, off); + if (err) + return 0; + off += sizeof (di); + + err = read_chunk (dir->data, &ino, sizeof (ino), + grub_le_to_cpu64 (dir->data->sb.inodeoffset) + + grub_le_to_cpu32 (dh.ino_chunk), + grub_cpu_to_le16 (di.ino_offset)); + if (err) + return 0; + + if (grub_add (grub_le_to_cpu16 (di.namelen), 2, &nlen)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + return 0; + } + buf = grub_malloc (nlen); + if (!buf) + return 0; + err = read_chunk (dir->data, buf, + grub_le_to_cpu16 (di.namelen) + 1, + grub_le_to_cpu64 (dir->data->sb.diroffset) + + chunk, off); + if (err) + { + grub_free (buf); + return 0; + } + + off += grub_le_to_cpu16 (di.namelen) + 1; + buf[grub_le_to_cpu16 (di.namelen) + 1] = 0; + if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_DIR) + filetype = GRUB_FSHELP_DIR; + if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK) + filetype = GRUB_FSHELP_SYMLINK; + + if (grub_add (dir->stsize, 1, &sz) || + grub_mul (sz, sizeof (dir->stack[0]), &sz) || + grub_add (sz, sizeof (*node), &sz)) + { + grub_free (buf); + return 0; + } + + node = grub_malloc (sz); + if (! node) + { + grub_free (buf); + return 0; + } + + grub_memcpy (node, dir, sz - sizeof(dir->stack[0])); + + node->ino = ino; + node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk); + node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset); + node->stsize++; + r = hook (buf, filetype, node, hook_data); + + grub_free (buf); + if (r) + return r; + } + } + return 0; +} + +static grub_err_t +make_root_node (struct grub_squash_data *data, struct grub_fshelp_node *root) +{ + grub_memset (root, 0, sizeof (*root)); + root->data = data; + root->stsize = 1; + root->stack[0].ino_chunk = grub_le_to_cpu32 (data->sb.root_ino_chunk); + root->stack[0].ino_offset = grub_cpu_to_le16 (data->sb.root_ino_offset); + return read_chunk (data, &root->ino, sizeof (root->ino), + grub_le_to_cpu64 (data->sb.inodeoffset) + + root->stack[0].ino_chunk, + root->stack[0].ino_offset); +} + +static void +squash_unmount (struct grub_squash_data *data) +{ + if (data->xzdec) + xz_dec_end (data->xzdec); + grub_free (data->xzbuf); + grub_free (data->ino.cumulated_block_sizes); + grub_free (data->ino.block_sizes); + grub_free (data); +} + + +/* Context for grub_squash_dir. */ +struct grub_squash_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_squash_dir. */ +static int +grub_squash_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_squash_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + info.mtimeset = 1; + info.mtime = grub_le_to_cpu32 (node->ino.mtime); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_squash_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_squash_dir_ctx ctx = { hook, hook_data }; + struct grub_squash_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + struct grub_fshelp_node root; + grub_err_t err; + + data = squash_mount (device->disk); + if (! data) + return grub_errno; + + err = make_root_node (data, &root); + if (err) + return err; + + grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir, + grub_squash_read_symlink, GRUB_FSHELP_DIR); + if (!grub_errno) + grub_squash_iterate_dir (fdiro, grub_squash_dir_iter, &ctx); + + squash_unmount (data); + + return grub_errno; +} + +static grub_err_t +grub_squash_open (struct grub_file *file, const char *name) +{ + struct grub_squash_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + struct grub_fshelp_node root; + grub_err_t err; + + data = squash_mount (file->device->disk); + if (! data) + return grub_errno; + + err = make_root_node (data, &root); + if (err) + return err; + + grub_fshelp_find_file (name, &root, &fdiro, grub_squash_iterate_dir, + grub_squash_read_symlink, GRUB_FSHELP_REG); + if (grub_errno) + { + squash_unmount (data); + return grub_errno; + } + + file->data = data; + data->ino.ino = fdiro->ino; + data->ino.block_sizes = NULL; + data->ino.cumulated_block_sizes = NULL; + data->ino.ino_chunk = fdiro->stack[fdiro->stsize - 1].ino_chunk; + data->ino.ino_offset = fdiro->stack[fdiro->stsize - 1].ino_offset; + + switch (fdiro->ino.type) + { + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR): + file->size = grub_le_to_cpu64 (fdiro->ino.long_file.size); + break; + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR): + file->size = grub_le_to_cpu32 (fdiro->ino.file.size); + break; + default: + { + grub_uint16_t type = grub_le_to_cpu16 (fdiro->ino.type); + grub_free (fdiro); + squash_unmount (data); + return grub_error (GRUB_ERR_BAD_FS, "unexpected ino type 0x%x", type); + } + } + + grub_free (fdiro); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +direct_read (struct grub_squash_data *data, + struct grub_squash_cache_inode *ino, + grub_off_t off, char *buf, grub_size_t len) +{ + grub_err_t err = GRUB_ERR_NONE; + grub_off_t cumulated_uncompressed_size = 0; + grub_uint64_t a = 0; + grub_size_t i; + grub_size_t origlen = len; + + switch (ino->ino.type) + { + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR): + a = grub_le_to_cpu64 (ino->ino.long_file.chunk); + break; + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR): + a = grub_le_to_cpu32 (ino->ino.file.chunk); + break; + } + + if (!ino->block_sizes) + { + grub_off_t total_size = 0; + grub_size_t total_blocks; + grub_size_t block_offset = 0; + switch (ino->ino.type) + { + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR): + total_size = grub_le_to_cpu64 (ino->ino.long_file.size); + block_offset = ((char *) &ino->ino.long_file.block_size + - (char *) &ino->ino); + break; + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR): + total_size = grub_le_to_cpu32 (ino->ino.file.size); + block_offset = ((char *) &ino->ino.file.block_size + - (char *) &ino->ino); + break; + } + total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz); + ino->block_sizes = grub_calloc (total_blocks, + sizeof (ino->block_sizes[0])); + ino->cumulated_block_sizes = grub_calloc (total_blocks, + sizeof (ino->cumulated_block_sizes[0])); + if (!ino->block_sizes || !ino->cumulated_block_sizes) + { + grub_free (ino->block_sizes); + grub_free (ino->cumulated_block_sizes); + ino->block_sizes = 0; + ino->cumulated_block_sizes = 0; + return -1; + } + err = read_chunk (data, ino->block_sizes, + total_blocks * sizeof (ino->block_sizes[0]), + grub_le_to_cpu64 (data->sb.inodeoffset) + + ino->ino_chunk, + ino->ino_offset + block_offset); + if (err) + { + grub_free (ino->block_sizes); + grub_free (ino->cumulated_block_sizes); + ino->block_sizes = 0; + ino->cumulated_block_sizes = 0; + return -1; + } + ino->cumulated_block_sizes[0] = 0; + for (i = 1; i < total_blocks; i++) + ino->cumulated_block_sizes[i] = ino->cumulated_block_sizes[i - 1] + + (grub_le_to_cpu32 (ino->block_sizes[i - 1]) & ~SQUASH_BLOCK_FLAGS); + } + + if (a == 0) + a = sizeof (struct grub_squash_super); + i = off >> data->log2_blksz; + cumulated_uncompressed_size = data->blksz * (grub_disk_addr_t) i; + while (cumulated_uncompressed_size < off + len) + { + grub_size_t boff, curread; + boff = off - cumulated_uncompressed_size; + curread = data->blksz - boff; + if (curread > len) + curread = len; + if (!ino->block_sizes[i]) + { + /* Sparse block */ + grub_memset (buf, '\0', curread); + } + else if (!(ino->block_sizes[i] + & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED))) + { + char *block; + grub_size_t csize; + csize = grub_le_to_cpu32 (ino->block_sizes[i]) & ~SQUASH_BLOCK_FLAGS; + block = grub_malloc (csize); + if (!block) + return -1; + err = grub_disk_read (data->disk, + (ino->cumulated_block_sizes[i] + a) + >> GRUB_DISK_SECTOR_BITS, + (ino->cumulated_block_sizes[i] + a) + & (GRUB_DISK_SECTOR_SIZE - 1), + csize, block); + if (err) + { + grub_free (block); + return -1; + } + if (data->decompress (block, csize, boff, buf, curread, data) + != (grub_ssize_t) curread) + { + grub_free (block); + if (!grub_errno) + grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk"); + return -1; + } + grub_free (block); + } + else + err = grub_disk_read (data->disk, + (ino->cumulated_block_sizes[i] + a + boff) + >> GRUB_DISK_SECTOR_BITS, + (ino->cumulated_block_sizes[i] + a + boff) + & (GRUB_DISK_SECTOR_SIZE - 1), + curread, buf); + if (err) + return -1; + off += curread; + len -= curread; + buf += curread; + cumulated_uncompressed_size += grub_le_to_cpu32 (data->sb.block_size); + i++; + } + return origlen; +} + + +static grub_ssize_t +grub_squash_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_squash_data *data = file->data; + struct grub_squash_cache_inode *ino = &data->ino; + grub_off_t off = file->offset; + grub_err_t err; + grub_uint64_t a, b; + grub_uint32_t fragment = 0; + int compressed = 0; + struct grub_squash_frag_desc frag; + grub_off_t direct_len; + grub_uint64_t mask = grub_le_to_cpu32 (data->sb.block_size) - 1; + grub_size_t orig_len = len; + + switch (ino->ino.type) + { + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR): + fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment); + break; + case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR): + fragment = grub_le_to_cpu32 (ino->ino.file.fragment); + break; + } + + /* Squash may pack file tail as fragment. So read initial part directly and + get tail from fragments */ + direct_len = fragment == 0xffffffff ? file->size : file->size & ~mask; + if (off < direct_len) + { + grub_size_t read_len = direct_len - off; + grub_ssize_t res; + + if (read_len > len) + read_len = len; + res = direct_read (data, ino, off, buf, read_len); + if ((grub_size_t) res != read_len) + return -1; /* FIXME: is short read possible here? */ + len -= read_len; + if (!len) + return read_len; + buf += read_len; + off = 0; + } + else + off -= direct_len; + + err = read_chunk (data, &frag, sizeof (frag), + data->fragments, sizeof (frag) * fragment); + if (err) + return -1; + a = grub_le_to_cpu64 (frag.offset); + compressed = !(frag.size & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)); + if (ino->ino.type == grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR)) + b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off; + else + b = grub_le_to_cpu32 (ino->ino.file.offset) + off; + + /* FIXME: cache uncompressed chunks. */ + if (compressed) + { + char *block; + block = grub_malloc (grub_le_to_cpu32 (frag.size)); + if (!block) + return -1; + err = grub_disk_read (data->disk, + a >> GRUB_DISK_SECTOR_BITS, + a & (GRUB_DISK_SECTOR_SIZE - 1), + grub_le_to_cpu32 (frag.size), block); + if (err) + { + grub_free (block); + return -1; + } + if (data->decompress (block, grub_le_to_cpu32 (frag.size), + b, buf, len, data) + != (grub_ssize_t) len) + { + grub_free (block); + if (!grub_errno) + grub_error (GRUB_ERR_BAD_FS, "incorrect compressed chunk"); + return -1; + } + grub_free (block); + } + else + { + err = grub_disk_read (data->disk, (a + b) >> GRUB_DISK_SECTOR_BITS, + (a + b) & (GRUB_DISK_SECTOR_SIZE - 1), len, buf); + if (err) + return -1; + } + return orig_len; +} + +static grub_err_t +grub_squash_close (grub_file_t file) +{ + squash_unmount (file->data); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_squash_mtime (grub_device_t dev, grub_int64_t *tm) +{ + struct grub_squash_data *data = 0; + + data = squash_mount (dev->disk); + if (! data) + return grub_errno; + *tm = grub_le_to_cpu32 (data->sb.creation_time); + squash_unmount (data); + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_squash_fs = + { + .name = "squash4", + .fs_dir = grub_squash_dir, + .fs_open = grub_squash_open, + .fs_read = grub_squash_read, + .fs_close = grub_squash_close, + .fs_mtime = grub_squash_mtime, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(squash4) +{ + grub_squash_fs.mod = mod; + grub_fs_register (&grub_squash_fs); +} + +GRUB_MOD_FINI(squash4) +{ + grub_fs_unregister (&grub_squash_fs); +} + diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c new file mode 100644 index 000000000..1eaa5349f --- /dev/null +++ b/grub-core/fs/tar.c @@ -0,0 +1,373 @@ +/* cpio.c - cpio and tar filesystem. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2009,2013 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* tar support */ +#define MAGIC "ustar" +struct head +{ + char name[100]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[100]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[155]; +} GRUB_PACKED; + +static inline unsigned long long +read_number (const char *str, grub_size_t size) +{ + unsigned long long ret = 0; + while (size-- && *str >= '0' && *str <= '7') + ret = (ret << 3) | (*str++ & 0xf); + return ret; +} + +struct grub_archelp_data +{ + grub_disk_t disk; + grub_off_t hofs, next_hofs; + grub_off_t dofs; + grub_off_t size; + char *linkname; + grub_size_t linkname_alloc; +}; + +static grub_err_t +grub_cpio_find_file (struct grub_archelp_data *data, char **name, + grub_int32_t *mtime, + grub_uint32_t *mode) +{ + struct head hd; + int reread = 0, have_longname = 0, have_longlink = 0; + grub_size_t sz; + + data->hofs = data->next_hofs; + *name = NULL; + + for (reread = 0; reread < 3; reread++) + { + if (grub_disk_read (data->disk, 0, data->hofs, sizeof (hd), &hd)) + return grub_errno; + + if (!hd.name[0] && !hd.prefix[0]) + { + *mode = GRUB_ARCHELP_ATTR_END; + return GRUB_ERR_NONE; + } + + if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1)) + return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); + + if (hd.typeflag == 'L') + { + grub_err_t err; + grub_size_t namesize; + + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &namesize) || + grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("name size overflow")); + + *name = grub_malloc (sz); + if (*name == NULL) + return grub_errno; + err = grub_disk_read (data->disk, 0, + data->hofs + GRUB_DISK_SECTOR_SIZE, namesize, + *name); + (*name)[namesize] = 0; + if (err) + return err; + data->hofs += GRUB_DISK_SECTOR_SIZE + + ((namesize + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + have_longname = 1; + continue; + } + + if (hd.typeflag == 'K') + { + grub_err_t err; + grub_size_t linksize; + + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &linksize) || + grub_add (linksize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("link size overflow")); + + if (data->linkname_alloc < sz) + { + char *n; + n = grub_calloc (2, sz); + if (!n) + return grub_errno; + grub_free (data->linkname); + data->linkname = n; + data->linkname_alloc = 2 * (sz); + } + + err = grub_disk_read (data->disk, 0, + data->hofs + GRUB_DISK_SECTOR_SIZE, linksize, + data->linkname); + if (err) + return err; + data->linkname[linksize] = 0; + data->hofs += GRUB_DISK_SECTOR_SIZE + + ((linksize + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + have_longlink = 1; + continue; + } + + if (!have_longname) + { + grub_size_t extra_size = 0; + + while (extra_size < sizeof (hd.prefix) + && hd.prefix[extra_size]) + extra_size++; + + if (grub_add (sizeof (hd.name) + 2, extra_size, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("long name size overflow")); + *name = grub_malloc (sz); + if (*name == NULL) + return grub_errno; + if (hd.prefix[0]) + { + grub_memcpy (*name, hd.prefix, extra_size); + (*name)[extra_size++] = '/'; + } + grub_memcpy (*name + extra_size, hd.name, sizeof (hd.name)); + (*name)[extra_size + sizeof (hd.name)] = 0; + } + + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + + data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; + data->next_hofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & + ~(GRUB_DISK_SECTOR_SIZE - 1)); + if (mtime) + { + if (grub_cast (read_number (hd.mtime, sizeof (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } + if (mode) + { + if (grub_cast (read_number (hd.mode, sizeof (hd.mode)), mode)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + + switch (hd.typeflag) + { + /* Hardlink. */ + case '1': + /* Symlink. */ + case '2': + *mode |= GRUB_ARCHELP_ATTR_LNK; + break; + case '0': + *mode |= GRUB_ARCHELP_ATTR_FILE; + break; + case '5': + *mode |= GRUB_ARCHELP_ATTR_DIR; + break; + } + } + if (!have_longlink) + { + if (data->linkname_alloc < 101) + { + char *n; + n = grub_malloc (101); + if (!n) + return grub_errno; + grub_free (data->linkname); + data->linkname = n; + data->linkname_alloc = 101; + } + grub_memcpy (data->linkname, hd.linkname, sizeof (hd.linkname)); + data->linkname[100] = 0; + } + return GRUB_ERR_NONE; + } + + if (*name == NULL) + return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); + + return GRUB_ERR_NONE; +} + +static char * +grub_cpio_get_link_target (struct grub_archelp_data *data) +{ + return grub_strdup (data->linkname); +} + +static void +grub_cpio_rewind (struct grub_archelp_data *data) +{ + data->next_hofs = 0; +} + +static struct grub_archelp_ops arcops = + { + .find_file = grub_cpio_find_file, + .get_link_target = grub_cpio_get_link_target, + .rewind = grub_cpio_rewind + }; + +static struct grub_archelp_data * +grub_cpio_mount (grub_disk_t disk) +{ + struct head hd; + struct grub_archelp_data *data; + + if (grub_disk_read (disk, 0, 0, sizeof (hd), &hd)) + goto fail; + + if (grub_memcmp (hd.magic, MAGIC, sizeof (MAGIC) - 1)) + goto fail; + + data = (struct grub_archelp_data *) grub_zalloc (sizeof (*data)); + if (!data) + goto fail; + + data->disk = disk; + + return data; + +fail: + grub_error (GRUB_ERR_BAD_FS, "not a tarfs filesystem"); + return 0; +} + +static grub_err_t +grub_cpio_dir (grub_device_t device, const char *path_in, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_dir (data, &arcops, + path_in, hook, hook_data); + + grub_free (data->linkname); + grub_free (data); + + return err; +} + +static grub_err_t +grub_cpio_open (grub_file_t file, const char *name_in) +{ + struct grub_archelp_data *data; + grub_err_t err; + + data = grub_cpio_mount (file->device->disk); + if (!data) + return grub_errno; + + err = grub_archelp_open (data, &arcops, name_in); + if (err) + { + grub_free (data->linkname); + grub_free (data); + } + else + { + file->data = data; + file->size = data->size; + } + return err; +} + +static grub_ssize_t +grub_cpio_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_archelp_data *data; + grub_ssize_t ret; + + data = file->data; + + data->disk->read_hook = file->read_hook; + data->disk->read_hook_data = file->read_hook_data; + ret = (grub_disk_read (data->disk, 0, data->dofs + file->offset, + len, buf)) ? -1 : (grub_ssize_t) len; + data->disk->read_hook = 0; + + return ret; +} + +static grub_err_t +grub_cpio_close (grub_file_t file) +{ + struct grub_archelp_data *data; + + data = file->data; + grub_free (data->linkname); + grub_free (data); + + return grub_errno; +} + +static struct grub_fs grub_cpio_fs = { + .name = "tarfs", + .fs_dir = grub_cpio_dir, + .fs_open = grub_cpio_open, + .fs_read = grub_cpio_read, + .fs_close = grub_cpio_close, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 0, +#endif +}; + +GRUB_MOD_INIT (tar) +{ + grub_cpio_fs.mod = mod; + grub_fs_register (&grub_cpio_fs); +} + +GRUB_MOD_FINI (tar) +{ + grub_fs_unregister (&grub_cpio_fs); +} diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c new file mode 100644 index 000000000..3d5ee5af5 --- /dev/null +++ b/grub-core/fs/udf.c @@ -0,0 +1,1471 @@ +/* udf.c - Universal Disk Format filesystem. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_UDF_MAX_PDS 2 +#define GRUB_UDF_MAX_PMS 6 + +#define U16 grub_le_to_cpu16 +#define U32 grub_le_to_cpu32 +#define U64 grub_le_to_cpu64 + +#define GRUB_UDF_TAG_IDENT_PVD 0x0001 +#define GRUB_UDF_TAG_IDENT_AVDP 0x0002 +#define GRUB_UDF_TAG_IDENT_VDP 0x0003 +#define GRUB_UDF_TAG_IDENT_IUVD 0x0004 +#define GRUB_UDF_TAG_IDENT_PD 0x0005 +#define GRUB_UDF_TAG_IDENT_LVD 0x0006 +#define GRUB_UDF_TAG_IDENT_USD 0x0007 +#define GRUB_UDF_TAG_IDENT_TD 0x0008 +#define GRUB_UDF_TAG_IDENT_LVID 0x0009 + +#define GRUB_UDF_TAG_IDENT_FSD 0x0100 +#define GRUB_UDF_TAG_IDENT_FID 0x0101 +#define GRUB_UDF_TAG_IDENT_AED 0x0102 +#define GRUB_UDF_TAG_IDENT_IE 0x0103 +#define GRUB_UDF_TAG_IDENT_TE 0x0104 +#define GRUB_UDF_TAG_IDENT_FE 0x0105 +#define GRUB_UDF_TAG_IDENT_EAHD 0x0106 +#define GRUB_UDF_TAG_IDENT_USE 0x0107 +#define GRUB_UDF_TAG_IDENT_SBD 0x0108 +#define GRUB_UDF_TAG_IDENT_PIE 0x0109 +#define GRUB_UDF_TAG_IDENT_EFE 0x010A + +#define GRUB_UDF_ICBTAG_TYPE_UNDEF 0x00 +#define GRUB_UDF_ICBTAG_TYPE_USE 0x01 +#define GRUB_UDF_ICBTAG_TYPE_PIE 0x02 +#define GRUB_UDF_ICBTAG_TYPE_IE 0x03 +#define GRUB_UDF_ICBTAG_TYPE_DIRECTORY 0x04 +#define GRUB_UDF_ICBTAG_TYPE_REGULAR 0x05 +#define GRUB_UDF_ICBTAG_TYPE_BLOCK 0x06 +#define GRUB_UDF_ICBTAG_TYPE_CHAR 0x07 +#define GRUB_UDF_ICBTAG_TYPE_EA 0x08 +#define GRUB_UDF_ICBTAG_TYPE_FIFO 0x09 +#define GRUB_UDF_ICBTAG_TYPE_SOCKET 0x0A +#define GRUB_UDF_ICBTAG_TYPE_TE 0x0B +#define GRUB_UDF_ICBTAG_TYPE_SYMLINK 0x0C +#define GRUB_UDF_ICBTAG_TYPE_STREAMDIR 0x0D + +#define GRUB_UDF_ICBTAG_FLAG_AD_MASK 0x0007 +#define GRUB_UDF_ICBTAG_FLAG_AD_SHORT 0x0000 +#define GRUB_UDF_ICBTAG_FLAG_AD_LONG 0x0001 +#define GRUB_UDF_ICBTAG_FLAG_AD_EXT 0x0002 +#define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB 0x0003 + +#define GRUB_UDF_EXT_NORMAL 0x00000000 +#define GRUB_UDF_EXT_NREC_ALLOC 0x40000000 +#define GRUB_UDF_EXT_NREC_NALLOC 0x80000000 +#define GRUB_UDF_EXT_MASK 0xC0000000 + +#define GRUB_UDF_FID_CHAR_HIDDEN 0x01 +#define GRUB_UDF_FID_CHAR_DIRECTORY 0x02 +#define GRUB_UDF_FID_CHAR_DELETED 0x04 +#define GRUB_UDF_FID_CHAR_PARENT 0x08 +#define GRUB_UDF_FID_CHAR_METADATA 0x10 + +#define GRUB_UDF_STD_IDENT_BEA01 "BEA01" +#define GRUB_UDF_STD_IDENT_BOOT2 "BOOT2" +#define GRUB_UDF_STD_IDENT_CD001 "CD001" +#define GRUB_UDF_STD_IDENT_CDW02 "CDW02" +#define GRUB_UDF_STD_IDENT_NSR02 "NSR02" +#define GRUB_UDF_STD_IDENT_NSR03 "NSR03" +#define GRUB_UDF_STD_IDENT_TEA01 "TEA01" + +#define GRUB_UDF_CHARSPEC_TYPE_CS0 0x00 +#define GRUB_UDF_CHARSPEC_TYPE_CS1 0x01 +#define GRUB_UDF_CHARSPEC_TYPE_CS2 0x02 +#define GRUB_UDF_CHARSPEC_TYPE_CS3 0x03 +#define GRUB_UDF_CHARSPEC_TYPE_CS4 0x04 +#define GRUB_UDF_CHARSPEC_TYPE_CS5 0x05 +#define GRUB_UDF_CHARSPEC_TYPE_CS6 0x06 +#define GRUB_UDF_CHARSPEC_TYPE_CS7 0x07 +#define GRUB_UDF_CHARSPEC_TYPE_CS8 0x08 + +#define GRUB_UDF_PARTMAP_TYPE_1 1 +#define GRUB_UDF_PARTMAP_TYPE_2 2 + +#define GRUB_UDF_INVALID_STRUCT_PTR(_ptr, _struct) \ + ((char *) (_ptr) >= end_ptr || \ + ((grub_ssize_t) (end_ptr - (char *) (_ptr)) < (grub_ssize_t) sizeof (_struct))) + +struct grub_udf_lb_addr +{ + grub_uint32_t block_num; + grub_uint16_t part_ref; +} GRUB_PACKED; + +struct grub_udf_short_ad +{ + grub_uint32_t length; + grub_uint32_t position; +} GRUB_PACKED; + +struct grub_udf_long_ad +{ + grub_uint32_t length; + struct grub_udf_lb_addr block; + grub_uint8_t imp_use[6]; +} GRUB_PACKED; + +struct grub_udf_extent_ad +{ + grub_uint32_t length; + grub_uint32_t start; +} GRUB_PACKED; + +struct grub_udf_charspec +{ + grub_uint8_t charset_type; + grub_uint8_t charset_info[63]; +} GRUB_PACKED; + +struct grub_udf_timestamp +{ + grub_uint16_t type_and_timezone; + grub_uint16_t year; + grub_uint8_t month; + grub_uint8_t day; + grub_uint8_t hour; + grub_uint8_t minute; + grub_uint8_t second; + grub_uint8_t centi_seconds; + grub_uint8_t hundreds_of_micro_seconds; + grub_uint8_t micro_seconds; +} GRUB_PACKED; + +struct grub_udf_regid +{ + grub_uint8_t flags; + grub_uint8_t ident[23]; + grub_uint8_t ident_suffix[8]; +} GRUB_PACKED; + +struct grub_udf_tag +{ + grub_uint16_t tag_ident; + grub_uint16_t desc_version; + grub_uint8_t tag_checksum; + grub_uint8_t reserved; + grub_uint16_t tag_serial_number; + grub_uint16_t desc_crc; + grub_uint16_t desc_crc_length; + grub_uint32_t tag_location; +} GRUB_PACKED; + +struct grub_udf_fileset +{ + struct grub_udf_tag tag; + struct grub_udf_timestamp datetime; + grub_uint16_t interchange_level; + grub_uint16_t max_interchange_level; + grub_uint32_t charset_list; + grub_uint32_t max_charset_list; + grub_uint32_t fileset_num; + grub_uint32_t fileset_desc_num; + struct grub_udf_charspec vol_charset; + grub_uint8_t vol_ident[128]; + struct grub_udf_charspec fileset_charset; + grub_uint8_t fileset_ident[32]; + grub_uint8_t copyright_file_ident[32]; + grub_uint8_t abstract_file_ident[32]; + struct grub_udf_long_ad root_icb; + struct grub_udf_regid domain_ident; + struct grub_udf_long_ad next_ext; + struct grub_udf_long_ad streamdir_icb; +} GRUB_PACKED; + +struct grub_udf_icbtag +{ + grub_uint32_t prior_recorded_num_direct_entries; + grub_uint16_t strategy_type; + grub_uint16_t strategy_parameter; + grub_uint16_t num_entries; + grub_uint8_t reserved; + grub_uint8_t file_type; + struct grub_udf_lb_addr parent_idb; + grub_uint16_t flags; +} GRUB_PACKED; + +struct grub_udf_file_ident +{ + struct grub_udf_tag tag; + grub_uint16_t version_num; + grub_uint8_t characteristics; +#define MAX_FILE_IDENT_LENGTH 256 + grub_uint8_t file_ident_length; + struct grub_udf_long_ad icb; + grub_uint16_t imp_use_length; +} GRUB_PACKED; + +struct grub_udf_file_entry +{ + struct grub_udf_tag tag; + struct grub_udf_icbtag icbtag; + grub_uint32_t uid; + grub_uint32_t gid; + grub_uint32_t permissions; + grub_uint16_t link_count; + grub_uint8_t record_format; + grub_uint8_t record_display_attr; + grub_uint32_t record_length; + grub_uint64_t file_size; + grub_uint64_t blocks_recorded; + struct grub_udf_timestamp access_time; + struct grub_udf_timestamp modification_time; + struct grub_udf_timestamp attr_time; + grub_uint32_t checkpoint; + struct grub_udf_long_ad extended_attr_idb; + struct grub_udf_regid imp_ident; + grub_uint64_t unique_id; + grub_uint32_t ext_attr_length; + grub_uint32_t alloc_descs_length; + grub_uint8_t ext_attr[0]; +} GRUB_PACKED; + +struct grub_udf_extended_file_entry +{ + struct grub_udf_tag tag; + struct grub_udf_icbtag icbtag; + grub_uint32_t uid; + grub_uint32_t gid; + grub_uint32_t permissions; + grub_uint16_t link_count; + grub_uint8_t record_format; + grub_uint8_t record_display_attr; + grub_uint32_t record_length; + grub_uint64_t file_size; + grub_uint64_t object_size; + grub_uint64_t blocks_recorded; + struct grub_udf_timestamp access_time; + struct grub_udf_timestamp modification_time; + struct grub_udf_timestamp create_time; + struct grub_udf_timestamp attr_time; + grub_uint32_t checkpoint; + grub_uint32_t reserved; + struct grub_udf_long_ad extended_attr_icb; + struct grub_udf_long_ad streamdir_icb; + struct grub_udf_regid imp_ident; + grub_uint64_t unique_id; + grub_uint32_t ext_attr_length; + grub_uint32_t alloc_descs_length; + grub_uint8_t ext_attr[0]; +} GRUB_PACKED; + +struct grub_udf_vrs +{ + grub_uint8_t type; + grub_uint8_t magic[5]; + grub_uint8_t version; +} GRUB_PACKED; + +struct grub_udf_avdp +{ + struct grub_udf_tag tag; + struct grub_udf_extent_ad vds; +} GRUB_PACKED; + +struct grub_udf_pd +{ + struct grub_udf_tag tag; + grub_uint32_t seq_num; + grub_uint16_t flags; + grub_uint16_t part_num; + struct grub_udf_regid contents; + grub_uint8_t contents_use[128]; + grub_uint32_t access_type; + grub_uint32_t start; + grub_uint32_t length; +} GRUB_PACKED; + +struct grub_udf_partmap +{ + grub_uint8_t type; + grub_uint8_t length; + union + { + struct + { + grub_uint16_t seq_num; + grub_uint16_t part_num; + } type1; + + struct + { + grub_uint8_t ident[62]; + } type2; + }; +} GRUB_PACKED; + +struct grub_udf_pvd +{ + struct grub_udf_tag tag; + grub_uint32_t seq_num; + grub_uint32_t pvd_num; + grub_uint8_t ident[32]; + grub_uint16_t vol_seq_num; + grub_uint16_t max_vol_seq_num; + grub_uint16_t interchange_level; + grub_uint16_t max_interchange_level; + grub_uint32_t charset_list; + grub_uint32_t max_charset_list; + grub_uint8_t volset_ident[128]; + struct grub_udf_charspec desc_charset; + struct grub_udf_charspec expl_charset; + struct grub_udf_extent_ad vol_abstract; + struct grub_udf_extent_ad vol_copyright; + struct grub_udf_regid app_ident; + struct grub_udf_timestamp recording_time; + struct grub_udf_regid imp_ident; + grub_uint8_t imp_use[64]; + grub_uint32_t pred_vds_loc; + grub_uint16_t flags; + grub_uint8_t reserved[22]; +} GRUB_PACKED; + +struct grub_udf_lvd +{ + struct grub_udf_tag tag; + grub_uint32_t seq_num; + struct grub_udf_charspec charset; + grub_uint8_t ident[128]; + grub_uint32_t bsize; + struct grub_udf_regid domain_ident; + struct grub_udf_long_ad root_fileset; + grub_uint32_t map_table_length; + grub_uint32_t num_part_maps; + struct grub_udf_regid imp_ident; + grub_uint8_t imp_use[128]; + struct grub_udf_extent_ad integrity_seq_ext; + grub_uint8_t part_maps[1608]; +} GRUB_PACKED; + +struct grub_udf_aed +{ + struct grub_udf_tag tag; + grub_uint32_t prev_ae; + grub_uint32_t ae_len; +} GRUB_PACKED; + +struct grub_udf_data +{ + grub_disk_t disk; + struct grub_udf_pvd pvd; + struct grub_udf_lvd lvd; + struct grub_udf_pd pds[GRUB_UDF_MAX_PDS]; + struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS]; + struct grub_udf_long_ad root_icb; + int npd, npm, lbshift; +}; + +struct grub_fshelp_node +{ + struct grub_udf_data *data; + int part_ref; + union + { + struct grub_udf_file_entry fe; + struct grub_udf_extended_file_entry efe; + char raw[0]; + } block; +}; + +static inline grub_size_t +get_fshelp_size (struct grub_udf_data *data) +{ + struct grub_fshelp_node *x = NULL; + return sizeof (*x) + + (1 << (GRUB_DISK_SECTOR_BITS + + data->lbshift)) - sizeof (x->block); +} + +static grub_dl_t my_mod; + +static grub_uint32_t +grub_udf_get_block (struct grub_udf_data *data, + grub_uint16_t part_ref, grub_uint32_t block) +{ + part_ref = U16 (part_ref); + + if (part_ref >= data->npm) + { + grub_error (GRUB_ERR_BAD_FS, "invalid part ref"); + return 0; + } + + return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start) + + U32 (block)); +} + +static grub_err_t +grub_udf_read_icb (struct grub_udf_data *data, + struct grub_udf_long_ad *icb, + struct grub_fshelp_node *node) +{ + grub_uint32_t block; + + block = grub_udf_get_block (data, + icb->block.part_ref, + icb->block.block_num); + + if (grub_errno) + return grub_errno; + + if (grub_disk_read (data->disk, block << data->lbshift, 0, + 1 << (GRUB_DISK_SECTOR_BITS + + data->lbshift), + &node->block)) + return grub_errno; + + if ((U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) && + (U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE)) + return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor"); + + node->part_ref = icb->block.part_ref; + node->data = data; + return 0; +} + +static grub_disk_addr_t +grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) +{ + char *buf = NULL; + char *ptr; + grub_ssize_t len; + grub_disk_addr_t filebytes; + char *end_ptr; + + switch (U16 (node->block.fe.tag.tag_ident)) + { + case GRUB_UDF_TAG_IDENT_FE: + ptr = (char *) &node->block.fe.ext_attr[0] + U32 (node->block.fe.ext_attr_length); + len = U32 (node->block.fe.alloc_descs_length); + break; + + case GRUB_UDF_TAG_IDENT_EFE: + ptr = (char *) &node->block.efe.ext_attr[0] + U32 (node->block.efe.ext_attr_length); + len = U32 (node->block.efe.alloc_descs_length); + break; + + default: + grub_error (GRUB_ERR_BAD_FS, "invalid file entry"); + return 0; + } + + end_ptr = (char *) node + get_fshelp_size (node->data); + + if ((U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK) + == GRUB_UDF_ICBTAG_FLAG_AD_SHORT) + { + if (GRUB_UDF_INVALID_STRUCT_PTR (ptr, struct grub_udf_short_ad)) + { + grub_error (GRUB_ERR_BAD_FS, "corrupted UDF file system"); + return 0; + } + + struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr; + + filebytes = fileblock * U32 (node->data->lvd.bsize); + while (len >= (grub_ssize_t) sizeof (struct grub_udf_short_ad)) + { + grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff; + grub_uint32_t adtype = U32 (ad->length) >> 30; + if (adtype == 3) + { + struct grub_udf_aed *extension; + grub_disk_addr_t sec = grub_udf_get_block(node->data, + node->part_ref, + ad->position); + if (!buf) + { + buf = grub_malloc (U32 (node->data->lvd.bsize)); + if (!buf) + return 0; + } + if (grub_disk_read (node->data->disk, sec << node->data->lbshift, + 0, adlen, buf)) + goto fail; + + extension = (struct grub_udf_aed *) buf; + if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED) + { + grub_error (GRUB_ERR_BAD_FS, "invalid aed tag"); + goto fail; + } + + len = U32 (extension->ae_len); + /* + * Ensure AE length is less than block size + * per UDF spec v2.01 section 2.3.11. + * + * node->data->lbshift is initialized by + * grub_udf_mount(). lbshift has a maximum value + * of 3 and it does not cause an overflow here. + */ + if (len < 0 || len > ((grub_ssize_t) 1 << node->data->lbshift)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid ae length"); + goto fail; + } + + ad = (struct grub_udf_short_ad *) + (buf + sizeof (struct grub_udf_aed)); + continue; + } + + if (filebytes < adlen) + { + grub_uint32_t ad_pos = ad->position; + grub_free (buf); + return ((U32 (ad_pos) & GRUB_UDF_EXT_MASK) ? 0 : + (grub_udf_get_block (node->data, node->part_ref, ad_pos) + + (filebytes >> (GRUB_DISK_SECTOR_BITS + + node->data->lbshift)))); + } + + filebytes -= adlen; + ad++; + len -= sizeof (struct grub_udf_short_ad); + + if (GRUB_UDF_INVALID_STRUCT_PTR (ad, struct grub_udf_short_ad)) + { + grub_error (GRUB_ERR_BAD_FS, "corrupted UDF file system"); + return 0; + } + } + } + else + { + if (GRUB_UDF_INVALID_STRUCT_PTR (ptr, struct grub_udf_long_ad)) + { + grub_error (GRUB_ERR_BAD_FS, "corrupted UDF file system"); + return 0; + } + + struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr; + + filebytes = fileblock * U32 (node->data->lvd.bsize); + while (len >= (grub_ssize_t) sizeof (struct grub_udf_long_ad)) + { + grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff; + grub_uint32_t adtype = U32 (ad->length) >> 30; + if (adtype == 3) + { + struct grub_udf_aed *extension; + grub_disk_addr_t sec = grub_udf_get_block(node->data, + ad->block.part_ref, + ad->block.block_num); + if (!buf) + { + buf = grub_malloc (U32 (node->data->lvd.bsize)); + if (!buf) + return 0; + } + if (grub_disk_read (node->data->disk, sec << node->data->lbshift, + 0, adlen, buf)) + goto fail; + + extension = (struct grub_udf_aed *) buf; + if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED) + { + grub_error (GRUB_ERR_BAD_FS, "invalid aed tag"); + goto fail; + } + + len = U32 (extension->ae_len); + /* + * Ensure AE length is less than block size + * per UDF spec v2.01 section 2.3.11. + * + * node->data->lbshift is initialized by + * grub_udf_mount(). lbshift has a maximum value + * of 3 and it does not cause an overflow here. + */ + if (len < 0 || len > ((grub_ssize_t) 1 << node->data->lbshift)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid ae length"); + goto fail; + } + + ad = (struct grub_udf_long_ad *) + (buf + sizeof (struct grub_udf_aed)); + continue; + } + + if (filebytes < adlen) + { + grub_uint32_t ad_block_num = ad->block.block_num; + grub_uint32_t ad_part_ref = ad->block.part_ref; + grub_free (buf); + return ((U32 (ad_block_num) & GRUB_UDF_EXT_MASK) ? 0 : + (grub_udf_get_block (node->data, ad_part_ref, + ad_block_num) + + (filebytes >> (GRUB_DISK_SECTOR_BITS + + node->data->lbshift)))); + } + + filebytes -= adlen; + ad++; + len -= sizeof (struct grub_udf_long_ad); + + if (GRUB_UDF_INVALID_STRUCT_PTR (ad, struct grub_udf_long_ad)) + { + grub_error (GRUB_ERR_BAD_FS, "corrupted UDF file system"); + return 0; + } + } + } + +fail: + grub_free (buf); + + return 0; +} + +static grub_ssize_t +grub_udf_read_file (grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) +{ + switch (U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK) + { + case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB: + { + char *ptr; + char *end_ptr = (char *) node + get_fshelp_size (node->data); + + ptr = ((U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ? + ((char *) &node->block.fe.ext_attr[0] + + U32 (node->block.fe.ext_attr_length)) : + ((char *) &node->block.efe.ext_attr[0] + + U32 (node->block.efe.ext_attr_length))); + + if ((ptr + pos + len) > end_ptr) + { + grub_error (GRUB_ERR_BAD_FS, "corrupted UDF file system"); + return 0; + } + + grub_memcpy (buf, ptr + pos, len); + + return len; + } + + case GRUB_UDF_ICBTAG_FLAG_AD_EXT: + grub_error (GRUB_ERR_BAD_FS, "invalid extent type"); + return 0; + } + + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, + pos, len, buf, grub_udf_read_block, + U64 (node->block.fe.file_size), + node->data->lbshift, 0); +} + +static unsigned sblocklist[] = { 256, 512, 0 }; + +static struct grub_udf_data * +grub_udf_mount (grub_disk_t disk) +{ + struct grub_udf_data *data = 0; + struct grub_udf_fileset root_fs; + unsigned *sblklist; + grub_uint32_t block, vblock; + int i, lbshift; + + data = grub_malloc (sizeof (struct grub_udf_data)); + if (!data) + return 0; + + data->disk = disk; + + /* Search for Anchor Volume Descriptor Pointer (AVDP) + * and determine logical block size. */ + block = 0; + for (lbshift = 0; lbshift < 4; lbshift++) + { + for (sblklist = sblocklist; *sblklist; sblklist++) + { + struct grub_udf_avdp avdp; + + if (grub_disk_read (disk, *sblklist << lbshift, 0, + sizeof (struct grub_udf_avdp), &avdp)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP && + U32 (avdp.tag.tag_location) == *sblklist) + { + block = U32 (avdp.vds.start); + break; + } + } + + if (block) + break; + } + + if (!block) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + data->lbshift = lbshift; + + /* Search for Volume Recognition Sequence (VRS). */ + for (vblock = (32767 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1;; + vblock += (2047 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1) + { + struct grub_udf_vrs vrs; + + if (grub_disk_read (disk, vblock << lbshift, 0, + sizeof (struct grub_udf_vrs), &vrs)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) || + (!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5))) + break; + + if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) && + (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) && + (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) && + (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) && + (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5))) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + } + + data->npd = data->npm = 0; + /* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD). */ + while (1) + { + struct grub_udf_tag tag; + + if (grub_disk_read (disk, block << lbshift, 0, + sizeof (struct grub_udf_tag), &tag)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + tag.tag_ident = U16 (tag.tag_ident); + if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PVD) + { + if (grub_disk_read (disk, block << lbshift, 0, + sizeof (struct grub_udf_pvd), + &data->pvd)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + } + else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD) + { + if (data->npd >= GRUB_UDF_MAX_PDS) + { + grub_error (GRUB_ERR_BAD_FS, "too many PDs"); + goto fail; + } + + if (grub_disk_read (disk, block << lbshift, 0, + sizeof (struct grub_udf_pd), + &data->pds[data->npd])) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + data->npd++; + } + else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD) + { + int k; + + struct grub_udf_partmap *ppm; + + if (grub_disk_read (disk, block << lbshift, 0, + sizeof (struct grub_udf_lvd), + &data->lvd)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS) + { + grub_error (GRUB_ERR_BAD_FS, "too many partition maps"); + goto fail; + } + + ppm = (struct grub_udf_partmap *) &data->lvd.part_maps; + for (k = U32 (data->lvd.num_part_maps); k > 0; k--) + { + if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1) + { + grub_error (GRUB_ERR_BAD_FS, "partmap type not supported"); + goto fail; + } + + data->pms[data->npm++] = ppm; + ppm = (struct grub_udf_partmap *) ((char *) ppm + + U32 (ppm->length)); + } + } + else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD) + { + grub_error (GRUB_ERR_BAD_FS, "invalid tag ident"); + goto fail; + } + else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD) + break; + + block++; + } + + for (i = 0; i < data->npm; i++) + { + int j; + + for (j = 0; j < data->npd; j++) + if (data->pms[i]->type1.part_num == data->pds[j].part_num) + { + data->pms[i]->type1.part_num = j; + break; + } + + if (j == data->npd) + { + grub_error (GRUB_ERR_BAD_FS, "can\'t find PD"); + goto fail; + } + } + + block = grub_udf_get_block (data, + data->lvd.root_fileset.block.part_ref, + data->lvd.root_fileset.block.block_num); + + if (grub_errno) + goto fail; + + if (grub_disk_read (disk, block << lbshift, 0, + sizeof (struct grub_udf_fileset), &root_fs)) + { + grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem"); + goto fail; + } + + if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD) + { + grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor"); + goto fail; + } + + data->root_icb = root_fs.root_icb; + + return data; + +fail: + grub_free (data); + return 0; +} + +#ifdef GRUB_UTIL +grub_disk_addr_t +grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn) +{ + grub_disk_addr_t ret; + static struct grub_udf_data *data; + + data = grub_udf_mount (disk); + if (!data) + return 0; + + ret = U32 (data->pds[data->pms[0]->type1.part_num].start); + *sec_per_lcn = 1ULL << data->lbshift; + grub_free (data); + return ret; +} +#endif + +static char * +read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf) +{ + grub_uint16_t *utf16 = NULL; + grub_size_t utf16len = 0; + + if (sz == 0) + return NULL; + + if (raw[0] != 8 && raw[0] != 16) + return NULL; + + if (raw[0] == 8) + { + unsigned i; + utf16len = sz - 1; + utf16 = grub_calloc (utf16len, sizeof (utf16[0])); + if (!utf16) + return NULL; + for (i = 0; i < utf16len; i++) + utf16[i] = raw[i + 1]; + } + if (raw[0] == 16) + { + unsigned i; + utf16len = (sz - 1) / 2; + utf16 = grub_calloc (utf16len, sizeof (utf16[0])); + if (!utf16) + return NULL; + for (i = 0; i < utf16len; i++) + utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2]; + } + if (!outbuf) + { + grub_size_t size; + + if (grub_mul (utf16len, GRUB_MAX_UTF8_PER_UTF16, &size) || + grub_add (size, 1, &size)) + goto fail; + + outbuf = grub_malloc (size); + } + if (outbuf) + *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0'; + + fail: + grub_free (utf16); + return outbuf; +} + +static char * +read_dstring (const grub_uint8_t *raw, grub_size_t sz) +{ + grub_size_t len; + + if (raw[0] == 0) { + char *outbuf = grub_malloc (1); + if (!outbuf) + return NULL; + outbuf[0] = 0; + return outbuf; + } + + len = raw[sz - 1]; + if (len > sz - 1) + len = sz - 1; + return read_string (raw, len, NULL); +} + +static int +grub_udf_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + grub_fshelp_node_t child; + struct grub_udf_file_ident dirent; + grub_off_t offset = 0; + + child = grub_malloc (get_fshelp_size (dir->data)); + if (!child) + return 0; + + /* The current directory is not stored. */ + grub_memcpy (child, dir, get_fshelp_size (dir->data)); + + if (hook (".", GRUB_FSHELP_DIR, child, hook_data)) + return 1; + + while (offset < U64 (dir->block.fe.file_size)) + { + if (grub_udf_read_file (dir, 0, 0, offset, sizeof (dirent), + (char *) &dirent) != sizeof (dirent)) + return 0; + + if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID) + { + grub_error (GRUB_ERR_BAD_FS, "invalid fid tag"); + return 0; + } + + offset += sizeof (dirent) + U16 (dirent.imp_use_length); + if (!(dirent.characteristics & GRUB_UDF_FID_CHAR_DELETED)) + { + child = grub_malloc (get_fshelp_size (dir->data)); + if (!child) + return 0; + + if (grub_udf_read_icb (dir->data, &dirent.icb, child)) + { + grub_free (child); + return 0; + } + if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT) + { + /* This is the parent directory. */ + if (hook ("..", GRUB_FSHELP_DIR, child, hook_data)) + return 1; + } + else + { + enum grub_fshelp_filetype type; + char *filename; + grub_uint8_t raw[MAX_FILE_IDENT_LENGTH]; + + type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ? + (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG)); + if (child->block.fe.icbtag.file_type == GRUB_UDF_ICBTAG_TYPE_SYMLINK) + type = GRUB_FSHELP_SYMLINK; + + if ((grub_udf_read_file (dir, 0, 0, offset, + dirent.file_ident_length, + (char *) raw)) + != dirent.file_ident_length) + { + grub_free (child); + return 0; + } + + filename = read_string (raw, dirent.file_ident_length, 0); + if (!filename) + { + /* As the hook won't get called. */ + grub_free (child); + grub_print_error (); + } + + if (filename && hook (filename, type, child, hook_data)) + { + grub_free (filename); + return 1; + } + grub_free (filename); + } + } + + /* Align to dword boundary. */ + offset = (offset + dirent.file_ident_length + 3) & (~3); + } + + return 0; +} + +static char * +grub_udf_read_symlink (grub_fshelp_node_t node) +{ + grub_size_t s, sz = U64 (node->block.fe.file_size); + grub_uint8_t *raw; + const grub_uint8_t *ptr; + char *out = NULL, *optr; + + if (sz < 4) + return NULL; + raw = grub_malloc (sz); + if (!raw) + return NULL; + if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0) + goto fail_1; + + /* + * Local sz is the size of the symlink file data, which contains a sequence + * of path components (ECMA-167 14.16.1) representing the link destination. + * This size is an upper-bound on the number of bytes of a contained and + * potentially compressed UTF-16 character string. Allocate 2*sz for the + * output buffer containing the string converted to UTF-8 because the + * resulting string can not be more than double the size (2-byte unicode + * code points will be converted to a maximum of 3 bytes in UTF-8). + */ + if (grub_mul (sz, 2, &s)) + goto fail_0; + + out = grub_malloc (s); + if (!out) + { + fail_0: + grub_free (raw); + return NULL; + } + + optr = out; + + for (ptr = raw; ptr < raw + sz; ) + { + if ((grub_size_t) (ptr - raw + 4) > sz) + goto fail_1; + if (!(ptr[2] == 0 && ptr[3] == 0)) + goto fail_1; + s = 4 + ptr[1]; + if ((grub_size_t) (ptr - raw + s) > sz) + goto fail_1; + switch (*ptr) + { + case 1: + if (ptr[1]) + goto fail_1; + /* Fallthrough. */ + case 2: + /* in 4 bytes. out: 1 byte. */ + optr = out; + *optr++ = '/'; + break; + case 3: + /* in 4 bytes. out: 3 bytes. */ + if (optr != out) + *optr++ = '/'; + *optr++ = '.'; + *optr++ = '.'; + break; + case 4: + /* in 4 bytes. out: 2 bytes. */ + if (optr != out) + *optr++ = '/'; + *optr++ = '.'; + break; + case 5: + /* in 4 + n bytes. out, at most: 1 + 2 * n bytes. */ + if (optr != out) + *optr++ = '/'; + if (!read_string (ptr + 4, s - 4, optr)) + goto fail_1; + optr += grub_strlen (optr); + break; + default: + goto fail_1; + } + ptr += s; + } + *optr = 0; + grub_free (raw); + return out; + + fail_1: + grub_free (raw); + grub_free (out); + grub_error (GRUB_ERR_BAD_FS, "invalid symlink"); + return NULL; +} + +/* Context for grub_udf_dir. */ +struct grub_udf_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_udf_dir. */ +static int +grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_udf_dir_ctx *ctx = data; + struct grub_dirhook_info info; + const struct grub_udf_timestamp *tstamp = NULL; + + grub_memset (&info, 0, sizeof (info)); + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) + tstamp = &node->block.fe.modification_time; + else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE) + tstamp = &node->block.efe.modification_time; + + if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000) + { + grub_int16_t tz; + struct grub_datetime datetime; + + datetime.year = U16 (tstamp->year); + datetime.month = tstamp->month; + datetime.day = tstamp->day; + datetime.hour = tstamp->hour; + datetime.minute = tstamp->minute; + datetime.second = tstamp->second; + + tz = U16 (tstamp->type_and_timezone) & 0xfff; + if (tz & 0x800) + tz |= 0xf000; + if (tz == -2047) + tz = 0; + + info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime); + + info.mtime -= 60 * tz; + } + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_udf_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_udf_dir_ctx ctx = { hook, hook_data }; + struct grub_udf_data *data = 0; + struct grub_fshelp_node *rootnode = 0; + struct grub_fshelp_node *foundnode = 0; + + grub_dl_ref (my_mod); + + data = grub_udf_mount (device->disk); + if (!data) + goto fail; + + rootnode = grub_malloc (get_fshelp_size (data)); + if (!rootnode) + goto fail; + + if (grub_udf_read_icb (data, &data->root_icb, rootnode)) + goto fail; + + if (grub_fshelp_find_file (path, rootnode, + &foundnode, + grub_udf_iterate_dir, grub_udf_read_symlink, + GRUB_FSHELP_DIR)) + goto fail; + + grub_udf_iterate_dir (foundnode, grub_udf_dir_iter, &ctx); + + if (foundnode != rootnode) + grub_free (foundnode); + +fail: + grub_free (rootnode); + + grub_free (data); + + grub_dl_unref (my_mod); + + return grub_errno; +} + +static grub_err_t +grub_udf_open (struct grub_file *file, const char *name) +{ + struct grub_udf_data *data; + struct grub_fshelp_node *rootnode = 0; + struct grub_fshelp_node *foundnode; + + grub_dl_ref (my_mod); + + data = grub_udf_mount (file->device->disk); + if (!data) + goto fail; + + rootnode = grub_malloc (get_fshelp_size (data)); + if (!rootnode) + goto fail; + + if (grub_udf_read_icb (data, &data->root_icb, rootnode)) + goto fail; + + if (grub_fshelp_find_file (name, rootnode, + &foundnode, + grub_udf_iterate_dir, grub_udf_read_symlink, + GRUB_FSHELP_REG)) + goto fail; + + file->data = foundnode; + file->offset = 0; + file->size = U64 (foundnode->block.fe.file_size); + + grub_free (rootnode); + + return 0; + +fail: + grub_dl_unref (my_mod); + + grub_free (data); + grub_free (rootnode); + + return grub_errno; +} + +static grub_ssize_t +grub_udf_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data; + + return grub_udf_read_file (node, file->read_hook, file->read_hook_data, + file->offset, len, buf); +} + +static grub_err_t +grub_udf_close (grub_file_t file) +{ + if (file->data) + { + struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data; + + grub_free (node->data); + grub_free (node); + } + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_udf_label (grub_device_t device, char **label) +{ + struct grub_udf_data *data; + data = grub_udf_mount (device->disk); + + if (data) + { + *label = read_dstring (data->lvd.ident, sizeof (data->lvd.ident)); + grub_free (data); + } + else + *label = 0; + + return grub_errno; +} + +static char * +gen_uuid_from_volset (char *volset_ident) +{ + grub_size_t i; + grub_size_t len; + grub_size_t nonhexpos; + grub_uint8_t buf[17]; + char *uuid; + + len = grub_strlen (volset_ident); + if (len < 8) + return NULL; + + uuid = grub_malloc (17); + if (!uuid) + return NULL; + + if (len > 16) + len = 16; + + grub_memset (buf, 0, sizeof (buf)); + grub_memcpy (buf, volset_ident, len); + + nonhexpos = 16; + for (i = 0; i < 16; ++i) + { + if (!grub_isxdigit (buf[i])) + { + nonhexpos = i; + break; + } + } + + if (nonhexpos < 8) + { + grub_snprintf (uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + } + else if (nonhexpos < 16) + { + for (i = 0; i < 8; ++i) + uuid[i] = grub_tolower (buf[i]); + grub_snprintf (uuid+8, 9, "%02x%02x%02x%02x", + buf[8], buf[9], buf[10], buf[11]); + } + else + { + for (i = 0; i < 16; ++i) + uuid[i] = grub_tolower (buf[i]); + uuid[16] = 0; + } + + return uuid; +} + +static grub_err_t +grub_udf_uuid (grub_device_t device, char **uuid) +{ + char *volset_ident; + struct grub_udf_data *data; + data = grub_udf_mount (device->disk); + + if (data) + { + volset_ident = read_dstring (data->pvd.volset_ident, sizeof (data->pvd.volset_ident)); + if (volset_ident) + { + *uuid = gen_uuid_from_volset (volset_ident); + grub_free (volset_ident); + } + else + *uuid = 0; + grub_free (data); + } + else + *uuid = 0; + + return grub_errno; +} + +static struct grub_fs grub_udf_fs = { + .name = "udf", + .fs_dir = grub_udf_dir, + .fs_open = grub_udf_open, + .fs_read = grub_udf_read, + .fs_close = grub_udf_close, + .fs_label = grub_udf_label, + .fs_uuid = grub_udf_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 1, + .blocklist_install = 1, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (udf) +{ + if (!grub_is_lockdown ()) + { + grub_udf_fs.mod = mod; + grub_fs_register (&grub_udf_fs); + } + my_mod = mod; +} + +GRUB_MOD_FINI (udf) +{ + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_udf_fs); +} diff --git a/fs/ufs.c b/grub-core/fs/ufs.c similarity index 54% rename from fs/ufs.c rename to grub-core/fs/ufs.c index 40cf068e6..8b5adbd48 100644 --- a/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -24,6 +24,10 @@ #include #include #include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); #ifdef MODE_UFS2 #define GRUB_UFS_MAGIC 0x19540119 @@ -46,24 +50,50 @@ #define GRUB_UFS_VOLNAME_LEN 32 -/* Calculate in which group the inode can be found. */ -#define UFS_BLKSZ(sblock) (grub_le_to_cpu32 (sblock->bsize)) - -#define INODE(data,field) data->inode. field -#ifdef MODE_UFS2 -#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits2 (data->inode.field) +#ifdef MODE_BIGENDIAN +#define grub_ufs_to_cpu16 grub_be_to_cpu16 +#define grub_ufs_to_cpu32 grub_be_to_cpu32 +#define grub_ufs_to_cpu64 grub_be_to_cpu64 +#define grub_cpu_to_ufs32_compile_time grub_cpu_to_be32_compile_time #else -#define INODE_ENDIAN(data,field,bits1,bits2) grub_le_to_cpu##bits1 (data->inode.field) +#define grub_ufs_to_cpu16 grub_le_to_cpu16 +#define grub_ufs_to_cpu32 grub_le_to_cpu32 +#define grub_ufs_to_cpu64 grub_le_to_cpu64 +#define grub_cpu_to_ufs32_compile_time grub_cpu_to_le32_compile_time #endif -#define INODE_SIZE(data) INODE_ENDIAN (data,size,32,64) -#define INODE_NBLOCKS(data) INODE_ENDIAN (data,nblocks,32,64) - -#define INODE_MODE(data) INODE_ENDIAN (data,mode,16,16) #ifdef MODE_UFS2 -#define INODE_BLKSZ 8 +typedef grub_uint64_t grub_ufs_blk_t; +static inline grub_disk_addr_t +grub_ufs_to_cpu_blk (grub_ufs_blk_t blk) +{ + return grub_ufs_to_cpu64 (blk); +} #else -#define INODE_BLKSZ 4 +typedef grub_uint32_t grub_ufs_blk_t; +static inline grub_disk_addr_t +grub_ufs_to_cpu_blk (grub_ufs_blk_t blk) +{ + return grub_ufs_to_cpu32 (blk); +} +#endif + +/* Calculate in which group the inode can be found. */ +#define UFS_BLKSZ(sblock) (grub_ufs_to_cpu32 (sblock->bsize)) +#define UFS_LOG_BLKSZ(sblock) (data->log2_blksz) + +#ifdef MODE_UFS2 +#define INODE_ENDIAN(data,field,bits1,bits2) grub_ufs_to_cpu##bits2 (data->inode.field) +#else +#define INODE_ENDIAN(data,field,bits1,bits2) grub_ufs_to_cpu##bits1 (data->inode.field) +#endif + +#define INODE_SIZE(data) grub_ufs_to_cpu64 (data->inode.size) +#define INODE_MODE(data) grub_ufs_to_cpu16 (data->inode.mode) +#ifdef MODE_UFS2 +#define LOG_INODE_BLKSZ 3 +#else +#define LOG_INODE_BLKSZ 2 #endif #ifdef MODE_UFS2 #define UFS_INODE_PER_BLOCK 2 @@ -132,15 +162,15 @@ struct grub_ufs_inode grub_uint32_t uid; grub_uint32_t gid; grub_uint32_t blocksize; - grub_int64_t size; + grub_uint64_t size; grub_int64_t nblocks; grub_uint64_t atime; grub_uint64_t mtime; grub_uint64_t ctime; grub_uint64_t create_time; - grub_uint32_t atime_sec; - grub_uint32_t mtime_sec; - grub_uint32_t ctime_sec; + grub_uint32_t atime_usec; + grub_uint32_t mtime_usec; + grub_uint32_t ctime_usec; grub_uint32_t create_time_sec; grub_uint32_t gen; grub_uint32_t kernel_flags; @@ -158,7 +188,7 @@ struct grub_ufs_inode }; grub_uint8_t unused[24]; -} __attribute__ ((packed)); +} GRUB_PACKED; #else /* UFS inode. */ struct grub_ufs_inode @@ -167,10 +197,13 @@ struct grub_ufs_inode grub_uint16_t nlinks; grub_uint16_t uid; grub_uint16_t gid; - grub_int64_t size; - grub_uint64_t atime; - grub_uint64_t mtime; - grub_uint64_t ctime; + grub_uint64_t size; + grub_uint32_t atime; + grub_uint32_t atime_usec; + grub_uint32_t mtime; + grub_uint32_t mtime_usec; + grub_uint32_t ctime; + grub_uint32_t ctime_usec; union { struct @@ -185,7 +218,7 @@ struct grub_ufs_inode grub_uint32_t gen; grub_uint32_t unused; grub_uint8_t pad[12]; -} __attribute__ ((packed)); +} GRUB_PACKED; #endif /* Directory entry. */ @@ -202,7 +235,7 @@ struct grub_ufs_dirent grub_uint8_t namelen_bsd; }; }; -} __attribute__ ((packed)); +} GRUB_PACKED; /* Information about a "mounted" ufs filesystem. */ struct grub_ufs_data @@ -212,6 +245,7 @@ struct grub_ufs_data struct grub_ufs_inode inode; int ino; int linknest; + int log2_blksz; }; static grub_dl_t my_mod; @@ -222,57 +256,80 @@ static grub_err_t grub_ufs_find_file (struct grub_ufs_data *data, static grub_disk_addr_t -grub_ufs_get_file_block (struct grub_ufs_data *data, unsigned int blk) +grub_ufs_get_file_block (struct grub_ufs_data *data, grub_disk_addr_t blk) { - struct grub_ufs_sblock *sblock = &data->sblock; - unsigned int indirsz; - int log2_blksz; + unsigned long indirsz; + int log2_blksz, log_indirsz; /* Direct. */ if (blk < GRUB_UFS_DIRBLKS) return INODE_DIRBLOCKS (data, blk); - log2_blksz = grub_le_to_cpu32 (data->sblock.log2_blksz); + log2_blksz = grub_ufs_to_cpu32 (data->sblock.log2_blksz); blk -= GRUB_UFS_DIRBLKS; - indirsz = UFS_BLKSZ (sblock) / INODE_BLKSZ; + log_indirsz = data->log2_blksz - LOG_INODE_BLKSZ; + indirsz = 1 << log_indirsz; + /* Single indirect block. */ if (blk < indirsz) { -#ifdef MODE_UFS2 - grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)]; -#else - grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)]; -#endif - grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 0) << log2_blksz, - 0, sizeof (indir), indir); - return indir[blk]; + grub_ufs_blk_t indir; + grub_disk_read (data->disk, + ((grub_disk_addr_t) INODE_INDIRBLOCKS (data, 0)) + << log2_blksz, + blk * sizeof (indir), sizeof (indir), &indir); + return indir; } blk -= indirsz; /* Double indirect block. */ - if (blk < indirsz * indirsz) + if (blk < (grub_disk_addr_t) indirsz * (grub_disk_addr_t) indirsz) { -#ifdef MODE_UFS2 - grub_uint64_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint64_t)]; -#else - grub_uint32_t indir[UFS_BLKSZ (sblock) / sizeof (grub_uint32_t)]; -#endif + grub_ufs_blk_t indir; - grub_disk_read (data->disk, INODE_INDIRBLOCKS (data, 1) << log2_blksz, - 0, sizeof (indir), indir); grub_disk_read (data->disk, - (indir [blk / indirsz]) + ((grub_disk_addr_t) INODE_INDIRBLOCKS (data, 1)) << log2_blksz, - 0, sizeof (indir), indir); + (blk >> log_indirsz) * sizeof (indir), + sizeof (indir), &indir); + grub_disk_read (data->disk, + grub_ufs_to_cpu_blk (indir) << log2_blksz, + (blk & ((1 << log_indirsz) - 1)) * sizeof (indir), + sizeof (indir), &indir); - return indir[blk % indirsz]; + return indir; } + blk -= (grub_disk_addr_t) indirsz * (grub_disk_addr_t) indirsz; - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "ufs does not support triple indirect blocks"); + /* Triple indirect block. */ + if (!(blk >> (3 * log_indirsz))) + { + grub_ufs_blk_t indir; + + grub_disk_read (data->disk, + ((grub_disk_addr_t) INODE_INDIRBLOCKS (data, 2)) + << log2_blksz, + (blk >> (2 * log_indirsz)) * sizeof (indir), + sizeof (indir), &indir); + grub_disk_read (data->disk, + grub_ufs_to_cpu_blk (indir) << log2_blksz, + ((blk >> log_indirsz) + & ((1 << log_indirsz) - 1)) * sizeof (indir), + sizeof (indir), &indir); + + grub_disk_read (data->disk, + grub_ufs_to_cpu_blk (indir) << log2_blksz, + (blk & ((1 << log_indirsz) - 1)) * sizeof (indir), + sizeof (indir), &indir); + + return indir; + } + + grub_error (GRUB_ERR_BAD_FS, + "ufs does not support quadruple indirect blocks"); return 0; } @@ -281,28 +338,29 @@ grub_ufs_get_file_block (struct grub_ufs_data *data, unsigned int blk) POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_ufs_read_file (struct grub_ufs_data *data, - void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, - unsigned offset, unsigned length), - int pos, grub_size_t len, char *buf) + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf) { struct grub_ufs_sblock *sblock = &data->sblock; - int i; - int blockcnt; + grub_off_t i; + grub_off_t blockcnt; /* Adjust len so it we can't read past the end of the file. */ if (len + pos > INODE_SIZE (data)) len = INODE_SIZE (data) - pos; - blockcnt = (len + pos + UFS_BLKSZ (sblock) - 1) / UFS_BLKSZ (sblock); + blockcnt = (len + pos + UFS_BLKSZ (sblock) - 1) >> UFS_LOG_BLKSZ (sblock); - for (i = pos / UFS_BLKSZ (sblock); i < blockcnt; i++) + for (i = pos >> UFS_LOG_BLKSZ (sblock); i < blockcnt; i++) { - int blknr; - int blockoff = pos % UFS_BLKSZ (sblock); - int blockend = UFS_BLKSZ (sblock); + grub_disk_addr_t blknr; + grub_off_t blockoff; + grub_off_t blockend = UFS_BLKSZ (sblock); int skipfirst = 0; + blockoff = pos & (UFS_BLKSZ (sblock) - 1); + blknr = grub_ufs_get_file_block (data, i); if (grub_errno) return -1; @@ -310,14 +368,14 @@ grub_ufs_read_file (struct grub_ufs_data *data, /* Last block. */ if (i == blockcnt - 1) { - blockend = (len + pos) % UFS_BLKSZ (sblock); + blockend = (len + pos) & (UFS_BLKSZ (sblock) - 1); if (!blockend) blockend = UFS_BLKSZ (sblock); } /* First block. */ - if (i == (pos / (int) UFS_BLKSZ (sblock))) + if (i == (pos >> UFS_LOG_BLKSZ (sblock))) { skipfirst = blockoff; blockend -= skipfirst; @@ -328,15 +386,16 @@ grub_ufs_read_file (struct grub_ufs_data *data, if (blknr) { data->disk->read_hook = read_hook; + data->disk->read_hook_data = read_hook_data; grub_disk_read (data->disk, - blknr << grub_le_to_cpu32 (data->sblock.log2_blksz), + blknr << grub_ufs_to_cpu32 (data->sblock.log2_blksz), skipfirst, blockend, buf); data->disk->read_hook = 0; if (grub_errno) return -1; } else - grub_memset (buf, UFS_BLKSZ (sblock) - skipfirst, 0); + grub_memset (buf, 0, blockend); buf += UFS_BLKSZ (sblock) - skipfirst; } @@ -352,17 +411,17 @@ grub_ufs_read_inode (struct grub_ufs_data *data, int ino, char *inode) struct grub_ufs_sblock *sblock = &data->sblock; /* Determine the group the inode is in. */ - int group = ino / grub_le_to_cpu32 (sblock->ino_per_group); + int group = ino / grub_ufs_to_cpu32 (sblock->ino_per_group); /* Determine the inode within the group. */ - int grpino = ino % grub_le_to_cpu32 (sblock->ino_per_group); + int grpino = ino % grub_ufs_to_cpu32 (sblock->ino_per_group); /* The first block of the group. */ - int grpblk = group * (grub_le_to_cpu32 (sblock->frags_per_group)); + int grpblk = group * (grub_ufs_to_cpu32 (sblock->frags_per_group)); #ifndef MODE_UFS2 - grpblk += grub_le_to_cpu32 (sblock->cylg_offset) - * (group & (~grub_le_to_cpu32 (sblock->cylg_mask))); + grpblk += grub_ufs_to_cpu32 (sblock->cylg_offset) + * (group & (~grub_ufs_to_cpu32 (sblock->cylg_mask))); #endif if (!inode) @@ -372,8 +431,8 @@ grub_ufs_read_inode (struct grub_ufs_data *data, int ino, char *inode) } grub_disk_read (data->disk, - ((grub_le_to_cpu32 (sblock->inoblk_offs) + grpblk) - << grub_le_to_cpu32 (data->sblock.log2_blksz)) + ((grub_ufs_to_cpu32 (sblock->inoblk_offs) + grpblk) + << grub_ufs_to_cpu32 (data->sblock.log2_blksz)) + grpino / UFS_INODE_PER_BLOCK, (grpino % UFS_INODE_PER_BLOCK) * sizeof (struct grub_ufs_inode), @@ -389,21 +448,32 @@ grub_ufs_read_inode (struct grub_ufs_data *data, int ino, char *inode) static grub_err_t grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino) { - char symlink[INODE_SIZE (data)]; + char *symlink; + grub_size_t sz = INODE_SIZE (data); if (++data->linknest > GRUB_UFS_MAX_SYMLNK_CNT) - return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks"); + return grub_error (GRUB_ERR_SYMLINK_LOOP, N_("too deep nesting of symlinks")); - if (INODE_NBLOCKS (data) == 0) - grub_strcpy (symlink, (char *) INODE (data, symlink)); + symlink = grub_malloc (sz + 1); + if (!symlink) + return grub_errno; + /* Normally we should just check that data->inode.nblocks == 0. + However old Linux doesn't maintain nblocks correctly and so it's always + 0. If size is bigger than inline space then the symlink is surely not + inline. */ + /* Check against zero is paylindromic, no need to swap. */ + if (data->inode.nblocks == 0 + && INODE_SIZE (data) <= sizeof (data->inode.symlink)) + grub_strlcpy (symlink, (char *) data->inode.symlink, sz); else { - grub_disk_read (data->disk, - (INODE_DIRBLOCKS (data, 0) - << grub_le_to_cpu32 (data->sblock.log2_blksz)), - 0, INODE_SIZE (data), symlink); - symlink[INODE_SIZE (data)] = '\0'; + if (grub_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0) + { + grub_free(symlink); + return grub_errno; + } } + symlink[sz] = '\0'; /* The symlink is an absolute path, go back to the root inode. */ if (symlink[0] == '/') @@ -411,11 +481,14 @@ grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino) /* Now load in the old inode. */ if (grub_ufs_read_inode (data, ino, 0)) - return grub_errno; + { + grub_free (symlink); + return grub_errno; + } grub_ufs_find_file (data, symlink); - if (grub_errno) - grub_error (grub_errno, "cannot follow symlink `%s'", symlink); + + grub_free (symlink); return grub_errno; } @@ -426,93 +499,91 @@ grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino) static grub_err_t grub_ufs_find_file (struct grub_ufs_data *data, const char *path) { - char fpath[grub_strlen (path) + 1]; - char *name = fpath; - char *next; + const char *name; + const char *next = path; unsigned int pos = 0; int dirino; + char *filename; - grub_strcpy (fpath, path); + /* We reject filenames longer than the one we're looking + for without reading, so this allocation is enough. */ + filename = grub_malloc (grub_strlen (path) + 2); + if (!filename) + return grub_errno; - /* Skip the first slash. */ - if (name[0] == '/') - { - name++; - if (!*name) - return 0; - } - - /* Extract the actual part from the pathname. */ - next = grub_strchr (name, '/'); - if (next) - { - next[0] = '\0'; - next++; - } - - do + while (1) { struct grub_ufs_dirent dirent; - int namelen; - if (grub_strlen (name) == 0) - return GRUB_ERR_NONE; + name = next; + /* Skip the first slash. */ + while (*name == '/') + name++; + if (*name == 0) + { + grub_free (filename); + return GRUB_ERR_NONE; + } - if (grub_ufs_read_file (data, 0, pos, sizeof (dirent), - (char *) &dirent) < 0) - return grub_errno; + if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) + != GRUB_UFS_ATTR_DIR) + { + grub_error (GRUB_ERR_BAD_FILE_TYPE, + N_("not a directory")); + goto fail; + } + + /* Extract the actual part from the pathname. */ + for (next = name; *next && *next != '/'; next++); + for (pos = 0; ; pos += grub_ufs_to_cpu16 (dirent.direntlen)) + { + int namelen; + + if (pos >= INODE_SIZE (data)) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("file `%s' not found"), + path); + goto fail; + } + + if (grub_ufs_read_file (data, 0, 0, pos, sizeof (dirent), + (char *) &dirent) < 0) + goto fail; #ifdef MODE_UFS2 - namelen = dirent.namelen_bsd; + namelen = dirent.namelen_bsd; #else - namelen = grub_le_to_cpu16 (dirent.namelen); + namelen = grub_ufs_to_cpu16 (dirent.namelen); #endif - { - char filename[namelen + 1]; - - if (grub_ufs_read_file (data, 0, pos + sizeof (dirent), - namelen, filename) < 0) - return grub_errno; - - filename[namelen] = '\0'; - - if (!grub_strcmp (name, filename)) - { - dirino = data->ino; - grub_ufs_read_inode (data, grub_le_to_cpu32 (dirent.ino), 0); - - if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) - == GRUB_UFS_ATTR_LNK) - { - grub_ufs_lookup_symlink (data, dirino); - if (grub_errno) - return grub_errno; - } - - if (!next) - return 0; - - pos = 0; - - name = next; - next = grub_strchr (name, '/'); - if (next) - { - next[0] = '\0'; - next++; - } - - if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); - + if (namelen < next - name) continue; - } - } - pos += grub_le_to_cpu16 (dirent.direntlen); - } while (pos < INODE_SIZE (data)); + if (grub_ufs_read_file (data, 0, 0, pos + sizeof (dirent), + next - name + (namelen != next - name), + filename) < 0) + goto fail; - grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found"); + if (grub_strncmp (name, filename, next - name) == 0 + && (namelen == next - name || filename[next - name] == '\0')) + { + dirino = data->ino; + grub_ufs_read_inode (data, grub_ufs_to_cpu32 (dirent.ino), 0); + + if ((INODE_MODE(data) & GRUB_UFS_ATTR_TYPE) + == GRUB_UFS_ATTR_LNK) + { + grub_ufs_lookup_symlink (data, dirino); + if (grub_errno) + goto fail; + } + + break; + } + } + } + fail: + grub_free (filename); return grub_errno; } @@ -536,8 +607,17 @@ grub_ufs_mount (grub_disk_t disk) if (grub_errno) goto fail; - if (grub_le_to_cpu32 (data->sblock.magic) == GRUB_UFS_MAGIC) + /* No need to byteswap bsize in this check. It works the same on both + endiannesses. */ + if (data->sblock.magic == grub_cpu_to_ufs32_compile_time (GRUB_UFS_MAGIC) + && data->sblock.bsize != 0 + && ((data->sblock.bsize & (data->sblock.bsize - 1)) == 0) + && data->sblock.ino_per_group != 0) { + for (data->log2_blksz = 0; + (1U << data->log2_blksz) < grub_ufs_to_cpu32 (data->sblock.bsize); + data->log2_blksz++); + data->disk = disk; data->linknest = 0; return data; @@ -564,11 +644,9 @@ grub_ufs_mount (grub_disk_t disk) static grub_err_t grub_ufs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { struct grub_ufs_data *data; - struct grub_ufs_sblock *sblock; unsigned int pos = 0; data = grub_ufs_mount (device->disk); @@ -579,11 +657,9 @@ grub_ufs_dir (grub_device_t device, const char *path, if (grub_errno) return grub_errno; - sblock = &data->sblock; - if (!path || path[0] != '/') { - grub_error (GRUB_ERR_BAD_FILENAME, "bad filename"); + grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path); return grub_errno; } @@ -593,7 +669,7 @@ grub_ufs_dir (grub_device_t device, const char *path, if ((INODE_MODE (data) & GRUB_UFS_ATTR_TYPE) != GRUB_UFS_ATTR_DIR) { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory"); + grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); goto fail; } @@ -602,40 +678,56 @@ grub_ufs_dir (grub_device_t device, const char *path, struct grub_ufs_dirent dirent; int namelen; - if (grub_ufs_read_file (data, 0, pos, sizeof (dirent), + if (grub_ufs_read_file (data, 0, 0, pos, sizeof (dirent), (char *) &dirent) < 0) break; + if (dirent.direntlen == 0) + break; + #ifdef MODE_UFS2 namelen = dirent.namelen_bsd; #else - namelen = grub_le_to_cpu16 (dirent.namelen); + namelen = grub_ufs_to_cpu16 (dirent.namelen); #endif - { - char filename[namelen + 1]; - struct grub_dirhook_info info; - struct grub_ufs_inode inode; + char *filename = grub_malloc (namelen + 1); + if (!filename) + goto fail; + struct grub_dirhook_info info; + struct grub_ufs_inode inode; - grub_memset (&info, 0, sizeof (info)); + grub_memset (&info, 0, sizeof (info)); - if (grub_ufs_read_file (data, 0, pos + sizeof (dirent), - namelen, filename) < 0) + if (grub_ufs_read_file (data, 0, 0, pos + sizeof (dirent), + namelen, filename) < 0) + { + grub_free (filename); break; + } - filename[namelen] = '\0'; - grub_ufs_read_inode (data, dirent.ino, (char *) &inode); + filename[namelen] = '\0'; + grub_ufs_read_inode (data, grub_ufs_to_cpu32 (dirent.ino), + (char *) &inode); - info.dir = ((grub_le_to_cpu16 (inode.mode) & GRUB_UFS_ATTR_TYPE) - == GRUB_UFS_ATTR_DIR); - info.mtime = grub_le_to_cpu64 (inode.mtime); - info.mtimeset = 1; + info.dir = ((grub_ufs_to_cpu16 (inode.mode) & GRUB_UFS_ATTR_TYPE) + == GRUB_UFS_ATTR_DIR); +#ifdef MODE_UFS2 + info.mtime = grub_ufs_to_cpu64 (inode.mtime); +#else + info.mtime = grub_ufs_to_cpu32 (inode.mtime); +#endif + info.mtimeset = 1; - if (hook (filename, &info)) + if (hook (filename, &info, hook_data)) + { + grub_free (filename); break; - } + } - pos += grub_le_to_cpu16 (dirent.direntlen); + grub_free (filename); + + pos += grub_ufs_to_cpu16 (dirent.direntlen); } fail: @@ -663,7 +755,7 @@ grub_ufs_open (struct grub_file *file, const char *name) if (!name || name[0] != '/') { - grub_error (GRUB_ERR_BAD_FILENAME, "bad filename"); + grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), name); return grub_errno; } @@ -687,7 +779,8 @@ grub_ufs_read (grub_file_t file, char *buf, grub_size_t len) struct grub_ufs_data *data = (struct grub_ufs_data *) file->data; - return grub_ufs_read_file (data, file->read_hook, file->offset, len, buf); + return grub_ufs_read_file (data, file->read_hook, file->read_hook_data, + file->offset, len, buf); } @@ -699,8 +792,6 @@ grub_ufs_close (grub_file_t file) return GRUB_ERR_NONE; } - -#ifdef MODE_UFS2 static grub_err_t grub_ufs_label (grub_device_t device, char **label) { @@ -720,7 +811,6 @@ grub_ufs_label (grub_device_t device, char **label) return grub_errno; } -#endif static grub_err_t grub_ufs_uuid (grub_device_t device, char **uuid) @@ -733,8 +823,8 @@ grub_ufs_uuid (grub_device_t device, char **uuid) data = grub_ufs_mount (disk); if (data && (data->sblock.uuidhi != 0 || data->sblock.uuidlow != 0)) *uuid = grub_xasprintf ("%08x%08x", - (unsigned) grub_le_to_cpu32 (data->sblock.uuidhi), - (unsigned) grub_le_to_cpu32 (data->sblock.uuidlow)); + (unsigned) grub_ufs_to_cpu32 (data->sblock.uuidhi), + (unsigned) grub_ufs_to_cpu32 (data->sblock.uuidlow)); else *uuid = NULL; @@ -748,7 +838,7 @@ grub_ufs_uuid (grub_device_t device, char **uuid) /* Get mtime. */ static grub_err_t -grub_ufs_mtime (grub_device_t device, grub_int32_t *tm) +grub_ufs_mtime (grub_device_t device, grub_int64_t *tm) { struct grub_ufs_data *data = 0; @@ -758,11 +848,13 @@ grub_ufs_mtime (grub_device_t device, grub_int32_t *tm) if (!data) *tm = 0; else + { + *tm = grub_ufs_to_cpu32 (data->sblock.mtime); #ifdef MODE_UFS2 - *tm = grub_le_to_cpu64 (data->sblock.mtime2); -#else - *tm = grub_le_to_cpu32 (data->sblock.mtime); + if (*tm < (grub_int64_t) grub_ufs_to_cpu64 (data->sblock.mtime2)) + *tm = grub_ufs_to_cpu64 (data->sblock.mtime2); #endif + } grub_dl_unref (my_mod); @@ -777,37 +869,56 @@ static struct grub_fs grub_ufs_fs = { #ifdef MODE_UFS2 .name = "ufs2", +#else +#ifdef MODE_BIGENDIAN + .name = "ufs1_be", #else .name = "ufs1", #endif - .dir = grub_ufs_dir, - .open = grub_ufs_open, - .read = grub_ufs_read, - .close = grub_ufs_close, -#ifdef MODE_UFS2 - .label = grub_ufs_label, #endif - .uuid = grub_ufs_uuid, - .mtime = grub_ufs_mtime, + .fs_dir = grub_ufs_dir, + .fs_open = grub_ufs_open, + .fs_read = grub_ufs_read, + .fs_close = grub_ufs_close, + .fs_label = grub_ufs_label, + .fs_uuid = grub_ufs_uuid, + .fs_mtime = grub_ufs_mtime, + /* FIXME: set reserved_first_sector. */ +#ifdef GRUB_UTIL + .blocklist_install = 1, +#endif .next = 0 }; #ifdef MODE_UFS2 GRUB_MOD_INIT(ufs2) #else +#ifdef MODE_BIGENDIAN +GRUB_MOD_INIT(ufs1_be) +#else GRUB_MOD_INIT(ufs1) #endif +#endif { - grub_fs_register (&grub_ufs_fs); + if (!grub_is_lockdown ()) + { + grub_ufs_fs.mod = mod; + grub_fs_register (&grub_ufs_fs); + } my_mod = mod; } #ifdef MODE_UFS2 GRUB_MOD_FINI(ufs2) #else +#ifdef MODE_BIGENDIAN +GRUB_MOD_FINI(ufs1_be) +#else GRUB_MOD_FINI(ufs1) #endif +#endif { - grub_fs_unregister (&grub_ufs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ufs_fs); } diff --git a/fs/ufs2.c b/grub-core/fs/ufs2.c similarity index 100% rename from fs/ufs2.c rename to grub-core/fs/ufs2.c diff --git a/fs/afs_be.c b/grub-core/fs/ufs_be.c similarity index 59% rename from fs/afs_be.c rename to grub-core/fs/ufs_be.c index 1f1f48fbb..a58f75a99 100644 --- a/fs/afs_be.c +++ b/grub-core/fs/ufs_be.c @@ -1,2 +1,2 @@ #define MODE_BIGENDIAN 1 -#include "afs.c" +#include "ufs.c" diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c new file mode 100644 index 000000000..1bc4017ca --- /dev/null +++ b/grub-core/fs/xfs.c @@ -0,0 +1,1350 @@ +/* xfs.c - XFS. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define XFS_INODE_EXTENTS 9 + +#define XFS_INODE_FORMAT_INO 1 +#define XFS_INODE_FORMAT_EXT 2 +#define XFS_INODE_FORMAT_BTREE 3 + +/* Superblock version field flags */ +#define XFS_SB_VERSION_NUMBITS 0x000f +#define XFS_SB_VERSION_ATTRBIT 0x0010 +#define XFS_SB_VERSION_NLINKBIT 0x0020 +#define XFS_SB_VERSION_QUOTABIT 0x0040 +#define XFS_SB_VERSION_ALIGNBIT 0x0080 +#define XFS_SB_VERSION_DALIGNBIT 0x0100 +#define XFS_SB_VERSION_LOGV2BIT 0x0400 +#define XFS_SB_VERSION_SECTORBIT 0x0800 +#define XFS_SB_VERSION_EXTFLGBIT 0x1000 +#define XFS_SB_VERSION_DIRV2BIT 0x2000 +#define XFS_SB_VERSION_MOREBITSBIT 0x8000 +#define XFS_SB_VERSION_BITS_SUPPORTED \ + (XFS_SB_VERSION_NUMBITS | \ + XFS_SB_VERSION_ATTRBIT | \ + XFS_SB_VERSION_NLINKBIT | \ + XFS_SB_VERSION_QUOTABIT | \ + XFS_SB_VERSION_ALIGNBIT | \ + XFS_SB_VERSION_DALIGNBIT | \ + XFS_SB_VERSION_LOGV2BIT | \ + XFS_SB_VERSION_SECTORBIT | \ + XFS_SB_VERSION_EXTFLGBIT | \ + XFS_SB_VERSION_DIRV2BIT | \ + XFS_SB_VERSION_MOREBITSBIT) + +/* Recognized xfs format versions */ +#define XFS_SB_VERSION_4 4 /* Good old XFS filesystem */ +#define XFS_SB_VERSION_5 5 /* CRC enabled filesystem */ + +/* features2 field flags */ +#define XFS_SB_VERSION2_LAZYSBCOUNTBIT 0x00000002 /* Superblk counters */ +#define XFS_SB_VERSION2_ATTR2BIT 0x00000008 /* Inline attr rework */ +#define XFS_SB_VERSION2_PROJID32BIT 0x00000080 /* 32-bit project ids */ +#define XFS_SB_VERSION2_FTYPE 0x00000200 /* inode type in dir */ +#define XFS_SB_VERSION2_BITS_SUPPORTED \ + (XFS_SB_VERSION2_LAZYSBCOUNTBIT | \ + XFS_SB_VERSION2_ATTR2BIT | \ + XFS_SB_VERSION2_PROJID32BIT | \ + XFS_SB_VERSION2_FTYPE) + +/* Inode flags2 flags */ +#define XFS_DIFLAG2_BIGTIME_BIT 3 +#define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) +#define XFS_DIFLAG2_NREXT64_BIT 4 +#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) + +/* incompat feature flags */ +#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ +#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */ +#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ +#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ +#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ +#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */ +#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */ +#define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 8) /* metadata dir tree */ + +/* + * Directory entries with ftype are explicitly handled by GRUB code. + * + * We do not currently read the inode btrees, so it is safe to read filesystems + * with the XFS_SB_FEAT_INCOMPAT_SPINODES feature. + * + * We do not currently verify metadata UUID, so it is safe to read filesystems + * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature. + * + * We do not currently replay the log, so it is safe to read filesystems + * with the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature. + * + * We do not currently read directory parent pointers, so it is safe to read + * filesystems with the XFS_SB_FEAT_INCOMPAT_PARENT feature. + * + * We do not currently look at realtime or quota metadata, so it is safe to + * read filesystems with the XFS_SB_FEAT_INCOMPAT_METADIR feature. + */ +#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \ + (XFS_SB_FEAT_INCOMPAT_FTYPE | \ + XFS_SB_FEAT_INCOMPAT_SPINODES | \ + XFS_SB_FEAT_INCOMPAT_META_UUID | \ + XFS_SB_FEAT_INCOMPAT_BIGTIME | \ + XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ + XFS_SB_FEAT_INCOMPAT_NREXT64 | \ + XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \ + XFS_SB_FEAT_INCOMPAT_PARENT | \ + XFS_SB_FEAT_INCOMPAT_METADIR) + +struct grub_xfs_sblock +{ + grub_uint8_t magic[4]; + grub_uint32_t bsize; + grub_uint8_t unused1[24]; + grub_uint16_t uuid[8]; + grub_uint8_t unused2[8]; + grub_uint64_t rootino; + grub_uint8_t unused3[20]; + grub_uint32_t agsize; + grub_uint8_t unused4[12]; + grub_uint16_t version; + grub_uint8_t unused5[6]; + grub_uint8_t label[12]; + grub_uint8_t log2_bsize; + grub_uint8_t log2_sect; + grub_uint8_t log2_inode; + grub_uint8_t log2_inop; + grub_uint8_t log2_agblk; + grub_uint8_t unused6[67]; + grub_uint8_t log2_dirblk; + grub_uint8_t unused7[7]; + grub_uint32_t features2; + grub_uint8_t unused8[4]; + grub_uint32_t sb_features_compat; + grub_uint32_t sb_features_ro_compat; + grub_uint32_t sb_features_incompat; + grub_uint32_t sb_features_log_incompat; +} GRUB_PACKED; + +struct grub_xfs_dir_header +{ + grub_uint8_t count; + grub_uint8_t largeino; + union + { + grub_uint32_t i4; + grub_uint64_t i8; + } GRUB_PACKED parent; +} GRUB_PACKED; + +/* Structure for directory entry inlined in the inode */ +struct grub_xfs_dir_entry +{ + grub_uint8_t len; + grub_uint16_t offset; + char name[1]; + /* Inode number follows, 32 / 64 bits. */ +} GRUB_PACKED; + +/* Structure for directory entry in a block */ +struct grub_xfs_dir2_entry +{ + grub_uint64_t inode; + grub_uint8_t len; +} GRUB_PACKED; + +struct grub_xfs_extent +{ + /* This should be a bitfield but bietfields are unportable, so just have + a raw array and functions extracting useful info from it. + */ + grub_uint32_t raw[4]; +} GRUB_PACKED; + +struct grub_xfs_btree_node +{ + grub_uint8_t magic[4]; + grub_uint16_t level; + grub_uint16_t numrecs; + grub_uint64_t left; + grub_uint64_t right; + /* In V5 here follow crc, uuid, etc. */ + /* Then follow keys and block pointers */ +} GRUB_PACKED; + +struct grub_xfs_btree_root +{ + grub_uint16_t level; + grub_uint16_t numrecs; + grub_uint64_t keys[1]; +} GRUB_PACKED; + +struct grub_xfs_time_legacy +{ + grub_uint32_t sec; + grub_uint32_t nanosec; +} GRUB_PACKED; + +/* + * The struct grub_xfs_inode layout was taken from the + * struct xfs_dinode_core which is described here: + * https://mirrors.edge.kernel.org/pub/linux/utils/fs/xfs/docs/xfs_filesystem_structure.pdf + */ +struct grub_xfs_inode +{ + grub_uint8_t magic[2]; + grub_uint16_t mode; + grub_uint8_t version; + grub_uint8_t format; + grub_uint8_t unused2[18]; + grub_uint64_t nextents_big; + grub_uint64_t atime; + grub_uint64_t mtime; + grub_uint64_t ctime; + grub_uint64_t size; + grub_uint64_t nblocks; + grub_uint32_t extsize; + grub_uint32_t nextents; + grub_uint16_t unused3; + grub_uint8_t fork_offset; + grub_uint8_t unused4[17]; /* Last member of inode v2. */ + grub_uint8_t unused5[20]; /* First member of inode v3. */ + grub_uint64_t flags2; + grub_uint8_t unused6[48]; /* Last member of inode v3. */ +} GRUB_PACKED; + +#define XFS_V3_INODE_SIZE sizeof(struct grub_xfs_inode) +/* Size of struct grub_xfs_inode v2, up to unused4 member included. */ +#define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 76) + +struct grub_xfs_dir_leaf_entry +{ + grub_uint32_t hashval; + grub_uint32_t address; +} GRUB_PACKED; + +struct grub_xfs_dirblock_tail +{ + grub_uint32_t leaf_count; + grub_uint32_t leaf_stale; +} GRUB_PACKED; + +struct grub_fshelp_node +{ + struct grub_xfs_data *data; + grub_uint64_t ino; + int inode_read; + struct grub_xfs_inode inode; +}; + +struct grub_xfs_data +{ + grub_size_t data_size; + struct grub_xfs_sblock sblock; + grub_disk_t disk; + int pos; + int bsize; + grub_uint32_t agsize; + unsigned int hasftype:1; + unsigned int hascrc:1; + struct grub_fshelp_node diropen; +}; + +static grub_dl_t my_mod; + + + +static int grub_xfs_sb_hascrc(struct grub_xfs_data *data) +{ + return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) == + grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5); +} + +static int grub_xfs_sb_hasftype(struct grub_xfs_data *data) +{ + if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) == + grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5) && + data->sblock.sb_features_incompat & grub_cpu_to_be32_compile_time(XFS_SB_FEAT_INCOMPAT_FTYPE)) + return 1; + if (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) && + data->sblock.features2 & grub_cpu_to_be32_compile_time(XFS_SB_VERSION2_FTYPE)) + return 1; + return 0; +} + +static int grub_xfs_sb_valid(struct grub_xfs_data *data) +{ + grub_dprintf("xfs", "Validating superblock\n"); + if (grub_strncmp ((char *) (data->sblock.magic), "XFSB", 4) + || data->sblock.log2_bsize < GRUB_DISK_SECTOR_BITS + || ((int) data->sblock.log2_bsize + + (int) data->sblock.log2_dirblk) >= 27) + { + grub_error (GRUB_ERR_BAD_FS, "not a XFS filesystem"); + return 0; + } + if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) == + grub_cpu_to_be16_compile_time(XFS_SB_VERSION_5)) + { + grub_dprintf("xfs", "XFS v5 superblock detected\n"); + if (data->sblock.sb_features_incompat & + grub_cpu_to_be32_compile_time(~XFS_SB_FEAT_INCOMPAT_SUPPORTED)) + { + grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported " + "incompatible features"); + return 0; + } + return 1; + } + else if ((data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) == + grub_cpu_to_be16_compile_time(XFS_SB_VERSION_4)) + { + grub_dprintf("xfs", "XFS v4 superblock detected\n"); + if (!(data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_DIRV2BIT))) + { + grub_error (GRUB_ERR_BAD_FS, "XFS filesystem without V2 directories " + "is unsupported"); + return 0; + } + if (data->sblock.version & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION_BITS_SUPPORTED) || + (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_MOREBITSBIT) && + data->sblock.features2 & grub_cpu_to_be16_compile_time(~XFS_SB_VERSION2_BITS_SUPPORTED))) + { + grub_error (GRUB_ERR_BAD_FS, "XFS filesystem has unsupported version " + "bits"); + return 0; + } + return 1; + } + + grub_error (GRUB_ERR_BAD_FS, "unsupported XFS filesystem version"); + return 0; +} + +static int +grub_xfs_sb_needs_repair (struct grub_xfs_data *data) +{ + return ((data->sblock.version & + grub_cpu_to_be16_compile_time (XFS_SB_VERSION_NUMBITS)) == + grub_cpu_to_be16_compile_time (XFS_SB_VERSION_5) && + (data->sblock.sb_features_incompat & + grub_cpu_to_be32_compile_time (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR))); +} + +/* Filetype information as used in inodes. */ +#define FILETYPE_INO_MASK 0170000 +#define FILETYPE_INO_REG 0100000 +#define FILETYPE_INO_DIRECTORY 0040000 +#define FILETYPE_INO_SYMLINK 0120000 + +static inline int +GRUB_XFS_INO_AGBITS(struct grub_xfs_data *data) +{ + return ((data)->sblock.log2_agblk + (data)->sblock.log2_inop); +} + +static inline grub_uint64_t +GRUB_XFS_INO_INOINAG (struct grub_xfs_data *data, + grub_uint64_t ino) +{ + return (ino & ((1LL << GRUB_XFS_INO_AGBITS (data)) - 1)); +} + +static inline grub_uint64_t +GRUB_XFS_INO_AG (struct grub_xfs_data *data, + grub_uint64_t ino) +{ + return (ino >> GRUB_XFS_INO_AGBITS (data)); +} + +static inline grub_disk_addr_t +GRUB_XFS_FSB_TO_BLOCK (struct grub_xfs_data *data, grub_disk_addr_t fsb) +{ + return ((fsb >> data->sblock.log2_agblk) * data->agsize + + (fsb & ((1LL << data->sblock.log2_agblk) - 1))); +} + +static inline grub_uint64_t +GRUB_XFS_EXTENT_OFFSET (struct grub_xfs_extent *exts, int ex) +{ + return ((grub_be_to_cpu32 (exts[ex].raw[0]) & ~(1 << 31)) << 23 + | grub_be_to_cpu32 (exts[ex].raw[1]) >> 9); +} + +static inline grub_uint64_t +GRUB_XFS_EXTENT_BLOCK (struct grub_xfs_extent *exts, int ex) +{ + return ((grub_uint64_t) (grub_be_to_cpu32 (exts[ex].raw[1]) + & (0x1ff)) << 43 + | (grub_uint64_t) grub_be_to_cpu32 (exts[ex].raw[2]) << 11 + | grub_be_to_cpu32 (exts[ex].raw[3]) >> 21); +} + +static inline grub_uint64_t +GRUB_XFS_EXTENT_SIZE (struct grub_xfs_extent *exts, int ex) +{ + return (grub_be_to_cpu32 (exts[ex].raw[3]) & ((1 << 21) - 1)); +} + + +static inline grub_uint64_t +grub_xfs_inode_block (struct grub_xfs_data *data, + grub_uint64_t ino) +{ + long long int inoinag = GRUB_XFS_INO_INOINAG (data, ino); + long long ag = GRUB_XFS_INO_AG (data, ino); + long long block; + + block = (inoinag >> data->sblock.log2_inop) + ag * data->agsize; + block <<= (data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS); + return block; +} + + +static inline int +grub_xfs_inode_offset (struct grub_xfs_data *data, + grub_uint64_t ino) +{ + int inoag = GRUB_XFS_INO_INOINAG (data, ino); + return ((inoag & ((1 << data->sblock.log2_inop) - 1)) << + data->sblock.log2_inode); +} + +static inline grub_size_t +grub_xfs_inode_size(struct grub_xfs_data *data) +{ + return (grub_size_t)1 << data->sblock.log2_inode; +} + +/* + * Returns size occupied by XFS inode stored in memory - we store struct + * grub_fshelp_node there but on disk inode size may be actually larger than + * struct grub_xfs_inode so we need to account for that so that we can read + * from disk directly into in-memory structure. + */ +static inline grub_size_t +grub_xfs_fshelp_size(struct grub_xfs_data *data) +{ + return sizeof (struct grub_fshelp_node) - sizeof (struct grub_xfs_inode) + + grub_xfs_inode_size(data); +} + +/* This should return void * but XFS code is error-prone with alignment, so + return char to retain cast-align. + */ +static char * +grub_xfs_inode_data(struct grub_xfs_inode *inode) +{ + if (inode->version <= 2) + return ((char *)inode) + XFS_V2_INODE_SIZE; + return ((char *)inode) + XFS_V3_INODE_SIZE; +} + +static struct grub_xfs_dir_entry * +grub_xfs_inline_de(struct grub_xfs_dir_header *head) +{ + /* + With small inode numbers the header is 4 bytes smaller because of + smaller parent pointer + */ + return (struct grub_xfs_dir_entry *) + (((char *) head) + sizeof(struct grub_xfs_dir_header) - + (head->largeino ? 0 : sizeof(grub_uint32_t))); +} + +static grub_uint8_t * +grub_xfs_inline_de_inopos(struct grub_xfs_data *data, + struct grub_xfs_dir_entry *de) +{ + return ((grub_uint8_t *)(de + 1)) + de->len - 1 + (data->hasftype ? 1 : 0); +} + +static struct grub_xfs_dir_entry * +grub_xfs_inline_next_de(struct grub_xfs_data *data, + struct grub_xfs_dir_header *head, + struct grub_xfs_dir_entry *de) +{ + char *p = (char *)de + sizeof(struct grub_xfs_dir_entry) - 1 + de->len; + + p += head->largeino ? sizeof(grub_uint64_t) : sizeof(grub_uint32_t); + if (data->hasftype) + p++; + + return (struct grub_xfs_dir_entry *)p; +} + +static struct grub_xfs_dirblock_tail * +grub_xfs_dir_tail(struct grub_xfs_data *data, void *dirblock) +{ + int dirblksize = 1 << (data->sblock.log2_bsize + data->sblock.log2_dirblk); + + return (struct grub_xfs_dirblock_tail *) + ((char *)dirblock + dirblksize - sizeof (struct grub_xfs_dirblock_tail)); +} + +static struct grub_xfs_dir2_entry * +grub_xfs_first_de(struct grub_xfs_data *data, void *dirblock) +{ + if (data->hascrc) + return (struct grub_xfs_dir2_entry *)((char *)dirblock + 64); + return (struct grub_xfs_dir2_entry *)((char *)dirblock + 16); +} + +static struct grub_xfs_dir2_entry * +grub_xfs_next_de(struct grub_xfs_data *data, struct grub_xfs_dir2_entry *de) +{ + int size = sizeof (struct grub_xfs_dir2_entry) + de->len + 2 /* Tag */; + + if (data->hasftype) + size++; /* File type */ + return (struct grub_xfs_dir2_entry *)(((char *)de) + ALIGN_UP(size, 8)); +} + +/* This should return void * but XFS code is error-prone with alignment, so + return char to retain cast-align. + */ +static char * +grub_xfs_btree_keys(struct grub_xfs_data *data, + struct grub_xfs_btree_node *leaf) +{ + char *keys = (char *)(leaf + 1); + + if (data->hascrc) + keys += 48; /* skip crc, uuid, ... */ + return keys; +} + +static grub_err_t +grub_xfs_read_inode (struct grub_xfs_data *data, grub_uint64_t ino, + struct grub_xfs_inode *inode) +{ + grub_uint64_t block = grub_xfs_inode_block (data, ino); + int offset = grub_xfs_inode_offset (data, ino); + + grub_dprintf("xfs", "Reading inode (%" PRIuGRUB_UINT64_T ") - %" PRIuGRUB_UINT64_T ", %d\n", + ino, block, offset); + /* Read the inode. */ + if (grub_disk_read (data->disk, block, offset, grub_xfs_inode_size(data), + inode)) + return grub_errno; + + if (grub_strncmp ((char *) inode->magic, "IN", 2)) + return grub_error (GRUB_ERR_BAD_FS, "not a correct XFS inode"); + + return 0; +} + +static grub_uint64_t +get_fsb (const void *keys, int idx) +{ + const char *p = (const char *) keys + sizeof(grub_uint64_t) * idx; + return grub_be_to_cpu64 (grub_get_unaligned64 (p)); +} + +static int +grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode) +{ + return inode->version >= 3 && + (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64)); +} + +static grub_uint64_t +grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode) +{ + return (grub_xfs_inode_has_large_extent_counts (inode)) ? + grub_be_to_cpu64 (inode->nextents_big) : + grub_be_to_cpu32 (inode->nextents); +} + +static grub_disk_addr_t +grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) +{ + struct grub_xfs_btree_node *leaf = 0; + grub_uint64_t ex, nrec; + struct grub_xfs_extent *exts; + grub_uint64_t ret = 0; + + if (node->inode.format == XFS_INODE_FORMAT_BTREE) + { + struct grub_xfs_btree_root *root; + const char *keys; + int recoffset; + + leaf = grub_malloc (node->data->bsize); + if (leaf == 0) + return 0; + + root = (struct grub_xfs_btree_root *) grub_xfs_inode_data(&node->inode); + nrec = grub_be_to_cpu16 (root->numrecs); + keys = (char *) &root->keys[0]; + if (node->inode.fork_offset) + recoffset = (node->inode.fork_offset - 1) / 2; + else + recoffset = (grub_xfs_inode_size(node->data) + - ((char *) keys - (char *) &node->inode)) + / (2 * sizeof (grub_uint64_t)); + do + { + grub_uint64_t i; + grub_addr_t keys_end, data_end; + + if (grub_mul (sizeof (grub_uint64_t), nrec, &keys_end) || + grub_add ((grub_addr_t) keys, keys_end, &keys_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + keys_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS root keys"); + grub_free (leaf); + return 0; + } + + for (i = 0; i < nrec; i++) + { + if (fileblock < get_fsb(keys, i)) + break; + } + + /* Sparse block. */ + if (i == 0) + { + grub_free (leaf); + return 0; + } + + if (grub_disk_read (node->data->disk, + GRUB_XFS_FSB_TO_BLOCK (node->data, get_fsb (keys, i - 1 + recoffset)) << (node->data->sblock.log2_bsize - GRUB_DISK_SECTOR_BITS), + 0, node->data->bsize, leaf)) + { + grub_free (leaf); + return 0; + } + + if ((!node->data->hascrc && + grub_strncmp ((char *) leaf->magic, "BMAP", 4)) || + (node->data->hascrc && + grub_strncmp ((char *) leaf->magic, "BMA3", 4))) + { + grub_free (leaf); + grub_error (GRUB_ERR_BAD_FS, "not a correct XFS BMAP node"); + return 0; + } + + nrec = grub_be_to_cpu16 (leaf->numrecs); + keys = grub_xfs_btree_keys(node->data, leaf); + recoffset = ((node->data->bsize - ((char *) keys + - (char *) leaf)) + / (2 * sizeof (grub_uint64_t))); + } + while (leaf->level); + exts = (struct grub_xfs_extent *) keys; + } + else if (node->inode.format == XFS_INODE_FORMAT_EXT) + { + grub_addr_t exts_end = 0; + grub_addr_t data_end = 0; + + nrec = grub_xfs_get_inode_nextents (&node->inode); + exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); + + if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) || + grub_add ((grub_addr_t) node->data, exts_end, &exts_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + exts_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS extents"); + return 0; + } + } + else + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "XFS does not support inode format %d yet", + node->inode.format); + return 0; + } + + /* Iterate over each extent to figure out which extent has + the block we are looking for. */ + for (ex = 0; ex < nrec; ex++) + { + grub_uint64_t start = GRUB_XFS_EXTENT_BLOCK (exts, ex); + grub_uint64_t offset = GRUB_XFS_EXTENT_OFFSET (exts, ex); + grub_uint64_t size = GRUB_XFS_EXTENT_SIZE (exts, ex); + + /* Sparse block. */ + if (fileblock < offset) + break; + else if (fileblock < offset + size) + { + ret = (fileblock - offset + start); + break; + } + } + + grub_free (leaf); + + return GRUB_XFS_FSB_TO_BLOCK(node->data, ret); +} + + +/* Read LEN bytes from the file described by DATA starting with byte + POS. Return the amount of read bytes in READ. */ +static grub_ssize_t +grub_xfs_read_file (grub_fshelp_node_t node, + grub_disk_read_hook_t read_hook, void *read_hook_data, + grub_off_t pos, grub_size_t len, char *buf, grub_uint32_t header_size) +{ + return grub_fshelp_read_file (node->data->disk, node, + read_hook, read_hook_data, + pos, len, buf, grub_xfs_read_block, + grub_be_to_cpu64 (node->inode.size) + header_size, + node->data->sblock.log2_bsize + - GRUB_DISK_SECTOR_BITS, 0); +} + + +static char * +grub_xfs_read_symlink (grub_fshelp_node_t node) +{ + grub_ssize_t size = grub_be_to_cpu64 (node->inode.size); + grub_size_t sz; + + if (size < 0) + { + grub_error (GRUB_ERR_BAD_FS, "invalid symlink"); + return 0; + } + + switch (node->inode.format) + { + case XFS_INODE_FORMAT_INO: + return grub_strndup (grub_xfs_inode_data(&node->inode), size); + + case XFS_INODE_FORMAT_EXT: + { + char *symlink; + grub_ssize_t numread; + int off = 0; + + if (node->data->hascrc) + off = 56; + + if (grub_add (size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); + if (!symlink) + return 0; + + node->inode.size = grub_be_to_cpu64 (size + off); + numread = grub_xfs_read_file (node, 0, 0, off, size, symlink, off); + if (numread != size) + { + grub_free (symlink); + return 0; + } + symlink[size] = '\0'; + return symlink; + } + } + + return 0; +} + + +static enum grub_fshelp_filetype +grub_xfs_mode_to_filetype (grub_uint16_t mode) +{ + if ((grub_be_to_cpu16 (mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_DIRECTORY) + return GRUB_FSHELP_DIR; + else if ((grub_be_to_cpu16 (mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_SYMLINK) + return GRUB_FSHELP_SYMLINK; + else if ((grub_be_to_cpu16 (mode) + & FILETYPE_INO_MASK) == FILETYPE_INO_REG) + return GRUB_FSHELP_REG; + return GRUB_FSHELP_UNKNOWN; +} + + +/* Context for grub_xfs_iterate_dir. */ +struct grub_xfs_iterate_dir_ctx +{ + grub_fshelp_iterate_dir_hook_t hook; + void *hook_data; + struct grub_fshelp_node *diro; +}; + +/* Helper for grub_xfs_iterate_dir. */ +static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename, + struct grub_xfs_iterate_dir_ctx *ctx) +{ + struct grub_fshelp_node *fdiro; + grub_err_t err; + grub_size_t sz; + + if (grub_add (grub_xfs_fshelp_size(ctx->diro->data), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory data size overflow")); + grub_print_error (); + return 0; + } + fdiro = grub_malloc (sz); + if (!fdiro) + { + grub_print_error (); + return 0; + } + + /* The inode should be read, otherwise the filetype can + not be determined. */ + fdiro->ino = ino; + fdiro->inode_read = 1; + fdiro->data = ctx->diro->data; + err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode); + if (err) + { + grub_print_error (); + grub_free (fdiro); + return 0; + } + + return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode), + fdiro, ctx->hook_data); +} + +static int +grub_xfs_iterate_dir (grub_fshelp_node_t dir, + grub_fshelp_iterate_dir_hook_t hook, void *hook_data) +{ + struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir; + struct grub_xfs_iterate_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data, + .diro = diro + }; + + switch (diro->inode.format) + { + case XFS_INODE_FORMAT_INO: + { + struct grub_xfs_dir_header *head = (struct grub_xfs_dir_header *) grub_xfs_inode_data(&diro->inode); + struct grub_xfs_dir_entry *de = grub_xfs_inline_de(head); + int smallino = !head->largeino; + int i; + grub_uint64_t parent; + + /* If small inode numbers are used to pack the direntry, the + parent inode number is small too. */ + if (smallino) + parent = grub_be_to_cpu32 (head->parent.i4); + else + parent = grub_be_to_cpu64 (head->parent.i8); + + /* Synthesize the direntries for `.' and `..'. */ + if (iterate_dir_call_hook (diro->ino, ".", &ctx)) + return 1; + + if (iterate_dir_call_hook (parent, "..", &ctx)) + return 1; + + for (i = 0; i < head->count && + (grub_uint8_t *) de < ((grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)); i++) + { + grub_uint64_t ino; + grub_uint8_t *inopos = grub_xfs_inline_de_inopos(dir->data, de); + grub_uint8_t c; + + if ((inopos + (smallino ? 4 : 8)) > (grub_uint8_t *) dir + grub_xfs_fshelp_size (dir->data)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid XFS inode"); + return 0; + } + + + /* inopos might be unaligned. */ + if (smallino) + ino = (((grub_uint32_t) inopos[0]) << 24) + | (((grub_uint32_t) inopos[1]) << 16) + | (((grub_uint32_t) inopos[2]) << 8) + | (((grub_uint32_t) inopos[3]) << 0); + else + ino = (((grub_uint64_t) inopos[0]) << 56) + | (((grub_uint64_t) inopos[1]) << 48) + | (((grub_uint64_t) inopos[2]) << 40) + | (((grub_uint64_t) inopos[3]) << 32) + | (((grub_uint64_t) inopos[4]) << 24) + | (((grub_uint64_t) inopos[5]) << 16) + | (((grub_uint64_t) inopos[6]) << 8) + | (((grub_uint64_t) inopos[7]) << 0); + + c = de->name[de->len]; + de->name[de->len] = '\0'; + if (iterate_dir_call_hook (ino, de->name, &ctx)) + { + de->name[de->len] = c; + return 1; + } + de->name[de->len] = c; + + de = grub_xfs_inline_next_de(dir->data, head, de); + } + break; + } + + case XFS_INODE_FORMAT_BTREE: + case XFS_INODE_FORMAT_EXT: + { + grub_ssize_t numread; + char *dirblock; + grub_uint64_t blk; + int dirblk_size, dirblk_log2; + + dirblk_log2 = (dir->data->sblock.log2_bsize + + dir->data->sblock.log2_dirblk); + dirblk_size = 1 << dirblk_log2; + + dirblock = grub_malloc (dirblk_size); + if (! dirblock) + return 0; + + /* Iterate over every block the directory has. */ + for (blk = 0; + blk < (grub_be_to_cpu64 (dir->inode.size) + >> dirblk_log2); + blk++) + { + struct grub_xfs_dir2_entry *direntry = + grub_xfs_first_de(dir->data, dirblock); + int entries = -1; + char *end = dirblock + dirblk_size; + grub_uint32_t magic; + + numread = grub_xfs_read_file (dir, 0, 0, + blk << dirblk_log2, + dirblk_size, dirblock, 0); + if (numread != dirblk_size) + { + grub_free (dirblock); + return 0; + } + + /* + * If this data block isn't actually part of the extent list then + * grub_xfs_read_file() returns a block of zeros. So, if the magic + * number field is all zeros then this block should be skipped. + */ + magic = *(grub_uint32_t *)(void *) dirblock; + if (!magic) + continue; + + /* + * Leaf and tail information are only in the data block if the number + * of extents is 1. + */ + if (grub_xfs_get_inode_nextents (&dir->inode) == 1) + { + struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); + + end = (char *) tail; + + /* Subtract the space used by leaf nodes. */ + end -= grub_be_to_cpu32 (tail->leaf_count) * sizeof (struct grub_xfs_dir_leaf_entry); + + entries = grub_be_to_cpu32 (tail->leaf_count) - grub_be_to_cpu32 (tail->leaf_stale); + + if (!entries) + continue; + } + + /* Iterate over all entries within this block. */ + while ((char *) direntry < (char *) end) + { + grub_uint8_t *freetag; + char *filename; + + freetag = (grub_uint8_t *) direntry; + + if (grub_get_unaligned16 (freetag) == 0XFFFF) + { + grub_uint8_t *skip = (freetag + sizeof (grub_uint16_t)); + + /* This entry is not used, go to the next one. */ + direntry = (struct grub_xfs_dir2_entry *) + (((char *)direntry) + + grub_be_to_cpu16 (grub_get_unaligned16 (skip))); + + continue; + } + + filename = (char *)(direntry + 1); + if (filename + direntry->len + 1 > (char *) end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid XFS directory entry"); + return 0; + } + + /* The byte after the filename is for the filetype, padding, or + tag, which is not used by GRUB. So it can be overwritten. */ + filename[direntry->len] = '\0'; + + if (iterate_dir_call_hook (grub_be_to_cpu64(direntry->inode), + filename, &ctx)) + { + grub_free (dirblock); + return 1; + } + + /* + * The expected number of directory entries is only tracked for the + * single extent case. + */ + if (grub_xfs_get_inode_nextents (&dir->inode) == 1) + { + /* Check if last direntry in this block is reached. */ + entries--; + if (!entries) + break; + } + + /* Select the next directory entry. */ + direntry = grub_xfs_next_de(dir->data, direntry); + } + } + grub_free (dirblock); + break; + } + + default: + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "XFS does not support inode format %d yet", + diro->inode.format); + } + return 0; +} + + +static struct grub_xfs_data * +grub_xfs_mount (grub_disk_t disk) +{ + struct grub_xfs_data *data = 0; + grub_size_t sz; + + data = grub_zalloc (sizeof (struct grub_xfs_data)); + if (!data) + return 0; + + data->data_size = sizeof (struct grub_xfs_data); + + grub_dprintf("xfs", "Reading sb\n"); + /* Read the superblock. */ + if (grub_disk_read (disk, 0, 0, + sizeof (struct grub_xfs_sblock), &data->sblock)) + goto fail; + + if (!grub_xfs_sb_valid(data)) + goto fail; + + if (grub_xfs_sb_needs_repair (data)) + grub_dprintf ("xfs", "XFS filesystem needs repair, boot may fail\n"); + + if (grub_add (grub_xfs_inode_size (data), + sizeof (struct grub_xfs_data) - sizeof (struct grub_xfs_inode) + 1, &sz)) + goto fail; + + data = grub_realloc (data, sz); + + if (! data) + goto fail; + + data->data_size = sz; + data->diropen.data = data; + data->diropen.ino = grub_be_to_cpu64(data->sblock.rootino); + data->diropen.inode_read = 1; + data->bsize = grub_be_to_cpu32 (data->sblock.bsize); + data->agsize = grub_be_to_cpu32 (data->sblock.agsize); + data->hasftype = grub_xfs_sb_hasftype(data); + data->hascrc = grub_xfs_sb_hascrc(data); + + data->disk = disk; + data->pos = 0; + grub_dprintf("xfs", "Reading root ino %" PRIuGRUB_UINT64_T "\n", + grub_cpu_to_be64(data->sblock.rootino)); + + grub_xfs_read_inode (data, data->diropen.ino, &data->diropen.inode); + + return data; + fail: + + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem"); + + grub_free (data); + + return 0; +} + + +/* Context for grub_xfs_dir. */ +struct grub_xfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; +}; + +/* Bigtime inodes helpers. */ +#define XFS_BIGTIME_EPOCH_OFFSET (-(grub_int64_t) GRUB_INT32_MIN) + +static int grub_xfs_inode_has_bigtime (const struct grub_xfs_inode *inode) +{ + return inode->version >= 3 && + (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_BIGTIME)); +} + +static grub_int64_t +grub_xfs_get_inode_time (struct grub_xfs_inode *inode) +{ + struct grub_xfs_time_legacy *lts; + + if (grub_xfs_inode_has_bigtime (inode)) + return grub_divmod64 (grub_be_to_cpu64 (inode->mtime), NSEC_PER_SEC, NULL) - XFS_BIGTIME_EPOCH_OFFSET; + + lts = (struct grub_xfs_time_legacy *) &inode->mtime; + return grub_be_to_cpu32 (lts->sec); +} + +/* Helper for grub_xfs_dir. */ +static int +grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, + grub_fshelp_node_t node, void *data) +{ + struct grub_xfs_dir_ctx *ctx = data; + struct grub_dirhook_info info; + + grub_memset (&info, 0, sizeof (info)); + if (node->inode_read) + { + info.mtimeset = 1; + info.mtime = grub_xfs_get_inode_time (&node->inode); + } + info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); + grub_free (node); + return ctx->hook (filename, &info, ctx->hook_data); +} + +static grub_err_t +grub_xfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_xfs_dir_ctx ctx = { hook, hook_data }; + struct grub_xfs_data *data = 0; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_xfs_mount (device->disk); + if (!data) + goto mount_fail; + + grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_xfs_iterate_dir, + grub_xfs_read_symlink, GRUB_FSHELP_DIR); + if (grub_errno) + goto fail; + + grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx); + + fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + mount_fail: + + grub_dl_unref (my_mod); + + return grub_errno; +} + + +/* Open a file named NAME and initialize FILE. */ +static grub_err_t +grub_xfs_open (struct grub_file *file, const char *name) +{ + struct grub_xfs_data *data; + struct grub_fshelp_node *fdiro = 0; + + grub_dl_ref (my_mod); + + data = grub_xfs_mount (file->device->disk); + if (!data) + goto mount_fail; + + grub_fshelp_find_file (name, &data->diropen, &fdiro, grub_xfs_iterate_dir, + grub_xfs_read_symlink, GRUB_FSHELP_REG); + if (grub_errno) + goto fail; + + if (!fdiro->inode_read) + { + grub_xfs_read_inode (data, fdiro->ino, &fdiro->inode); + if (grub_errno) + goto fail; + } + + if (fdiro != &data->diropen) + { + grub_memcpy (&data->diropen, fdiro, grub_xfs_fshelp_size(data)); + grub_free (fdiro); + } + + file->size = grub_be_to_cpu64 (data->diropen.inode.size); + file->data = data; + file->offset = 0; + + return 0; + + fail: + if (fdiro != &data->diropen) + grub_free (fdiro); + grub_free (data); + + mount_fail: + grub_dl_unref (my_mod); + + return grub_errno; +} + + +static grub_ssize_t +grub_xfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_xfs_data *data = + (struct grub_xfs_data *) file->data; + + return grub_xfs_read_file (&data->diropen, + file->read_hook, file->read_hook_data, + file->offset, len, buf, 0); +} + + +static grub_err_t +grub_xfs_close (grub_file_t file) +{ + grub_free (file->data); + + grub_dl_unref (my_mod); + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_xfs_label (grub_device_t device, char **label) +{ + struct grub_xfs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_xfs_mount (disk); + if (data) + *label = grub_strndup ((char *) (data->sblock.label), 12); + else + *label = 0; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + +static grub_err_t +grub_xfs_uuid (grub_device_t device, char **uuid) +{ + struct grub_xfs_data *data; + grub_disk_t disk = device->disk; + + grub_dl_ref (my_mod); + + data = grub_xfs_mount (disk); + if (data) + { + *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); + } + else + *uuid = NULL; + + grub_dl_unref (my_mod); + + grub_free (data); + + return grub_errno; +} + + + +static struct grub_fs grub_xfs_fs = + { + .name = "xfs", + .fs_dir = grub_xfs_dir, + .fs_open = grub_xfs_open, + .fs_read = grub_xfs_read, + .fs_close = grub_xfs_close, + .fs_label = grub_xfs_label, + .fs_uuid = grub_xfs_uuid, +#ifdef GRUB_UTIL + .reserved_first_sector = 0, + .blocklist_install = 1, +#endif + .next = 0 + }; + +GRUB_MOD_INIT(xfs) +{ + grub_xfs_fs.mod = mod; + grub_fs_register (&grub_xfs_fs); + my_mod = mod; +} + +GRUB_MOD_FINI(xfs) +{ + grub_fs_unregister (&grub_xfs_fs); +} diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c new file mode 100644 index 000000000..bff9d0208 --- /dev/null +++ b/grub-core/fs/zfs/zfs.c @@ -0,0 +1,4576 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2009,2010,2011 Free Software Foundation, Inc. + * Copyright 2010 Sun Microsystems, Inc. + * Copyright (c) 2012 by Delphix. All rights reserved. + * + * 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 . + */ +/* + * The zfs plug-in routines for GRUB are: + * + * zfs_mount() - locates a valid uberblock of the root pool and reads + * in its MOS at the memory address MOS. + * + * zfs_open() - locates a plain file object by following the MOS + * and places its dnode at the memory address DNODE. + * + * zfs_read() - read in the data blocks pointed by the DNODE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define ZPOOL_PROP_BOOTFS "bootfs" + +/* + * For nvlist manipulation. (from nvpair.h) + */ +#define NV_ENCODE_NATIVE 0 +#define NV_ENCODE_XDR 1 +#define NV_BIG_ENDIAN 0 +#define NV_LITTLE_ENDIAN 1 +#define DATA_TYPE_UINT64 8 +#define DATA_TYPE_STRING 9 +#define DATA_TYPE_NVLIST 19 +#define DATA_TYPE_NVLIST_ARRAY 20 + +#define DNODE_NUM_MASK 0xffffffffffffULL + +#ifndef GRUB_UTIL +static grub_dl_t my_mod; +#endif + +#define P2PHASE(x, align) ((x) & ((align) - 1)) + +static inline grub_disk_addr_t +DVA_OFFSET_TO_PHYS_SECTOR (grub_disk_addr_t offset) +{ + return ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT); +} + +/* + * FAT ZAP data structures + */ +#define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ +static inline grub_uint64_t +ZAP_HASH_IDX (grub_uint64_t hash, grub_uint64_t n) +{ + return (((n) == 0) ? 0 : ((hash) >> (64 - (n)))); +} + +#define CHAIN_END 0xffff /* end of the chunk chain */ + +/* + * The amount of space within the chunk available for the array is: + * chunk size - space for type (1) - space for next pointer (2) + */ +#define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) + +static inline int +ZAP_LEAF_HASH_SHIFT (int bs) +{ + return bs - 5; +} + +static inline int +ZAP_LEAF_HASH_NUMENTRIES (int bs) +{ + return 1 << ZAP_LEAF_HASH_SHIFT(bs); +} + +static inline grub_size_t +LEAF_HASH (int bs, grub_uint64_t h, zap_leaf_phys_t *l) +{ + return ((ZAP_LEAF_HASH_NUMENTRIES (bs)-1) + & ((h) >> (64 - ZAP_LEAF_HASH_SHIFT (bs) - l->l_hdr.lh_prefix_len))); +} + +/* + * The amount of space available for chunks is: + * block size shift - hash entry size (2) * number of hash + * entries - header space (2*chunksize) + */ +static inline int +ZAP_LEAF_NUMCHUNKS (int bs) +{ + return (((1U << bs) - 2 * ZAP_LEAF_HASH_NUMENTRIES (bs)) / + ZAP_LEAF_CHUNKSIZE - 2); +} + +/* + * The chunks start immediately after the hash table. The end of the + * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a + * chunk_t. + */ +static inline zap_leaf_chunk_t * +ZAP_LEAF_CHUNK (zap_leaf_phys_t *l, int bs, int idx) +{ + grub_properly_aligned_t *l_entries; + + l_entries = (grub_properly_aligned_t *) ALIGN_UP((grub_addr_t)l->l_hash, sizeof (grub_properly_aligned_t)); + return &((zap_leaf_chunk_t *) (l_entries + + (ZAP_LEAF_HASH_NUMENTRIES(bs) * 2) + / sizeof (grub_properly_aligned_t)))[idx]; +} + +static inline struct zap_leaf_entry * +ZAP_LEAF_ENTRY(zap_leaf_phys_t *l, int bs, int idx) +{ + return &ZAP_LEAF_CHUNK(l, bs, idx)->l_entry; +} + + +/* + * Decompression Entry - lzjb & lz4 + */ + +extern grub_err_t lzjb_decompress (void *, void *, grub_size_t, grub_size_t); + +extern grub_err_t lz4_decompress (void *, void *, grub_size_t, grub_size_t); + +typedef grub_err_t zfs_decomp_func_t (void *s_start, void *d_start, + grub_size_t s_len, grub_size_t d_len); +typedef struct decomp_entry +{ + const char *name; + zfs_decomp_func_t *decomp_func; +} decomp_entry_t; + +/* + * Signature for checksum functions. + */ +typedef void zio_checksum_t(const void *data, grub_uint64_t size, + grub_zfs_endian_t endian, zio_cksum_t *zcp); + +/* + * Information about each checksum function. + */ +typedef struct zio_checksum_info { + zio_checksum_t *ci_func; /* checksum function for each byteorder */ + int ci_correctable; /* number of correctable bits */ + int ci_eck; /* uses zio embedded checksum? */ + const char *ci_name; /* descriptive name */ +} zio_checksum_info_t; + +typedef struct dnode_end +{ + dnode_phys_t dn; + grub_zfs_endian_t endian; +} dnode_end_t; + +struct grub_zfs_device_desc +{ + enum { DEVICE_LEAF, DEVICE_MIRROR, DEVICE_RAIDZ } type; + grub_uint64_t id; + grub_uint64_t guid; + unsigned ashift; + unsigned max_children_ashift; + + /* Valid only for non-leafs. */ + unsigned n_children; + struct grub_zfs_device_desc *children; + + /* Valid only for RAIDZ. */ + unsigned nparity; + + /* Valid only for leaf devices. */ + grub_device_t dev; + grub_disk_addr_t vdev_phys_sector; + uberblock_t current_uberblock; + int original; +}; + +struct subvolume +{ + dnode_end_t mdn; + grub_uint64_t obj; + grub_uint64_t case_insensitive; + grub_size_t nkeys; + struct + { + grub_crypto_cipher_handle_t cipher; + grub_uint64_t txg; + grub_uint64_t algo; + } *keyring; +}; + +struct grub_zfs_data +{ + /* cache for a file block of the currently zfs_open()-ed file */ + char *file_buf; + grub_uint64_t file_start; + grub_uint64_t file_end; + + /* cache for a dnode block */ + dnode_phys_t *dnode_buf; + dnode_phys_t *dnode_mdn; + grub_uint64_t dnode_start; + grub_uint64_t dnode_end; + grub_zfs_endian_t dnode_endian; + + dnode_end_t mos; + dnode_end_t dnode; + struct subvolume subvol; + + struct grub_zfs_device_desc *devices_attached; + unsigned n_devices_attached; + unsigned n_devices_allocated; + struct grub_zfs_device_desc *device_original; + + uberblock_t current_uberblock; + + grub_uint64_t guid; +}; + +/* Context for grub_zfs_dir. */ +struct grub_zfs_dir_ctx +{ + grub_fs_dir_hook_t hook; + void *hook_data; + struct grub_zfs_data *data; +}; + +grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher, + grub_uint64_t algo, + void *nonce, + char *buf, grub_size_t size, + const grub_uint32_t *expected_mac, + grub_zfs_endian_t endian) = NULL; +grub_crypto_cipher_handle_t (*grub_zfs_load_key) (const struct grub_zfs_key *key, + grub_size_t keysize, + grub_uint64_t salt, + grub_uint64_t algo) = NULL; +/* + * List of pool features that the grub implementation of ZFS supports for + * read. Note that features that are only required for write do not need + * to be listed here since grub opens pools in read-only mode. + */ +#define MAX_SUPPORTED_FEATURE_STRLEN 50 +static const char *spa_feature_names[] = { + "org.illumos:lz4_compress", + "com.delphix:hole_birth", + "com.delphix:embedded_data", + "com.delphix:extensible_dataset", + "org.open-zfs:large_blocks", + "com.klarasystems:vdev_zaps_v2", + "com.delphix:head_errlog", + "org.freebsd:zstd_compress", + NULL +}; + +static int +check_feature(const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx); +static grub_err_t +check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ); + +static grub_err_t +zlib_decompress (void *s, void *d, + grub_size_t slen, grub_size_t dlen) +{ + if (grub_zlib_decompress (s, slen, 0, d, dlen) == (grub_ssize_t) dlen) + return GRUB_ERR_NONE; + + if (!grub_errno) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "premature end of compressed"); + return grub_errno; +} + +static grub_err_t +zstd_decompress (void *ibuf, void *obuf, grub_size_t isize, + grub_size_t osize) +{ + grub_size_t zstd_ret; + grub_uint32_t c_len; + grub_uint8_t *byte_buf = (grub_uint8_t *) ibuf; + + if (isize < 8) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "zstd data too short"); + + c_len = grub_be_to_cpu32 (grub_get_unaligned32 (byte_buf)); + + if (c_len > isize - 8) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "zstd data announced size overflow"); + + /* + * ZFS uses non-stadard magic for zstd streams. Rather than adjusting + * library functions, replace non-standard magic with standard one. + */ + byte_buf[4] = 0x28; + byte_buf[5] = 0xb5; + byte_buf[6] = 0x2f; + byte_buf[7] = 0xfd; + zstd_ret = ZSTD_decompress (obuf, osize, byte_buf + 4, c_len + 4); + + if (ZSTD_isError (zstd_ret)) + return grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "zstd data corrupted (error %d)", (int) zstd_ret); + + return GRUB_ERR_NONE; +} + +static grub_err_t +zle_decompress (void *s, void *d, + grub_size_t slen, grub_size_t dlen) +{ + grub_uint8_t *iptr, *optr; + grub_size_t clen; + for (iptr = s, optr = d; iptr < (grub_uint8_t *) s + slen + && optr < (grub_uint8_t *) d + dlen;) + { + if (*iptr & 0x80) + clen = ((*iptr) & 0x7f) + 0x41; + else + clen = ((*iptr) & 0x3f) + 1; + if ((grub_ssize_t) clen > (grub_uint8_t *) d + dlen - optr) + clen = (grub_uint8_t *) d + dlen - optr; + if (*iptr & 0x40 || *iptr & 0x80) + { + grub_memset (optr, 0, clen); + iptr++; + optr += clen; + continue; + } + if ((grub_ssize_t) clen > (grub_uint8_t *) s + slen - iptr - 1) + clen = (grub_uint8_t *) s + slen - iptr - 1; + grub_memcpy (optr, iptr + 1, clen); + optr += clen; + iptr += clen + 1; + } + if (optr < (grub_uint8_t *) d + dlen) + grub_memset (optr, 0, (grub_uint8_t *) d + dlen - optr); + return GRUB_ERR_NONE; +} + +static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { + {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ + {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ + {"off", NULL}, /* ZIO_COMPRESS_OFF */ + {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ + {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ + {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ + {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ + {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ + {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ + {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ + {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ + {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ + {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ + {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ + {"zle", zle_decompress}, /* ZIO_COMPRESS_ZLE */ + {"lz4", lz4_decompress}, /* ZIO_COMPRESS_LZ4 */ + {"zstd", zstd_decompress}, /* ZIO_COMPRESS_ZSTD */ +}; + +static grub_err_t zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, + void *buf, struct grub_zfs_data *data); + +/* + * Our own version of log2(). Same thing as highbit()-1. + */ +static int +zfs_log2 (grub_uint64_t num) +{ + int i = 0; + + while (num > 1) + { + i++; + num = num >> 1; + } + + return i; +} + +/* Checksum Functions */ +static void +zio_checksum_off (const void *buf __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + grub_zfs_endian_t endian __attribute__ ((unused)), + zio_cksum_t * zcp) +{ + ZIO_SET_CHECKSUM (zcp, 0, 0, 0, 0); +} + +/* Checksum Table and Values */ +static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { + {NULL, 0, 0, "inherit"}, + {NULL, 0, 0, "on"}, + {zio_checksum_off, 0, 0, "off"}, + {zio_checksum_SHA256, 1, 1, "label"}, + {zio_checksum_SHA256, 1, 1, "gang_header"}, + {NULL, 0, 0, "zilog"}, + {fletcher_2, 0, 0, "fletcher2"}, + {fletcher_4, 1, 0, "fletcher4"}, + {zio_checksum_SHA256, 1, 0, "SHA256"}, + {NULL, 0, 0, "zilog2"}, + {zio_checksum_SHA256, 1, 0, "SHA256+MAC"}, +}; + +/* + * zio_checksum_verify: Provides support for checksum verification. + * + * Fletcher2, Fletcher4, and SHA256 are supported. + * + */ +static grub_err_t +zio_checksum_verify (zio_cksum_t zc, grub_uint32_t checksum, + grub_zfs_endian_t endian, + char *buf, grub_size_t size) +{ + zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; + zio_checksum_info_t *ci = &zio_checksum_table[checksum]; + zio_cksum_t actual_cksum, expected_cksum; + + if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) + { + grub_dprintf ("zfs", "unknown checksum function %d\n", checksum); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unknown checksum function %d", checksum); + } + + if (ci->ci_eck) + { + expected_cksum = zec->zec_cksum; + zec->zec_cksum = zc; + ci->ci_func (buf, size, endian, &actual_cksum); + zec->zec_cksum = expected_cksum; + zc = expected_cksum; + } + else + ci->ci_func (buf, size, endian, &actual_cksum); + + if (grub_memcmp (&actual_cksum, &zc, + checksum != ZIO_CHECKSUM_SHA256_MAC ? 32 : 20) != 0) + { + grub_dprintf ("zfs", "checksum %s verification failed\n", ci->ci_name); + grub_dprintf ("zfs", "actual checksum %016llx %016llx %016llx %016llx\n", + (unsigned long long) actual_cksum.zc_word[0], + (unsigned long long) actual_cksum.zc_word[1], + (unsigned long long) actual_cksum.zc_word[2], + (unsigned long long) actual_cksum.zc_word[3]); + grub_dprintf ("zfs", "expected checksum %016llx %016llx %016llx %016llx\n", + (unsigned long long) zc.zc_word[0], + (unsigned long long) zc.zc_word[1], + (unsigned long long) zc.zc_word[2], + (unsigned long long) zc.zc_word[3]); + return grub_error (GRUB_ERR_BAD_FS, N_("checksum verification failed")); + } + + return GRUB_ERR_NONE; +} + +/* + * vdev_uberblock_compare takes two uberblock structures and returns an integer + * indicating the more recent of the two. + * Return Value = 1 if ub2 is more recent + * Return Value = -1 if ub1 is more recent + * The most recent uberblock is determined using its transaction number and + * timestamp. The uberblock with the highest transaction number is + * considered "newer". If the transaction numbers of the two blocks match, the + * timestamps are compared to determine the "newer" of the two. + */ +static int +vdev_uberblock_compare (uberblock_t * ub1, uberblock_t * ub2) +{ + grub_zfs_endian_t ub1_endian, ub2_endian; + if (grub_zfs_to_cpu64 (ub1->ub_magic, GRUB_ZFS_LITTLE_ENDIAN) + == UBERBLOCK_MAGIC) + ub1_endian = GRUB_ZFS_LITTLE_ENDIAN; + else + ub1_endian = GRUB_ZFS_BIG_ENDIAN; + if (grub_zfs_to_cpu64 (ub2->ub_magic, GRUB_ZFS_LITTLE_ENDIAN) + == UBERBLOCK_MAGIC) + ub2_endian = GRUB_ZFS_LITTLE_ENDIAN; + else + ub2_endian = GRUB_ZFS_BIG_ENDIAN; + + if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) + < grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) + return -1; + if (grub_zfs_to_cpu64 (ub1->ub_txg, ub1_endian) + > grub_zfs_to_cpu64 (ub2->ub_txg, ub2_endian)) + return 1; + + if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) + < grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) + return -1; + if (grub_zfs_to_cpu64 (ub1->ub_timestamp, ub1_endian) + > grub_zfs_to_cpu64 (ub2->ub_timestamp, ub2_endian)) + return 1; + + return 0; +} + +/* + * Three pieces of information are needed to verify an uberblock: the magic + * number, the version number, and the checksum. + * + * Currently Implemented: version number, magic number, checksum + * + */ +static grub_err_t +uberblock_verify (uberblock_phys_t * ub, grub_uint64_t offset, + grub_size_t s) +{ + uberblock_t *uber = &ub->ubp_uberblock; + grub_err_t err; + grub_zfs_endian_t endian = GRUB_ZFS_UNKNOWN_ENDIAN; + zio_cksum_t zc; + + if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_LITTLE_ENDIAN) + == UBERBLOCK_MAGIC + && SPA_VERSION_IS_SUPPORTED(grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_LITTLE_ENDIAN))) + endian = GRUB_ZFS_LITTLE_ENDIAN; + + if (grub_zfs_to_cpu64 (uber->ub_magic, GRUB_ZFS_BIG_ENDIAN) == UBERBLOCK_MAGIC + && SPA_VERSION_IS_SUPPORTED(grub_zfs_to_cpu64 (uber->ub_version, GRUB_ZFS_BIG_ENDIAN))) + endian = GRUB_ZFS_BIG_ENDIAN; + + if (endian == GRUB_ZFS_UNKNOWN_ENDIAN) + return grub_error (GRUB_ERR_BAD_FS, "invalid uberblock magic"); + + grub_memset (&zc, 0, sizeof (zc)); + + zc.zc_word[0] = grub_cpu_to_zfs64 (offset, endian); + err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL, endian, + (char *) ub, s); + + return err; +} + +/* + * Find the best uberblock. + * Return: + * Success - Pointer to the best uberblock. + * Failure - NULL + */ +static uberblock_phys_t * +find_bestub (uberblock_phys_t * ub_array, + const struct grub_zfs_device_desc *desc) +{ + uberblock_phys_t *ubbest = NULL, *ubptr; + int i; + grub_disk_addr_t offset; + grub_err_t err = GRUB_ERR_NONE; + int ub_shift; + + ub_shift = desc->ashift; + if (ub_shift < VDEV_UBERBLOCK_SHIFT) + ub_shift = VDEV_UBERBLOCK_SHIFT; + + for (i = 0; i < (VDEV_UBERBLOCK_RING >> ub_shift); i++) + { + offset = (desc->vdev_phys_sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE + + (i << ub_shift); + + ubptr = (uberblock_phys_t *) ((grub_properly_aligned_t *) ub_array + + ((i << ub_shift) + / sizeof (grub_properly_aligned_t))); + err = uberblock_verify (ubptr, offset, (grub_size_t) 1 << ub_shift); + if (err) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + if (ubbest == NULL + || vdev_uberblock_compare (&(ubptr->ubp_uberblock), + &(ubbest->ubp_uberblock)) > 0) + ubbest = ubptr; + } + if (!ubbest) + grub_errno = err; + + return ubbest; +} + +static inline grub_size_t +get_psize (blkptr_t * bp, grub_zfs_endian_t endian) +{ + return ((((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) >> 16) & 0xffff) + 1) + << SPA_MINBLOCKSHIFT); +} + +static grub_uint64_t +dva_get_offset (const dva_t *dva, grub_zfs_endian_t endian) +{ + grub_dprintf ("zfs", "dva=%llx, %llx\n", + (unsigned long long) dva->dva_word[0], + (unsigned long long) dva->dva_word[1]); + return grub_zfs_to_cpu64 ((dva)->dva_word[1], + endian) << SPA_MINBLOCKSHIFT; +} + +static grub_err_t +zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist) +{ + grub_err_t err; + + *nvlist = 0; + + if (!diskdesc->dev) + return grub_error (GRUB_ERR_BUG, "member drive unknown"); + + *nvlist = grub_malloc (VDEV_PHYS_SIZE); + if (!*nvlist) + return grub_errno; + + /* Read in the vdev name-value pair list (112K). */ + err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0, + VDEV_PHYS_SIZE, *nvlist); + if (err) + { + grub_free (*nvlist); + *nvlist = 0; + return err; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +fill_vdev_info_real (struct grub_zfs_data *data, + const char *nvlist, + struct grub_zfs_device_desc *fill, + struct grub_zfs_device_desc *insert, + int *inserted, + unsigned ashift) +{ + char *type; + + type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); + + if (!type) + return grub_errno; + + if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &(fill->id))) + { + grub_free (type); + return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); + } + + if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "guid", &(fill->guid))) + { + grub_free (type); + return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); + } + + { + grub_uint64_t par; + if (grub_zfs_nvlist_lookup_uint64 (nvlist, "ashift", &par)) + fill->ashift = par; + else if (ashift != 0xffffffff) + fill->ashift = ashift; + else + { + grub_free (type); + return grub_error (GRUB_ERR_BAD_FS, "couldn't find ashift"); + } + } + + fill->max_children_ashift = 0; + + if (grub_strcmp (type, VDEV_TYPE_DISK) == 0 + || grub_strcmp (type, VDEV_TYPE_FILE) == 0) + { + fill->type = DEVICE_LEAF; + + if (!fill->dev && fill->guid == insert->guid) + { + fill->dev = insert->dev; + fill->vdev_phys_sector = insert->vdev_phys_sector; + fill->current_uberblock = insert->current_uberblock; + fill->original = insert->original; + if (!data->device_original) + data->device_original = fill; + insert->ashift = fill->ashift; + *inserted = 1; + } + + grub_free (type); + + return GRUB_ERR_NONE; + } + + if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0 + || grub_strcmp (type, VDEV_TYPE_RAIDZ) == 0) + { + int nelm, i; + + if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0) + fill->type = DEVICE_MIRROR; + else + { + grub_uint64_t par; + fill->type = DEVICE_RAIDZ; + if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "nparity", &par)) + { + grub_free (type); + return grub_error (GRUB_ERR_BAD_FS, "couldn't find raidz parity"); + } + fill->nparity = par; + } + + nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm (nvlist, + ZPOOL_CONFIG_CHILDREN); + + if (nelm <= 0) + { + grub_free (type); + return grub_error (GRUB_ERR_BAD_FS, "incorrect mirror VDEV"); + } + + if (!fill->children) + { + fill->n_children = nelm; + + fill->children = grub_calloc (fill->n_children, + sizeof (fill->children[0])); + if (!fill->children) + { + grub_free (type); + return grub_errno; + } + } + + for (i = 0; i < nelm; i++) + { + char *child; + grub_err_t err; + + child = grub_zfs_nvlist_lookup_nvlist_array + (nvlist, ZPOOL_CONFIG_CHILDREN, i); + + err = fill_vdev_info_real (data, child, &fill->children[i], insert, + inserted, fill->ashift); + + grub_free (child); + + if (err) + { + grub_free (type); + return err; + } + if (fill->children[i].ashift > fill->max_children_ashift) + fill->max_children_ashift = fill->children[i].ashift; + } + grub_free (type); + return GRUB_ERR_NONE; + } + + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "vdev %s isn't supported", type); + grub_free (type); + return grub_errno; +} + +static grub_err_t +fill_vdev_info (struct grub_zfs_data *data, + char *nvlist, struct grub_zfs_device_desc *diskdesc, + int *inserted) +{ + grub_uint64_t id; + unsigned i; + + *inserted = 0; + + if (!grub_zfs_nvlist_lookup_uint64 (nvlist, "id", &id)) + return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev id"); + + for (i = 0; i < data->n_devices_attached; i++) + if (data->devices_attached[i].id == id) + return fill_vdev_info_real (data, nvlist, &data->devices_attached[i], + diskdesc, inserted, 0xffffffff); + + data->n_devices_attached++; + if (data->n_devices_attached > data->n_devices_allocated) + { + void *tmp; + grub_size_t sz; + + if (grub_mul (data->n_devices_attached, 2, &data->n_devices_allocated) || + grub_add (data->n_devices_allocated, 1, &data->n_devices_allocated) || + grub_mul (data->n_devices_allocated, sizeof (data->devices_attached[0]), &sz)) + return GRUB_ERR_OUT_OF_RANGE; + + data->devices_attached = grub_realloc (tmp = data->devices_attached, sz); + if (!data->devices_attached) + { + data->devices_attached = tmp; + return grub_errno; + } + } + + grub_memset (&data->devices_attached[data->n_devices_attached - 1], + 0, sizeof (data->devices_attached[data->n_devices_attached - 1])); + + return fill_vdev_info_real (data, nvlist, + &data->devices_attached[data->n_devices_attached - 1], + diskdesc, inserted, 0xffffffff); +} + +/* + * For a given XDR packed nvlist, verify the first 4 bytes and move on. + * + * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : + * + * encoding method/host endian (4 bytes) + * nvl_version (4 bytes) + * nvl_nvflag (4 bytes) + * encoded nvpairs: + * encoded size of the nvpair (4 bytes) + * decoded size of the nvpair (4 bytes) + * name string size (4 bytes) + * name string data (sizeof(NV_ALIGN4(string)) + * data type (4 bytes) + * # of elements in the nvpair (4 bytes) + * data + * 2 zero's for the last nvpair + * (end of the entire list) (8 bytes) + * + */ + +/* + * The nvlist_next_nvpair() function returns a handle to the next nvpair in the + * list following nvpair. If nvpair is NULL, the first pair is returned. If + * nvpair is the last pair in the nvlist, NULL is returned. + */ +static const char * +nvlist_next_nvpair (const char *nvl, const char *nvpair) +{ + const char *nvp; + int encode_size; + int name_len; + if (nvl == NULL) + return NULL; + + if (nvpair == NULL) + { + /* skip over header, nvl_version and nvl_nvflag */ + nvpair = nvl + 4 * 3; + } + else + { + /* skip to the next nvpair */ + encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvpair)); + nvpair += encode_size; + /*If encode_size equals 0 nvlist_next_nvpair would return + * the same pair received in input, leading to an infinite loop. + * If encode_size is less than 0, this will move the pointer + * backwards, *possibly* examinining two times the same nvpair + * and potentially getting into an infinite loop. */ + if(encode_size <= 0) + { + grub_dprintf ("zfs", "nvpair with size <= 0\n"); + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); + return NULL; + } + } + /* 8 bytes of 0 marks the end of the list */ + if (grub_get_unaligned64 (nvpair) == 0) + return NULL; + /*consistency checks*/ + if (nvpair + 4 * 3 >= nvl + VDEV_PHYS_SIZE) + { + grub_dprintf ("zfs", "nvlist overflow\n"); + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); + return NULL; + } + encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvpair)); + + nvp = nvpair + 4*2; + name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + nvp += 4; + + nvp = nvp + ((name_len + 3) & ~3); // align + if (nvp + 4 >= nvl + VDEV_PHYS_SIZE + || encode_size < 0 + || nvp + 4 + encode_size > nvl + VDEV_PHYS_SIZE) + { + grub_dprintf ("zfs", "nvlist overflow\n"); + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); + return NULL; + } + /* end consistency checks */ + + return nvpair; +} + +/* + * This function returns 0 on success and 1 on failure. On success, a string + * containing the name of nvpair is saved in buf. + */ +static int +nvpair_name (const char *nvp, char **buf, grub_size_t *buflen) +{ + /* skip over encode/decode size */ + nvp += 4 * 2; + + *buf = (char *) (nvp + 4); + *buflen = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + + return 0; +} + +/* + * This function retrieves the value of the nvpair in the form of enumerated + * type data_type_t. + */ +static int +nvpair_type (const char *nvp) +{ + int name_len, type; + + /* skip over encode/decode size */ + nvp += 4 * 2; + + /* skip over name_len */ + name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + nvp += 4; + + /* skip over name */ + nvp = nvp + ((name_len + 3) & ~3); /* align */ + + type = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + + return type; +} + +static int +nvpair_value (const char *nvp,char **val, + grub_size_t *size_out, grub_size_t *nelm_out) +{ + int name_len,nelm,encode_size; + + /* skip over encode/decode size */ + encode_size = grub_be_to_cpu32 (grub_get_unaligned32(nvp)); + nvp += 8; + + /* skip over name_len */ + name_len = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + nvp += 4; + + /* skip over name */ + nvp = nvp + ((name_len + 3) & ~3); /* align */ + + /* skip over type */ + nvp += 4; + nelm = grub_be_to_cpu32 (grub_get_unaligned32 (nvp)); + nvp +=4; + if (nelm < 1) + { + grub_error (GRUB_ERR_BAD_FS, "empty nvpair"); + return 0; + } + *val = (char *) nvp; + *size_out = encode_size; + if (nelm_out) + *nelm_out = nelm; + + return 1; +} + +/* + * Check the disk label information and retrieve needed vdev name-value pairs. + * + */ +static grub_err_t +check_pool_label (struct grub_zfs_data *data, + struct grub_zfs_device_desc *diskdesc, + int *inserted, int original) +{ + grub_uint64_t pool_state, txg = 0; + char *nvlist,*features; +#if 0 + char *nv; +#endif + grub_uint64_t poolguid; + grub_uint64_t version; + int found; + grub_err_t err; + grub_zfs_endian_t endian; + vdev_phys_t *phys; + zio_cksum_t emptycksum; + + *inserted = 0; + + err = zfs_fetch_nvlist (diskdesc, &nvlist); + if (err) + return err; + + phys = (vdev_phys_t*) nvlist; + if (grub_zfs_to_cpu64 (phys->vp_zbt.zec_magic, + GRUB_ZFS_LITTLE_ENDIAN) + == ZEC_MAGIC) + endian = GRUB_ZFS_LITTLE_ENDIAN; + else if (grub_zfs_to_cpu64 (phys->vp_zbt.zec_magic, + GRUB_ZFS_BIG_ENDIAN) + == ZEC_MAGIC) + endian = GRUB_ZFS_BIG_ENDIAN; + else + { + grub_free (nvlist); + return grub_error (GRUB_ERR_BAD_FS, + "bad vdev_phys_t.vp_zbt.zec_magic number"); + } + /* Now check the integrity of the vdev_phys_t structure though checksum. */ + ZIO_SET_CHECKSUM(&emptycksum, diskdesc->vdev_phys_sector << 9, 0, 0, 0); + err = zio_checksum_verify (emptycksum, ZIO_CHECKSUM_LABEL, endian, + nvlist, VDEV_PHYS_SIZE); + if (err) { + grub_free (nvlist); + return err; + } + + grub_dprintf ("zfs", "check 2 passed\n"); + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, + &pool_state); + if (! found) + { + grub_free (nvlist); + if (! grub_errno) + grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_STATE " not found"); + return grub_errno; + } + grub_dprintf ("zfs", "check 3 passed\n"); + + if (pool_state == POOL_STATE_DESTROYED) + { + grub_free (nvlist); + return grub_error (GRUB_ERR_BAD_FS, "zpool is marked as destroyed"); + } + grub_dprintf ("zfs", "check 4 passed\n"); + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_TXG, &txg); + if (!found) + { + grub_free (nvlist); + if (! grub_errno) + grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_TXG " not found"); + return grub_errno; + } + grub_dprintf ("zfs", "check 6 passed\n"); + + /* not an active device */ + if (txg == 0) + { + grub_free (nvlist); + return grub_error (GRUB_ERR_BAD_FS, "zpool isn't active"); + } + grub_dprintf ("zfs", "check 7 passed\n"); + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_VERSION, + &version); + if (! found) + { + grub_free (nvlist); + if (! grub_errno) + grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_VERSION " not found"); + return grub_errno; + } + grub_dprintf ("zfs", "check 8 passed\n"); + + if (!SPA_VERSION_IS_SUPPORTED(version)) + { + grub_free (nvlist); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "too new version %llu > %llu", + (unsigned long long) version, + (unsigned long long) SPA_VERSION_BEFORE_FEATURES); + } + grub_dprintf ("zfs", "check 9 passed\n"); + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_GUID, + &(diskdesc->guid)); + if (! found) + { + grub_free (nvlist); + if (! grub_errno) + grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_GUID " not found"); + return grub_errno; + } + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, + &poolguid); + if (! found) + { + grub_free (nvlist); + if (! grub_errno) + grub_error (GRUB_ERR_BAD_FS, ZPOOL_CONFIG_POOL_GUID " not found"); + return grub_errno; + } + + grub_dprintf ("zfs", "check 11 passed\n"); + + if (original) + data->guid = poolguid; + + if (data->guid != poolguid) { + grub_free (nvlist); + return grub_error (GRUB_ERR_BAD_FS, "another zpool"); + } + + { + char *nv; + nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); + + if (!nv) + { + grub_free (nvlist); + return grub_error (GRUB_ERR_BAD_FS, "couldn't find vdev tree"); + } + err = fill_vdev_info (data, nv, diskdesc, inserted); + if (err) + { + grub_free (nv); + grub_free (nvlist); + return err; + } + grub_free (nv); + } + grub_dprintf ("zfs", "check 10 passed\n"); + features = grub_zfs_nvlist_lookup_nvlist(nvlist, + ZPOOL_CONFIG_FEATURES_FOR_READ); + if (features) + { + const char *nvp=NULL; + char name[MAX_SUPPORTED_FEATURE_STRLEN + 1]; + char *nameptr; + grub_size_t namelen; + while ((nvp = nvlist_next_nvpair(features, nvp)) != NULL) + { + nvpair_name (nvp, &nameptr, &namelen); + if(namelen > MAX_SUPPORTED_FEATURE_STRLEN) + namelen = MAX_SUPPORTED_FEATURE_STRLEN; + grub_memcpy (name, nameptr, namelen); + name[namelen] = '\0'; + grub_dprintf("zfs","str=%s\n",name); + if (check_feature(name,1, NULL) != 0) + { + grub_dprintf("zfs","feature missing in check_pool_label:%s\n",name); + err= grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET," check_pool_label missing feature '%s' for read",name); + grub_free(features); + grub_free(nvlist); + return err; + } + } + grub_free(features); + } + grub_dprintf ("zfs", "check 12 passed (feature flags)\n"); + grub_free (nvlist); + + return GRUB_ERR_NONE; +} + +static grub_err_t +scan_disk (grub_device_t dev, struct grub_zfs_data *data, + int original, int *inserted) +{ + int label = 0; + uberblock_phys_t *ub_array, *ubbest = NULL; + vdev_boot_header_t *bh; + grub_err_t err; + int vdevnum; + struct grub_zfs_device_desc desc; + + ub_array = grub_malloc (VDEV_UBERBLOCK_RING); + if (!ub_array) + return grub_errno; + + bh = grub_malloc (VDEV_BOOT_HEADER_SIZE); + if (!bh) + { + grub_free (ub_array); + return grub_errno; + } + + vdevnum = VDEV_LABELS; + + desc.dev = dev; + desc.original = original; + + /* Don't check back labels on CDROM. */ + if (grub_disk_native_sectors (dev->disk) == GRUB_DISK_SIZE_UNKNOWN) + vdevnum = VDEV_LABELS / 2; + + for (label = 0; ubbest == NULL && label < vdevnum; label++) + { + desc.vdev_phys_sector + = label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT) + + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT) + + (label < VDEV_LABELS / 2 ? 0 : + ALIGN_DOWN (grub_disk_native_sectors (dev->disk), sizeof (vdev_label_t)) + - VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)); + + /* Read in the uberblock ring (128K). */ + err = grub_disk_read (dev->disk, desc.vdev_phys_sector + + (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT), + 0, VDEV_UBERBLOCK_RING, (char *) ub_array); + if (err) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + grub_dprintf ("zfs", "label ok %d\n", label); + + err = check_pool_label (data, &desc, inserted, original); + if (err || !*inserted) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + ubbest = find_bestub (ub_array, &desc); + if (!ubbest) + { + grub_dprintf ("zfs", "No uberblock found\n"); + grub_errno = GRUB_ERR_NONE; + continue; + } + + grub_memmove (&(desc.current_uberblock), + &ubbest->ubp_uberblock, sizeof (uberblock_t)); + if (original) + grub_memmove (&(data->current_uberblock), + &ubbest->ubp_uberblock, sizeof (uberblock_t)); + +#if 0 + if (find_best_root && + vdev_uberblock_compare (&ubbest->ubp_uberblock, + &(current_uberblock)) <= 0) + continue; +#endif + grub_free (ub_array); + grub_free (bh); + return GRUB_ERR_NONE; + } + + grub_free (ub_array); + grub_free (bh); + + return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label"); +} + +/* Helper for scan_devices. */ +static int +scan_devices_iter (const char *name, void *hook_data) +{ + struct grub_zfs_data *data = hook_data; + grub_device_t dev; + grub_err_t err; + int inserted; + + dev = grub_device_open (name); + if (!dev) + return 0; + if (!dev->disk) + { + grub_device_close (dev); + return 0; + } + err = scan_disk (dev, data, 0, &inserted); + if (err == GRUB_ERR_BAD_FS) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (err) + { + grub_device_close (dev); + grub_print_error (); + return 0; + } + + if (!inserted) + grub_device_close (dev); + + return 0; +} + +static grub_err_t +scan_devices (struct grub_zfs_data *data) +{ + grub_device_iterate (scan_devices_iter, data); + return GRUB_ERR_NONE; +} + +/* x**y. */ +static grub_uint8_t powx[255 * 2]; +/* Such an s that x**s = y */ +static int powx_inv[256]; +static const grub_uint8_t poly = 0x1d; + +/* perform the operation a ^= b * (x ** (known_idx * recovery_pow) ) */ +static inline void +xor_out (grub_uint8_t *a, const grub_uint8_t *b, grub_size_t s, + unsigned known_idx, unsigned recovery_pow) +{ + unsigned add; + + /* Simple xor. */ + if (known_idx == 0 || recovery_pow == 0) + { + grub_crypto_xor (a, a, b, s); + return; + } + add = (known_idx * recovery_pow) % 255; + for (;s--; b++, a++) + if (*b) + *a ^= powx[powx_inv[*b] + add]; +} + +static inline grub_uint8_t +gf_mul (grub_uint8_t a, grub_uint8_t b) +{ + if (a == 0 || b == 0) + return 0; + return powx[powx_inv[a] + powx_inv[b]]; +} + +#define MAX_NBUFS 4 + +static grub_err_t +recovery (grub_uint8_t *bufs[4], grub_size_t s, const int nbufs, + const unsigned *powers, + const unsigned *idx) +{ + grub_dprintf ("zfs", "recovering %u buffers\n", nbufs); + /* Now we have */ + /* b_i = sum (r_j* (x ** (powers[i] * idx[j])))*/ + /* Let's invert the matrix in question. */ + switch (nbufs) + { + /* Easy: r_0 = bufs[0] / (x << (powers[i] * idx[j])). */ + case 1: + { + int add; + grub_uint8_t *a; + if (powers[0] == 0 || idx[0] == 0) + return GRUB_ERR_NONE; + add = 255 - ((powers[0] * idx[0]) % 255); + for (a = bufs[0]; s--; a++) + if (*a) + *a = powx[powx_inv[*a] + add]; + return GRUB_ERR_NONE; + } + /* Case 2x2: Let's use the determinant formula. */ + case 2: + { + grub_uint8_t det, det_inv; + grub_uint8_t matrixinv[2][2]; + unsigned i; + /* The determinant is: */ + det = (powx[(powers[0] * idx[0] + powers[1] * idx[1]) % 255] + ^ powx[(powers[0] * idx[1] + powers[1] * idx[0]) % 255]); + if (det == 0) + return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); + det_inv = powx[255 - powx_inv[det]]; + matrixinv[0][0] = gf_mul (powx[(powers[1] * idx[1]) % 255], det_inv); + matrixinv[1][1] = gf_mul (powx[(powers[0] * idx[0]) % 255], det_inv); + matrixinv[0][1] = gf_mul (powx[(powers[0] * idx[1]) % 255], det_inv); + matrixinv[1][0] = gf_mul (powx[(powers[1] * idx[0]) % 255], det_inv); + for (i = 0; i < s; i++) + { + grub_uint8_t b0, b1; + b0 = bufs[0][i]; + b1 = bufs[1][i]; + + bufs[0][i] = (gf_mul (b0, matrixinv[0][0]) + ^ gf_mul (b1, matrixinv[0][1])); + bufs[1][i] = (gf_mul (b0, matrixinv[1][0]) + ^ gf_mul (b1, matrixinv[1][1])); + } + return GRUB_ERR_NONE; + } + /* Otherwise use Gauss. */ + case 3: + { + grub_uint8_t matrix1[MAX_NBUFS][MAX_NBUFS], matrix2[MAX_NBUFS][MAX_NBUFS]; + int i, j, k; + + for (i = 0; i < nbufs; i++) + for (j = 0; j < nbufs; j++) + matrix1[i][j] = powx[(powers[i] * idx[j]) % 255]; + for (i = 0; i < nbufs; i++) + for (j = 0; j < nbufs; j++) + matrix2[i][j] = 0; + for (i = 0; i < nbufs; i++) + matrix2[i][i] = 1; + + for (i = 0; i < nbufs; i++) + { + grub_uint8_t mul; + for (j = i; j < nbufs; j++) + if (matrix1[i][j]) + break; + if (j == nbufs) + return grub_error (GRUB_ERR_BAD_FS, "singular recovery matrix"); + if (j != i) + { + int xchng; + xchng = j; + for (j = 0; j < nbufs; j++) + { + grub_uint8_t t; + t = matrix1[xchng][j]; + matrix1[xchng][j] = matrix1[i][j]; + matrix1[i][j] = t; + } + for (j = 0; j < nbufs; j++) + { + grub_uint8_t t; + t = matrix2[xchng][j]; + matrix2[xchng][j] = matrix2[i][j]; + matrix2[i][j] = t; + } + } + mul = powx[255 - powx_inv[matrix1[i][i]]]; + for (j = 0; j < nbufs; j++) + matrix1[i][j] = gf_mul (matrix1[i][j], mul); + for (j = 0; j < nbufs; j++) + matrix2[i][j] = gf_mul (matrix2[i][j], mul); + for (j = i + 1; j < nbufs; j++) + { + mul = matrix1[j][i]; + for (k = 0; k < nbufs; k++) + matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); + for (k = 0; k < nbufs; k++) + matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); + } + } + for (i = nbufs - 1; i >= 0; i--) + { + for (j = 0; j < i; j++) + { + grub_uint8_t mul; + mul = matrix1[j][i]; + for (k = 0; k < nbufs; k++) + matrix1[j][k] ^= gf_mul (matrix1[i][k], mul); + for (k = 0; k < nbufs; k++) + matrix2[j][k] ^= gf_mul (matrix2[i][k], mul); + } + } + + for (i = 0; i < (int) s; i++) + { + grub_uint8_t b[MAX_NBUFS]; + for (j = 0; j < nbufs; j++) + b[j] = bufs[j][i]; + for (j = 0; j < nbufs; j++) + { + bufs[j][i] = 0; + for (k = 0; k < nbufs; k++) + bufs[j][i] ^= gf_mul (matrix2[j][k], b[k]); + } + } + return GRUB_ERR_NONE; + } + default: + return grub_error (GRUB_ERR_BUG, "too big matrix"); + } +} + +static grub_err_t +read_device (grub_uint64_t offset, struct grub_zfs_device_desc *desc, + grub_size_t len, void *buf) +{ + switch (desc->type) + { + case DEVICE_LEAF: + { + grub_uint64_t sector; + sector = DVA_OFFSET_TO_PHYS_SECTOR (offset); + if (!desc->dev) + { + return grub_error (GRUB_ERR_BAD_FS, + N_("couldn't find a necessary member device " + "of multi-device filesystem")); + } + /* read in a data block */ + return grub_disk_read (desc->dev->disk, sector, 0, len, buf); + } + case DEVICE_MIRROR: + { + grub_err_t err = GRUB_ERR_NONE; + unsigned i; + if (desc->n_children <= 0) + return grub_error (GRUB_ERR_BAD_FS, + "non-positive number of mirror children"); + for (i = 0; i < desc->n_children; i++) + { + err = read_device (offset, &desc->children[i], + len, buf); + if (!err) + break; + grub_errno = GRUB_ERR_NONE; + } + grub_errno = err; + + return err; + } + case DEVICE_RAIDZ: + { + unsigned c = 0; + grub_uint64_t high; + grub_uint64_t devn; + grub_uint64_t m; + grub_uint32_t s, orig_s; + void *orig_buf = buf; + grub_size_t orig_len = len; + grub_uint8_t *recovery_buf[4]; + grub_size_t recovery_len[4]; + unsigned recovery_idx[4]; + unsigned failed_devices = 0; + int idx, orig_idx; + + if (desc->nparity < 1 || desc->nparity > 3) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "raidz%d is not supported", desc->nparity); + + if (desc->n_children <= desc->nparity || desc->n_children < 1) + return grub_error(GRUB_ERR_BAD_FS, "too little devices for given parity"); + + orig_s = (((len + (1 << desc->ashift) - 1) >> desc->ashift) + + (desc->n_children - desc->nparity) - 1); + s = orig_s; + + high = grub_divmod64 ((offset >> desc->ashift), + desc->n_children, &m); + if (desc->nparity == 2) + c = 2; + if (desc->nparity == 3) + c = 3; + if (((len + (1 << desc->ashift) - 1) >> desc->ashift) + >= (desc->n_children - desc->nparity)) + idx = (desc->n_children - desc->nparity - 1); + else + idx = ((len + (1 << desc->ashift) - 1) >> desc->ashift) - 1; + orig_idx = idx; + while (len > 0) + { + grub_size_t csize; + grub_uint32_t bsize; + grub_err_t err; + bsize = s / (desc->n_children - desc->nparity); + + if (desc->nparity == 1 + && ((offset >> (desc->ashift + 20 - desc->max_children_ashift)) + & 1) == c) + c++; + + high = grub_divmod64 ((offset >> desc->ashift) + c, + desc->n_children, &devn); + csize = (grub_size_t) bsize << desc->ashift; + if (csize > len) + csize = len; + + grub_dprintf ("zfs", "RAIDZ mapping 0x%" PRIxGRUB_UINT64_T + "+%u (%" PRIxGRUB_SIZE ", %" PRIxGRUB_UINT32_T + ") -> (0x%" PRIxGRUB_UINT64_T ", 0x%" + PRIxGRUB_UINT64_T ")\n", + offset >> desc->ashift, c, len, bsize, high, + devn); + err = read_device ((high << desc->ashift) + | (offset & ((1 << desc->ashift) - 1)), + &desc->children[devn], + csize, buf); + if (err && failed_devices < desc->nparity) + { + recovery_buf[failed_devices] = buf; + recovery_len[failed_devices] = csize; + recovery_idx[failed_devices] = idx; + failed_devices++; + grub_errno = err = 0; + } + if (err) + return err; + + c++; + idx--; + s--; + buf = (char *) buf + csize; + len -= csize; + } + if (failed_devices) + { + unsigned redundancy_pow[4]; + unsigned cur_redundancy_pow = 0; + unsigned n_redundancy = 0; + unsigned i, j; + grub_err_t err; + + /* Compute mul. x**s has a period of 255. */ + if (powx[0] == 0) + { + grub_uint8_t cur = 1; + for (i = 0; i < 255; i++) + { + powx[i] = cur; + powx[i + 255] = cur; + powx_inv[cur] = i; + if (cur & 0x80) + cur = (cur << 1) ^ poly; + else + cur <<= 1; + } + } + + /* Read redundancy data. */ + for (n_redundancy = 0, cur_redundancy_pow = 0; + n_redundancy < failed_devices; + cur_redundancy_pow++) + { + high = grub_divmod64 ((offset >> desc->ashift) + + cur_redundancy_pow + + ((desc->nparity == 1) + && ((offset >> (desc->ashift + 20 + - desc->max_children_ashift)) + & 1)), + desc->n_children, &devn); + err = read_device ((high << desc->ashift) + | (offset & ((1 << desc->ashift) - 1)), + &desc->children[devn], + recovery_len[n_redundancy], + recovery_buf[n_redundancy]); + /* Ignore error if we may still have enough devices. */ + if (err && n_redundancy + desc->nparity - cur_redundancy_pow - 1 + >= failed_devices) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + if (err) + return err; + redundancy_pow[n_redundancy] = cur_redundancy_pow; + n_redundancy++; + } + /* Now xor-our the parts we already know. */ + buf = orig_buf; + len = orig_len; + s = orig_s; + idx = orig_idx; + + while (len > 0) + { + grub_size_t csize = s; + csize = ((csize / (desc->n_children - desc->nparity)) + << desc->ashift); + if (csize > len) + csize = len; + + for (j = 0; j < failed_devices; j++) + if (buf == recovery_buf[j]) + break; + + if (j == failed_devices) + for (j = 0; j < failed_devices; j++) + xor_out (recovery_buf[j], buf, + csize < recovery_len[j] ? csize : recovery_len[j], + idx, redundancy_pow[j]); + + s--; + buf = (char *) buf + csize; + len -= csize; + idx--; + } + for (i = 0; i < failed_devices + && recovery_len[i] == recovery_len[0]; + i++); + /* Since the chunks have variable length handle the last block + separately. */ + if (i != failed_devices) + { + grub_uint8_t *tmp_recovery_buf[4]; + for (j = 0; j < i; j++) + tmp_recovery_buf[j] = recovery_buf[j] + recovery_len[failed_devices - 1]; + err = recovery (tmp_recovery_buf, recovery_len[0] - recovery_len[failed_devices - 1], i, redundancy_pow, + recovery_idx); + if (err) + return err; + } + err = recovery (recovery_buf, recovery_len[failed_devices - 1], + failed_devices, redundancy_pow, recovery_idx); + if (err) + return err; + } + return GRUB_ERR_NONE; + } + } + return grub_error (GRUB_ERR_BAD_FS, "unsupported device type"); +} + +static grub_err_t +read_dva (const dva_t *dva, + grub_zfs_endian_t endian, struct grub_zfs_data *data, + void *buf, grub_size_t len) +{ + grub_uint64_t offset; + unsigned i; + grub_err_t err = 0; + int try = 0; + offset = dva_get_offset (dva, endian); + + for (try = 0; try < 2; try++) + { + for (i = 0; i < data->n_devices_attached; i++) + if (data->devices_attached[i].id == DVA_GET_VDEV (dva)) + { + err = read_device (offset, &data->devices_attached[i], len, buf); + if (!err) + return GRUB_ERR_NONE; + break; + } + if (try == 1) + break; + err = scan_devices (data); + if (err) + return err; + } + if (!err) + return grub_error (GRUB_ERR_BAD_FS, "unknown device %d", + (int) DVA_GET_VDEV (dva)); + return err; +} + +/* + * Read a block of data based on the gang block address dva, + * and put its data in buf. + * + */ +static grub_err_t +zio_read_gang (blkptr_t * bp, grub_zfs_endian_t endian, dva_t * dva, void *buf, + struct grub_zfs_data *data) +{ + zio_gbh_phys_t *zio_gb; + unsigned i; + grub_err_t err; + zio_cksum_t zc; + + grub_memset (&zc, 0, sizeof (zc)); + + zio_gb = grub_malloc (SPA_GANGBLOCKSIZE); + if (!zio_gb) + return grub_errno; + grub_dprintf ("zfs", endian == GRUB_ZFS_LITTLE_ENDIAN ? "little-endian gang\n" + :"big-endian gang\n"); + + err = read_dva (dva, endian, data, zio_gb, SPA_GANGBLOCKSIZE); + if (err) + { + grub_free (zio_gb); + return err; + } + + /* XXX */ + /* self checksuming the gang block header */ + ZIO_SET_CHECKSUM (&zc, DVA_GET_VDEV (dva), + dva_get_offset (dva, endian), bp->blk_birth, 0); + err = zio_checksum_verify (zc, ZIO_CHECKSUM_GANG_HEADER, endian, + (char *) zio_gb, SPA_GANGBLOCKSIZE); + if (err) + { + grub_free (zio_gb); + return err; + } + + endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; + + for (i = 0; i < SPA_GBH_NBLKPTRS; i++) + { + if (BP_IS_HOLE(&zio_gb->zg_blkptr[i])) + continue; + + err = zio_read_data (&zio_gb->zg_blkptr[i], endian, buf, data); + if (err) + { + grub_free (zio_gb); + return err; + } + buf = (char *) buf + get_psize (&zio_gb->zg_blkptr[i], endian); + } + grub_free (zio_gb); + return GRUB_ERR_NONE; +} + +/* + * Read in a block of raw data to buf. + */ +static grub_err_t +zio_read_data (blkptr_t * bp, grub_zfs_endian_t endian, void *buf, + struct grub_zfs_data *data) +{ + int i, psize; + grub_err_t err = GRUB_ERR_NONE; + + psize = get_psize (bp, endian); + + /* pick a good dva from the block pointer */ + for (i = 0; i < SPA_DVAS_PER_BP; i++) + { + if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) + continue; + + if ((grub_zfs_to_cpu64 (bp->blk_dva[i].dva_word[1], endian)>>63) & 1) + err = zio_read_gang (bp, endian, &bp->blk_dva[i], buf, data); + else + err = read_dva (&bp->blk_dva[i], endian, data, buf, psize); + if (!err) + return GRUB_ERR_NONE; + grub_errno = GRUB_ERR_NONE; + } + + if (!err) + err = grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid DVA"); + grub_errno = err; + + return err; +} + +/* + * buf must be at least BPE_GET_PSIZE(bp) bytes long (which will never be + * more than BPE_PAYLOAD_SIZE bytes). + */ +static grub_err_t +decode_embedded_bp_compressed(const blkptr_t *bp, void *buf) +{ + grub_size_t psize, i; + grub_uint8_t *buf8 = buf; + grub_uint64_t w = 0; + const grub_uint64_t *bp64 = (const grub_uint64_t *)bp; + + psize = BPE_GET_PSIZE(bp); + + /* + * Decode the words of the block pointer into the byte array. + * Low bits of first word are the first byte (little endian). + */ + for (i = 0; i < psize; i++) + { + if (i % sizeof (w) == 0) + { + /* beginning of a word */ + w = *bp64; + bp64++; + if (!BPE_IS_PAYLOADWORD(bp, bp64)) + bp64++; + } + buf8[i] = BF64_GET(w, (i % sizeof (w)) * 8, 8); + } + return GRUB_ERR_NONE; +} + +/* + * Read in a block of data, verify its checksum, decompress if needed, + * and put the uncompressed data in buf. + */ +static grub_err_t +zio_read (blkptr_t *bp, grub_zfs_endian_t endian, void **buf, + grub_size_t *size, struct grub_zfs_data *data) +{ + grub_size_t lsize, psize; + unsigned int comp, encrypted; + char *compbuf = NULL; + grub_err_t err; + zio_cksum_t zc = bp->blk_cksum; + grub_uint32_t checksum; + + *buf = NULL; + + checksum = (grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; + comp = (grub_zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0x7f; + encrypted = ((grub_zfs_to_cpu64((bp)->blk_prop, endian) >> 60) & 3); + if (BP_IS_EMBEDDED(bp)) + { + if (BPE_GET_ETYPE(bp) != BP_EMBEDDED_TYPE_DATA) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported embedded BP (type=%" + PRIuGRUB_UINT64_T ")\n", + BPE_GET_ETYPE (bp)); + lsize = BPE_GET_LSIZE(bp); + psize = BF64_GET_SB(grub_zfs_to_cpu64 ((bp)->blk_prop, endian), 25, 7, 0, 1); + } + else + { + lsize = (BP_IS_HOLE(bp) ? 0 : + (((grub_zfs_to_cpu64 ((bp)->blk_prop, endian) & 0xffff) + 1) + << SPA_MINBLOCKSHIFT)); + psize = get_psize (bp, endian); + } + grub_dprintf("zfs", "zio_read: E %d: size %" PRIdGRUB_SSIZE "/%" + PRIdGRUB_SSIZE "\n", (int)BP_IS_EMBEDDED(bp), lsize, psize); + + if (size) + *size = lsize; + + if (comp >= ZIO_COMPRESS_FUNCTIONS) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression algorithm %u not supported\n", (unsigned int) comp); + + if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "compression algorithm %s not supported\n", decomp_table[comp].name); + + if (comp != ZIO_COMPRESS_OFF) + /* It's not really necessary to align to 16, just for safety. */ + compbuf = grub_malloc (ALIGN_UP (psize, 16)); + else + compbuf = *buf = grub_malloc (lsize); + if (! compbuf) + return grub_errno; + + grub_dprintf ("zfs", "endian = %d\n", endian); + if (BP_IS_EMBEDDED(bp)) + err = decode_embedded_bp_compressed(bp, compbuf); + else + { + err = zio_read_data (bp, endian, compbuf, data); + /* FIXME is it really necessary? */ + if (comp != ZIO_COMPRESS_OFF) + grub_memset (compbuf + psize, 0, ALIGN_UP (psize, 16) - psize); + } + if (err) + { + grub_free (compbuf); + *buf = NULL; + return err; + } + + if (!BP_IS_EMBEDDED(bp)) + { + err = zio_checksum_verify (zc, checksum, endian, + compbuf, psize); + if (err) + { + grub_dprintf ("zfs", "incorrect checksum\n"); + grub_free (compbuf); + *buf = NULL; + return err; + } + } + + if (encrypted) + { + if (!grub_zfs_decrypt) + err = grub_error (GRUB_ERR_BAD_FS, + N_("module `%s' isn't loaded"), + "zfscrypt"); + else + { + unsigned i, besti = 0; + grub_uint64_t bestval = 0; + for (i = 0; i < data->subvol.nkeys; i++) + if (data->subvol.keyring[i].txg <= grub_zfs_to_cpu64 (bp->blk_birth, + endian) + && data->subvol.keyring[i].txg > bestval) + { + besti = i; + bestval = data->subvol.keyring[i].txg; + } + if (bestval == 0) + { + grub_free (compbuf); + *buf = NULL; + grub_dprintf ("zfs", "no key for txg %" PRIxGRUB_UINT64_T "\n", + grub_zfs_to_cpu64 (bp->blk_birth, + endian)); + return grub_error (GRUB_ERR_BAD_FS, "no key found in keychain"); + } + grub_dprintf ("zfs", "using key %u (%" PRIxGRUB_UINT64_T + ", %p) for txg %" PRIxGRUB_UINT64_T "\n", + besti, data->subvol.keyring[besti].txg, + data->subvol.keyring[besti].cipher, + grub_zfs_to_cpu64 (bp->blk_birth, + endian)); + err = grub_zfs_decrypt (data->subvol.keyring[besti].cipher, + data->subvol.keyring[besti].algo, + &(bp)->blk_dva[encrypted], + compbuf, psize, zc.zc_mac, + endian); + } + if (err) + { + grub_free (compbuf); + *buf = NULL; + return err; + } + } + + if (comp != ZIO_COMPRESS_OFF) + { + *buf = grub_malloc (lsize); + if (!*buf) + { + grub_free (compbuf); + return grub_errno; + } + + err = decomp_table[comp].decomp_func (compbuf, *buf, psize, lsize); + grub_free (compbuf); + if (err) + { + grub_free (*buf); + *buf = NULL; + return err; + } + } + + return GRUB_ERR_NONE; +} + +/* + * Get the block from a block id. + * push the block onto the stack. + * + */ +static grub_err_t +dmu_read (dnode_end_t * dn, grub_uint64_t blkid, void **buf, + grub_zfs_endian_t *endian_out, struct grub_zfs_data *data) +{ + int level; + grub_off_t idx; + blkptr_t *bp_array = dn->dn.dn_blkptr; + int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT; + blkptr_t *bp; + void *tmpbuf = 0; + grub_zfs_endian_t endian; + grub_err_t err = GRUB_ERR_NONE; + + bp = grub_malloc (sizeof (blkptr_t)); + if (!bp) + return grub_errno; + + endian = dn->endian; + for (level = dn->dn.dn_nlevels - 1; level >= 0; level--) + { + grub_dprintf ("zfs", "endian = %d\n", endian); + idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1); + *bp = bp_array[idx]; + if (bp_array != dn->dn.dn_blkptr) + { + grub_free (bp_array); + bp_array = 0; + } + + if (BP_IS_HOLE (bp)) + { + grub_size_t size = grub_zfs_to_cpu16 (dn->dn.dn_datablkszsec, + dn->endian) + << SPA_MINBLOCKSHIFT; + *buf = grub_malloc (size); + if (!*buf) + { + err = grub_errno; + break; + } + grub_memset (*buf, 0, size); + endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; + break; + } + if (level == 0) + { + grub_dprintf ("zfs", "endian = %d\n", endian); + err = zio_read (bp, endian, buf, 0, data); + endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; + break; + } + grub_dprintf ("zfs", "endian = %d\n", endian); + err = zio_read (bp, endian, &tmpbuf, 0, data); + endian = (grub_zfs_to_cpu64 (bp->blk_prop, endian) >> 63) & 1; + if (err) + break; + bp_array = tmpbuf; + } + if (bp_array != dn->dn.dn_blkptr) + grub_free (bp_array); + if (endian_out) + *endian_out = endian; + + grub_free (bp); + return err; +} + +/* + * mzap_lookup: Looks up property described by "name" and returns the value + * in "value". + */ +static grub_err_t +mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian, + grub_uint32_t objsize, const char *name, grub_uint64_t * value, + int case_insensitive) +{ + grub_uint32_t i, chunks; + mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; + + if (objsize < MZAP_ENT_LEN) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name); + chunks = objsize / MZAP_ENT_LEN - 1; + for (i = 0; i < chunks; i++) + { + if (case_insensitive ? (grub_strcasecmp (mzap_ent[i].mze_name, name) == 0) + : (grub_strcmp (mzap_ent[i].mze_name, name) == 0)) + { + *value = grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian); + return GRUB_ERR_NONE; + } + } + + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name); +} + +static int +mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, + int (*hook) (const char *name, grub_uint64_t val, + struct grub_zfs_dir_ctx *ctx), + struct grub_zfs_dir_ctx *ctx) +{ + int i, chunks; + mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; + + chunks = objsize / MZAP_ENT_LEN - 1; + for (i = 0; i < chunks; i++) + { + grub_dprintf ("zfs", "zap: name = %s, value = %llx, cd = %x\n", + mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value, + (int)mzap_ent[i].mze_cd); + if (hook (mzap_ent[i].mze_name, + grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian), ctx)) + return 1; + } + + return 0; +} + +static grub_uint64_t +zap_hash (grub_uint64_t salt, const char *name, + int case_insensitive) +{ + static grub_uint64_t table[256]; + const grub_uint8_t *cp; + grub_uint8_t c; + grub_uint64_t crc = salt; + + if (table[128] == 0) + { + grub_uint64_t *ct; + int i, j; + for (i = 0; i < 256; i++) + { + for (ct = table + i, *ct = i, j = 8; j > 0; j--) + *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); + } + } + + if (case_insensitive) + for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) + crc = (crc >> 8) ^ table[(crc ^ grub_toupper (c)) & 0xFF]; + else + for (cp = (const grub_uint8_t *) name; (c = *cp) != '\0'; cp++) + crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; + + /* + * Only use 28 bits, since we need 4 bits in the cookie for the + * collision differentiator. We MUST use the high bits, since + * those are the onces that we first pay attention to when + * chosing the bucket. + */ + crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); + + return crc; +} + +/* + * Only to be used on 8-bit arrays. + * array_len is actual len in bytes (not encoded le_value_length). + * buf is null-terminated. + */ + +static inline int +name_cmp (const char *s1, const char *s2, grub_size_t n, + int case_insensitive) +{ + const char *t1 = (const char *) s1; + const char *t2 = (const char *) s2; + + if (!case_insensitive) + return grub_memcmp (t1, t2, n); + + while (n--) + { + if (grub_toupper (*t1) != grub_toupper (*t2)) + return (int) grub_toupper (*t1) - (int) grub_toupper (*t2); + + t1++; + t2++; + } + + return 0; +} + +/* XXX */ +static int +zap_leaf_array_equal (zap_leaf_phys_t * l, grub_zfs_endian_t endian, + int blksft, int chunk, grub_size_t array_len, + const char *buf, int case_insensitive) +{ + grub_size_t bseen = 0; + + while (bseen < array_len) + { + struct zap_leaf_array *la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array; + grub_size_t toread = array_len - bseen; + + if (toread > ZAP_LEAF_ARRAY_BYTES) + toread = ZAP_LEAF_ARRAY_BYTES; + + if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) + return 0; + + if (name_cmp ((char *) la->la_array, buf + bseen, toread, + case_insensitive) != 0) + break; + chunk = grub_zfs_to_cpu16 (la->la_next, endian); + bseen += toread; + } + return (bseen == array_len); +} + +/* XXX */ +static grub_err_t +zap_leaf_array_get (zap_leaf_phys_t * l, grub_zfs_endian_t endian, int blksft, + int chunk, grub_size_t array_len, char *buf) +{ + grub_size_t bseen = 0; + + while (bseen < array_len) + { + struct zap_leaf_array *la; + grub_size_t toread = array_len - bseen; + + if (toread > ZAP_LEAF_ARRAY_BYTES) + toread = ZAP_LEAF_ARRAY_BYTES; + + if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) + /* Don't use grub_error because this error is to be ignored. */ + return GRUB_ERR_BAD_FS; + + la = &ZAP_LEAF_CHUNK (l, blksft, chunk)->l_array; + grub_memcpy (buf + bseen,la->la_array, toread); + chunk = grub_zfs_to_cpu16 (la->la_next, endian); + bseen += toread; + } + return GRUB_ERR_NONE; +} + + +/* + * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the + * value for the property "name". + * + */ +/* XXX */ +static grub_err_t +zap_leaf_lookup (zap_leaf_phys_t * l, grub_zfs_endian_t endian, + int blksft, grub_uint64_t h, + const char *name, grub_uint64_t * value, + int case_insensitive) +{ + grub_uint16_t chunk; + struct zap_leaf_entry *le; + + /* Verify if this is a valid leaf block */ + if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF) + return grub_error (GRUB_ERR_BAD_FS, "invalid leaf type"); + if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "invalid leaf magic"); + + for (chunk = grub_zfs_to_cpu16 (l->l_hash[LEAF_HASH (blksft, h, l)], endian); + chunk != CHAIN_END; chunk = grub_zfs_to_cpu16 (le->le_next, endian)) + { + + if (chunk >= ZAP_LEAF_NUMCHUNKS (blksft)) + return grub_error (GRUB_ERR_BAD_FS, "invalid chunk number"); + + le = ZAP_LEAF_ENTRY (l, blksft, chunk); + + /* Verify the chunk entry */ + if (le->le_type != ZAP_CHUNK_ENTRY) + return grub_error (GRUB_ERR_BAD_FS, "invalid chunk entry"); + + if (grub_zfs_to_cpu64 (le->le_hash,endian) != h) + continue; + + grub_dprintf ("zfs", "fzap: length %d\n", (int) le->le_name_length); + + if (zap_leaf_array_equal (l, endian, blksft, + grub_zfs_to_cpu16 (le->le_name_chunk,endian), + grub_zfs_to_cpu16 (le->le_name_length, endian), + name, case_insensitive)) + { + struct zap_leaf_array *la; + + if (le->le_int_size != 8 || grub_zfs_to_cpu16 (le->le_value_length, + endian) != 1) + return grub_error (GRUB_ERR_BAD_FS, "invalid leaf chunk entry"); + + /* get the uint64_t property value */ + la = &ZAP_LEAF_CHUNK (l, blksft, le->le_value_chunk)->l_array; + + *value = grub_be_to_cpu64 (la->la_array64); + + return GRUB_ERR_NONE; + } + } + + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), name); +} + + +/* Verify if this is a fat zap header block */ +static grub_err_t +zap_verify (zap_phys_t *zap, grub_zfs_endian_t endian) +{ + if (grub_zfs_to_cpu64 (zap->zap_magic, endian) != (grub_uint64_t) ZAP_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "bad ZAP magic"); + + if (zap->zap_salt == 0) + return grub_error (GRUB_ERR_BAD_FS, "bad ZAP salt"); + + return GRUB_ERR_NONE; +} + +/* + * Fat ZAP lookup + * + */ +/* XXX */ +static grub_err_t +fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap, + const char *name, grub_uint64_t * value, + struct grub_zfs_data *data, int case_insensitive) +{ + void *l; + grub_uint64_t hash, idx, blkid; + int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + zap_dnode->endian) << DNODE_SHIFT); + grub_err_t err; + grub_zfs_endian_t leafendian; + + err = zap_verify (zap, zap_dnode->endian); + if (err) + return err; + + hash = zap_hash (zap->zap_salt, name, case_insensitive); + + /* get block id from index */ + if (zap->zap_ptrtbl.zt_numblks != 0) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "external pointer tables not supported"); + idx = ZAP_HASH_IDX (hash, zap->zap_ptrtbl.zt_shift); + blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], zap_dnode->endian); + + /* Get the leaf block */ + if ((1U << blksft) < sizeof (zap_leaf_phys_t)) + return grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); + err = dmu_read (zap_dnode, blkid, &l, &leafendian, data); + if (err) + return err; + + err = zap_leaf_lookup (l, leafendian, blksft, hash, name, value, + case_insensitive); + grub_free (l); + return err; +} + +/* XXX */ +static int +fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, + grub_size_t name_elem_length, + int (*hook) (const void *name, grub_size_t name_length, + const void *val_in, + grub_size_t nelem, grub_size_t elemsize, + void *data), + void *hook_data, struct grub_zfs_data *data) +{ + zap_leaf_phys_t *l; + void *l_in; + grub_uint64_t idx, idx2, blkid; + grub_uint16_t chunk; + int blksft = zfs_log2 (grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + zap_dnode->endian) << DNODE_SHIFT); + grub_err_t err; + grub_zfs_endian_t endian; + grub_size_t sz; + + if (zap_verify (zap, zap_dnode->endian)) + return 0; + + /* get block id from index */ + if (zap->zap_ptrtbl.zt_numblks != 0) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "external pointer tables not supported"); + return 0; + } + /* Get the leaf block */ + if ((1U << blksft) < sizeof (zap_leaf_phys_t)) + { + grub_error (GRUB_ERR_BAD_FS, "ZAP leaf is too small"); + return 0; + } + for (idx = 0; idx < (1ULL << zap->zap_ptrtbl.zt_shift); idx++) + { + blkid = grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))], + zap_dnode->endian); + + for (idx2 = 0; idx2 < idx; idx2++) + if (blkid == grub_zfs_to_cpu64 (((grub_uint64_t *) zap)[idx2 + (1 << (blksft - 3 - 1))], + zap_dnode->endian)) + break; + if (idx2 != idx) + continue; + + err = dmu_read (zap_dnode, blkid, &l_in, &endian, data); + l = l_in; + if (err) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + /* Verify if this is a valid leaf block */ + if (grub_zfs_to_cpu64 (l->l_hdr.lh_block_type, endian) != ZBT_LEAF) + { + grub_free (l); + continue; + } + if (grub_zfs_to_cpu32 (l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) + { + grub_free (l); + continue; + } + + for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS (blksft); chunk++) + { + char *buf; + struct zap_leaf_entry *le; + char *val; + grub_size_t val_length; + le = ZAP_LEAF_ENTRY (l, blksft, chunk); + + /* Verify the chunk entry */ + if (le->le_type != ZAP_CHUNK_ENTRY) + continue; + + if (grub_mul (grub_zfs_to_cpu16 (le->le_name_length, endian), name_elem_length, &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + grub_free (l); + return grub_errno; + } + buf = grub_malloc (sz); + if (!buf) + { + grub_free (l); + return grub_errno; + } + if (zap_leaf_array_get (l, endian, blksft, + grub_zfs_to_cpu16 (le->le_name_chunk, + endian), + grub_zfs_to_cpu16 (le->le_name_length, + endian) + * name_elem_length, buf)) + { + grub_free (buf); + continue; + } + buf[le->le_name_length * name_elem_length] = 0; + + val_length = ((int) le->le_value_length + * (int) le->le_int_size); + val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian)); + if (!val) + { + grub_free (l); + grub_free (buf); + return grub_errno; + } + if (zap_leaf_array_get (l, endian, blksft, + grub_zfs_to_cpu16 (le->le_value_chunk, + endian), + val_length, val)) + { + grub_free (buf); + grub_free (val); + continue; + } + + if (hook (buf, le->le_name_length, + val, le->le_value_length, le->le_int_size, hook_data)) + { + grub_free (l); + return 1; + } + grub_free (buf); + grub_free (val); + } + grub_free (l); + } + return 0; +} + +/* + * Read in the data of a zap object and find the value for a matching + * property name. + * + */ +static grub_err_t +zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val, + struct grub_zfs_data *data, int case_insensitive) +{ + grub_uint64_t block_type; + grub_uint32_t size; + void *zapbuf; + grub_err_t err; + grub_zfs_endian_t endian; + + grub_dprintf ("zfs", "looking for '%s'\n", name); + + /* Read in the first block of the zap object data. */ + size = (grub_uint32_t) grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, + zap_dnode->endian) << SPA_MINBLOCKSHIFT; + err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); + if (err) + return err; + block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); + + grub_dprintf ("zfs", "zap read\n"); + + if (block_type == ZBT_MICRO) + { + grub_dprintf ("zfs", "micro zap\n"); + err = mzap_lookup (zapbuf, endian, size, name, val, + case_insensitive); + grub_dprintf ("zfs", "returned %d\n", err); + grub_free (zapbuf); + return err; + } + else if (block_type == ZBT_HEADER) + { + grub_dprintf ("zfs", "fat zap\n"); + /* this is a fat zap */ + err = fzap_lookup (zap_dnode, zapbuf, name, val, data, + case_insensitive); + grub_dprintf ("zfs", "returned %d\n", err); + grub_free (zapbuf); + return err; + } + + grub_free (zapbuf); + return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); +} + +/* Context for zap_iterate_u64. */ +struct zap_iterate_u64_ctx +{ + int (*hook) (const char *, grub_uint64_t, struct grub_zfs_dir_ctx *); + struct grub_zfs_dir_ctx *dir_ctx; +}; + +/* Helper for zap_iterate_u64. */ +static int +zap_iterate_u64_transform (const void *name, + grub_size_t namelen __attribute__ ((unused)), + const void *val_in, + grub_size_t nelem, + grub_size_t elemsize, + void *data) +{ + struct zap_iterate_u64_ctx *ctx = data; + + if (elemsize != sizeof (grub_uint64_t) || nelem != 1) + return 0; + return ctx->hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in), + ctx->dir_ctx); +} + +static int +zap_iterate_u64 (dnode_end_t * zap_dnode, + int (*hook) (const char *name, grub_uint64_t val, + struct grub_zfs_dir_ctx *ctx), + struct grub_zfs_data *data, struct grub_zfs_dir_ctx *ctx) +{ + grub_uint64_t block_type; + int size; + void *zapbuf; + grub_err_t err; + int ret; + grub_zfs_endian_t endian; + + /* Read in the first block of the zap object data. */ + size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; + err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); + if (err) + return 0; + block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); + + grub_dprintf ("zfs", "zap iterate\n"); + + if (block_type == ZBT_MICRO) + { + grub_dprintf ("zfs", "micro zap\n"); + ret = mzap_iterate (zapbuf, endian, size, hook, ctx); + grub_free (zapbuf); + return ret; + } + else if (block_type == ZBT_HEADER) + { + struct zap_iterate_u64_ctx transform_ctx = { + .hook = hook, + .dir_ctx = ctx + }; + + grub_dprintf ("zfs", "fat zap\n"); + /* this is a fat zap */ + ret = fzap_iterate (zap_dnode, zapbuf, 1, + zap_iterate_u64_transform, &transform_ctx, data); + grub_free (zapbuf); + return ret; + } + grub_free (zapbuf); + grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); + return 0; +} + +static int +zap_iterate (dnode_end_t * zap_dnode, + grub_size_t nameelemlen, + int (*hook) (const void *name, grub_size_t namelen, + const void *val_in, + grub_size_t nelem, grub_size_t elemsize, + void *data), + void *hook_data, struct grub_zfs_data *data) +{ + grub_uint64_t block_type; + void *zapbuf; + grub_err_t err; + int ret; + grub_zfs_endian_t endian; + + /* Read in the first block of the zap object data. */ + err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data); + if (err) + return 0; + block_type = grub_zfs_to_cpu64 (*((grub_uint64_t *) zapbuf), endian); + + grub_dprintf ("zfs", "zap iterate\n"); + + if (block_type == ZBT_MICRO) + { + grub_error (GRUB_ERR_BAD_FS, "micro ZAP where FAT ZAP expected"); + grub_free (zapbuf); + return 0; + } + if (block_type == ZBT_HEADER) + { + grub_dprintf ("zfs", "fat zap\n"); + /* this is a fat zap */ + ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, hook_data, + data); + grub_free (zapbuf); + return ret; + } + grub_free (zapbuf); + grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type"); + return 0; +} + + +/* + * Get the dnode of an object number from the metadnode of an object set. + * + * Input + * mdn - metadnode to get the object dnode + * objnum - object number for the object dnode + * buf - data buffer that holds the returning dnode + */ +static grub_err_t +dnode_get (dnode_end_t * mdn, grub_uint64_t objnum, grub_uint8_t type, + dnode_end_t * buf, struct grub_zfs_data *data) +{ + grub_uint64_t blkid, blksz; /* the block id this object dnode is in */ + int epbs; /* shift of number of dnodes in a block */ + int idx; /* index within a block */ + void *dnbuf; + grub_err_t err; + grub_zfs_endian_t endian; + + objnum &= DNODE_NUM_MASK; + + blksz = grub_zfs_to_cpu16 (mdn->dn.dn_datablkszsec, + mdn->endian) << SPA_MINBLOCKSHIFT; + epbs = zfs_log2 (blksz) - DNODE_SHIFT; + + /* While this should never happen, we should check that epbs is not negative. */ + if (epbs < 0) + epbs = 0; + + blkid = objnum >> epbs; + idx = objnum & ((1 << epbs) - 1); + + if (data->dnode_buf != NULL && grub_memcmp (data->dnode_mdn, &mdn->dn, + sizeof (mdn->dn)) == 0 + && objnum >= data->dnode_start && objnum < data->dnode_end) + { + grub_memmove (&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); + buf->endian = data->dnode_endian; + if (type && buf->dn.dn_type != type) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); + return GRUB_ERR_NONE; + } + + grub_dprintf ("zfs", "endian = %d, blkid=%llx\n", mdn->endian, + (unsigned long long) blkid); + err = dmu_read (mdn, blkid, &dnbuf, &endian, data); + if (err) + return err; + grub_dprintf ("zfs", "alive\n"); + + grub_free (data->dnode_buf); + grub_free (data->dnode_mdn); + data->dnode_mdn = grub_malloc (sizeof (*mdn)); + if (! data->dnode_mdn) + { + grub_errno = GRUB_ERR_NONE; + data->dnode_buf = 0; + } + else + { + grub_memcpy (data->dnode_mdn, mdn, sizeof (*mdn)); + data->dnode_buf = dnbuf; + data->dnode_start = blkid << epbs; + data->dnode_end = (blkid + 1) << epbs; + data->dnode_endian = endian; + } + + grub_memmove (&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); + if (data->dnode_buf == 0) + /* dnbuf not used anymore if data->dnode_mdn malloc failed */ + grub_free (dnbuf); + buf->endian = endian; + if (type && buf->dn.dn_type != type) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dnode type"); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wstrict-aliasing" + +/* + * Get the file dnode for a given file name where mdn is the meta dnode + * for this ZFS object set. When found, place the file dnode in dn. + * The 'path' argument will be mangled. + * + */ +static grub_err_t +dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, + struct grub_zfs_data *data) +{ + grub_uint64_t objnum, version; + char *cname, ch; + grub_err_t err = GRUB_ERR_NONE; + char *path, *path_buf; + struct dnode_chain + { + struct dnode_chain *next; + dnode_end_t dn; + }; + struct dnode_chain *dnode_path = 0, *dn_new, *root; + + dn_new = grub_malloc (sizeof (*dn_new)); + if (! dn_new) + return grub_errno; + dn_new->next = 0; + dnode_path = root = dn_new; + + err = dnode_get (&subvol->mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, + &(dnode_path->dn), data); + if (err) + { + grub_free (dn_new); + return err; + } + + err = zap_lookup (&(dnode_path->dn), ZPL_VERSION_STR, &version, + data, 0); + if (err) + { + grub_free (dn_new); + return err; + } + + if (version > ZPL_VERSION) + { + grub_free (dn_new); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "too new ZPL version"); + } + + err = zap_lookup (&(dnode_path->dn), "casesensitivity", + &subvol->case_insensitive, + data, 0); + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + grub_errno = GRUB_ERR_NONE; + subvol->case_insensitive = 0; + } + + err = zap_lookup (&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data, 0); + if (err) + { + grub_free (dn_new); + return err; + } + + err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data); + if (err) + { + grub_free (dn_new); + return err; + } + + path = path_buf = grub_strdup (path_in); + if (!path_buf) + { + grub_free (dn_new); + return grub_errno; + } + + while (1) + { + /* skip leading slashes */ + while (*path == '/') + path++; + if (!*path) + break; + /* get the next component name */ + cname = path; + while (*path && *path != '/') + path++; + /* Skip dot. */ + if (cname + 1 == path && cname[0] == '.') + continue; + /* Handle double dot. */ + if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') + { + if (dn_new->next) + { + dn_new = dnode_path; + dnode_path = dn_new->next; + grub_free (dn_new); + } + else + { + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + "can't resolve .."); + break; + } + continue; + } + + ch = *path; + *path = 0; /* ensure null termination */ + + if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + break; + } + err = zap_lookup (&(dnode_path->dn), cname, &objnum, + data, subvol->case_insensitive); + if (err) + break; + + dn_new = grub_malloc (sizeof (*dn_new)); + if (! dn_new) + { + err = grub_errno; + break; + } + dn_new->next = dnode_path; + dnode_path = dn_new; + + objnum = ZFS_DIRENT_OBJ (objnum); + err = dnode_get (&subvol->mdn, objnum, 0, &(dnode_path->dn), data); + if (err) + break; + + *path = ch; + if (dnode_path->dn.dn.dn_bonustype == DMU_OT_ZNODE + && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) + { + char *sym_value; + grub_size_t sz; + grub_size_t sym_sz; + int free_symval = 0; + char *oldpath = path, *oldpathbuf = path_buf; + sym_value = ((char *) DN_BONUS (&dnode_path->dn.dn) + sizeof (struct znode_phys)); + + sym_sz = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_size, dnode_path->dn.endian); + + if (dnode_path->dn.dn.dn_flags & 1) + { + grub_size_t block; + grub_size_t blksz; + blksz = (grub_zfs_to_cpu16 (dnode_path->dn.dn.dn_datablkszsec, + dnode_path->dn.endian) + << SPA_MINBLOCKSHIFT); + + if (blksz == 0) + { + err = grub_error (GRUB_ERR_BAD_FS, "0-sized block"); + break; + } + + sym_value = grub_malloc (sym_sz); + if (!sym_value) + { + err = grub_errno; + break; + } + + for (block = 0; block < (sym_sz + blksz - 1) / blksz; block++) + { + void *t; + grub_size_t movesize; + + err = dmu_read (&(dnode_path->dn), block, &t, 0, data); + if (err) + { + grub_free (sym_value); + break; + } + + movesize = sym_sz - block * blksz; + if (movesize > blksz) + movesize = blksz; + + grub_memcpy (sym_value + block * blksz, t, movesize); + grub_free (t); + } + if (err) + break; + free_symval = 1; + } + + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + if (free_symval) + grub_free (sym_value); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); + if (!path_buf) + { + grub_free (oldpathbuf); + if (free_symval) + grub_free (sym_value); + err = grub_errno; + break; + } + grub_memcpy (path, sym_value, sym_sz); + if (free_symval) + grub_free (sym_value); + path [sym_sz] = 0; + grub_memcpy (path + grub_strlen (path), oldpath, + grub_strlen (oldpath) + 1); + + grub_free (oldpathbuf); + + /* Restart dnode walk using path of symlink. */ + if (path[0] != '/') + { + dn_new = dnode_path; + dnode_path = dn_new->next; + grub_free (dn_new); + } + else while (dnode_path != root) + { + dn_new = dnode_path; + dnode_path = dn_new->next; + grub_free (dn_new); + } + dn_new = dnode_path; + } + if (dnode_path->dn.dn.dn_bonustype == DMU_OT_SA) + { + void *sahdrp; + int hdrsize; + grub_size_t sz; + + if (dnode_path->dn.dn.dn_bonuslen != 0) + { + sahdrp = DN_BONUS (&dnode_path->dn.dn); + } + else if (dnode_path->dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) + { + blkptr_t *bp = &dnode_path->dn.dn.dn_spill; + + err = zio_read (bp, dnode_path->dn.endian, &sahdrp, NULL, data); + if (err) + break; + } + else + { + err = grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); + break; + } + + hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); + + if (((grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + + hdrsize + + SA_TYPE_OFFSET), + dnode_path->dn.endian) >> 12) & 0xf) == 0xa) + { + char *sym_value = (char *) sahdrp + hdrsize + SA_SYMLINK_OFFSET; + grub_size_t sym_sz = + grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + + hdrsize + + SA_SIZE_OFFSET), + dnode_path->dn.endian); + char *oldpath = path, *oldpathbuf = path_buf; + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); + if (!path_buf) + { + grub_free (oldpathbuf); + err = grub_errno; + break; + } + grub_memcpy (path, sym_value, sym_sz); + path [sym_sz] = 0; + grub_memcpy (path + grub_strlen (path), oldpath, + grub_strlen (oldpath) + 1); + + grub_free (oldpathbuf); + + /* Restart dnode walk using path of symlink. */ + if (path[0] != '/') + { + dn_new = dnode_path; + dnode_path = dn_new->next; + grub_free (dn_new); + } + else while (dnode_path != root) + { + dn_new = dnode_path; + dnode_path = dn_new->next; + grub_free (dn_new); + } + dn_new = dnode_path; + } + } + } + + if (!err) + grub_memcpy (dn, &(dnode_path->dn), sizeof (*dn)); + + while (dnode_path) + { + dn_new = dnode_path->next; + grub_free (dnode_path); + dnode_path = dn_new; + } + grub_free (path_buf); + return err; +} + +#if 0 +/* + * Get the default 'bootfs' property value from the rootpool. + * + */ +static grub_err_t +get_default_bootfsobj (dnode_phys_t * mosmdn, grub_uint64_t * obj, + struct grub_zfs_data *data) +{ + grub_uint64_t objnum = 0; + dnode_phys_t *dn; + if (!dn) + return grub_errno; + + if ((grub_errno = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, dn, data))) + { + grub_free (dn); + return (grub_errno); + } + + /* + * find the object number for 'pool_props', and get the dnode + * of the 'pool_props'. + */ + if (zap_lookup (dn, DMU_POOL_PROPS, &objnum, data)) + { + grub_free (dn); + return (GRUB_ERR_BAD_FS); + } + if ((grub_errno = dnode_get (mosmdn, objnum, DMU_OT_POOL_PROPS, dn, data))) + { + grub_free (dn); + return (grub_errno); + } + if (zap_lookup (dn, ZPOOL_PROP_BOOTFS, &objnum, data)) + { + grub_free (dn); + return (GRUB_ERR_BAD_FS); + } + + if (!objnum) + { + grub_free (dn); + return (GRUB_ERR_BAD_FS); + } + + *obj = objnum; + return (0); +} +#endif +/* + * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), + * e.g. pool/rootfs, or a given object number (obj), e.g. the object number + * of pool/rootfs. + * + * If no fsname and no obj are given, return the DSL_DIR metadnode. + * If fsname is given, return its metadnode and its matching object number. + * If only obj is given, return the metadnode for this object number. + * + */ +static grub_err_t +get_filesystem_dnode (dnode_end_t * mosmdn, char *fsname, + dnode_end_t * mdn, struct grub_zfs_data *data) +{ + grub_uint64_t objnum; + grub_err_t err; + + grub_dprintf ("zfs", "endian = %d\n", mosmdn->endian); + + err = dnode_get (mosmdn, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, mdn, data); + if (err) + return err; + + grub_dprintf ("zfs", "alive\n"); + + err = zap_lookup (mdn, DMU_POOL_ROOT_DATASET, &objnum, data, 0); + if (err) + return err; + + grub_dprintf ("zfs", "alive\n"); + + err = dnode_get (mosmdn, objnum, 0, mdn, data); + if (err) + return err; + + grub_dprintf ("zfs", "alive\n"); + + while (*fsname) + { + grub_uint64_t childobj; + char *cname, ch; + + while (*fsname == '/') + fsname++; + + if (! *fsname || *fsname == '@') + break; + + cname = fsname; + while (*fsname && *fsname != '/') + fsname++; + ch = *fsname; + *fsname = 0; + + childobj = grub_zfs_to_cpu64 ((((dsl_dir_phys_t *) DN_BONUS (&mdn->dn)))->dd_child_dir_zapobj, mdn->endian); + err = dnode_get (mosmdn, childobj, + DMU_OT_DSL_DIR_CHILD_MAP, mdn, data); + if (err) + return err; + + err = zap_lookup (mdn, cname, &objnum, data, 0); + if (err) + return err; + + err = dnode_get (mosmdn, objnum, 0, mdn, data); + if (err) + return err; + + *fsname = ch; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data) +{ + objset_phys_t *osp; + blkptr_t *bp; + grub_size_t ospsize = 0; + grub_err_t err; + + grub_dprintf ("zfs", "endian = %d\n", mdn->endian); + + bp = &(((dsl_dataset_phys_t *) DN_BONUS (&mdn->dn))->ds_bp); + err = zio_read (bp, mdn->endian, (void **) &osp, &ospsize, data); + if (err) + return err; + if (ospsize < OBJSET_PHYS_SIZE_V14) + { + grub_free (osp); + return grub_error (GRUB_ERR_BAD_FS, "too small osp"); + } + + mdn->endian = (grub_zfs_to_cpu64 (bp->blk_prop, mdn->endian)>>63) & 1; + grub_memmove ((char *) &(mdn->dn), + (char *) &(osp)->os_meta_dnode, DNODE_SIZE); + grub_free (osp); + return GRUB_ERR_NONE; +} + +/* Context for dnode_get_fullpath. */ +struct dnode_get_fullpath_ctx +{ + struct subvolume *subvol; + grub_uint64_t salt; + int keyn; +}; + +/* Helper for dnode_get_fullpath. */ +static int +count_zap_keys (const void *name __attribute__ ((unused)), + grub_size_t namelen __attribute__ ((unused)), + const void *val_in __attribute__ ((unused)), + grub_size_t nelem __attribute__ ((unused)), + grub_size_t elemsize __attribute__ ((unused)), + void *data) +{ + struct dnode_get_fullpath_ctx *ctx = data; + + ctx->subvol->nkeys++; + return 0; +} + +/* Helper for dnode_get_fullpath. */ +static int +load_zap_key (const void *name, grub_size_t namelen, const void *val_in, + grub_size_t nelem, grub_size_t elemsize, void *data) +{ + struct dnode_get_fullpath_ctx *ctx = data; + + if (namelen != 1) + { + grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n", + namelen); + return 0; + } + + if (elemsize != 1) + { + grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n", + elemsize); + return 0; + } + + ctx->subvol->keyring[ctx->keyn].txg = + grub_be_to_cpu64 (*(grub_uint64_t *) name); + ctx->subvol->keyring[ctx->keyn].algo = + grub_le_to_cpu64 (*(grub_uint64_t *) val_in); + ctx->subvol->keyring[ctx->keyn].cipher = + grub_zfs_load_key (val_in, nelem, ctx->salt, + ctx->subvol->keyring[ctx->keyn].algo); + ctx->keyn++; + return 0; +} + +static grub_err_t +dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, + dnode_end_t * dn, int *isfs, + struct grub_zfs_data *data) +{ + char *fsname, *snapname; + const char *ptr_at, *filename; + grub_uint64_t headobj; + grub_uint64_t keychainobj; + grub_err_t err; + + ptr_at = grub_strchr (fullpath, '@'); + if (! ptr_at) + { + *isfs = 1; + filename = 0; + snapname = 0; + fsname = grub_strdup (fullpath); + if (!fsname) + return grub_errno; + } + else + { + const char *ptr_slash = grub_strchr (ptr_at, '/'); + + *isfs = 0; + fsname = grub_malloc (ptr_at - fullpath + 1); + if (!fsname) + return grub_errno; + grub_memcpy (fsname, fullpath, ptr_at - fullpath); + fsname[ptr_at - fullpath] = 0; + if (ptr_at[1] && ptr_at[1] != '/') + { + snapname = grub_malloc (ptr_slash - ptr_at); + if (!snapname) + { + grub_free (fsname); + return grub_errno; + } + grub_memcpy (snapname, ptr_at + 1, ptr_slash - ptr_at - 1); + snapname[ptr_slash - ptr_at - 1] = 0; + } + else + snapname = 0; + if (ptr_slash) + filename = ptr_slash; + else + filename = "/"; + grub_dprintf ("zfs", "fsname = '%s' snapname='%s' filename = '%s'\n", + fsname, snapname, filename); + } + grub_dprintf ("zfs", "alive\n"); + err = get_filesystem_dnode (&(data->mos), fsname, dn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + + grub_dprintf ("zfs", "alive\n"); + + headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_head_dataset_obj, dn->endian); + + grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); + + err = dnode_get (&(data->mos), headobj, 0, &subvol->mdn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); + + keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian); + if (grub_zfs_load_key && keychainobj) + { + struct dnode_get_fullpath_ctx ctx = { + .subvol = subvol, + .keyn = 0 + }; + dnode_end_t keychain_dn, props_dn; + grub_uint64_t propsobj; + propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian); + + err = dnode_get (&(data->mos), propsobj, DMU_OT_DSL_PROPS, + &props_dn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + + err = zap_lookup (&props_dn, "salt", &ctx.salt, data, 0); + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + err = 0; + grub_errno = 0; + ctx.salt = 0; + } + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + grub_free (fsname); + grub_free (snapname); + return err; + } + + err = dnode_get (&(data->mos), keychainobj, DMU_OT_DSL_KEYCHAIN, + &keychain_dn, data); + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + subvol->nkeys = 0; + zap_iterate (&keychain_dn, 8, count_zap_keys, &ctx, data); + subvol->keyring = grub_calloc (subvol->nkeys, sizeof (subvol->keyring[0])); + if (!subvol->keyring) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + zap_iterate (&keychain_dn, 8, load_zap_key, &ctx, data); + } + + if (snapname) + { + grub_uint64_t snapobj; + + snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&subvol->mdn.dn))->ds_snapnames_zapobj, subvol->mdn.endian); + + err = dnode_get (&(data->mos), snapobj, + DMU_OT_DSL_DS_SNAP_MAP, &subvol->mdn, data); + if (!err) + err = zap_lookup (&subvol->mdn, snapname, &headobj, data, 0); + if (!err) + err = dnode_get (&(data->mos), headobj, 0, + &subvol->mdn, data); + if (!err && subvol->mdn.dn.dn_type != DMU_OT_DSL_DATASET && subvol->mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) { + grub_free (fsname); + grub_free (snapname); + return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type"); + } + + if (err) + { + grub_free (fsname); + grub_free (snapname); + return err; + } + } + + subvol->obj = headobj; + + make_mdn (&subvol->mdn, data); + + grub_dprintf ("zfs", "endian = %d\n", subvol->mdn.endian); + + if (*isfs) + { + grub_free (fsname); + grub_free (snapname); + return GRUB_ERR_NONE; + } + err = dnode_get_path (subvol, filename, dn, data); + grub_free (fsname); + grub_free (snapname); + return err; +} + +static int +nvlist_find_value (const char *nvlist_in, const char *name, + int valtype, char **val, + grub_size_t *size_out, grub_size_t *nelm_out) +{ + grub_size_t nvp_name_len, name_len = grub_strlen(name); + int type; + const char *nvpair=NULL,*nvlist=nvlist_in; + char *nvp_name; + + /* Verify if the 1st and 2nd byte in the nvlist are valid. */ + /* NOTE: independently of what endianness header announces all + subsequent values are big-endian. */ + if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN + && nvlist[1] != NV_BIG_ENDIAN)) + { + grub_dprintf ("zfs", "incorrect nvlist header\n"); + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist"); + return 0; + } + + /* + * Loop thru the nvpair list + * The XDR representation of an integer is in big-endian byte order. + */ + while ((nvpair=nvlist_next_nvpair(nvlist,nvpair))) + { + nvpair_name(nvpair,&nvp_name, &nvp_name_len); + type = nvpair_type(nvpair); + if (type == valtype + && (nvp_name_len == name_len + || (nvp_name_len > name_len && nvp_name[name_len] == '\0')) + && grub_memcmp (nvp_name, name, name_len) == 0) + { + return nvpair_value(nvpair,val,size_out,nelm_out); + } + } + return 0; +} + +int +grub_zfs_nvlist_lookup_uint64 (const char *nvlist, const char *name, + grub_uint64_t * out) +{ + char *nvpair; + grub_size_t size; + int found; + + found = nvlist_find_value (nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0); + if (!found) + return 0; + if (size < sizeof (grub_uint64_t)) + { + grub_error (GRUB_ERR_BAD_FS, "invalid uint64"); + return 0; + } + + *out = grub_be_to_cpu64 (grub_get_unaligned64 (nvpair)); + return 1; +} + +char * +grub_zfs_nvlist_lookup_string (const char *nvlist, const char *name) +{ + char *nvpair; + char *ret; + grub_size_t slen; + grub_size_t size; + int found; + + found = nvlist_find_value (nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0); + if (!found) + return 0; + if (size < 4) + { + grub_error (GRUB_ERR_BAD_FS, "invalid string"); + return 0; + } + slen = grub_be_to_cpu32 (grub_get_unaligned32 (nvpair)); + if (slen > size - 4) + slen = size - 4; + ret = grub_malloc (slen + 1); + if (!ret) + return 0; + grub_memcpy (ret, nvpair + 4, slen); + ret[slen] = 0; + return ret; +} + +char * +grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name) +{ + char *nvpair; + char *ret; + grub_size_t size, sz; + int found; + + found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair, + &size, 0); + if (!found) + return 0; + + if (grub_add (size, 3 * sizeof (grub_uint32_t), &sz)) + return 0; + + ret = grub_zalloc (sz); + if (!ret) + return 0; + grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); + + grub_memcpy (ret + sizeof (grub_uint32_t), nvpair, size); + return ret; +} + +int +grub_zfs_nvlist_lookup_nvlist_array_get_nelm (const char *nvlist, + const char *name) +{ + char *nvpair; + grub_size_t nelm, size; + int found; + + found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, + &size, &nelm); + if (! found) + return -1; + return nelm; +} + +static int +get_nvlist_size (const char *beg, const char *limit) +{ + const char *ptr; + grub_uint32_t encode_size; + + ptr = beg + 8; + + while (ptr < limit + && (encode_size = grub_be_to_cpu32 (grub_get_unaligned32 (ptr)))) + ptr += encode_size; /* goto the next nvpair */ + ptr += 8; + return (ptr > limit) ? -1 : (ptr - beg); +} + +char * +grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, + grub_size_t index) +{ + char *nvpair, *nvpairptr; + int found; + char *ret; + grub_size_t size; + unsigned i; + grub_size_t nelm; + int elemsize = 0; + int sz; + + found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, + &size, &nelm); + if (!found) + return 0; + if (index >= nelm) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "trying to lookup past nvlist array"); + return 0; + } + + nvpairptr = nvpair; + + for (i = 0; i < index; i++) + { + int r; + r = get_nvlist_size (nvpairptr, nvpair + size); + + if (r < 0) + { + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); + return NULL; + } + nvpairptr += r; + } + + elemsize = get_nvlist_size (nvpairptr, nvpair + size); + + if (elemsize < 0) + { + grub_error (GRUB_ERR_BAD_FS, "incorrect nvlist array"); + return 0; + } + + if (grub_add (elemsize, sizeof (grub_uint32_t), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("elemsize overflow")); + return 0; + } + ret = grub_zalloc (sz); + if (!ret) + return 0; + grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); + + grub_memcpy (ret + sizeof (grub_uint32_t), nvpairptr, elemsize); + return ret; +} + +static void +unmount_device (struct grub_zfs_device_desc *desc) +{ + unsigned i; + switch (desc->type) + { + case DEVICE_LEAF: + if (!desc->original && desc->dev) + grub_device_close (desc->dev); + return; + case DEVICE_RAIDZ: + case DEVICE_MIRROR: + for (i = 0; i < desc->n_children; i++) + unmount_device (&desc->children[i]); + grub_free (desc->children); + return; + } +} + +static void +zfs_unmount (struct grub_zfs_data *data) +{ + unsigned i; + for (i = 0; i < data->n_devices_attached; i++) + unmount_device (&data->devices_attached[i]); + grub_free (data->devices_attached); + grub_free (data->dnode_buf); + grub_free (data->dnode_mdn); + grub_free (data->file_buf); + for (i = 0; i < data->subvol.nkeys; i++) + grub_crypto_cipher_close (data->subvol.keyring[i].cipher); + grub_free (data->subvol.keyring); + grub_free (data); +} + +/* + * zfs_mount() locates a valid uberblock of the root pool and read in its MOS + * to the memory address MOS. + * + */ +static struct grub_zfs_data * +zfs_mount (grub_device_t dev) +{ + struct grub_zfs_data *data = 0; + grub_err_t err; + objset_phys_t *osp = 0; + grub_size_t ospsize; + grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN; + uberblock_t *ub; + int inserted; + + if (! dev->disk) + { + grub_error (GRUB_ERR_BAD_DEVICE, "not a disk"); + return 0; + } + + data = grub_zalloc (sizeof (*data)); + if (!data) + return 0; +#if 0 + /* if it's our first time here, zero the best uberblock out */ + if (data->best_drive == 0 && data->best_part == 0 && find_best_root) + grub_memset (¤t_uberblock, 0, sizeof (uberblock_t)); +#endif + + data->n_devices_allocated = 16; + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); + if (!data->devices_attached) + { + grub_free (data); + return NULL; + } + data->n_devices_attached = 0; + err = scan_disk (dev, data, 1, &inserted); + if (err) + { + zfs_unmount (data); + return NULL; + } + + ub = &(data->current_uberblock); + ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, + GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC + ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN); + + err = zio_read (&ub->ub_rootbp, ub_endian, + (void **) &osp, &ospsize, data); + if (err) + { + zfs_unmount (data); + return NULL; + } + + if (ospsize < OBJSET_PHYS_SIZE_V14) + { + grub_error (GRUB_ERR_BAD_FS, "OSP too small"); + grub_free (osp); + zfs_unmount (data); + return NULL; + } + + if (ub->ub_version >= SPA_VERSION_FEATURES && + check_mos_features(&osp->os_meta_dnode, ub_endian, data) != 0) + { + grub_error (GRUB_ERR_BAD_FS, "Unsupported features in pool"); + grub_free (osp); + zfs_unmount (data); + return NULL; + } + + /* Got the MOS. Save it at the memory addr MOS. */ + grub_memmove (&(data->mos.dn), &osp->os_meta_dnode, DNODE_SIZE); + data->mos.endian = (grub_zfs_to_cpu64 (ub->ub_rootbp.blk_prop, + ub_endian) >> 63) & 1; + grub_free (osp); + + return data; +} + +grub_err_t +grub_zfs_fetch_nvlist (grub_device_t dev, char **nvlist) +{ + struct grub_zfs_data *zfs; + grub_err_t err; + + zfs = zfs_mount (dev); + if (!zfs) + return grub_errno; + err = zfs_fetch_nvlist (zfs->device_original, nvlist); + zfs_unmount (zfs); + return err; +} + +static grub_err_t +zfs_label (grub_device_t device, char **label) +{ + char *nvlist; + grub_err_t err; + struct grub_zfs_data *data; + + data = zfs_mount (device); + if (! data) + return grub_errno; + + err = zfs_fetch_nvlist (data->device_original, &nvlist); + if (err) + { + zfs_unmount (data); + return err; + } + + *label = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); + grub_free (nvlist); + zfs_unmount (data); + return grub_errno; +} + +static grub_err_t +zfs_uuid (grub_device_t device, char **uuid) +{ + struct grub_zfs_data *data; + + *uuid = 0; + + data = zfs_mount (device); + if (! data) + return grub_errno; + + *uuid = grub_xasprintf ("%016llx", (long long unsigned) data->guid); + zfs_unmount (data); + if (! *uuid) + return grub_errno; + return GRUB_ERR_NONE; +} + +static grub_err_t +zfs_mtime (grub_device_t device, grub_int64_t *mt) +{ + struct grub_zfs_data *data; + grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN; + uberblock_t *ub; + + *mt = 0; + + data = zfs_mount (device); + if (! data) + return grub_errno; + + ub = &(data->current_uberblock); + ub_endian = (grub_zfs_to_cpu64 (ub->ub_magic, + GRUB_ZFS_LITTLE_ENDIAN) == UBERBLOCK_MAGIC + ? GRUB_ZFS_LITTLE_ENDIAN : GRUB_ZFS_BIG_ENDIAN); + + *mt = grub_zfs_to_cpu64 (ub->ub_timestamp, ub_endian); + zfs_unmount (data); + return GRUB_ERR_NONE; +} + +/* + * zfs_open() locates a file in the rootpool by following the + * MOS and places the dnode of the file in the memory address DNODE. + */ +static grub_err_t +grub_zfs_open (struct grub_file *file, const char *fsfilename) +{ + struct grub_zfs_data *data; + grub_err_t err; + int isfs; + + data = zfs_mount (file->device); + if (! data) + return grub_errno; + + err = dnode_get_fullpath (fsfilename, &(data->subvol), + &(data->dnode), &isfs, data); + if (err) + { + zfs_unmount (data); + return err; + } + + if (isfs) + { + zfs_unmount (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("missing `%c' symbol"), '@'); + } + + /* We found the dnode for this file. Verify if it is a plain file. */ + if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) + { + zfs_unmount (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file")); + } + + /* get the file size and set the file position to 0 */ + + /* + * For DMU_OT_SA we will need to locate the SIZE attribute + * attribute, which could be either in the bonus buffer + * or the "spill" block. + */ + if (data->dnode.dn.dn_bonustype == DMU_OT_SA) + { + void *sahdrp; + int hdrsize; + bool free_sahdrp = false; + + if (data->dnode.dn.dn_bonuslen != 0) + { + sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn); + } + else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) + { + blkptr_t *bp = &data->dnode.dn.dn_spill; + + err = zio_read (bp, data->dnode.endian, &sahdrp, NULL, data); + if (err) + return err; + free_sahdrp = true; + } + else + { + return grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); + } + + hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); + file->size = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET), data->dnode.endian); + if (free_sahdrp) + grub_free(sahdrp); + } + else if (data->dnode.dn.dn_bonustype == DMU_OT_ZNODE) + { + file->size = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&data->dnode.dn))->zp_size, data->dnode.endian); + } + else + return grub_error (GRUB_ERR_BAD_FS, "bad bonus type"); + + file->data = data; + file->offset = 0; + +#ifndef GRUB_UTIL + grub_dl_ref (my_mod); +#endif + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_zfs_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_zfs_data *data = (struct grub_zfs_data *) file->data; + grub_size_t blksz, movesize; + grub_size_t length; + grub_size_t read; + grub_err_t err; + + /* + * If offset is in memory, move it into the buffer provided and return. + */ + if (file->offset >= data->file_start + && file->offset + len <= data->file_end) + { + grub_memmove (buf, data->file_buf + file->offset - data->file_start, + len); + return len; + } + + blksz = grub_zfs_to_cpu16 (data->dnode.dn.dn_datablkszsec, + data->dnode.endian) << SPA_MINBLOCKSHIFT; + + if (blksz == 0) + { + grub_error (GRUB_ERR_BAD_FS, "0-sized block"); + return -1; + } + + /* + * Entire Dnode is too big to fit into the space available. We + * will need to read it in chunks. This could be optimized to + * read in as large a chunk as there is space available, but for + * now, this only reads in one data block at a time. + */ + length = len; + read = 0; + while (length) + { + void *t; + /* + * Find requested blkid and the offset within that block. + */ + grub_uint64_t blkid = grub_divmod64 (file->offset + read, blksz, 0); + grub_free (data->file_buf); + data->file_buf = 0; + + err = dmu_read (&(data->dnode), blkid, &t, + 0, data); + data->file_buf = t; + if (err) + { + data->file_buf = NULL; + data->file_start = data->file_end = 0; + return -1; + } + + data->file_start = blkid * blksz; + data->file_end = data->file_start + blksz; + + movesize = data->file_end - file->offset - read; + if (movesize > length) + movesize = length; + + grub_memmove (buf, data->file_buf + file->offset + read + - data->file_start, movesize); + buf += movesize; + length -= movesize; + read += movesize; + } + + return len; +} + +static grub_err_t +grub_zfs_close (grub_file_t file) +{ + zfs_unmount ((struct grub_zfs_data *) file->data); + +#ifndef GRUB_UTIL + grub_dl_unref (my_mod); +#endif + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_zfs_getmdnobj (grub_device_t dev, const char *fsfilename, + grub_uint64_t *mdnobj) +{ + struct grub_zfs_data *data; + grub_err_t err; + int isfs; + + data = zfs_mount (dev); + if (! data) + return grub_errno; + + err = dnode_get_fullpath (fsfilename, &(data->subvol), + &(data->dnode), &isfs, data); + *mdnobj = data->subvol.obj; + zfs_unmount (data); + return err; +} + +/* + * Note: fill_fs_info() uses functions such as make_mdn() that modify + * the input dnode_end_t parameter. However, we should not allow it. + * Therefore, we are making mdn_in constant - fill_fs_info() makes + * a local copy of it. + */ +static grub_err_t +fill_fs_info (struct grub_dirhook_info *info, + const dnode_end_t *mdn_in, struct grub_zfs_data *data) +{ + grub_err_t err; + dnode_end_t dn; + grub_uint64_t objnum; + grub_uint64_t headobj; + dnode_end_t mdn; + + grub_memcpy (&mdn, mdn_in, sizeof (*mdn_in)); + + grub_memset (info, 0, sizeof (*info)); + + info->dir = 1; + + if (mdn.dn.dn_type == DMU_OT_DSL_DIR || mdn.dn.dn_bonustype == DMU_OT_DSL_DIR) + { + headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&mdn.dn))->dd_head_dataset_obj, mdn.endian); + + err = dnode_get (&(data->mos), headobj, 0, &mdn, data); + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + return err; + } + } + err = make_mdn (&mdn, data); + if (err) + return err; + err = dnode_get (&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, + &dn, data); + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + return err; + } + + err = zap_lookup (&dn, ZFS_ROOT_OBJ, &objnum, data, 0); + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + return err; + } + + err = dnode_get (&mdn, objnum, 0, &dn, data); + if (err) + { + grub_dprintf ("zfs", "failed here\n"); + return err; + } + + if (dn.dn.dn_bonustype == DMU_OT_SA) + { + void *sahdrp; + int hdrsize; + bool free_sahdrp = false; + + if (dn.dn.dn_bonuslen != 0) + { + sahdrp = (sa_hdr_phys_t *) DN_BONUS (&dn.dn); + } + else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) + { + blkptr_t *bp = &dn.dn.dn_spill; + + err = zio_read (bp, dn.endian, &sahdrp, NULL, data); + if (err) + return err; + free_sahdrp = true; + } + else + { + grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); + return grub_errno; + } + + hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); + info->mtimeset = 1; + info->mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); + if (free_sahdrp) + grub_free (sahdrp); + } + + if (dn.dn.dn_bonustype == DMU_OT_ZNODE) + { + info->mtimeset = 1; + info->mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], dn.endian); + } + return 0; +} + +/* Helper for grub_zfs_dir. */ +static int +iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx) +{ + grub_err_t err; + struct grub_dirhook_info info; + + dnode_end_t dn; + grub_memset (&info, 0, sizeof (info)); + + err = dnode_get (&(ctx->data->subvol.mdn), val, 0, &dn, ctx->data); + if (err) + { + grub_print_error (); + return 0; + } + + if (dn.dn.dn_bonustype == DMU_OT_SA) + { + void *sahdrp; + int hdrsize; + bool free_sahdrp = false; + + if (dn.dn.dn_bonuslen != 0) + { + sahdrp = (sa_hdr_phys_t *) DN_BONUS (&ctx->data->dnode.dn); + } + else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) + { + blkptr_t *bp = &dn.dn.dn_spill; + + err = zio_read (bp, dn.endian, &sahdrp, NULL, ctx->data); + if (err) + { + grub_print_error (); + return 0; + } + free_sahdrp = true; + } + else + { + grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt"); + grub_print_error (); + return 0; + } + + hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp)); + info.mtimeset = 1; + info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian); + info.case_insensitive = ctx->data->subvol.case_insensitive; + if (free_sahdrp) + grub_free (sahdrp); + } + + if (dn.dn.dn_bonustype == DMU_OT_ZNODE) + { + info.mtimeset = 1; + info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0], + dn.endian); + } + info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); + grub_dprintf ("zfs", "type=%d, name=%s\n", + (int)dn.dn.dn_type, (char *)name); + return ctx->hook (name, &info, ctx->hook_data); +} + +/* Helper for grub_zfs_dir. */ +static int +iterate_zap_fs (const char *name, grub_uint64_t val, + struct grub_zfs_dir_ctx *ctx) +{ + grub_err_t err; + struct grub_dirhook_info info; + + if (name[0] == 0 && val == 0) + return 0; + + dnode_end_t mdn; + err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data); + if (err) + { + grub_errno = 0; + return 0; + } + if (mdn.dn.dn_type != DMU_OT_DSL_DIR && mdn.dn.dn_bonustype != DMU_OT_DSL_DIR) { + grub_dprintf ("zfs", "type = 0x%x, val = 0x%llx\n", mdn.dn.dn_type, (long long)val); + return 0; + } + + err = fill_fs_info (&info, &mdn, ctx->data); + if (err) + { + grub_errno = 0; + return 0; + } + return ctx->hook (name, &info, ctx->hook_data); +} + +/* Helper for grub_zfs_dir. */ +static int +iterate_zap_snap (const char *name, grub_uint64_t val, + struct grub_zfs_dir_ctx *ctx) +{ + grub_err_t err; + struct grub_dirhook_info info; + char *name2; + int ret; + grub_size_t sz; + + dnode_end_t mdn; + + err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data); + if (err) + { + grub_errno = 0; + return 0; + } + + if (mdn.dn.dn_type != DMU_OT_DSL_DATASET && mdn.dn.dn_bonustype != DMU_OT_DSL_DATASET) + return 0; + + err = fill_fs_info (&info, &mdn, ctx->data); + if (err) + { + grub_errno = 0; + return 0; + } + + if (grub_add (grub_strlen (name), 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + + name2 = grub_malloc (sz); + if (!name2) + return grub_errno; + + name2[0] = '@'; + grub_memcpy (name2 + 1, name, grub_strlen (name) + 1); + ret = ctx->hook (name2, &info, ctx->hook_data); + grub_free (name2); + return ret; +} + +static grub_err_t +grub_zfs_dir (grub_device_t device, const char *path, + grub_fs_dir_hook_t hook, void *hook_data) +{ + struct grub_zfs_dir_ctx ctx = { + .hook = hook, + .hook_data = hook_data + }; + struct grub_zfs_data *data; + grub_err_t err; + int isfs; + + data = zfs_mount (device); + if (! data) + return grub_errno; + err = dnode_get_fullpath (path, &(data->subvol), &(data->dnode), &isfs, data); + if (err) + { + zfs_unmount (data); + return err; + } + ctx.data = data; + + if (isfs) + { + grub_uint64_t childobj, headobj; + grub_uint64_t snapobj; + dnode_end_t dn; + struct grub_dirhook_info info; + + err = fill_fs_info (&info, &data->dnode, data); + if (err) + { + zfs_unmount (data); + return err; + } + if (hook ("@", &info, hook_data)) + { + zfs_unmount (data); + return GRUB_ERR_NONE; + } + + childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian); + headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian); + err = dnode_get (&(data->mos), childobj, + DMU_OT_DSL_DIR_CHILD_MAP, &dn, data); + if (err) + { + zfs_unmount (data); + return err; + } + + zap_iterate_u64 (&dn, iterate_zap_fs, data, &ctx); + + err = dnode_get (&(data->mos), headobj, 0, &dn, data); + if (!err && dn.dn.dn_type != DMU_OT_DSL_DATASET && dn.dn.dn_bonustype != DMU_OT_DSL_DATASET) + return grub_error(GRUB_ERR_BAD_FS, "incorrect dataset dnode type"); + + if (err) + { + zfs_unmount (data); + return err; + } + + snapobj = grub_zfs_to_cpu64 (((dsl_dataset_phys_t *) DN_BONUS (&dn.dn))->ds_snapnames_zapobj, dn.endian); + + err = dnode_get (&(data->mos), snapobj, + DMU_OT_DSL_DS_SNAP_MAP, &dn, data); + if (err) + { + zfs_unmount (data); + return err; + } + + zap_iterate_u64 (&dn, iterate_zap_snap, data, &ctx); + } + else + { + if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) + { + zfs_unmount (data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + } + zap_iterate_u64 (&(data->dnode), iterate_zap, data, &ctx); + } + zfs_unmount (data); + return grub_errno; +} + +static int +check_feature (const char *name, grub_uint64_t val, + struct grub_zfs_dir_ctx *ctx __attribute__((unused))) +{ + int i; + if (val == 0) + return 0; + if (name[0] == 0) + return 0; + for (i = 0; spa_feature_names[i] != NULL; i++) + if (grub_strcmp (name, spa_feature_names[i]) == 0) + return 0; + return 1; +} + +/* + * Checks whether the MOS features that are active are supported by this + * (GRUB's) implementation of ZFS. + * + * Return: + * 0: Success. + * errnum: Failure. + */ + +static grub_err_t +check_mos_features(dnode_phys_t *mosmdn_phys,grub_zfs_endian_t endian,struct grub_zfs_data* data ) +{ + grub_uint64_t objnum; + grub_err_t errnum = 0; + dnode_end_t dn,mosmdn; + mzap_phys_t* mzp; + grub_zfs_endian_t endianzap; + int size, ret; + grub_memmove(&(mosmdn.dn),mosmdn_phys,sizeof(dnode_phys_t)); + mosmdn.endian=endian; + errnum = dnode_get(&mosmdn, DMU_POOL_DIRECTORY_OBJECT, + DMU_OT_OBJECT_DIRECTORY, &dn,data); + if (errnum != 0) + return errnum; + + /* + * Find the object number for 'features_for_read' and retrieve its + * corresponding dnode. Note that we don't check features_for_write + * because GRUB is not opening the pool for write. + */ + errnum = zap_lookup(&dn, DMU_POOL_FEATURES_FOR_READ, &objnum, data,0); + if (errnum != 0) + return errnum; + + errnum = dnode_get(&mosmdn, objnum, DMU_OTN_ZAP_METADATA, &dn, data); + if (errnum != 0) + return errnum; + + errnum = dmu_read(&dn, 0, (void**)&mzp, &endianzap,data); + if (errnum != 0) + return errnum; + + size = grub_zfs_to_cpu16 (dn.dn.dn_datablkszsec, dn.endian) << SPA_MINBLOCKSHIFT; + ret = mzap_iterate (mzp,endianzap, size, check_feature,NULL); + grub_free(mzp); + return ret; +} + + +#ifdef GRUB_UTIL +static grub_err_t +grub_zfs_embed (grub_device_t device __attribute__ ((unused)), + unsigned int *nsectors, + unsigned int max_nsectors, + grub_embed_type_t embed_type, + grub_disk_addr_t **sectors) +{ + unsigned i; + + if (embed_type != GRUB_EMBED_PCBIOS) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "ZFS currently supports only PC-BIOS embedding"); + + if ((VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS) < *nsectors) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("your core.img is unusually large. " + "It won't fit in the embedding area")); + + *nsectors = (VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS); + if (*nsectors > max_nsectors) + *nsectors = max_nsectors; + *sectors = grub_calloc (*nsectors, sizeof (**sectors)); + if (!*sectors) + return grub_errno; + for (i = 0; i < *nsectors; i++) + (*sectors)[i] = i + (VDEV_BOOT_OFFSET >> GRUB_DISK_SECTOR_BITS); + + return GRUB_ERR_NONE; +} +#endif + +static struct grub_fs grub_zfs_fs = { + .name = "zfs", + .fs_dir = grub_zfs_dir, + .fs_open = grub_zfs_open, + .fs_read = grub_zfs_read, + .fs_close = grub_zfs_close, + .fs_label = zfs_label, + .fs_uuid = zfs_uuid, + .fs_mtime = zfs_mtime, +#ifdef GRUB_UTIL + .fs_embed = grub_zfs_embed, + .reserved_first_sector = 1, + .blocklist_install = 0, +#endif + .next = 0 +}; + +GRUB_MOD_INIT (zfs) +{ + COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE); + grub_zfs_fs.mod = mod; + grub_fs_register (&grub_zfs_fs); +#ifndef GRUB_UTIL + my_mod = mod; +#endif +} + +GRUB_MOD_FINI (zfs) +{ + grub_fs_unregister (&grub_zfs_fs); +} diff --git a/grub-core/fs/zfs/zfs_fletcher.c b/grub-core/fs/zfs/zfs_fletcher.c new file mode 100644 index 000000000..ad3be6705 --- /dev/null +++ b/grub-core/fs/zfs/zfs_fletcher.c @@ -0,0 +1,84 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc. + * Copyright 2007 Sun Microsystems, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +fletcher_2(const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, + zio_cksum_t *zcp) +{ + const grub_uint64_t *ip = buf; + const grub_uint64_t *ipend = ip + (size / sizeof (grub_uint64_t)); + grub_uint64_t a0, b0, a1, b1; + + for (a0 = b0 = a1 = b1 = 0; ip < ipend; ip += 2) + { + a0 += grub_zfs_to_cpu64 (ip[0], endian); + a1 += grub_zfs_to_cpu64 (ip[1], endian); + b0 += a0; + b1 += a1; + } + + zcp->zc_word[0] = grub_cpu_to_zfs64 (a0, endian); + zcp->zc_word[1] = grub_cpu_to_zfs64 (a1, endian); + zcp->zc_word[2] = grub_cpu_to_zfs64 (b0, endian); + zcp->zc_word[3] = grub_cpu_to_zfs64 (b1, endian); +} + +void +fletcher_4 (const void *buf, grub_uint64_t size, grub_zfs_endian_t endian, + zio_cksum_t *zcp) +{ + const grub_uint32_t *ip = buf; + const grub_uint32_t *ipend = ip + (size / sizeof (grub_uint32_t)); + grub_uint64_t a, b, c, d; + + for (a = b = c = d = 0; ip < ipend; ip++) + { + a += grub_zfs_to_cpu32 (ip[0], endian);; + b += a; + c += b; + d += c; + } + + zcp->zc_word[0] = grub_cpu_to_zfs64 (a, endian); + zcp->zc_word[1] = grub_cpu_to_zfs64 (b, endian); + zcp->zc_word[2] = grub_cpu_to_zfs64 (c, endian); + zcp->zc_word[3] = grub_cpu_to_zfs64 (d, endian); +} + diff --git a/grub-core/fs/zfs/zfs_lz4.c b/grub-core/fs/zfs/zfs_lz4.c new file mode 100644 index 000000000..5453822d0 --- /dev/null +++ b/grub-core/fs/zfs/zfs_lz4.c @@ -0,0 +1,285 @@ +/* + * LZ4 - Fast LZ compression algorithm + * Header File + * Copyright (C) 2011-2013, Yann Collet. + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + * + * You can contact the author at : + * - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html + * - LZ4 source repository : http://code.google.com/p/lz4/ + */ + +#include +#include +#include +#include + +static int LZ4_uncompress_unknownOutputSize(const char *source, char *dest, + int isize, int maxOutputSize); + +/* + * CPU Feature Detection + */ + +/* 32 or 64 bits ? */ +#if (GRUB_CPU_SIZEOF_VOID_P == 8) +#define LZ4_ARCH64 1 +#else +#define LZ4_ARCH64 0 +#endif + +/* + * Compiler Options + */ + + +#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +#if (GCC_VERSION >= 302) || (defined (__INTEL_COMPILER) && __INTEL_COMPILER >= 800) || defined(__clang__) +#define expect(expr, value) (__builtin_expect((expr), (value))) +#else +#define expect(expr, value) (expr) +#endif + +#define likely(expr) expect((expr) != 0, 1) +#define unlikely(expr) expect((expr) != 0, 0) + +/* Basic types */ +#define BYTE grub_uint8_t +#define U16 grub_uint16_t +#define U32 grub_uint32_t +#define S32 grub_int32_t +#define U64 grub_uint64_t + +typedef struct _U16_S { + U16 v; +} GRUB_PACKED U16_S; +typedef struct _U32_S { + U32 v; +} GRUB_PACKED U32_S; +typedef struct _U64_S { + U64 v; +} GRUB_PACKED U64_S; + +#define A64(x) (((U64_S *)(x))->v) +#define A32(x) (((U32_S *)(x))->v) +#define A16(x) (((U16_S *)(x))->v) + +/* + * Constants + */ +#define MINMATCH 4 + +#define COPYLENGTH 8 +#define LASTLITERALS 5 + +#define ML_BITS 4 +#define ML_MASK ((1U< s_len) + return grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed."); + + /* + * Returns 0 on success (decompression function returned non-negative) + * and appropriate error on failure (decompression function returned negative). + */ + return (LZ4_uncompress_unknownOutputSize((char*)s_start + 4, d_start, bufsiz, + d_len) < 0)?grub_error(GRUB_ERR_BAD_FS,"lz4 decompression failed."):0; +} + +static int +LZ4_uncompress_unknownOutputSize(const char *source, + char *dest, int isize, int maxOutputSize) +{ + /* Local Variables */ + const BYTE * ip = (const BYTE *) source; + const BYTE *const iend = ip + isize; + const BYTE * ref; + + BYTE * op = (BYTE *) dest; + BYTE *const oend = op + maxOutputSize; + BYTE *cpy; + + grub_size_t dec[] = { 0, 3, 2, 3, 0, 0, 0, 0 }; + + /* Main Loop */ + while (ip < iend) { + BYTE token; + int length; + + /* get runlength */ + token = *ip++; + if ((length = (token >> ML_BITS)) == RUN_MASK) { + int s = 255; + while ((ip < iend) && (s == 255)) { + s = *ip++; + length += s; + } + } + /* copy literals */ + if ((grub_addr_t) length > ~(grub_addr_t)op) + goto _output_error; + cpy = op + length; + if ((cpy > oend - COPYLENGTH) || + (ip + length > iend - COPYLENGTH)) { + if (cpy > oend) + /* + * Error: request to write beyond destination + * buffer. + */ + goto _output_error; + if (ip + length > iend) + /* + * Error : request to read beyond source + * buffer. + */ + goto _output_error; + grub_memcpy(op, ip, length); + op += length; + ip += length; + if (ip < iend) + /* Error : LZ4 format violation */ + goto _output_error; + /* Necessarily EOF, due to parsing restrictions. */ + break; + } + LZ4_WILDCOPY(ip, op, cpy); + ip -= (op - cpy); + op = cpy; + + /* get offset */ + LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip); + ip += 2; + if (ref < (BYTE * const) dest) + /* + * Error: offset creates reference outside of + * destination buffer. + */ + goto _output_error; + + /* get matchlength */ + if ((length = (token & ML_MASK)) == ML_MASK) { + while (ip < iend) { + int s = *ip++; + length += s; + if (s == 255) + continue; + break; + } + } + /* copy repeated sequence */ + if unlikely(op - ref < STEPSIZE) { +#if LZ4_ARCH64 + grub_size_t dec2table[] = { 0, 0, 0, -1, 0, 1, 2, 3 }; + grub_size_t dec2 = dec2table[op - ref]; +#else + const int dec2 = 0; +#endif + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + *op++ = *ref++; + ref -= dec[op - ref]; + A32(op) = A32(ref); + op += STEPSIZE - 4; + ref -= dec2; + } else { + LZ4_COPYSTEP(ref, op); + } + cpy = op + length - (STEPSIZE - 4); + if (cpy > oend - COPYLENGTH) { + if (cpy > oend) + /* + * Error: request to write outside of + * destination buffer. + */ + goto _output_error; + LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH)); + while (op < cpy) + *op++ = *ref++; + op = cpy; + if (op == oend) + /* + * Check EOF (should never happen, since last + * 5 bytes are supposed to be literals). + */ + break; + continue; + } + LZ4_SECURECOPY(ref, op, cpy); + op = cpy; /* correction */ + } + + /* end of decoding */ + return (int)(((char *)op) - dest); + + /* write overflow error detected */ + _output_error: + return (int)(-(((char *)ip) - source)); +} diff --git a/grub-core/fs/zfs/zfs_lzjb.c b/grub-core/fs/zfs/zfs_lzjb.c new file mode 100644 index 000000000..62b5ea6a0 --- /dev/null +++ b/grub-core/fs/zfs/zfs_lzjb.c @@ -0,0 +1,93 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc. + * Copyright 2007 Sun Microsystems, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MATCH_BITS 6 +#define MATCH_MIN 3 +#define OFFSET_MASK ((1 << (16 - MATCH_BITS)) - 1) + +/* + * Decompression Entry - lzjb + */ +#ifndef NBBY +#define NBBY 8 +#endif + +grub_err_t +lzjb_decompress (void *s_start, void *d_start, grub_size_t s_len, + grub_size_t d_len); + +grub_err_t +lzjb_decompress (void *s_start, void *d_start, grub_size_t s_len, + grub_size_t d_len) +{ + grub_uint8_t *src = s_start; + grub_uint8_t *dst = d_start; + grub_uint8_t *d_end = (grub_uint8_t *) d_start + d_len; + grub_uint8_t *s_end = (grub_uint8_t *) s_start + s_len; + grub_uint8_t *cpy, copymap = 0; + int copymask = 1 << (NBBY - 1); + + while (dst < d_end && src < s_end) + { + if ((copymask <<= 1) == (1 << NBBY)) + { + copymask = 1; + copymap = *src++; + } + if (src >= s_end) + return grub_error (GRUB_ERR_BAD_FS, "lzjb decompression failed"); + if (copymap & copymask) + { + int mlen = (src[0] >> (NBBY - MATCH_BITS)) + MATCH_MIN; + int offset = ((src[0] << NBBY) | src[1]) & OFFSET_MASK; + src += 2; + cpy = dst - offset; + if (src > s_end || cpy < (grub_uint8_t *) d_start) + return grub_error (GRUB_ERR_BAD_FS, "lzjb decompression failed"); + while (--mlen >= 0 && dst < d_end) + *dst++ = *cpy++; + } + else + *dst++ = *src++; + } + if (dst < d_end) + return grub_error (GRUB_ERR_BAD_FS, "lzjb decompression failed"); + return GRUB_ERR_NONE; +} diff --git a/grub-core/fs/zfs/zfs_sha256.c b/grub-core/fs/zfs/zfs_sha256.c new file mode 100644 index 000000000..f042fa61a --- /dev/null +++ b/grub-core/fs/zfs/zfs_sha256.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc. + * Copyright 2007 Sun Microsystems, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * SHA-256 checksum, as specified in FIPS 180-2, available at: + * http://csrc.nist.gov/cryptval + * + * This is a very compact implementation of SHA-256. + * It is designed to be simple and portable, not to be fast. + */ + +/* + * The literal definitions according to FIPS180-2 would be: + * + * Ch(x, y, z) (((x) & (y)) ^ ((~(x)) & (z))) + * Maj(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) + * + * We use logical equivalents which require one less op. + */ +#define Ch(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x, y, z) (((x) & (y)) ^ ((z) & ((x) ^ (y)))) +#define Rot32(x, s) (((x) >> s) | ((x) << (32 - s))) +#define SIGMA0(x) (Rot32(x, 2) ^ Rot32(x, 13) ^ Rot32(x, 22)) +#define SIGMA1(x) (Rot32(x, 6) ^ Rot32(x, 11) ^ Rot32(x, 25)) +#define sigma0(x) (Rot32(x, 7) ^ Rot32(x, 18) ^ ((x) >> 3)) +#define sigma1(x) (Rot32(x, 17) ^ Rot32(x, 19) ^ ((x) >> 10)) + +static const grub_uint32_t SHA256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static void +SHA256Transform(grub_uint32_t *H, const grub_uint8_t *cp) +{ + grub_uint32_t a, b, c, d, e, f, g, h, t, T1, T2, W[64]; + + for (t = 0; t < 16; t++, cp += 4) + W[t] = (cp[0] << 24) | (cp[1] << 16) | (cp[2] << 8) | cp[3]; + + for (t = 16; t < 64; t++) + W[t] = sigma1(W[t - 2]) + W[t - 7] + + sigma0(W[t - 15]) + W[t - 16]; + + a = H[0]; b = H[1]; c = H[2]; d = H[3]; + e = H[4]; f = H[5]; g = H[6]; h = H[7]; + + for (t = 0; t < 64; t++) { + T1 = h + SIGMA1(e) + Ch(e, f, g) + SHA256_K[t] + W[t]; + T2 = SIGMA0(a) + Maj(a, b, c); + h = g; g = f; f = e; e = d + T1; + d = c; c = b; b = a; a = T1 + T2; + } + + H[0] += a; H[1] += b; H[2] += c; H[3] += d; + H[4] += e; H[5] += f; H[6] += g; H[7] += h; +} + +void +zio_checksum_SHA256(const void *buf, grub_uint64_t size, + grub_zfs_endian_t endian, zio_cksum_t *zcp) +{ + grub_uint32_t H[8] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 }; + grub_uint8_t pad[128]; + unsigned padsize = size & 63; + unsigned i; + + for (i = 0; i < size - padsize; i += 64) + SHA256Transform(H, (grub_uint8_t *)buf + i); + + for (i = 0; i < padsize; i++) + pad[i] = ((grub_uint8_t *)buf)[i]; + + for (pad[padsize++] = 0x80; (padsize & 63) != 56; padsize++) + pad[padsize] = 0; + + for (i = 0; i < 8; i++) + pad[padsize++] = (size << 3) >> (56 - 8 * i); + + for (i = 0; i < padsize && i <= 64; i += 64) + SHA256Transform(H, pad + i); + + zcp->zc_word[0] = grub_cpu_to_zfs64 ((grub_uint64_t)H[0] << 32 | H[1], + endian); + zcp->zc_word[1] = grub_cpu_to_zfs64 ((grub_uint64_t)H[2] << 32 | H[3], + endian); + zcp->zc_word[2] = grub_cpu_to_zfs64 ((grub_uint64_t)H[4] << 32 | H[5], + endian); + zcp->zc_word[3] = grub_cpu_to_zfs64 ((grub_uint64_t)H[6] << 32 | H[7], + endian); +} diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c new file mode 100644 index 000000000..da30e9ab3 --- /dev/null +++ b/grub-core/fs/zfs/zfscrypt.c @@ -0,0 +1,491 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + Mostly based on following article: + https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on + */ + +enum grub_zfs_algo + { + GRUB_ZFS_ALGO_CCM, + GRUB_ZFS_ALGO_GCM, + }; + +struct grub_zfs_key +{ + grub_uint64_t algo; + grub_uint8_t enc_nonce[13]; + grub_uint8_t unused[3]; + grub_uint8_t enc_key[48]; + grub_uint8_t unknown_purpose_nonce[13]; + grub_uint8_t unused2[3]; + grub_uint8_t unknown_purpose_key[48]; +}; + +struct grub_zfs_wrap_key +{ + struct grub_zfs_wrap_key *next; + grub_size_t keylen; + int is_passphrase; + grub_uint64_t key[0]; +}; + +static struct grub_zfs_wrap_key *zfs_wrap_keys; + +grub_err_t +grub_zfs_add_key (grub_uint8_t *key_in, + grub_size_t keylen, + int passphrase) +{ + struct grub_zfs_wrap_key *key; + grub_size_t sz; + + if (!passphrase && keylen > 32) + keylen = 32; + if (grub_add (sizeof (*key), keylen, &sz)) + return GRUB_ERR_OUT_OF_RANGE; + key = grub_malloc (sz); + if (!key) + return grub_errno; + key->is_passphrase = passphrase; + key->keylen = keylen; + grub_memcpy (key->key, key_in, keylen); + key->next = zfs_wrap_keys; + zfs_wrap_keys = key; + return GRUB_ERR_NONE; +} + +static gcry_err_code_t +grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher, + grub_uint8_t *out, const grub_uint8_t *in, + grub_size_t psize, + void *mac_out, const void *nonce, + unsigned l, unsigned m) +{ + grub_uint8_t iv[16]; + grub_uint8_t mul[16]; + grub_uint32_t mac[4]; + unsigned i, j; + gcry_err_code_t err; + + grub_memcpy (iv + 1, nonce, 15 - l); + + iv[0] = (l - 1) | (((m-2) / 2) << 3); + for (j = 0; j < l; j++) + iv[15 - j] = psize >> (8 * j); + err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16); + if (err) + return err; + + iv[0] = l - 1; + + for (i = 0; i < (psize + 15) / 16; i++) + { + grub_size_t csize; + csize = 16; + if (csize > psize - 16 * i) + csize = psize - 16 * i; + for (j = 0; j < l; j++) + iv[15 - j] = (i + 1) >> (8 * j); + err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); + if (err) + return err; + grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize); + grub_crypto_xor (mac, mac, out + 16 * i, csize); + err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16); + if (err) + return err; + } + for (j = 0; j < l; j++) + iv[15 - j] = 0; + err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); + if (err) + return err; + if (mac_out) + grub_crypto_xor (mac_out, mac, mul, m); + return GPG_ERR_NO_ERROR; +} + +static void +grub_gcm_mul_x (grub_uint8_t *a) +{ + int i; + int c = 0, d = 0; + for (i = 0; i < 16; i++) + { + c = a[i] & 0x1; + a[i] = (a[i] >> 1) | (d << 7); + d = c; + } + if (d) + a[0] ^= 0xe1; +} + +static void +grub_gcm_mul (grub_uint8_t *a, const grub_uint8_t *b) +{ + grub_uint8_t res[16], bs[16]; + int i; + grub_memcpy (bs, b, 16); + grub_memset (res, 0, 16); + for (i = 0; i < 128; i++) + { + if ((a[i / 8] << (i % 8)) & 0x80) + grub_crypto_xor (res, res, bs, 16); + grub_gcm_mul_x (bs); + } + + grub_memcpy (a, res, 16); +} + +static gcry_err_code_t +grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher, + grub_uint8_t *out, const grub_uint8_t *in, + grub_size_t psize, + void *mac_out, const void *nonce, + unsigned nonce_len, unsigned m) +{ + grub_uint8_t iv[16]; + grub_uint8_t mul[16]; + grub_uint8_t mac[16], h[16], mac_xor[16]; + unsigned i, j; + gcry_err_code_t err; + + grub_memset (mac, 0, sizeof (mac)); + + err = grub_crypto_ecb_encrypt (cipher, h, mac, 16); + if (err) + return err; + + if (nonce_len == 12) + { + grub_memcpy (iv, nonce, 12); + iv[12] = 0; + iv[13] = 0; + iv[14] = 0; + iv[15] = 1; + } + else + { + grub_memset (iv, 0, sizeof (iv)); + grub_memcpy (iv, nonce, nonce_len); + grub_gcm_mul (iv, h); + iv[15] ^= nonce_len * 8; + grub_gcm_mul (iv, h); + } + + err = grub_crypto_ecb_encrypt (cipher, mac_xor, iv, 16); + if (err) + return err; + + for (i = 0; i < (psize + 15) / 16; i++) + { + grub_size_t csize; + csize = 16; + if (csize > psize - 16 * i) + csize = psize - 16 * i; + for (j = 0; j < 4; j++) + { + iv[15 - j]++; + if (iv[15 - j] != 0) + break; + } + grub_crypto_xor (mac, mac, in + 16 * i, csize); + grub_gcm_mul (mac, h); + err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); + if (err) + return err; + grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize); + } + for (j = 0; j < 8; j++) + mac[15 - j] ^= ((((grub_uint64_t) psize) * 8) >> (8 * j)); + grub_gcm_mul (mac, h); + + if (mac_out) + grub_crypto_xor (mac_out, mac, mac_xor, m); + + return GPG_ERR_NO_ERROR; +} + + +static gcry_err_code_t +algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo, + grub_uint8_t *out, const grub_uint8_t *in, + grub_size_t psize, + void *mac_out, const void *nonce, + unsigned l, unsigned m) +{ + switch (algo) + { + case 0: + return grub_ccm_decrypt (cipher, out, in, psize, + mac_out, nonce, l, m); + case 1: + return grub_gcm_decrypt (cipher, out, in, psize, + mac_out, nonce, + 15 - l, m); + default: + return GPG_ERR_CIPHER_ALGO; + } +} + +static grub_err_t +grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, + grub_uint64_t algo, + void *nonce, + char *buf, grub_size_t size, + const grub_uint32_t *expected_mac, + grub_zfs_endian_t endian) +{ + grub_uint32_t mac[4]; + unsigned i; + grub_uint32_t sw[4]; + gcry_err_code_t err; + + grub_memcpy (sw, nonce, 16); + if (endian != GRUB_ZFS_BIG_ENDIAN) + for (i = 0; i < 4; i++) + sw[i] = grub_swap_bytes32 (sw[i]); + + if (!cipher) + return grub_error (GRUB_ERR_ACCESS_DENIED, + N_("no decryption key available")); + err = algo_decrypt (cipher, algo, + (grub_uint8_t *) buf, + (grub_uint8_t *) buf, + size, mac, + sw + 1, 3, 12); + if (err) + return grub_crypto_gcry_error (err); + + for (i = 0; i < 3; i++) + if (grub_zfs_to_cpu32 (expected_mac[i], endian) + != grub_be_to_cpu32 (mac[i])) + return grub_error (GRUB_ERR_BAD_FS, N_("MAC verification failed")); + return GRUB_ERR_NONE; +} + +static grub_crypto_cipher_handle_t +grub_zfs_load_key_real (const struct grub_zfs_key *key, + grub_size_t keysize, + grub_uint64_t salt, + grub_uint64_t algo) +{ + unsigned keylen; + struct grub_zfs_wrap_key *wrap_key; + grub_crypto_cipher_handle_t ret = NULL; + + if (keysize != sizeof (*key)) + { + grub_dprintf ("zfs", "Unexpected key size %" PRIuGRUB_SIZE "\n", keysize); + return 0; + } + + if (grub_memcmp (key->enc_key + 32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) + == 0) + keylen = 16; + else if (grub_memcmp (key->enc_key + 40, "\0\0\0\0\0\0\0\0", 8) == 0) + keylen = 24; + else + keylen = 32; + + for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next) + { + grub_crypto_cipher_handle_t cipher; + grub_uint8_t decrypted[32], mac[32], wrap_key_real[32]; + gcry_err_code_t err; + cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); + if (!cipher) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_memset (wrap_key_real, 0, sizeof (wrap_key_real)); + err = 0; + if (!wrap_key->is_passphrase) + grub_memcpy(wrap_key_real, wrap_key->key, + wrap_key->keylen < keylen ? wrap_key->keylen : keylen); + else + err = grub_crypto_pbkdf2 (GRUB_MD_SHA1, + (const grub_uint8_t *) wrap_key->key, + wrap_key->keylen, + (const grub_uint8_t *) &salt, sizeof (salt), + 1000, wrap_key_real, keylen); + if (err) + { + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (cipher); + continue; + } + + err = grub_crypto_cipher_set_key (cipher, wrap_key_real, + keylen); + if (err) + { + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (cipher); + continue; + } + + err = algo_decrypt (cipher, algo, decrypted, key->unknown_purpose_key, 32, + mac, key->unknown_purpose_nonce, 2, 16); + if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) + != 0)) + { + grub_dprintf ("zfs", "key loading failed\n"); + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (cipher); + continue; + } + + err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, mac, + key->enc_nonce, 2, 16); + if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0) + { + grub_dprintf ("zfs", "key loading failed\n"); + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (cipher); + continue; + } + ret = grub_crypto_cipher_open (GRUB_CIPHER_AES); + if (!ret) + { + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (cipher); + continue; + } + err = grub_crypto_cipher_set_key (ret, decrypted, keylen); + if (err) + { + grub_errno = GRUB_ERR_NONE; + grub_crypto_cipher_close (ret); + grub_crypto_cipher_close (cipher); + continue; + } + grub_crypto_cipher_close (cipher); + return ret; + } + return NULL; +} + +static const struct grub_arg_option options[] = + { + {"raw", 'r', 0, N_("Assume input is raw."), 0, 0}, + {"hex", 'h', 0, N_("Assume input is hex."), 0, 0}, + {"passphrase", 'p', 0, N_("Assume input is passphrase."), 0, 0}, + {0, 0, 0, 0, 0, 0} + }; + +static grub_err_t +grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args) +{ + grub_uint8_t buf[1024]; + grub_ssize_t real_size; + + if (argc > 0) + { + grub_file_t file; + file = grub_file_open (args[0], GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY); + if (!file) + return grub_errno; + real_size = grub_file_read (file, buf, 1024); + if (real_size < 0) + return grub_errno; + } + else + { + grub_xputs (_("Enter ZFS password: ")); + if (!grub_password_get ((char *) buf, 1023)) + return grub_errno; + real_size = grub_strlen ((char *) buf); + } + + if (ctxt->state[1].set) + { + int i; + grub_err_t err; + for (i = 0; i < real_size / 2; i++) + { + char c1 = grub_tolower (buf[2 * i]) - '0'; + char c2 = grub_tolower (buf[2 * i + 1]) - '0'; + if (c1 > 9) + c1 += '0' - 'a' + 10; + if (c2 > 9) + c2 += '0' - 'a' + 10; + buf[i] = (c1 << 4) | c2; + } + err = grub_zfs_add_key (buf, real_size / 2, 0); + if (err) + return err; + return GRUB_ERR_NONE; + } + + return grub_zfs_add_key (buf, real_size, + ctxt->state[2].set + || (argc == 0 && !ctxt->state[0].set + && !ctxt->state[1].set)); +} + +static grub_extcmd_t cmd_key; + +GRUB_MOD_INIT(zfscrypt) +{ + grub_zfs_decrypt = grub_zfs_decrypt_real; + grub_zfs_load_key = grub_zfs_load_key_real; + cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0, + N_("[-h|-p|-r] [FILE]"), + N_("Import ZFS wrapping key stored in FILE."), + options); +} + +GRUB_MOD_FINI(zfscrypt) +{ + grub_zfs_decrypt = 0; + grub_zfs_load_key = 0; + grub_unregister_extcmd (cmd_key); +} diff --git a/grub-core/fs/zfs/zfsinfo.c b/grub-core/fs/zfs/zfsinfo.c new file mode 100644 index 000000000..31d767bbd --- /dev/null +++ b/grub-core/fs/zfs/zfsinfo.c @@ -0,0 +1,444 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2009 Free Software Foundation, Inc. + * Copyright 2008 Sun Microsystems, 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static inline void +print_tabs (int n) +{ + int i; + + for (i = 0; i < n; i++) + grub_printf (" "); +} + +static grub_err_t +print_state (char *nvlist, int tab) +{ + grub_uint64_t ival; + int isok = 1; + + print_tabs (tab); + + if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_REMOVED, &ival)) + { + grub_puts_ (N_("Virtual device is removed")); + isok = 0; + } + + if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival)) + { + grub_puts_ (N_("Virtual device is faulted")); + isok = 0; + } + + if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_OFFLINE, &ival)) + { + grub_puts_ (N_("Virtual device is offline")); + isok = 0; + } + + if (grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_FAULTED, &ival)) + /* TRANSLATORS: degraded doesn't mean broken but that some of + component are missing but virtual device as whole is still usable. */ + grub_puts_ (N_("Virtual device is degraded")); + + if (isok) + grub_puts_ (N_("Virtual device is online")); + grub_xputs ("\n"); + + return GRUB_ERR_NONE; +} + +static grub_err_t +print_vdev_info (char *nvlist, int tab) +{ + char *type = 0; + + type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); + + if (!type) + { + print_tabs (tab); + grub_puts_ (N_("Incorrect virtual device: no type available")); + return grub_errno; + } + + if (grub_strcmp (type, VDEV_TYPE_DISK) == 0) + { + char *bootpath = 0; + char *path = 0; + char *devid = 0; + + print_tabs (tab); + /* TRANSLATORS: The virtual devices form a tree (in graph-theoretical + sense). The nodes like mirror or raidz have children: member devices. + The "real" devices which actually store data are called "leafs" + (again borrowed from graph theory) and can be either disks + (or partitions) or files. */ + grub_puts_ (N_("Leaf virtual device (file or disk)")); + + print_state (nvlist, tab); + + bootpath = + grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_PHYS_PATH); + print_tabs (tab); + if (!bootpath) + grub_puts_ (N_("Bootpath: unavailable\n")); + else + grub_printf_ (N_("Bootpath: %s\n"), bootpath); + + path = grub_zfs_nvlist_lookup_string (nvlist, "path"); + print_tabs (tab); + if (!path) + grub_puts_ (N_("Path: unavailable")); + else + grub_printf_ (N_("Path: %s\n"), path); + + devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID); + print_tabs (tab); + if (!devid) + grub_puts_ (N_("Devid: unavailable")); + else + grub_printf_ (N_("Devid: %s\n"), devid); + grub_free (bootpath); + grub_free (devid); + grub_free (path); + grub_free (type); + return GRUB_ERR_NONE; + } + char is_mirror=(grub_strcmp(type,VDEV_TYPE_MIRROR) == 0); + char is_raidz=(grub_strcmp(type,VDEV_TYPE_RAIDZ) == 0); + grub_free (type); + + if (is_mirror || is_raidz) + { + int nelm, i; + + nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm + (nvlist, ZPOOL_CONFIG_CHILDREN); + + if(is_mirror){ + grub_puts_ (N_("This VDEV is a mirror")); + } + else if(is_raidz){ + grub_uint64_t parity; + grub_zfs_nvlist_lookup_uint64(nvlist,"nparity",&parity); + grub_printf_ (N_("This VDEV is a RAIDZ%llu\n"),(unsigned long long)parity); + } + print_tabs (tab); + if (nelm <= 0) + { + grub_puts_ (N_("Incorrect VDEV")); + return GRUB_ERR_NONE; + } + grub_printf_ (N_("VDEV with %d children\n"), nelm); + print_state (nvlist, tab); + for (i = 0; i < nelm; i++) + { + char *child; + + child = grub_zfs_nvlist_lookup_nvlist_array + (nvlist, ZPOOL_CONFIG_CHILDREN, i); + + print_tabs (tab); + if (!child) + { + /* TRANSLATORS: it's the element carying the number %d, not + total element number. And the number itself is fine, + only the element isn't. + */ + grub_printf_ (N_("VDEV element number %d isn't correct\n"), i); + continue; + } + + /* TRANSLATORS: it's the element carying the number %d, not + total element number. This is used in enumeration + "Element number 1", "Element number 2", ... */ + grub_printf_ (N_("VDEV element number %d:\n"), i); + print_vdev_info (child, tab + 1); + + grub_free (child); + } + return GRUB_ERR_NONE; + } + + print_tabs (tab); + grub_printf_ (N_("Unknown virtual device type: %s\n"), type); + + return GRUB_ERR_NONE; +} + +static grub_err_t +get_bootpath (char *nvlist, char **bootpath, char **devid) +{ + char *type = 0; + + type = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_TYPE); + + if (!type) + return grub_errno; + + if (grub_strcmp (type, VDEV_TYPE_DISK) == 0) + { + *bootpath = grub_zfs_nvlist_lookup_string (nvlist, + ZPOOL_CONFIG_PHYS_PATH); + *devid = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_DEVID); + if (!*bootpath || !*devid) + { + grub_free (*bootpath); + grub_free (*devid); + *bootpath = 0; + *devid = 0; + } + return GRUB_ERR_NONE; + } + + if (grub_strcmp (type, VDEV_TYPE_MIRROR) == 0) + { + int nelm, i; + + nelm = grub_zfs_nvlist_lookup_nvlist_array_get_nelm + (nvlist, ZPOOL_CONFIG_CHILDREN); + + for (i = 0; i < nelm; i++) + { + char *child; + + child = grub_zfs_nvlist_lookup_nvlist_array (nvlist, + ZPOOL_CONFIG_CHILDREN, + i); + + get_bootpath (child, bootpath, devid); + + grub_free (child); + + if (*bootpath && *devid) + return GRUB_ERR_NONE; + } + } + + return GRUB_ERR_NONE; +} + +static const char *poolstates[] = { + /* TRANSLATORS: Here we speak about ZFS pools it's semi-marketing, + semi-technical term by Sun/Oracle and should be translated in sync with + other ZFS-related software and documentation. */ + [POOL_STATE_ACTIVE] = N_("Pool state: active"), + [POOL_STATE_EXPORTED] = N_("Pool state: exported"), + [POOL_STATE_DESTROYED] = N_("Pool state: destroyed"), + [POOL_STATE_SPARE] = N_("Pool state: reserved for hot spare"), + [POOL_STATE_L2CACHE] = N_("Pool state: level 2 ARC device"), + [POOL_STATE_UNINITIALIZED] = N_("Pool state: uninitialized"), + [POOL_STATE_UNAVAIL] = N_("Pool state: unavailable"), + [POOL_STATE_POTENTIALLY_ACTIVE] = N_("Pool state: potentially active") +}; + +static grub_err_t +grub_cmd_zfsinfo (grub_command_t cmd __attribute__ ((unused)), int argc, + char **args) +{ + grub_device_t dev; + char *devname; + grub_err_t err; + char *nvlist = 0; + char *nv = 0; + char *poolname; + grub_uint64_t guid; + grub_uint64_t pool_state; + int found; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')') + { + devname = grub_strdup (args[0] + 1); + if (devname) + devname[grub_strlen (devname) - 1] = 0; + } + else + devname = grub_strdup (args[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + err = grub_zfs_fetch_nvlist (dev, &nvlist); + + grub_device_close (dev); + + if (err) + return err; + + poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); + if (!poolname) + grub_puts_ (N_("Pool name: unavailable")); + else + grub_printf_ (N_("Pool name: %s\n"), poolname); + + found = + grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_GUID, &guid); + if (!found) + grub_puts_ (N_("Pool GUID: unavailable")); + else + grub_printf_ (N_("Pool GUID: %016llx\n"), (long long unsigned) guid); + + found = grub_zfs_nvlist_lookup_uint64 (nvlist, ZPOOL_CONFIG_POOL_STATE, + &pool_state); + if (!found) + grub_puts_ (N_("Unable to retrieve pool state")); + else if (pool_state >= ARRAY_SIZE (poolstates)) + grub_puts_ (N_("Unrecognized pool state")); + else + grub_puts_ (poolstates[pool_state]); + + nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); + + if (!nv) + /* TRANSLATORS: There are undetermined number of virtual devices + in a device tree, not just one. + */ + grub_puts_ (N_("No virtual device tree available")); + else + print_vdev_info (nv, 1); + + grub_free (nv); + grub_free (nvlist); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_zfs_bootfs (grub_command_t cmd __attribute__ ((unused)), int argc, + char **args) +{ + grub_device_t dev; + char *devname; + grub_err_t err; + char *nvlist = 0; + char *nv = 0; + char *bootpath = 0, *devid = 0; + char *fsname; + char *bootfs; + char *poolname; + grub_uint64_t mdnobj; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + devname = grub_file_get_device_name (args[0]); + if (grub_errno) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + err = grub_zfs_fetch_nvlist (dev, &nvlist); + + fsname = grub_strchr (args[0], ')'); + if (fsname) + fsname++; + else + fsname = args[0]; + + if (!err) + err = grub_zfs_getmdnobj (dev, fsname, &mdnobj); + + grub_device_close (dev); + + if (err) { + grub_free (nvlist); + return err; + } + + poolname = grub_zfs_nvlist_lookup_string (nvlist, ZPOOL_CONFIG_POOL_NAME); + if (!poolname) + { + if (!grub_errno) + grub_error (GRUB_ERR_BAD_FS, "No poolname found"); + grub_free (nvlist); + return grub_errno; + } + + nv = grub_zfs_nvlist_lookup_nvlist (nvlist, ZPOOL_CONFIG_VDEV_TREE); + + if (nv) + get_bootpath (nv, &bootpath, &devid); + + grub_free (nv); + grub_free (nvlist); + + bootfs = grub_xasprintf ("zfs-bootfs=%s/%llu%s%s%s%s%s%s", + poolname, (unsigned long long) mdnobj, + bootpath ? ",bootpath=\"" : "", + bootpath ? : "", + bootpath ? "\"" : "", + devid ? ",diskdevid=\"" : "", + devid ? : "", + devid ? "\"" : ""); + if (!bootfs) + return grub_errno; + if (argc >= 2) + grub_env_set (args[1], bootfs); + else + grub_printf ("%s\n", bootfs); + + grub_free (bootfs); + grub_free (poolname); + grub_free (bootpath); + grub_free (devid); + + return GRUB_ERR_NONE; +} + + +static grub_command_t cmd_info, cmd_bootfs; + +GRUB_MOD_INIT (zfsinfo) +{ + cmd_info = grub_register_command ("zfsinfo", grub_cmd_zfsinfo, + N_("DEVICE"), + N_("Print ZFS info about DEVICE.")); + cmd_bootfs = grub_register_command ("zfs-bootfs", grub_cmd_zfs_bootfs, + N_("FILESYSTEM [VARIABLE]"), + N_("Print ZFS-BOOTFSOBJ or store it into VARIABLE")); +} + +GRUB_MOD_FINI (zfsinfo) +{ + grub_unregister_command (cmd_info); + grub_unregister_command (cmd_bootfs); +} diff --git a/grub-core/gdb/cstub.c b/grub-core/gdb/cstub.c new file mode 100644 index 000000000..b64acd70f --- /dev/null +++ b/grub-core/gdb/cstub.c @@ -0,0 +1,366 @@ +/* cstub.c - machine independent portion of remote GDB stub */ +/* + * Copyright (C) 2006 Lubomir Kundrak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +static const char hexchars[] = "0123456789abcdef"; +int grub_gdb_regs[GRUB_MACHINE_NR_REGS]; + +#define GRUB_GDB_COMBUF_SIZE 400 /* At least sizeof(grub_gdb_regs)*2 are needed for + register packets. */ +static char grub_gdb_inbuf[GRUB_GDB_COMBUF_SIZE + 1]; +static char grub_gdb_outbuf[GRUB_GDB_COMBUF_SIZE + 1]; + +struct grub_serial_port *grub_gdb_port; + +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* Scan for the sequence $#. */ +static char * +grub_gdb_getpacket (void) +{ + char *buffer = &grub_gdb_inbuf[0]; + unsigned char checksum; + unsigned char xmitcsum; + int count; + int ch; + + while (1) + { + /* Wait around for the start character, ignore all other + characters. */ + while ((ch = grub_serial_port_fetch (grub_gdb_port)) != '$'); + + retry: + checksum = 0; + xmitcsum = -1; + count = 0; + + /* Now read until a # or end of buffer is found. */ + while (count < GRUB_GDB_COMBUF_SIZE) + { + do + ch = grub_serial_port_fetch (grub_gdb_port); + while (ch < 0); + if (ch == '$') + goto retry; + if (ch == '#') + break; + checksum += ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + if (ch == '#') + { + do + ch = grub_serial_port_fetch (grub_gdb_port); + while (ch < 0); + xmitcsum = hex (ch) << 4; + do + ch = grub_serial_port_fetch (grub_gdb_port); + while (ch < 0); + xmitcsum += hex (ch); + + if (checksum != xmitcsum) + grub_serial_port_put (grub_gdb_port, '-'); /* Failed checksum. */ + else + { + grub_serial_port_put (grub_gdb_port, '+'); /* Successful transfer. */ + + /* If a sequence char is present, reply the sequence ID. */ + if (buffer[2] == ':') + { + grub_serial_port_put (grub_gdb_port, buffer[0]); + grub_serial_port_put (grub_gdb_port, buffer[1]); + + return &buffer[3]; + } + return &buffer[0]; + } + } + } +} + +/* Send the packet in buffer. */ +static void +grub_gdb_putpacket (char *buffer) +{ + grub_uint8_t checksum; + + /* $#. */ + do + { + char *ptr; + grub_serial_port_put (grub_gdb_port, '$'); + checksum = 0; + + for (ptr = buffer; *ptr; ptr++) + { + grub_serial_port_put (grub_gdb_port, *ptr); + checksum += *ptr; + } + + grub_serial_port_put (grub_gdb_port, '#'); + grub_serial_port_put (grub_gdb_port, hexchars[checksum >> 4]); + grub_serial_port_put (grub_gdb_port, hexchars[checksum & 0xf]); + } + while (grub_serial_port_fetch (grub_gdb_port) != '+'); +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + Return a pointer to the last char put in buf (NULL). */ +static char * +grub_gdb_mem2hex (char *mem, char *buf, grub_size_t count) +{ + grub_size_t i; + unsigned char ch; + + for (i = 0; i < count; i++) + { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch % 16]; + } + *buf = 0; + return (buf); +} + +/* Convert the hex array pointed to by buf into binary to be placed in mem. + Return a pointer to the character after the last byte written. */ +static char * +grub_gdb_hex2mem (char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + for (i = 0; i < count; i++) + { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return (mem); +} + +/* Convert hex characters to int and return the number of characters + processed. */ +static int +grub_gdb_hex2int (char **ptr, grub_uint64_t *int_value) +{ + int num_chars = 0; + int hex_value; + + *int_value = 0; + + while (**ptr) + { + hex_value = hex (**ptr); + if (hex_value >= 0) + { + *int_value = (*int_value << 4) | hex_value; + num_chars++; + } + else + break; + + (*ptr)++; + } + + return (num_chars); +} + +/* This function does all command procesing for interfacing to gdb. */ +void __attribute__ ((regparm(3))) +grub_gdb_trap (int trap_no) +{ + unsigned int sig_no; + int stepping; + grub_uint64_t addr; + grub_uint64_t length; + char *ptr; + + if (!grub_gdb_port) + { + grub_printf ("Unhandled exception 0x%x at ", trap_no); + grub_backtrace_print_address ((void *) grub_gdb_regs[PC]); + grub_printf ("\n"); + grub_backtrace_pointer ((void *) grub_gdb_regs[EBP]); + grub_fatal ("Unhandled exception"); + } + + sig_no = grub_gdb_trap2sig (trap_no); + + ptr = grub_gdb_outbuf; + + /* Reply to host that an exception has occurred. */ + + *ptr++ = 'T'; /* Notify gdb with signo, PC, FP and SP. */ + + *ptr++ = hexchars[sig_no >> 4]; + *ptr++ = hexchars[sig_no & 0xf]; + + /* Stack pointer. */ + *ptr++ = hexchars[SP]; + *ptr++ = ':'; + ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[ESP], ptr, 4); + *ptr++ = ';'; + + /* Frame pointer. */ + *ptr++ = hexchars[FP]; + *ptr++ = ':'; + ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[EBP], ptr, 4); + *ptr++ = ';'; + + /* Program counter. */ + *ptr++ = hexchars[PC]; + *ptr++ = ':'; + ptr = grub_gdb_mem2hex ((char *) &grub_gdb_regs[PC], ptr, 4); + *ptr++ = ';'; + + *ptr = '\0'; + + grub_gdb_putpacket (grub_gdb_outbuf); + + stepping = 0; + + while (1) + { + grub_gdb_outbuf[0] = 0; + ptr = grub_gdb_getpacket (); + + switch (*ptr++) + { + case '?': + grub_gdb_outbuf[0] = 'S'; + grub_gdb_outbuf[1] = hexchars[sig_no >> 4]; + grub_gdb_outbuf[2] = hexchars[sig_no & 0xf]; + grub_gdb_outbuf[3] = 0; + break; + + /* Return values of the CPU registers. */ + case 'g': + grub_gdb_mem2hex ((char *) grub_gdb_regs, grub_gdb_outbuf, + sizeof (grub_gdb_regs)); + break; + + /* Set values of the CPU registers -- return OK. */ + case 'G': + grub_gdb_hex2mem (ptr, (char *) grub_gdb_regs, + sizeof (grub_gdb_regs)); + grub_strcpy (grub_gdb_outbuf, "OK"); + break; + + /* Set the value of a single CPU register -- return OK. */ + case 'P': + { + grub_uint64_t regno; + + if (grub_gdb_hex2int (&ptr, ®no) && *ptr++ == '=') + if (regno < GRUB_MACHINE_NR_REGS) + { + grub_gdb_hex2mem (ptr, (char *) &grub_gdb_regs[regno], 4); + grub_strcpy (grub_gdb_outbuf, "OK"); + break; + } + /* FIXME: GDB requires setting orig_eax. I don't know what's + this register is about. For now just simulate setting any + registers. */ + grub_strcpy (grub_gdb_outbuf, /*"E01"*/ "OK"); + break; + } + + /* mAA..AA,LLLL: Read LLLL bytes at address AA..AA. */ + case 'm': + /* Try to read %x,%x. Set ptr = 0 if successful. */ + if (grub_gdb_hex2int (&ptr, &addr)) + if (*(ptr++) == ',') + if (grub_gdb_hex2int (&ptr, &length)) + { + ptr = 0; + grub_gdb_mem2hex ((char *) (grub_addr_t) addr, + grub_gdb_outbuf, length); + } + if (ptr) + grub_strcpy (grub_gdb_outbuf, "E01"); + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA -- return OK. */ + case 'M': + /* Try to read %x,%x. Set ptr = 0 if successful. */ + if (grub_gdb_hex2int (&ptr, &addr)) + if (*(ptr++) == ',') + if (grub_gdb_hex2int (&ptr, &length)) + if (*(ptr++) == ':') + { + grub_gdb_hex2mem (ptr, (char *) (grub_addr_t) addr, length); + grub_strcpy (grub_gdb_outbuf, "OK"); + ptr = 0; + } + if (ptr) + { + grub_strcpy (grub_gdb_outbuf, "E02"); + } + break; + + /* sAA..AA: Step one instruction from AA..AA(optional). */ + case 's': + stepping = 1; + /* FALLTHROUGH */ + + /* cAA..AA: Continue at address AA..AA(optional). */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + if (grub_gdb_hex2int (&ptr, &addr)) + grub_gdb_regs[PC] = addr; + + /* Clear the trace bit. */ + grub_gdb_regs[PS] &= 0xfffffeff; + + /* Set the trace bit if we're stepping. */ + if (stepping) + grub_gdb_regs[PS] |= 0x100; + + return; + + /* Kill the program. */ + case 'k': + /* Do nothing. */ + return; + } + + /* Reply to the request. */ + grub_gdb_putpacket (grub_gdb_outbuf); + } +} + diff --git a/grub-core/gdb/gdb.c b/grub-core/gdb/gdb.c new file mode 100644 index 000000000..9e091ee1b --- /dev/null +++ b/grub-core/gdb/gdb.c @@ -0,0 +1,105 @@ +/* gdb.c - gdb remote stub module */ +/* + * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2006 Lubomir Kundrak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_gdbstub (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_serial_port *port; + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "port required"); + port = grub_serial_find (args[0]); + if (!port) + return grub_errno; + grub_gdb_port = port; + /* TRANSLATORS: at this position GRUB waits for the user to do an action + in remote debugger, namely to tell it to establish connection. */ + grub_puts_ (N_("Now connect the remote debugger, please.")); + grub_gdb_breakpoint (); + return 0; +} + +static grub_err_t +grub_cmd_gdbstop (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_gdb_port = NULL; + return 0; +} + +static grub_err_t +grub_cmd_gdb_break (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + if (!grub_gdb_port) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "No GDB stub is running"); + grub_gdb_breakpoint (); + return 0; +} + +static grub_command_t cmd, cmd_stop, cmd_break; + +GRUB_MOD_INIT (gdb) +{ + grub_gdb_idtinit (); + cmd = grub_register_command_lockdown ("gdbstub", grub_cmd_gdbstub, + N_("PORT"), + /* + * TRANSLATORS: GDB stub is a small part of + * GDB functionality running on local host + * which allows remote debugger to + * connect to it. + */ + N_("Start GDB stub on given port")); + cmd_break = grub_register_command_lockdown ("gdbstub_break", grub_cmd_gdb_break, + /* + * TRANSLATORS: this refers to triggering + * a breakpoint so that the user will land + * into GDB. + */ + 0, N_("Break into GDB")); + cmd_stop = grub_register_command_lockdown ("gdbstub_stop", grub_cmd_gdbstop, + 0, N_("Stop GDB stub")); +} + +GRUB_MOD_FINI (gdb) +{ + grub_unregister_command (cmd); + grub_unregister_command (cmd_break); + grub_unregister_command (cmd_stop); + grub_gdb_idtrestore (); +} + diff --git a/grub-core/gdb/i386/idt.c b/grub-core/gdb/i386/idt.c new file mode 100644 index 000000000..43bde51a0 --- /dev/null +++ b/grub-core/gdb/i386/idt.c @@ -0,0 +1,78 @@ +/* idt.c - routines for constructing IDT for the GDB stub */ +/* + * Copyright (C) 2006 Lubomir Kundrak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +static struct grub_cpu_interrupt_gate grub_gdb_idt[GRUB_GDB_LAST_TRAP + 1] + __attribute__ ((aligned(16))); + +/* Sets up a gate descriptor in the IDT table. */ +static void +grub_idt_gate (struct grub_cpu_interrupt_gate *gate, void (*offset) (void), + grub_uint16_t selector, grub_uint8_t type, grub_uint8_t dpl) +{ + gate->offset_lo = (int) offset & 0xffff; + gate->selector = selector & 0xffff; + gate->unused = 0; + gate->gate = (type & 0x1f) | ((dpl & 0x3) << 5) | 0x80; + gate->offset_hi = ((int) offset >> 16) & 0xffff; +} + +static struct grub_cpu_idt_descriptor grub_gdb_orig_idt_desc + __attribute__ ((aligned(16))); +static struct grub_cpu_idt_descriptor grub_gdb_idt_desc + __attribute__ ((aligned(16))); + +/* Set up interrupt and trap handler descriptors in IDT. */ +void +grub_gdb_idtinit (void) +{ + int i; + grub_uint16_t seg; + + asm volatile ("xorl %%eax, %%eax\n" + "mov %%cs, %%ax\n" :"=a" (seg)); + + for (i = 0; i <= GRUB_GDB_LAST_TRAP; i++) + { + grub_idt_gate (&grub_gdb_idt[i], + grub_gdb_trapvec[i], seg, + GRUB_CPU_TRAP_GATE, 0); + } + + grub_gdb_idt_desc.base = (grub_addr_t) grub_gdb_idt; + grub_gdb_idt_desc.limit = sizeof (grub_gdb_idt) - 1; + asm volatile ("sidt %0" : : "m" (grub_gdb_orig_idt_desc)); + asm volatile ("lidt %0" : : "m" (grub_gdb_idt_desc)); +} + +void +grub_gdb_idtrestore (void) +{ + asm volatile ("lidt %0" : : "m" (grub_gdb_orig_idt_desc)); +} + +void +grub_gdb_breakpoint (void) +{ + asm volatile ("int $3"); +} diff --git a/grub-core/gdb/i386/machdep.S b/grub-core/gdb/i386/machdep.S new file mode 100644 index 000000000..f96d2b9c4 --- /dev/null +++ b/grub-core/gdb/i386/machdep.S @@ -0,0 +1,245 @@ +/* machdep.S - machine dependent assembly routines for the GDB stub */ +/* + * Copyright (C) 2006 Lubomir Kundrak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#define EC_PRESENT 1 +#define EC_ABSENT 0 + +#define GRUB_GDB_STACKSIZE 40000 + +#define SEP , + +#ifdef __APPLE__ + .zerofill __DATA, __bss, LOCAL(grub_gdb_stack_end), GRUB_GDB_STACKSIZE, 4 + LOCAL(grub_gdb_stack) = LOCAL(grub_gdb_stack_end) +#else +/* + * The .data index for the address vector. + */ + +#define VECTOR 1 + + .bss + .space GRUB_GDB_STACKSIZE +VARIABLE(grub_gdb_stack) +#endif + +/* + * Supplemental macros for register saving/restoration + * on exception handler entry/leave. + */ + +#ifdef __APPLE__ +.macro save32 +#define REG $0 +#define NDX $1 +#else +.macro save32 reg ndx +#define REG \reg +#define NDX \ndx +#endif + movl REG, EXT_C(grub_gdb_regs)+(NDX * 4) +.endm + +#undef REG +#undef NDX + +#ifdef __APPLE__ +.macro save16 +#define REG $0 +#define NDX $1 +#else +.macro save16 reg ndx +#define REG \reg +#define NDX \ndx +#endif + xorl %eax, %eax + movw REG, EXT_C(grub_gdb_regs)+(NDX * 4) + movw %ax, EXT_C(grub_gdb_regs)+(NDX * 4 + 2) + movl EXT_C(grub_gdb_regs)+(EAX * 4), %eax +.endm + +#undef REG +#undef NDX + +#ifdef __APPLE__ +.macro load32 +#define NDX $0 +#define REG $1 +#else +.macro load32 ndx reg +#define REG \reg +#define NDX \ndx +#endif + movl EXT_C(grub_gdb_regs)+(NDX * 4), REG +.endm + +#undef REG +#undef NDX + +#ifdef __APPLE__ +.macro load16 +#define NDX $0 +#define REG $1 +#else +.macro load16 ndx reg +#define NDX \ndx +#define REG \reg +#endif + movw EXT_C(grub_gdb_regs)+(NDX * 4), REG +.endm + +#undef REG +#undef NDX + +.macro save_context + save32 %eax, EAX + + save32 %ecx, ECX + save32 %edx, EDX + save32 %ebx, EBX + save32 %ebp, EBP + save32 %esi, ESI + save32 %edi, EDI + + popl %ebx + save32 %ebx, EIP + popl %ebx + save32 %ebx, CS + popl %ebx + save32 %ebx, EFLAGS + + save32 %esp, ESP + + save16 %ds, DS + save16 %es, ES + save16 %fs, FS + save16 %gs, GS + save16 %ss, SS +.endm + +.macro load_context + load16 SS, %ss + load32 ESP, %esp + + load32 EBP, %ebp + load32 ESI, %esi + load32 EDI, %edi + + load16 DS, %ds + load16 ES, %es + load16 FS, %fs + load16 GS, %gs + + load32 EFLAGS, %eax + pushl %eax + load32 CS, %eax + pushl %eax + load32 EIP, %eax + pushl %eax + + load32 EBX, %ebx + load32 EDX, %edx + load32 ECX, %ecx + load32 EAX, %eax +.endm + +/* + * This macro creates handlers for a given range of exception numbers + * and adds their addresses to the grub_gdb_trapvec array. + */ + +#ifdef __APPLE__ +.macro ent +#define EC $0 +#define BEG $1 +#define END $2 +#else +.macro ent ec beg end=0 +#define EC \ec +#define BEG \beg +#define END \end +#endif + + /* + * Wrapper body itself. + */ + + .text +1: + .if EC + add MACRO_DOLLAR(4), %esp + .endif + + save_context +#ifdef __APPLE__ + mov $LOCAL(grub_gdb_stack), %esp +#else + mov $EXT_C(grub_gdb_stack), %esp +#endif + mov $(BEG), %eax /* trap number */ + call EXT_C(grub_gdb_trap) + load_context + iret + + /* + * Address entry in trapvec array. + */ + +#ifdef __APPLE__ + .section __DATA, VECTOR +#else + .data VECTOR +#endif + .long 1b + + /* + * Next... (recursion). + */ + + .if END-BEG > 0 +#ifdef __APPLE__ + ent EC, (BEG+1), END +#else + ent \ec "(\beg+1)" \end +#endif + .endif +.endm + +/* + * Here does the actual construction of the address array and handlers + * take place. + */ +#ifdef __APPLE__ + .section __DATA, VECTOR +#else + .data VECTOR +#endif +VARIABLE(grub_gdb_trapvec) + ent EC_ABSENT, 0, 7 + ent EC_PRESENT, 8 + ent EC_ABSENT, 9 + ent EC_PRESENT, 10, 14 + /* + * You may have to split this further or as(1) + * will complain about nesting being too deep. + */ + ent EC_ABSENT, 15, GRUB_GDB_LAST_TRAP diff --git a/grub-core/gdb/i386/signal.c b/grub-core/gdb/i386/signal.c new file mode 100644 index 000000000..b169c6b23 --- /dev/null +++ b/grub-core/gdb/i386/signal.c @@ -0,0 +1,53 @@ +/* idt.c - routines for constructing IDT for the GDB stub */ +/* + * Copyright (C) 2006 Lubomir Kundrak + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include + +/* Converting CPU trap number to UNIX signal number as + described in System V ABI i386 Processor Supplement, 3-25. */ +unsigned int +grub_gdb_trap2sig (int trap_no) +{ + const int signals[] = { + SIGFPE, /* 0: Divide error fault */ + SIGTRAP, /* 1: Single step trap fault */ + SIGABRT, /* 2: # Nonmaskable interrupt */ + SIGTRAP, /* 3: Breakpoint trap */ + SIGSEGV, /* 4: Overflow trap */ + SIGSEGV, /* 5: Bounds check fault */ + SIGILL, /* 6: Invalid opcode fault */ + SIGFPE, /* 7: No coprocessor fault */ + SIGABRT, /* 8: # Double fault abort */ + SIGSEGV, /* 9: Coprocessor overrun abort */ + SIGSEGV, /* 10: Invalid TSS fault */ + SIGSEGV, /* 11: Segment not present fault */ + SIGSEGV, /* 12: Stack exception fault */ + SIGSEGV, /* 13: General protection fault abort */ + SIGSEGV, /* 14: Page fault */ + SIGABRT, /* 15: (reserved) */ + SIGFPE, /* 16: Coprocessor error fault */ + SIGUSR1 /* other */ + }; + + return signals[trap_no < 17 ? trap_no : 17]; +} + diff --git a/grub-core/gdb_grub.in b/grub-core/gdb_grub.in new file mode 100644 index 000000000..f188a842a --- /dev/null +++ b/grub-core/gdb_grub.in @@ -0,0 +1,136 @@ +### +### Load debuging information about GNU GRUB 2 modules into GDB +### automatically. Needs readelf, objdump, Python and gdb_helper.py script +### +### Has to be launched from the writable and trusted +### directory containing *.image and *.module +### +### $Id: .gdbinit,v 1.1 2006/05/14 11:38:08 lkundrak Exp $ +### Lubomir Kundrak +### + +source gdb_helper.py + + +define dynamic_load_symbols + dynamic_load_kernel_exec_symbols $arg0 + + run_on_start + + # We may have been very late to loading the kernel.exec symbols and + # and modules may already be loaded. So load symbols for any already + # loaded. + load_all_modules + + if $is_grub_loaded() + runtime_load_module + end +end +document dynamic_load_symbols + Load debugging symbols from kernel.exec and any loaded modules given + the address of the .text segment of the UEFI binary in memory. Also + setup session to automatically load module symbols for modules loaded + in the future. +end + +define load_all_modules + set $this = grub_dl_head + while ($this != 0) + load_module $this + set $this = $this->next + end +end +document load_all_modules + Load debugging information for all loaded modules. +end + +define runtime_load_module + break grub_dl_add + commands + silent + load_module mod + cont + end +end +document runtime_load_module + Load module symbols at runtime as they are loaded. +end + +define run_on_start + # TODO: Add check to see if _start symbol is defined, if not, then + # the symbols have not yet been loaded and this command will not work. + watch *_start + set $break_efi_start_bpnum = $bpnum + commands + silent + delete $break_efi_start_bpnum + + # Save the breakpoints here before the GRUB image is loaded + # into memory, then delete them. Later they will be reloaded + # once the GRUB image has been loaded. This avoids the issue + # where the loading of the GRUB image overwrites the software + # breakpoints, thus confusing GDB and effectively clearing + # those breakpoints. + save breakpoints .early-breakpoints.gdb + delete breakpoints + + tbreak _start + commands + silent + + # Reload the breakpoints now that the GRUB image has + # finished being loaded into memory. + source .early-breakpoints.gdb + + runtime_load_module + + if $is_user_command("onstart") + onstart + end + continue + end + continue + end +end +document run_on_start + On some targets, such as x86_64-efi, even if you know where the + firmware will load the GRUB image, you can not simply set a break + point before the image is loaded because loading the image + overwrites the break point in memory. So setup a hardware watch + point, which does not have that problem, and if that gets triggered, + then reset the break point. If a user-defined command named + "onstart" exists it will be run after the start is hit. + NOTE: This assumes symbols have already been correctly loaded for + the EFI application. +end + +### + +set confirm off + +# Note: On EFI and other platforms that load GRUB to an address that is +# determined at runtime, the symbols in kernel.exec will be wrong. +# However, we must start by loading some executable file or GDB will +# fail. + +set $platform_efi = $_streq("@platform@", "efi") +set $target = "@target_cpu@-@platform@" + +if ! $runonce + if $platform_efi + # Only load the executable file, not the symbols + exec-file kernel.exec + else + if $_streq($target, "i386-pc") + add-symbol-file boot.image + add-symbol-file diskboot.image + add-symbol-file lzma_decompress.image + end + file kernel.exec + run_on_start + runtime_load_module + end + + target remote :1234 + set $runonce = 1 +end diff --git a/grub-core/gdb_helper.py.in b/grub-core/gdb_helper.py.in new file mode 100644 index 000000000..a2cab416a --- /dev/null +++ b/grub-core/gdb_helper.py.in @@ -0,0 +1,173 @@ +import os +import re +import subprocess + +def prompt_hook (current_prompt): + return "(grub gdb) " +gdb.prompt_hook = prompt_hook + +##### Convenience functions ##### + +class IsGrubLoaded (gdb.Function): + """Return 1 if GRUB has been loaded in memory, otherwise 0. +The heuristic used is checking if the first 4 bytes of the memory pointed +to by the _start symbol are not 0. This is true for QEMU on the first run +of GRUB. This may not be true on physical hardware, where memory is not +necessarily cleared on soft reset. This may not also be true in QEMU on +soft resets. Also this many not be true when chainloading GRUB. +""" + + def __init__ (self): + super (IsGrubLoaded, self).__init__ ("is_grub_loaded") + + def invoke (self): + return int (gdb.parse_and_eval ("*(int *) _start")) != 0 + +is_grub_loaded = IsGrubLoaded () + +class IsUserCommand (gdb.Function): + """Set the second argument to true value if first argument is the name +of a user-defined command. +""" + + def __init__ (self): + super (IsUserCommand, self).__init__ ("is_user_command") + + def invoke (self, fmt, *args): + name = fmt.string () % tuple(a.string () for a in args) + for line in gdb.execute ("help user-defined", to_string=True).splitlines (): + line_parts = line.split(' -- ', 1) + if len (line_parts) > 1 and line_parts[0] == name: + return True + return False + +is_user_command = IsUserCommand () + +##### Commands ##### + +# Loading symbols is complicated by the fact that kernel.exec is an ELF +# ELF binary, but the UEFI runtime is PE32+. All the data sections of +# the ELF binary are concatenated (accounting for ELF section alignment) +# and put into one .data section of the PE32+ runtime image. So given +# the load address of the .data PE32+ section we can determine the +# addresses each ELF data section maps to. The UEFI application is +# loaded into memory just as it is laid out in the file. It is not +# assumed that the binary is available, but it is known that the .text +# section directly precedes the .data section and that .data is EFI +# page aligned. Using this, the .data offset can be found from the .text +# address. +class GrubLoadKernelExecSymbols (gdb.Command): + """Load debugging symbols from kernel.exec given the address of the +.text segment of the UEFI binary in memory.""" + + PE_SECTION_ALIGN = 12 + + def __init__ (self): + super (GrubLoadKernelExecSymbols, self).__init__ ("dynamic_load_kernel_exec_symbols", + gdb.COMMAND_USER, + gdb.COMPLETE_EXPRESSION) + + def invoke (self, arg, from_tty): + self.dont_repeat () + args = gdb.string_to_argv (arg) + + if len (args) != 1: + raise RuntimeError ("dynamic_load_kernel_exec_symbols expects exactly one argument") + + sections = self.parse_objdump_sections ("kernel.exec") + pe_text = args[0] + text_size = [s['size'] for s in sections if s['name'] == '.text'][0] + pe_data_offset = self.alignup (text_size, self.PE_SECTION_ALIGN) + + sym_load_cmd_parts = ["add-symbol-file", "kernel.exec", pe_text] + offset = 0 + for section in sections: + if 'DATA' in section["flags"] or section["name"] == ".bss": + offset = self.alignup (offset, section["align"]) + sym_load_cmd_parts.extend (["-s", section["name"], "(%s+0x%x+0x%x)" % (pe_text, pe_data_offset, offset)]) + offset += section["size"] + gdb.execute (' '.join (sym_load_cmd_parts)) + + @staticmethod + def parse_objdump_sections (filename): + fields = ("idx", "name", "size", "vma", "lma", "fileoff", "align") + re_section = re.compile ("^\s*" + "\s+".join(["(?P<%s>\S+)" % f for f in fields])) + c = subprocess.run (["objdump", "-h", filename], text=True, capture_output=True) + section_lines = c.stdout.splitlines ()[5:] + sections = [] + + for i in range (len (section_lines) >> 1): + m = re_section.match (section_lines[i * 2]) + s = dict (m.groupdict ()) + for f in ("size", "vma", "lma", "fileoff"): + s[f] = int (s[f], 16) + s["idx"] = int (s["idx"]) + s["align"] = int (s["align"].split ("**", 1)[1]) + s["flags"] = section_lines[(i * 2) + 1].strip ().split (", ") + sections.append (s) + return sections + + @staticmethod + def alignup (addr, align): + pad = (addr % (1 << align)) and 1 or 0 + return ((addr >> align) + pad) << align + +dynamic_load_kernel_exec_symbols = GrubLoadKernelExecSymbols () + + +class GrubLoadModuleSymbols (gdb.Command): + """Load module symbols at correct locations. +Takes one argument which is a pointer to a grub_dl_t struct.""" + + def __init__ (self): + super (GrubLoadModuleSymbols, self).__init__ ("load_module", + gdb.COMMAND_USER, + gdb.COMPLETE_EXPRESSION) + + def invoke (self, arg, from_tty): + self.dont_repeat () + args = gdb.string_to_argv (arg) + self.mod = gdb.parse_and_eval (args[0]) + sections = self.get_section_offsets () + section_names = self.get_section_names () + + sym_load_cmd_parts = ["add-symbol-file", + "%s.module" % (self.mod['name'].string (),)] + for idx, addr in sections: + section_name = section_names[idx] + if section_name == ".text": + sym_load_cmd_parts.append (addr) + else: + sym_load_cmd_parts.extend (["-s", section_name, addr]) + gdb.execute (' '.join (sym_load_cmd_parts)) + + if is_user_command.invoke (gdb.Value ("onload_%s"), self.mod['name']): + gdb.execute ("onload_%s (grub_dl_t)%s" % (self.mod['name'].string (), + self.mod.format_string (format='x'))) + + def get_section_offsets (self): + sections = [] + segment = self.mod['segment'] + while segment: + sections.append ((int (segment['section']), segment['addr'].format_string (format='x'))) + segment = segment['next'] + return sections + + def get_section_names (self): + re_index = re.compile ("^\s+\[\s*(\d+)\] (\S*)") + names = {} + modfilename = "%s.mod" % (self.mod['name'].string (),) + + if not os.path.exists (modfilename): + raise RuntimeError ("%s not found in current directory" % (modfilename,)) + + c = subprocess.run (["readelf", "-SW", modfilename], text=True, capture_output=True) + for line in c.stdout.splitlines ()[4:]: + m = re_index.match (line) + if not m: + continue + idx, name = m.groups () + names[int (idx)] = name + return names + +grub_load_module = GrubLoadModuleSymbols () diff --git a/genmodsrc.sh b/grub-core/genemuinit.sh similarity index 60% rename from genmodsrc.sh rename to grub-core/genemuinit.sh index 2d420550a..8c6bb1c18 100644 --- a/genmodsrc.sh +++ b/grub-core/genemuinit.sh @@ -1,8 +1,8 @@ #! /bin/sh # -# Copyright (C) 2002,2007 Free Software Foundation, Inc. +# Copyright (C) 2002,2005,2007 Free Software Foundation, Inc. # -# This genmodsrc.sh is free software; the author +# This gensymlist.sh is free software; the author # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # @@ -11,16 +11,14 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. -set -e - -mod_name="$1" -deps="$2" +nm="$1" +shift cat <. */ -#include +#include "grub_emu_init.h" EOF -echo "GRUB_MOD_NAME(${mod_name});" +cat < /dev/null; then + echo "grub_${line%%.*}_init ();" + fi done + +cat < /dev/null; then + echo "grub_${line%%.*}_fini ();" + fi +done + +cat < /dev/null; then + echo "void grub_${line%%.*}_init (void);" + fi + if ${nm} --defined-only -P -p ${line} | grep grub_mod_fini > /dev/null; then + echo "void grub_${line%%.*}_fini (void);" + fi +done diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in new file mode 100644 index 000000000..337753c57 --- /dev/null +++ b/grub-core/genmod.sh.in @@ -0,0 +1,106 @@ +#! @BUILD_SHEBANG@ +set -e + +# Copyright (C) 2010 Free Software Foundation, Inc. +# +# This gensymlist.sh is free software; the author +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Example: +# +# genmod.sh moddep.lst normal.module build-grub-module-verifier normal.mod +# + +moddep=$1 +infile=$2 +outfile=$4 + +tmpfile=${outfile}.tmp +modname=`echo $infile | sed -e 's@\.module.*$@@'` + +if ! grep ^$modname: $moddep >/dev/null; then + echo "warning: moddep.lst has no dependencies for $modname" >&2 + exit 0 +fi + +deps=`grep ^$modname: $moddep | sed s@^.*:@@` + +# remove old files if any +rm -f $tmpfile $outfile + +if test x@TARGET_APPLE_LINKER@ != x1; then + # stripout .modname and .moddeps sections from input module + @TARGET_OBJCOPY@ -R .modname -R .moddeps $infile $tmpfile + + # Attach .modname and .moddeps sections + t1=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + printf "$modname\0" >$t1 + + t2=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1 + for dep in $deps; do printf "$dep\0" >> $t2; done + + if test -n "$deps"; then + @TARGET_OBJCOPY@ --add-section .modname=$t1 --add-section .moddeps=$t2 $tmpfile + else + @TARGET_OBJCOPY@ --add-section .modname=$t1 $tmpfile + fi + rm -f $t1 $t2 + + if test x@platform@ != xemu; then + @TARGET_STRIP@ --strip-unneeded \ + -K grub_mod_init -K grub_mod_fini \ + -K _grub_mod_init -K _grub_mod_fini \ + -R .note.GNU-stack \ + -R .note.gnu.gold-version \ + -R .note.gnu.property \ + -R .gnu.build.attributes \ + -R '.llvm*' \ + -R .rel.gnu.build.attributes \ + -R .rela.gnu.build.attributes \ + -R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \ + -R .note -R .comment -R .ARM.exidx $tmpfile || exit 1 + fi + if ! test -z "${TARGET_OBJ2ELF}"; then + "${TARGET_OBJ2ELF}" $tmpfile || exit 1 + fi +else + tmpfile2=${outfile}.tmp2 + t1=${outfile}.t1.c + t2=${outfile}.t2.c + + # remove old files if any + rm -f $t1 $t2 + + cp $infile $tmpfile + + # Attach .modname and .moddeps sections + echo "char modname[] __attribute__ ((section(\"_modname, _modname\"))) = \"$modname\";" >$t1 + + for dep in $deps; do echo "char moddep_$dep[] __attribute__ ((section(\"_moddeps, _moddeps\"))) = \"$dep\";" >>$t2; done + + if test -n "$deps"; then + @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 $t2 $tmpfile -Wl,-r + else + @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 $tmpfile -Wl,-r + fi + rm -f $t1 $t2 $tmpfile + mv $tmpfile2 $tmpfile + + cp $tmpfile $tmpfile.bin + @TARGET_OBJCONV@ -f@TARGET_MODULE_FORMAT@ \ + -nr:_grub_mod_init:grub_mod_init \ + -nr:_grub_mod_fini:grub_mod_fini \ + -wd1106 -nu -nd $tmpfile.bin $tmpfile || exit 1 + rm -f $tmpfile.bin +fi +if test x@platform@ != xemu; then + ./build-grub-module-verifier@BUILD_EXEEXT@ $tmpfile @target_cpu@ @platform@ +fi +mv $tmpfile $outfile diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk new file mode 100644 index 000000000..ab457cb2b --- /dev/null +++ b/grub-core/genmoddep.awk @@ -0,0 +1,108 @@ +#! /usr/bin/awk -f +# +# Copyright (C) 2006 Free Software Foundation, Inc. +# +# This genmoddep.awk is free software; the author +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Read symbols' info from stdin. +BEGIN { + error = 0 +} + +{ + if ($1 == "defined") { + if ($3 !~ /^\.refptr\./ && $3 in symtab) { + printf "%s in %s is duplicated in %s\n", $3, $2, symtab[$3] >"/dev/stderr"; + error++; + } + symtab[$3] = $2; + modtab[$2] = "" modtab[$2] + } else if ($1 == "undefined") { + if ($3 in symtab) + modtab[$2] = modtab[$2] " " symtab[$3]; + else if ($3 != "__gnu_local_gp" && $3 != "_gp_disp") { + printf "%s in %s is not defined\n", $3, $2 >"/dev/stderr"; + error++; + } + } else if ($1 == "depends") { + for (i = 3; i <= NF; i++) { + modtab[$2] = modtab[$2] " " $i; + } + } else if ($1 == "") {} #Skip empty lines + else { + printf "error: %u: unrecognized input format\n", NR >"/dev/stderr"; + error++; + } +} + +# Output the result. +END { + if (error >= 1) + exit 1; + + total_depcount = 0 + + for (mod in modtab) { + # Remove duplications. + split(modtab[mod], depmods, " "); + for (depmod in uniqmods) { + delete uniqmods[depmod]; + } + for (i in depmods) { + depmod = depmods[i]; + # Ignore kernel, as always loaded. + if (depmod != "kernel" && depmod != mod) + uniqmods[depmod] = 1; + } + modlist = "" + depcount[mod] = 0 + n = asorti(uniqmods, w) + for (i = 1; i <= n; i++) { + depmod = w[i] + modlist = modlist " " depmod; + inverse_dependencies[depmod] = inverse_dependencies[depmod] " " mod + depcount[mod]++ + total_depcount++ + } + if (mod == "all_video") { + continue; + } + printf "%s:%s\n", mod, modlist; + } + + # Check that we have no dependency circles + while (total_depcount != 0) { + something_done = 0 + for (mod in depcount) { + if (depcount[mod] == 0) { + delete depcount[mod] + split(inverse_dependencies[mod], inv_depmods, " "); + for (ctr in inv_depmods) { + depcount[inv_depmods[ctr]]-- + total_depcount-- + } + delete inverse_dependencies[mod] + something_done = 1 + } + } + if (something_done == 0) { + for (mod in depcount) { + circle = circle " " mod + } + printf "error: modules %s form a dependency circle\n", circle >"/dev/stderr"; + exit 1 + } + } + modlist = "" + while (getline <"video.lst") { + modlist = modlist " " $1; + } + printf "all_video:%s\n", modlist; +} diff --git a/grub-core/gensyminfo.sh.in b/grub-core/gensyminfo.sh.in new file mode 100644 index 000000000..9bc767532 --- /dev/null +++ b/grub-core/gensyminfo.sh.in @@ -0,0 +1,37 @@ +#! @BUILD_SHEBANG@ +set -e + +# Copyright (C) 2010 Free Software Foundation, Inc. +# +# This gensymlist.sh is free software; the author +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# +# Example: +# +# gensyms.sh normal.module +# + +module=$1 +modname=`echo $module | sed -e 's@\.module.*$@@'` + +# Print all symbols defined by module +if test x"@TARGET_NMFLAGS_DEFINED_ONLY@" = x && test x"@TARGET_NMFLAGS_MINUS_P@" = x; then + @TARGET_NM@ -g -p $module | \ + sed -n "s@^\([0-9a-fA-F]*\) *[TBRDS] *\([^ ]*\).*@defined $modname \2@p" +elif test x"@TARGET_NMFLAGS_DEFINED_ONLY@" = x; then + @TARGET_NM@ -g @TARGET_NMFLAGS_MINUS_P@ -p $module | \ + sed -n "s@^\([^ ]*\) *[TBRDS] *\([0-9a-fA-F]*\).*@defined $modname \1@p" +else + @TARGET_NM@ -g --defined-only @TARGET_NMFLAGS_MINUS_P@ -p $module | \ + sed "s@^\([^ ]*\).*@defined $modname \1@g" +fi + +# Print all undefined symbols used by module +@TARGET_NM@ -u @TARGET_NMFLAGS_MINUS_P@ -p $module | sed "s@^\([^ ]*\).*@undefined $modname \1@g" diff --git a/gensymlist.sh.in b/grub-core/gensymlist.sh similarity index 76% rename from gensymlist.sh.in rename to grub-core/gensymlist.sh index 7cd3b48e3..5074ef6aa 100644 --- a/gensymlist.sh.in +++ b/grub-core/gensymlist.sh @@ -1,6 +1,6 @@ #! /bin/sh # -# Copyright (C) 2002,2006,2007,2008 Free Software Foundation, Inc. +# Copyright (C) 2002,2006,2007,2008,2009,2010 Free Software Foundation, Inc. # # This gensymlist.sh.in is free software; the author # gives unlimited permission to copy and/or distribute it, @@ -11,17 +11,11 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. -### The configure script will replace these variables. - -: ${srcdir=@srcdir@} -: ${CC=@TARGET_CC@} - - cat <. */ +#include <../config-util.h> EOF for i in $*; do @@ -47,31 +42,33 @@ cat < sizeof (tab[0])); for (p = tab; p->name; p++) - grub_dl_register_symbol (p->name, p->addr, 0); + grub_dl_register_symbol (p->name, p->addr, p->isfunc, 0); } EOF diff --git a/gentrigtables.c b/grub-core/gentrigtables.c similarity index 70% rename from gentrigtables.c rename to grub-core/gentrigtables.c index 772cd6224..fface6efc 100644 --- a/gentrigtables.c +++ b/grub-core/gentrigtables.c @@ -24,14 +24,23 @@ #include int -main () +main (int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) { int i; printf ("#include \n"); + printf ("#include \n"); + printf ("\n"); + + printf ("/* Under copyright legislature such automated output isn't\n"); + printf ("covered by any copyright. Hence it's public domain. Public\n"); + printf ("domain works can be dual-licenced with any license. */\n"); + printf ("GRUB_MOD_LICENSE (\"GPLv3+\");"); + printf ("GRUB_MOD_DUAL_LICENSE (\"Public Domain\");"); #define TAB(op) \ - printf ("grub_int16_t grub_trig_" #op "tab[] =\n{"); \ + printf ("const grub_int16_t grub_trig_" #op "tab[] =\n{"); \ for (i = 0; i < GRUB_TRIG_ANGLE_MAX; i++) \ { \ double x = i * 2 * M_PI / GRUB_TRIG_ANGLE_MAX; \ diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c new file mode 100644 index 000000000..9ffc73428 --- /dev/null +++ b/grub-core/gettext/gettext.c @@ -0,0 +1,551 @@ +/* gettext.c - gettext module */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + .mo file information from: + http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html . +*/ + +static const char *(*grub_gettext_original) (const char *s); + +struct grub_gettext_msg +{ + char *name; + char *translated; +}; + +struct header +{ + grub_uint32_t magic; + grub_uint32_t version; + grub_uint32_t number_of_strings; + grub_uint32_t offset_original; + grub_uint32_t offset_translation; +}; + +struct string_descriptor +{ + grub_uint32_t length; + grub_uint32_t offset; +}; + +struct grub_gettext_context +{ + grub_file_t fd_mo; + grub_off_t grub_gettext_offset_original; + grub_off_t grub_gettext_offset_translation; + grub_size_t grub_gettext_max; + int grub_gettext_max_log; + struct grub_gettext_msg *grub_gettext_msg_list; +}; + +static struct grub_gettext_context main_context, secondary_context; + +#define MO_MAGIC_NUMBER 0x950412de + +static grub_err_t +grub_gettext_pread (grub_file_t file, void *buf, grub_size_t len, + grub_off_t offset) +{ + if (len == 0) + return GRUB_ERR_NONE; + if (grub_file_seek (file, offset) == (grub_off_t) - 1) + return grub_errno; + if (grub_file_read (file, buf, len) != (grub_ssize_t) len) + { + if (!grub_errno) + grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file")); + return grub_errno; + } + return GRUB_ERR_NONE; +} + +static char * +grub_gettext_getstr_from_position (struct grub_gettext_context *ctx, + grub_off_t off, + grub_size_t position) +{ + grub_off_t internal_position; + grub_size_t length; + grub_off_t offset; + char *translation; + struct string_descriptor desc; + grub_err_t err; + grub_size_t alloc_sz; + + internal_position = (off + position * sizeof (desc)); + + err = grub_gettext_pread (ctx->fd_mo, (char *) &desc, + sizeof (desc), internal_position); + if (err) + return NULL; + length = grub_cpu_to_le32 (desc.length); + offset = grub_cpu_to_le32 (desc.offset); + + if (grub_add (length, 1, &alloc_sz)) + return NULL; + + translation = grub_malloc (alloc_sz); + if (!translation) + return NULL; + + err = grub_gettext_pread (ctx->fd_mo, translation, length, offset); + if (err) + { + grub_free (translation); + return NULL; + } + translation[length] = '\0'; + + return translation; +} + +static const char * +grub_gettext_gettranslation_from_position (struct grub_gettext_context *ctx, + grub_size_t position) +{ + if (!ctx->grub_gettext_msg_list[position].translated) + ctx->grub_gettext_msg_list[position].translated + = grub_gettext_getstr_from_position (ctx, + ctx->grub_gettext_offset_translation, + position); + return ctx->grub_gettext_msg_list[position].translated; +} + +static const char * +grub_gettext_getstring_from_position (struct grub_gettext_context *ctx, + grub_size_t position) +{ + if (!ctx->grub_gettext_msg_list[position].name) + ctx->grub_gettext_msg_list[position].name + = grub_gettext_getstr_from_position (ctx, + ctx->grub_gettext_offset_original, + position); + return ctx->grub_gettext_msg_list[position].name; +} + +static const char * +grub_gettext_translate_real (struct grub_gettext_context *ctx, + const char *orig) +{ + grub_size_t current = 0; + int i; + const char *current_string; + static int depth = 0; + + if (!ctx->grub_gettext_msg_list || !ctx->fd_mo) + return NULL; + + /* Shouldn't happen. Just a precaution if our own code + calls gettext somehow. */ + if (depth > 2) + return NULL; + depth++; + + /* Make sure we can use grub_gettext_translate for error messages. Push + active error message to error stack and reset error message. */ + grub_error_push (); + + for (i = ctx->grub_gettext_max_log; i >= 0; i--) + { + grub_size_t test; + int cmp; + + test = current | (1 << i); + if (test >= ctx->grub_gettext_max) + continue; + + current_string = grub_gettext_getstring_from_position (ctx, test); + + if (!current_string) + { + grub_errno = GRUB_ERR_NONE; + grub_error_pop (); + depth--; + return NULL; + } + + /* Search by bisection. */ + cmp = grub_strcmp (current_string, orig); + if (cmp <= 0) + current = test; + if (cmp == 0) + { + const char *ret = 0; + ret = grub_gettext_gettranslation_from_position (ctx, current); + if (!ret) + { + grub_errno = GRUB_ERR_NONE; + grub_error_pop (); + depth--; + return NULL; + } + grub_error_pop (); + depth--; + return ret; + } + } + + if (current == 0 && ctx->grub_gettext_max != 0) + { + current_string = grub_gettext_getstring_from_position (ctx, 0); + + if (!current_string) + { + grub_errno = GRUB_ERR_NONE; + grub_error_pop (); + depth--; + return NULL; + } + + if (grub_strcmp (current_string, orig) == 0) + { + const char *ret = 0; + ret = grub_gettext_gettranslation_from_position (ctx, current); + if (!ret) + { + grub_errno = GRUB_ERR_NONE; + grub_error_pop (); + depth--; + return NULL; + } + grub_error_pop (); + depth--; + return ret; + } + } + + grub_error_pop (); + depth--; + return NULL; +} + +static const char * +grub_gettext_translate (const char *orig) +{ + const char *ret; + if (orig[0] == 0) + return orig; + + ret = grub_gettext_translate_real (&main_context, orig); + if (ret) + return ret; + ret = grub_gettext_translate_real (&secondary_context, orig); + if (ret) + return ret; + return orig; +} + +static void +grub_gettext_delete_list (struct grub_gettext_context *ctx) +{ + struct grub_gettext_msg *l = ctx->grub_gettext_msg_list; + grub_size_t i; + + if (!l) + return; + ctx->grub_gettext_msg_list = 0; + for (i = 0; i < ctx->grub_gettext_max; i++) + grub_free (l[i].name); + /* Don't delete the translated message because could be in use. */ + grub_free (l); + if (ctx->fd_mo) + grub_file_close (ctx->fd_mo); + ctx->fd_mo = 0; + grub_memset (ctx, 0, sizeof (*ctx)); +} + +/* This is similar to grub_file_open. */ +static grub_err_t +grub_mofile_open (struct grub_gettext_context *ctx, + const char *filename) +{ + struct header head; + grub_err_t err; + grub_file_t fd; + + /* Using fd_mo and not another variable because + it's needed for grub_gettext_get_info. */ + + fd = grub_file_open (filename, GRUB_FILE_TYPE_GETTEXT_CATALOG); + + if (!fd) + return grub_errno; + + err = grub_gettext_pread (fd, &head, sizeof (head), 0); + if (err) + { + grub_file_close (fd); + return err; + } + + if (head.magic != grub_cpu_to_le32_compile_time (MO_MAGIC_NUMBER)) + { + grub_file_close (fd); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "mo: invalid mo magic in file: %s", filename); + } + + if (head.version != 0) + { + grub_file_close (fd); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "mo: invalid mo version in file: %s", filename); + } + + ctx->grub_gettext_offset_original = grub_le_to_cpu32 (head.offset_original); + ctx->grub_gettext_offset_translation = grub_le_to_cpu32 (head.offset_translation); + ctx->grub_gettext_max = grub_le_to_cpu32 (head.number_of_strings); + for (ctx->grub_gettext_max_log = 0; ctx->grub_gettext_max >> ctx->grub_gettext_max_log; + ctx->grub_gettext_max_log++); + + ctx->grub_gettext_msg_list = grub_calloc (ctx->grub_gettext_max, + sizeof (ctx->grub_gettext_msg_list[0])); + if (!ctx->grub_gettext_msg_list) + { + grub_file_close (fd); + return grub_errno; + } + ctx->fd_mo = fd; + if (grub_gettext != grub_gettext_translate) + { + grub_gettext_original = grub_gettext; + grub_gettext = grub_gettext_translate; + } + return 0; +} + +/* Returning grub_file_t would be more natural, but grub_mofile_open assigns + to fd_mo anyway ... */ +static grub_err_t +grub_mofile_open_lang (struct grub_gettext_context *ctx, + const char *part1, const char *part2, const char *locale) +{ + char *mo_file; + grub_err_t err; + + /* mo_file e.g.: /boot/grub/locale/ca.mo */ + + mo_file = grub_xasprintf ("%s%s/%s.mo", part1, part2, locale); + if (!mo_file) + return grub_errno; + + err = grub_mofile_open (ctx, mo_file); + grub_free (mo_file); + + /* Will try adding .gz as well. */ + if (err) + { + grub_errno = GRUB_ERR_NONE; + mo_file = grub_xasprintf ("%s%s/%s.mo.gz", part1, part2, locale); + if (!mo_file) + return grub_errno; + err = grub_mofile_open (ctx, mo_file); + grub_free (mo_file); + } + + /* Will try adding .gmo as well. */ + if (err) + { + grub_errno = GRUB_ERR_NONE; + mo_file = grub_xasprintf ("%s%s/%s.gmo", part1, part2, locale); + if (!mo_file) + return grub_errno; + err = grub_mofile_open (ctx, mo_file); + grub_free (mo_file); + } + + return err; +} + +static grub_err_t +grub_gettext_init_ext (struct grub_gettext_context *ctx, + const char *locale, + const char *locale_dir, const char *prefix) +{ + const char *part1, *part2; + grub_err_t err; + + grub_gettext_delete_list (ctx); + + if (!locale || locale[0] == 0) + return 0; + + part1 = locale_dir; + part2 = ""; + if (!part1 || part1[0] == 0) + { + part1 = prefix; + part2 = "/locale"; + } + + if (!part1 || part1[0] == 0) + return 0; + + err = grub_mofile_open_lang (ctx, part1, part2, locale); + + /* ll_CC didn't work, so try ll. */ + if (err) + { + char *lang = grub_strdup (locale); + char *underscore = lang ? grub_strchr (lang, '_') : 0; + + if (underscore) + { + *underscore = '\0'; + grub_errno = GRUB_ERR_NONE; + err = grub_mofile_open_lang (ctx, part1, part2, lang); + } + + grub_free (lang); + } + + if (locale[0] == 'e' && locale[1] == 'n' + && (locale[2] == '\0' || locale[2] == '_')) + grub_errno = err = GRUB_ERR_NONE; + return err; +} + +static char * +grub_gettext_env_write_lang (struct grub_env_var *var + __attribute__ ((unused)), const char *val) +{ + grub_err_t err; + err = grub_gettext_init_ext (&main_context, val, grub_env_get ("locale_dir"), + grub_env_get ("prefix")); + if (err) + grub_print_error (); + + err = grub_gettext_init_ext (&secondary_context, val, + grub_env_get ("secondary_locale_dir"), 0); + if (err) + grub_print_error (); + + return grub_strdup (val); +} + +void +grub_gettext_reread_prefix (const char *val) +{ + grub_err_t err; + err = grub_gettext_init_ext (&main_context, grub_env_get ("lang"), + grub_env_get ("locale_dir"), + val); + if (err) + grub_print_error (); +} + +static char * +read_main (struct grub_env_var *var + __attribute__ ((unused)), const char *val) +{ + grub_err_t err; + err = grub_gettext_init_ext (&main_context, grub_env_get ("lang"), val, + grub_env_get ("prefix")); + if (err) + grub_print_error (); + return grub_strdup (val); +} + +static char * +read_secondary (struct grub_env_var *var + __attribute__ ((unused)), const char *val) +{ + grub_err_t err; + err = grub_gettext_init_ext (&secondary_context, grub_env_get ("lang"), val, + 0); + if (err) + grub_print_error (); + + return grub_strdup (val); +} + +static grub_err_t +grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + const char *translation; + translation = grub_gettext_translate (args[0]); + grub_printf ("%s\n", translation); + return 0; +} + +GRUB_MOD_INIT (gettext) +{ + const char *lang; + grub_err_t err; + + lang = grub_env_get ("lang"); + + err = grub_gettext_init_ext (&main_context, lang, grub_env_get ("locale_dir"), + grub_env_get ("prefix")); + if (err) + grub_print_error (); + err = grub_gettext_init_ext (&secondary_context, lang, + grub_env_get ("secondary_locale_dir"), 0); + if (err) + grub_print_error (); + + grub_register_variable_hook ("locale_dir", NULL, read_main); + grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary); + + grub_register_command_p1 ("gettext", grub_cmd_translate, + N_("STRING"), + /* TRANSLATORS: It refers to passing the string through gettext. + So it's "translate" in the same meaning as in what you're + doing now. + */ + N_("Translates the string with the current settings.")); + + /* Reload .mo file information if lang changes. */ + grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang); + + /* Preserve hooks after context changes. */ + grub_env_export ("lang"); + grub_env_export ("locale_dir"); + grub_env_export ("secondary_locale_dir"); +} + +GRUB_MOD_FINI (gettext) +{ + grub_register_variable_hook ("locale_dir", NULL, NULL); + grub_register_variable_hook ("secondary_locale_dir", NULL, NULL); + grub_register_variable_hook ("lang", NULL, NULL); + + grub_gettext_delete_list (&main_context); + grub_gettext_delete_list (&secondary_context); + + grub_gettext = grub_gettext_original; +} diff --git a/grub-core/gfxmenu/font.c b/grub-core/gfxmenu/font.c new file mode 100644 index 000000000..756c24f20 --- /dev/null +++ b/grub-core/gfxmenu/font.c @@ -0,0 +1,116 @@ +/* font.c - Font API and font file loader. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Draw a UTF-8 string of text on the current video render target. + The x coordinate specifies the starting x position for the first character, + while the y coordinate specifies the baseline position. + If the string contains a character that FONT does not contain, then + a glyph from another loaded font may be used instead. */ +grub_err_t +grub_font_draw_string (const char *str, grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y) +{ + int x; + grub_uint32_t *logical; + grub_ssize_t logical_len, visual_len; + struct grub_unicode_glyph *visual, *ptr; + grub_err_t err; + + logical_len = grub_utf8_to_ucs4_alloc (str, &logical, 0); + if (logical_len < 0) + return grub_errno; + + visual_len = grub_bidi_logical_to_visual (logical, logical_len, &visual, + 0, 0, 0, 0, 0, 0, 0); + grub_free (logical); + if (visual_len < 0) + return grub_errno; + + err = GRUB_ERR_NONE; + for (ptr = visual, x = left_x; ptr < visual + visual_len; ptr++) + { + struct grub_font_glyph *glyph; + glyph = grub_font_construct_glyph (font, ptr); + if (!glyph) + { + err = grub_errno; + goto out; + } + err = grub_font_draw_glyph (glyph, color, x, baseline_y); + if (err) + goto out; + x += glyph->device_width; + } + +out: + for (ptr = visual; ptr < visual + visual_len; ptr++) + grub_unicode_destroy_glyph (ptr); + grub_free (visual); + + return err; +} + +/* Get the width in pixels of the specified UTF-8 string, when rendered in + in the specified font (but falling back on other fonts for glyphs that + are missing). */ +int +grub_font_get_string_width (grub_font_t font, const char *str) +{ + int width = 0; + grub_uint32_t *ptr; + grub_ssize_t logical_len; + grub_uint32_t *logical; + + logical_len = grub_utf8_to_ucs4_alloc (str, &logical, 0); + if (logical_len < 0) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + + for (ptr = logical; ptr < logical + logical_len;) + { + struct grub_unicode_glyph glyph; + + ptr += grub_unicode_aglomerate_comb (ptr, + logical_len - (ptr - logical), + &glyph); + width += grub_font_get_constructed_device_width (font, &glyph); + + grub_unicode_destroy_glyph (&glyph); + } + grub_free (logical); + + return width; +} diff --git a/gfxmenu/gfxmenu.c b/grub-core/gfxmenu/gfxmenu.c similarity index 74% rename from gfxmenu/gfxmenu.c rename to grub-core/gfxmenu/gfxmenu.c index a2e765156..72b39f90f 100644 --- a/gfxmenu/gfxmenu.c +++ b/grub-core/gfxmenu/gfxmenu.c @@ -36,10 +36,13 @@ #include #include #include +#include -grub_gfxmenu_view_t cached_view; +GRUB_MOD_LICENSE ("GPLv3+"); -static void +static grub_gfxmenu_view_t cached_view; + +static void grub_gfxmenu_viewer_fini (void *data __attribute__ ((unused))) { } @@ -50,53 +53,49 @@ grub_gfxmenu_try (int entry, grub_menu_t menu, int nested) { grub_gfxmenu_view_t view = NULL; const char *theme_path; + char *full_theme_path = 0; struct grub_menu_viewer *instance; grub_err_t err; struct grub_video_mode_info mode_info; theme_path = grub_env_get ("theme"); if (! theme_path) - { - grub_error_push (); - grub_gfxterm_fullscreen (); - grub_error_pop (); - return grub_error (GRUB_ERR_FILE_NOT_FOUND, "no theme specified"); - } - - instance = grub_zalloc (sizeof (*instance)); - if (!instance) - { - grub_error_push (); - grub_gfxterm_fullscreen (); - grub_error_pop (); - return grub_errno; - } + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), + "theme"); err = grub_video_get_info (&mode_info); if (err) + return err; + + instance = grub_zalloc (sizeof (*instance)); + if (!instance) + return grub_errno; + + if (theme_path[0] != '/' && theme_path[0] != '(') { - grub_error_push (); - grub_gfxterm_fullscreen (); - grub_error_pop (); - return err; + const char *prefix; + prefix = grub_env_get ("prefix"); + full_theme_path = grub_xasprintf ("%s/themes/%s", + prefix, + theme_path); } - if (!cached_view || grub_strcmp (cached_view->theme_path, theme_path) != 0 + if (!cached_view || grub_strcmp (cached_view->theme_path, + full_theme_path ? : theme_path) != 0 || cached_view->screen.width != mode_info.width || cached_view->screen.height != mode_info.height) { - grub_free (cached_view); + grub_gfxmenu_view_destroy (cached_view); /* Create the view. */ - cached_view = grub_gfxmenu_view_new (theme_path, mode_info.width, + cached_view = grub_gfxmenu_view_new (full_theme_path ? : theme_path, + mode_info.width, mode_info.height); } + grub_free (full_theme_path); if (! cached_view) { grub_free (instance); - grub_error_push (); - grub_gfxterm_fullscreen (); - grub_error_pop (); return grub_errno; } @@ -110,6 +109,13 @@ grub_gfxmenu_try (int entry, grub_menu_t menu, int nested) view->nested = nested; view->first_timeout = -1; + grub_video_set_viewport (0, 0, mode_info.width, mode_info.height); + if (view->double_repaint) + { + grub_video_swap_buffers (); + grub_video_set_viewport (0, 0, mode_info.width, mode_info.height); + } + grub_gfxmenu_view_draw (view); instance->data = view; @@ -128,9 +134,9 @@ GRUB_MOD_INIT (gfxmenu) struct grub_term_output *term; FOR_ACTIVE_TERM_OUTPUTS(term) - if (grub_gfxmenu_try_hook && grub_strcmp (term->name, "gfxterm") == 0) + if (grub_gfxmenu_try_hook && term->fullscreen) { - grub_gfxterm_fullscreen (); + term->fullscreen (); break; } diff --git a/gfxmenu/gui_box.c b/grub-core/gfxmenu/gui_box.c similarity index 95% rename from gfxmenu/gui_box.c rename to grub-core/gfxmenu/gui_box.c index 38b15f96d..37bab3fbb 100644 --- a/gfxmenu/gui_box.c +++ b/grub-core/gfxmenu/gui_box.c @@ -234,14 +234,30 @@ static void box_paint (void *vself, const grub_video_rect_t *region) { grub_gui_box_t self = vself; + struct component_node *cur; grub_video_rect_t vpsave; + grub_video_area_status_t box_area_status; + grub_video_get_area_status (&box_area_status); + grub_gui_set_viewport (&self->bounds, &vpsave); for (cur = self->chead.next; cur != &self->ctail; cur = cur->next) { grub_gui_component_t comp = cur->component; + grub_video_rect_t r; + comp->ops->get_bounds(comp, &r); + + if (!grub_video_have_common_points (region, &r)) + continue; + + /* Paint the child. */ + if (box_area_status == GRUB_VIDEO_AREA_ENABLED + && grub_video_bounds_inside_region (&r, region)) + grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED); comp->ops->paint (comp, region); + if (box_area_status == GRUB_VIDEO_AREA_ENABLED) + grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED); } grub_gui_restore_viewport (&vpsave); } diff --git a/gfxmenu/gui_canvas.c b/grub-core/gfxmenu/gui_canvas.c similarity index 92% rename from gfxmenu/gui_canvas.c rename to grub-core/gfxmenu/gui_canvas.c index b3919c2d3..a05491242 100644 --- a/gfxmenu/gui_canvas.c +++ b/grub-core/gfxmenu/gui_canvas.c @@ -80,9 +80,13 @@ static void canvas_paint (void *vself, const grub_video_rect_t *region) { grub_gui_canvas_t self = vself; + struct component_node *cur; grub_video_rect_t vpsave; + grub_video_area_status_t canvas_area_status; + grub_video_get_area_status (&canvas_area_status); + grub_gui_set_viewport (&self->bounds, &vpsave); for (cur = self->components.next; cur; cur = cur->next) { @@ -135,9 +139,16 @@ canvas_paint (void *vself, const grub_video_rect_t *region) r.height = h; comp->ops->set_bounds (comp, &r); + if (!grub_video_have_common_points (region, &r)) + continue; + /* Paint the child. */ - if (grub_video_have_common_points (region, &r)) - comp->ops->paint (comp, region); + if (canvas_area_status == GRUB_VIDEO_AREA_ENABLED + && grub_video_bounds_inside_region (&r, region)) + grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED); + comp->ops->paint (comp, region); + if (canvas_area_status == GRUB_VIDEO_AREA_ENABLED) + grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED); } grub_gui_restore_viewport (&vpsave); } diff --git a/gfxmenu/gui_circular_progress.c b/grub-core/gfxmenu/gui_circular_progress.c similarity index 74% rename from gfxmenu/gui_circular_progress.c rename to grub-core/gfxmenu/gui_circular_progress.c index 9a859ee2e..db1edb8f4 100644 --- a/gfxmenu/gui_circular_progress.c +++ b/grub-core/gfxmenu/gui_circular_progress.c @@ -37,7 +37,7 @@ struct grub_gui_circular_progress int start; int end; int value; - int num_ticks; + unsigned num_ticks; int start_angle; int ticks_disappear; char *theme_dir; @@ -54,6 +54,7 @@ static void circprog_destroy (void *vself) { circular_progress_t self = vself; + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self); } @@ -137,49 +138,53 @@ circprog_paint (void *vself, const grub_video_rect_t *region) (height - center_height) / 2, 0, 0, center_width, center_height); - int radius = width / 2 - tick_width / 2 - 1; - int nticks; - int tick_begin; - int tick_end; - if (self->end == self->start) - nticks = 0; - else - nticks = (self->num_ticks - * (self->value - self->start) - / (self->end - self->start)); - /* Do ticks appear or disappear as the value approached the end? */ - if (self->ticks_disappear) + if (self->num_ticks) { - tick_begin = nticks; - tick_end = self->num_ticks - 1; + int radius = grub_min (height, width) / 2 - grub_max (tick_height, tick_width) / 2 - 1; + unsigned nticks; + unsigned tick_begin; + unsigned tick_end; + if (self->end <= self->start + || self->value <= self->start) + nticks = 0; + else + nticks = ((unsigned) (self->num_ticks + * (self->value - self->start))) + / ((unsigned) (self->end - self->start)); + /* Do ticks appear or disappear as the value approached the end? */ + if (self->ticks_disappear) + { + tick_begin = nticks; + tick_end = self->num_ticks; + } + else + { + tick_begin = 0; + tick_end = nticks; + } + + unsigned i; + for (i = tick_begin; i < tick_end; i++) + { + int x; + int y; + int angle; + + /* Calculate the location of the tick. */ + angle = self->start_angle + + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks; + x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE); + + /* Adjust (x,y) so the tick is centered. */ + x -= tick_width / 2; + y -= tick_height / 2; + + /* Draw the tick. */ + grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, tick_width, tick_height); + } } - else - { - tick_begin = 0; - tick_end = nticks - 1; - } - - int i; - for (i = tick_begin; i < tick_end; i++) - { - int x; - int y; - int angle; - - /* Calculate the location of the tick. */ - angle = self->start_angle + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks; - x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE); - y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE); - - /* Adjust (x,y) so the tick is centered. */ - x -= tick_width / 2; - y -= tick_height / 2; - - /* Draw the tick. */ - grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND, - x, y, 0, 0, tick_width, tick_height); - } - grub_gui_restore_viewport (&vpsave); } @@ -211,17 +216,47 @@ circprog_get_bounds (void *vself, grub_video_rect_t *bounds) *bounds = self->bounds; } +static void +circprog_set_state (void *vself, int visible, int start, + int current, int end) +{ + circular_progress_t self = vself; + self->visible = visible; + self->start = start; + self->value = current; + self->end = end; +} + +static int +parse_angle (const char *value) +{ + const char *ptr; + int angle; + + angle = grub_strtol (value, &ptr, 10); + if (grub_errno) + return 0; + while (grub_isspace (*ptr)) + ptr++; + if (grub_strcmp (ptr, "deg") == 0 + /* Unicode symbol of degrees (a circle, U+b0). Put here in UTF-8 to + avoid potential problem with text file reesncoding */ + || grub_strcmp (ptr, "\xc2\xb0") == 0) + angle = grub_divide_round (angle * 64, 90); + return angle; +} + static grub_err_t circprog_set_property (void *vself, const char *name, const char *value) { circular_progress_t self = vself; if (grub_strcmp (name, "num_ticks") == 0) { - self->num_ticks = grub_strtol (value, 0, 10); + self->num_ticks = grub_strtoul (value, 0, 10); } else if (grub_strcmp (name, "start_angle") == 0) { - self->start_angle = grub_strtol (value, 0, 10); + self->start_angle = parse_angle (value); } else if (grub_strcmp (name, "ticks_disappear") == 0) { @@ -247,26 +282,20 @@ circprog_set_property (void *vself, const char *name, const char *value) } else if (grub_strcmp (name, "id") == 0) { + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self->id); if (value) self->id = grub_strdup (value); else self->id = 0; + if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID) + == 0) + grub_gfxmenu_timeout_register ((grub_gui_component_t) self, + circprog_set_state); } return grub_errno; } -static void -circprog_set_state (void *vself, int visible, int start, - int current, int end) -{ - circular_progress_t self = vself; - self->visible = visible; - self->start = start; - self->value = current; - self->end = end; -} - static struct grub_gui_component_ops circprog_ops = { .destroy = circprog_destroy, diff --git a/gfxmenu/gui_image.c b/grub-core/gfxmenu/gui_image.c similarity index 89% rename from gfxmenu/gui_image.c rename to grub-core/gfxmenu/gui_image.c index 3988f4ba8..eed4b6b87 100644 --- a/gfxmenu/gui_image.c +++ b/grub-core/gfxmenu/gui_image.c @@ -101,6 +101,9 @@ image_get_parent (void *vself) static grub_err_t rescale_image (grub_gui_image_t self) { + signed width; + signed height; + if (! self->raw_bitmap) { if (self->bitmap) @@ -111,12 +114,12 @@ rescale_image (grub_gui_image_t self) return grub_errno; } - unsigned width = self->bounds.width; - unsigned height = self->bounds.height; + width = self->bounds.width; + height = self->bounds.height; if (self->bitmap - && (grub_video_bitmap_get_width (self->bitmap) == width) - && (grub_video_bitmap_get_height (self->bitmap) == height)) + && ((signed) grub_video_bitmap_get_width (self->bitmap) == width) + && ((signed) grub_video_bitmap_get_height (self->bitmap) == height)) { /* Nothing to do; already the right size. */ return grub_errno; @@ -131,15 +134,15 @@ rescale_image (grub_gui_image_t self) /* Create a scaled bitmap, unless the requested size is the same as the raw size -- in that case a reference is made. */ - if (grub_video_bitmap_get_width (self->raw_bitmap) == width - && grub_video_bitmap_get_height (self->raw_bitmap) == height) + if ((signed) grub_video_bitmap_get_width (self->raw_bitmap) == width + && (signed) grub_video_bitmap_get_height (self->raw_bitmap) == height) { self->bitmap = self->raw_bitmap; return grub_errno; } /* Don't scale to an invalid size. */ - if (width == 0 || height == 0) + if (width <= 0 || height <= 0) return grub_errno; /* Create the scaled bitmap. */ @@ -148,11 +151,6 @@ rescale_image (grub_gui_image_t self) height, self->raw_bitmap, GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); - if (grub_errno != GRUB_ERR_NONE) - { - grub_error_push (); - grub_error (grub_errno, "failed to scale bitmap for image component"); - } return grub_errno; } @@ -201,6 +199,12 @@ load_image (grub_gui_image_t self, const char *path) if (self->raw_bitmap) grub_video_bitmap_destroy (self->raw_bitmap); + /* + * Either self->bitmap is being freed or it shares memory with + * self->raw_bitmap which is being freed. To ensure self->bitmap doesn't + * point to memory that has been freed, we can set it to NULL. + */ + self->bitmap = NULL; self->raw_bitmap = bitmap; return rescale_image (self); } @@ -221,7 +225,7 @@ image_set_property (void *vself, const char *name, const char *value) /* Resolve to an absolute path. */ if (! self->theme_dir) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "unspecified theme_dir"); + return grub_error (GRUB_ERR_BUG, "unspecified theme_dir"); absvalue = grub_resolve_relative_path (self->theme_dir, value); if (! absvalue) return grub_errno; diff --git a/gfxmenu/gui_label.c b/grub-core/gfxmenu/gui_label.c similarity index 71% rename from gfxmenu/gui_label.c rename to grub-core/gfxmenu/gui_label.c index a9dd575ac..ffd50223c 100644 --- a/gfxmenu/gui_label.c +++ b/grub-core/gfxmenu/gui_label.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include static const char *align_options[] = { @@ -46,8 +48,10 @@ struct grub_gui_label char *id; int visible; char *text; + char *template; grub_font_t font; - grub_gui_color_t color; + grub_video_rgba_color_t color; + int value; enum align_mode align; }; @@ -57,7 +61,9 @@ static void label_destroy (void *vself) { grub_gui_label_t self = vself; + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self->text); + grub_free (self->template); grub_free (self); } @@ -90,20 +96,22 @@ label_paint (void *vself, const grub_video_rect_t *region) if (self->align == align_left) left_x = 0; else if (self->align == align_center) - left_x = ((self->bounds.width - - grub_font_get_string_width (self->font, self->text)) - ) / 2; + left_x = (self->bounds.width + - grub_font_get_string_width (self->font, self->text)) / 2; else if (self->align == align_right) left_x = (self->bounds.width - grub_font_get_string_width (self->font, self->text)); else return; /* Invalid alignment. */ + if (left_x < 0 || left_x > (int) self->bounds.width) + left_x = 0; + grub_video_rect_t vpsave; grub_gui_set_viewport (&self->bounds, &vpsave); grub_font_draw_string (self->text, self->font, - grub_gui_map_color (self->color), + grub_video_map_rgba_color (self->color), left_x, grub_font_get_ascent (self->font)); grub_gui_restore_viewport (&vpsave); @@ -146,6 +154,19 @@ label_get_minimal_size (void *vself, unsigned *width, unsigned *height) + grub_font_get_descent (self->font)); } +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +static void +label_set_state (void *vself, int visible, int start __attribute__ ((unused)), + int current, int end __attribute__ ((unused))) +{ + grub_gui_label_t self = vself; + self->value = -current; + self->visible = visible; + grub_free (self->text); + self->text = grub_xasprintf (self->template ? : "%d", self->value); +} + static grub_err_t label_set_property (void *vself, const char *name, const char *value) { @@ -153,9 +174,32 @@ label_set_property (void *vself, const char *name, const char *value) if (grub_strcmp (name, "text") == 0) { grub_free (self->text); + grub_free (self->template); if (! value) - value = ""; - self->text = grub_strdup (value); + { + self->template = NULL; + self->text = grub_strdup (""); + } + else + { + if (grub_strcmp (value, "@KEYMAP_LONG@") == 0) + value = _("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."); + else if (grub_strcmp (value, "@KEYMAP_MIDDLE@") == 0) + value = _("Press enter to boot the selected OS, " + "`e' to edit the commands before booting " + "or `c' for a command-line."); + else if (grub_strcmp (value, "@KEYMAP_SHORT@") == 0) + value = _("enter: boot, `e': options, `c': cmd-line"); + /* FIXME: Add more templates here if needed. */ + + if (grub_printf_fmt_check(value, "%d") != GRUB_ERR_NONE) + value = ""; /* Unsupported format. */ + + self->template = grub_strdup (value); + self->text = grub_xasprintf (value, self->value); + } } else if (grub_strcmp (name, "font") == 0) { @@ -163,7 +207,7 @@ label_set_property (void *vself, const char *name, const char *value) } else if (grub_strcmp (name, "color") == 0) { - grub_gui_parse_color (value, &self->color); + grub_video_parse_color (value, &self->color); } else if (grub_strcmp (name, "align") == 0) { @@ -183,15 +227,22 @@ label_set_property (void *vself, const char *name, const char *value) } else if (grub_strcmp (name, "id") == 0) { + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self->id); if (value) self->id = grub_strdup (value); else self->id = 0; + if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID) + == 0) + grub_gfxmenu_timeout_register ((grub_gui_component_t) self, + label_set_state); } return GRUB_ERR_NONE; } +#pragma GCC diagnostic error "-Wformat-nonliteral" + static struct grub_gui_component_ops label_ops = { .destroy = label_destroy, diff --git a/grub-core/gfxmenu/gui_list.c b/grub-core/gfxmenu/gui_list.c new file mode 100644 index 000000000..2ccd4345f --- /dev/null +++ b/grub-core/gfxmenu/gui_list.c @@ -0,0 +1,953 @@ +/* gui_list.c - GUI component to display a selectable list of items. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +enum scrollbar_slice_mode { + SCROLLBAR_SLICE_WEST, + SCROLLBAR_SLICE_CENTER, + SCROLLBAR_SLICE_EAST +}; + +struct grub_gui_list_impl +{ + struct grub_gui_list list; + + grub_gui_container_t parent; + grub_video_rect_t bounds; + char *id; + int visible; + + int icon_width; + int icon_height; + int item_height; + int item_padding; + int item_icon_space; + int item_spacing; + grub_font_t item_font; + int selected_item_font_inherit; + grub_font_t selected_item_font; + grub_video_rgba_color_t item_color; + int selected_item_color_inherit; + grub_video_rgba_color_t selected_item_color; + + int draw_scrollbar; + int need_to_recreate_scrollbar; + char *scrollbar_frame_pattern; + char *scrollbar_thumb_pattern; + grub_gfxmenu_box_t scrollbar_frame; + grub_gfxmenu_box_t scrollbar_thumb; + int scrollbar_thumb_overlay; + int scrollbar_width; + enum scrollbar_slice_mode scrollbar_slice; + int scrollbar_left_pad; + int scrollbar_right_pad; + int scrollbar_top_pad; + int scrollbar_bottom_pad; + + int first_shown_index; + + int need_to_recreate_boxes; + char *theme_dir; + char *menu_box_pattern; + char *item_box_pattern; + int selected_item_box_pattern_inherit; + char *selected_item_box_pattern; + grub_gfxmenu_box_t menu_box; + grub_gfxmenu_box_t selected_item_box; + grub_gfxmenu_box_t item_box; + + grub_gfxmenu_icon_manager_t icon_manager; + + grub_gfxmenu_view_t view; +}; + +typedef struct grub_gui_list_impl *list_impl_t; + +static void +list_destroy (void *vself) +{ + list_impl_t self = vself; + + grub_free (self->theme_dir); + grub_free (self->menu_box_pattern); + grub_free (self->item_box_pattern); + grub_free (self->selected_item_box_pattern); + if (self->menu_box) + self->menu_box->destroy (self->menu_box); + if (self->item_box) + self->item_box->destroy (self->item_box); + if (self->selected_item_box) + self->selected_item_box->destroy (self->selected_item_box); + if (self->icon_manager) + grub_gfxmenu_icon_manager_destroy (self->icon_manager); + if (self->scrollbar_thumb) + self->scrollbar_thumb->destroy (self->scrollbar_thumb); + if (self->scrollbar_frame) + self->scrollbar_frame->destroy (self->scrollbar_frame); + grub_free (self->scrollbar_thumb_pattern); + grub_free (self->scrollbar_frame_pattern); + grub_free (self); +} + +static int +get_num_shown_items (list_impl_t self) +{ + int boxpad = self->item_padding; + int item_vspace = self->item_spacing; + int item_height = self->item_height; + + grub_gfxmenu_box_t box = self->menu_box; + int box_top_pad = box->get_top_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + grub_gfxmenu_box_t itembox = self->item_box; + grub_gfxmenu_box_t selbox = self->selected_item_box; + int item_top_pad = itembox->get_top_pad (itembox); + int item_bottom_pad = itembox->get_bottom_pad (itembox); + int sel_top_pad = selbox->get_top_pad (selbox); + int sel_bottom_pad = selbox->get_bottom_pad (selbox); + int max_top_pad = grub_max (item_top_pad, sel_top_pad); + int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad); + + if (item_height + item_vspace <= 0) + return 1; + + return (self->bounds.height + item_vspace - 2 * boxpad + - max_top_pad - max_bottom_pad + - box_top_pad - box_bottom_pad) / (item_height + item_vspace); +} + +static int +check_boxes (list_impl_t self) +{ + if (self->need_to_recreate_boxes) + { + grub_gui_recreate_box (&self->menu_box, + self->menu_box_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->item_box, + self->item_box_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->selected_item_box, + self->selected_item_box_pattern, + self->theme_dir); + + self->need_to_recreate_boxes = 0; + } + + return (self->menu_box != 0 && self->selected_item_box != 0 + && self->item_box != 0); +} + +static int +check_scrollbar (list_impl_t self) +{ + if (self->need_to_recreate_scrollbar) + { + grub_gui_recreate_box (&self->scrollbar_frame, + self->scrollbar_frame_pattern, + self->theme_dir); + + grub_gui_recreate_box (&self->scrollbar_thumb, + self->scrollbar_thumb_pattern, + self->theme_dir); + + self->need_to_recreate_scrollbar = 0; + } + + if (self->scrollbar_frame == 0 || self->scrollbar_thumb == 0) + return 0; + + /* Sanity checks. */ + grub_gfxmenu_box_t frame = self->scrollbar_frame; + grub_gfxmenu_box_t thumb = self->scrollbar_thumb; + grub_gfxmenu_box_t menu = self->menu_box; + int min_width = frame->get_left_pad (frame) + + frame->get_right_pad (frame); + int min_height = frame->get_top_pad (frame) + + frame->get_bottom_pad (frame) + + self->scrollbar_top_pad + self->scrollbar_bottom_pad + + menu->get_top_pad (menu) + + menu->get_bottom_pad (menu); + if (!self->scrollbar_thumb_overlay) + { + min_width += thumb->get_left_pad (thumb) + + thumb->get_right_pad (thumb); + min_height += thumb->get_top_pad (thumb) + + thumb->get_bottom_pad (thumb); + } + if (min_width <= self->scrollbar_width + && min_height <= (int) self->bounds.height) + return 1; + + /* Unprintable dimenstions. */ + self->draw_scrollbar = 0; + return 0; +} + +static const char * +list_get_id (void *vself) +{ + list_impl_t self = vself; + return self->id; +} + +static int +list_is_instance (void *vself __attribute__((unused)), const char *type) +{ + return (grub_strcmp (type, "component") == 0 + || grub_strcmp (type, "list") == 0); +} + +static struct grub_video_bitmap * +get_item_icon (list_impl_t self, int item_index) +{ + grub_menu_entry_t entry; + entry = grub_menu_get_entry (self->view->menu, item_index); + if (! entry) + return 0; + + return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry); +} + +static void +make_selected_item_visible (list_impl_t self) +{ + int selected_index = self->view->selected; + if (selected_index < 0) + return; /* No item is selected. */ + int num_shown_items = get_num_shown_items (self); + int last_shown_index = self->first_shown_index + (num_shown_items - 1); + if (selected_index < self->first_shown_index) + self->first_shown_index = selected_index; + else if (selected_index > last_shown_index) + self->first_shown_index = selected_index - (num_shown_items - 1); +} + +/* Draw a scrollbar on the menu. */ +static void +draw_scrollbar (list_impl_t self, + int value, int extent, int min, int max, + int scrollbar_width, int scrollbar_height) +{ + unsigned thumby, thumbheight; + + grub_gfxmenu_box_t frame = self->scrollbar_frame; + grub_gfxmenu_box_t thumb = self->scrollbar_thumb; + int frame_vertical_pad = (frame->get_top_pad (frame) + + frame->get_bottom_pad (frame)); + int frame_horizontal_pad = (frame->get_left_pad (frame) + + frame->get_right_pad (frame)); + unsigned thumb_vertical_pad = (thumb->get_top_pad (thumb) + + thumb->get_bottom_pad (thumb)); + int thumb_horizontal_pad = (thumb->get_left_pad (thumb) + + thumb->get_right_pad (thumb)); + int tracktop = frame->get_top_pad (frame); + unsigned tracklen; + if (scrollbar_height <= frame_vertical_pad) + tracklen = 0; + else + tracklen = scrollbar_height - frame_vertical_pad; + frame->set_content_size (frame, + scrollbar_width - frame_horizontal_pad, + tracklen); + if (self->scrollbar_thumb_overlay) + { + tracklen += thumb_vertical_pad; + tracktop -= thumb->get_top_pad (thumb); + } + if (value <= min || max <= min) + thumby = 0; + else + thumby = ((unsigned) tracklen * (value - min)) + / ((unsigned) (max - min)); + if (max <= min) + thumbheight = 1; + else + thumbheight = ((unsigned) (tracklen * extent) + / ((unsigned) (max - min))) + 1; + /* Rare occasion: too many entries or too low height. */ + if (thumbheight < thumb_vertical_pad) + { + thumbheight = thumb_vertical_pad; + if (value <= min || max <= extent + || tracklen <= thumb_vertical_pad) + thumby = 0; + else + thumby = ((unsigned) ((tracklen - thumb_vertical_pad) * (value - min)) + / ((unsigned)(max - extent))); + } + thumby += tracktop; + int thumbx = frame->get_left_pad (frame); + int thumbwidth = scrollbar_width - frame_horizontal_pad; + if (!self->scrollbar_thumb_overlay) + thumbwidth -= thumb_horizontal_pad; + else + thumbx -= thumb->get_left_pad (thumb); + thumb->set_content_size (thumb, thumbwidth, + thumbheight - thumb_vertical_pad); + frame->draw (frame, 0, 0); + thumb->draw (thumb, thumbx, thumby); +} + +/* Draw the list of items. */ +static void +draw_menu (list_impl_t self, int num_shown_items) +{ + if (! self->menu_box || ! self->selected_item_box || ! self->item_box) + return; + + int boxpad = self->item_padding; + int icon_text_space = self->item_icon_space; + int item_vspace = self->item_spacing; + + int ascent = grub_font_get_ascent (self->item_font); + int descent = grub_font_get_descent (self->item_font); + int selected_ascent = grub_font_get_ascent (self->selected_item_font); + int selected_descent = grub_font_get_descent (self->selected_item_font); + int text_box_height = self->item_height; + + make_selected_item_visible (self); + + grub_gfxmenu_box_t itembox = self->item_box; + grub_gfxmenu_box_t selbox = self->selected_item_box; + int item_leftpad = itembox->get_left_pad (itembox); + int item_rightpad = itembox->get_right_pad (itembox); + int item_border_width = item_leftpad + item_rightpad; + int item_toppad = itembox->get_top_pad (itembox); + int sel_leftpad = selbox->get_left_pad (selbox); + int sel_rightpad = selbox->get_right_pad (selbox); + int sel_border_width = sel_leftpad + sel_rightpad; + int sel_toppad = selbox->get_top_pad (selbox); + + int max_leftpad = grub_max (item_leftpad, sel_leftpad); + int max_toppad = grub_max (item_toppad, sel_toppad); + int item_top = 0; + int menu_index; + int visible_index; + struct grub_video_rect oviewport; + + grub_video_get_viewport (&oviewport.x, &oviewport.y, + &oviewport.width, &oviewport.height); + grub_video_set_viewport (oviewport.x + boxpad, + oviewport.y + boxpad, + oviewport.width - 2 * boxpad, + oviewport.height - 2 * boxpad); + + int cwidth = oviewport.width - 2 * boxpad; + + itembox->set_content_size (itembox, cwidth - item_border_width, + text_box_height); + selbox->set_content_size (selbox, cwidth - sel_border_width, + text_box_height); + + int text_left_offset = self->icon_width + icon_text_space; + int item_text_top_offset = (text_box_height - (ascent + descent)) / 2 + ascent; + int sel_text_top_offset = (text_box_height - (selected_ascent + + selected_descent)) / 2 + + selected_ascent; + + grub_video_rect_t svpsave, sviewport; + sviewport.x = max_leftpad + text_left_offset; + int text_viewport_width = cwidth - sviewport.x; + sviewport.height = text_box_height; + + grub_video_color_t item_color; + grub_video_color_t sel_color; + item_color = grub_video_map_rgba_color (self->item_color); + sel_color = grub_video_map_rgba_color (self->selected_item_color); + + int item_box_top_offset = max_toppad - item_toppad; + int sel_box_top_offset = max_toppad - sel_toppad; + int item_viewport_width = text_viewport_width - item_rightpad; + int sel_viewport_width = text_viewport_width - sel_rightpad; + int tmp_icon_top_offset = (text_box_height - self->icon_height) / 2; + int item_icon_top_offset = item_toppad + tmp_icon_top_offset; + int sel_icon_top_offset = sel_toppad + tmp_icon_top_offset; + + for (visible_index = 0, menu_index = self->first_shown_index; + visible_index < num_shown_items && menu_index < self->view->menu->size; + visible_index++, menu_index++) + { + int is_selected = (menu_index == self->view->selected); + struct grub_video_bitmap *icon; + grub_font_t font; + grub_video_color_t color; + int text_top_offset; + int top_pad; + int icon_top_offset; + int viewport_width; + + if (is_selected) + { + selbox->draw (selbox, 0, item_top + sel_box_top_offset); + font = self->selected_item_font; + color = sel_color; + text_top_offset = sel_text_top_offset; + top_pad = sel_toppad; + icon_top_offset = sel_icon_top_offset; + viewport_width = sel_viewport_width; + } + else + { + itembox->draw (itembox, 0, item_top + item_box_top_offset); + font = self->item_font; + color = item_color; + text_top_offset = item_text_top_offset; + top_pad = item_toppad; + icon_top_offset = item_icon_top_offset; + viewport_width = item_viewport_width; + } + + icon = get_item_icon (self, menu_index); + if (icon != 0) + grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND, + max_leftpad, + item_top + icon_top_offset, + 0, 0, self->icon_width, self->icon_height); + + const char *item_title = + grub_menu_get_entry (self->view->menu, menu_index)->title; + + sviewport.y = item_top + top_pad; + sviewport.width = viewport_width; + grub_gui_set_viewport (&sviewport, &svpsave); + grub_font_draw_string (item_title, + font, + color, + 0, + text_top_offset); + grub_gui_restore_viewport (&svpsave); + + item_top += text_box_height + item_vspace; + } + grub_video_set_viewport (oviewport.x, + oviewport.y, + oviewport.width, + oviewport.height); +} + +static void +list_paint (void *vself, const grub_video_rect_t *region) +{ + list_impl_t self = vself; + grub_video_rect_t vpsave; + + if (! self->visible) + return; + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + check_boxes (self); + + if (! self->menu_box || ! self->selected_item_box || ! self->item_box) + return; + + grub_gui_set_viewport (&self->bounds, &vpsave); + { + grub_gfxmenu_box_t box = self->menu_box; + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + grub_video_rect_t vpsave2, content_rect; + int num_shown_items = get_num_shown_items (self); + int drawing_scrollbar = (self->draw_scrollbar + && (num_shown_items < self->view->menu->size) + && check_scrollbar (self)); + int scrollbar_width = self->scrollbar_width; + + content_rect.x = box_left_pad; + content_rect.y = box_top_pad; + content_rect.width = self->bounds.width - box_left_pad - box_right_pad; + content_rect.height = self->bounds.height - box_top_pad - box_bottom_pad; + + box->set_content_size (box, content_rect.width, content_rect.height); + + box->draw (box, 0, 0); + + switch (self->scrollbar_slice) + { + case SCROLLBAR_SLICE_WEST: + content_rect.x += self->scrollbar_right_pad; + content_rect.width -= self->scrollbar_right_pad; + break; + case SCROLLBAR_SLICE_CENTER: + if (drawing_scrollbar) + content_rect.width -= scrollbar_width + self->scrollbar_left_pad + + self->scrollbar_right_pad; + break; + case SCROLLBAR_SLICE_EAST: + content_rect.width -= self->scrollbar_left_pad; + break; + } + + grub_gui_set_viewport (&content_rect, &vpsave2); + draw_menu (self, num_shown_items); + grub_gui_restore_viewport (&vpsave2); + + if (drawing_scrollbar) + { + content_rect.y += self->scrollbar_top_pad; + content_rect.height -= self->scrollbar_top_pad + + self->scrollbar_bottom_pad; + content_rect.width = scrollbar_width; + switch (self->scrollbar_slice) + { + case SCROLLBAR_SLICE_WEST: + if (box_left_pad > scrollbar_width) + { + content_rect.x = box_left_pad - scrollbar_width; + content_rect.width = scrollbar_width; + } + else + { + content_rect.x = 0; + content_rect.width = box_left_pad; + } + break; + case SCROLLBAR_SLICE_CENTER: + content_rect.x = self->bounds.width - box_right_pad + - scrollbar_width - self->scrollbar_right_pad; + content_rect.width = scrollbar_width; + break; + case SCROLLBAR_SLICE_EAST: + content_rect.x = self->bounds.width - box_right_pad; + content_rect.width = box_right_pad; + break; + } + + grub_gui_set_viewport (&content_rect, &vpsave2); + draw_scrollbar (self, + self->first_shown_index, num_shown_items, + 0, self->view->menu->size, + scrollbar_width, + content_rect.height); + grub_gui_restore_viewport (&vpsave2); + } + } + + grub_gui_restore_viewport (&vpsave); +} + +static void +list_set_parent (void *vself, grub_gui_container_t parent) +{ + list_impl_t self = vself; + self->parent = parent; +} + +static grub_gui_container_t +list_get_parent (void *vself) +{ + list_impl_t self = vself; + return self->parent; +} + +static void +list_set_bounds (void *vself, const grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + self->bounds = *bounds; +} + +static void +list_get_bounds (void *vself, grub_video_rect_t *bounds) +{ + list_impl_t self = vself; + *bounds = self->bounds; +} + +static void +list_get_minimal_size (void *vself, unsigned *width, unsigned *height) +{ + list_impl_t self = vself; + + if (check_boxes (self)) + { + int boxpad = self->item_padding; + int item_vspace = self->item_spacing; + int item_height = self->item_height; + int num_items = 3; + + grub_gfxmenu_box_t box = self->menu_box; + int box_left_pad = box->get_left_pad (box); + int box_top_pad = box->get_top_pad (box); + int box_right_pad = box->get_right_pad (box); + int box_bottom_pad = box->get_bottom_pad (box); + unsigned width_s; + + grub_gfxmenu_box_t selbox = self->selected_item_box; + int sel_top_pad = selbox->get_top_pad (selbox); + int sel_bottom_pad = selbox->get_bottom_pad (selbox); + int sel_left_pad = selbox->get_left_pad (selbox); + int sel_right_pad = selbox->get_right_pad (selbox); + + grub_gfxmenu_box_t itembox = self->item_box; + int item_top_pad = itembox->get_top_pad (itembox); + int item_bottom_pad = itembox->get_bottom_pad (itembox); + int item_left_pad = itembox->get_left_pad (itembox); + int item_right_pad = itembox->get_right_pad (itembox); + + int max_left_pad = grub_max (item_left_pad, sel_left_pad); + int max_right_pad = grub_max (item_right_pad, sel_right_pad); + int max_top_pad = grub_max (item_top_pad, sel_top_pad); + int max_bottom_pad = grub_max (item_bottom_pad, sel_bottom_pad); + + *width = grub_font_get_string_width (self->item_font, "Typical OS"); + width_s = grub_font_get_string_width (self->selected_item_font, + "Typical OS"); + if (*width < width_s) + *width = width_s; + + *width += 2 * boxpad + box_left_pad + box_right_pad + + max_left_pad + max_right_pad + + self->item_icon_space + self->icon_width; + + switch (self->scrollbar_slice) + { + case SCROLLBAR_SLICE_WEST: + *width += self->scrollbar_right_pad; + break; + case SCROLLBAR_SLICE_CENTER: + *width += self->scrollbar_width + self->scrollbar_left_pad + + self->scrollbar_right_pad; + break; + case SCROLLBAR_SLICE_EAST: + *width += self->scrollbar_left_pad; + break; + } + + /* Set the menu box height to fit the items. */ + *height = (item_height * num_items + + item_vspace * (num_items - 1) + + 2 * boxpad + + box_top_pad + box_bottom_pad + + max_top_pad + max_bottom_pad); + } + else + { + *width = 0; + *height = 0; + } +} + +static grub_err_t +list_set_property (void *vself, const char *name, const char *value) +{ + list_impl_t self = vself; + if (grub_strcmp (name, "item_font") == 0) + { + self->item_font = grub_font_get (value); + if (self->selected_item_font_inherit) + self->selected_item_font = self->item_font; + } + else if (grub_strcmp (name, "selected_item_font") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + { + self->selected_item_font = self->item_font; + self->selected_item_font_inherit = 1; + } + else + { + self->selected_item_font = grub_font_get (value); + self->selected_item_font_inherit = 0; + } + } + else if (grub_strcmp (name, "item_color") == 0) + { + grub_video_rgba_color_t color; + if (grub_video_parse_color (value, &color) == GRUB_ERR_NONE) + { + self->item_color = color; + if (self->selected_item_color_inherit) + self->selected_item_color = self->item_color; + } + } + else if (grub_strcmp (name, "selected_item_color") == 0) + { + if (! value || grub_strcmp (value, "inherit") == 0) + { + self->selected_item_color = self->item_color; + self->selected_item_color_inherit = 1; + } + else + { + grub_video_rgba_color_t color; + if (grub_video_parse_color (value, &color) + == GRUB_ERR_NONE) + { + self->selected_item_color = color; + self->selected_item_color_inherit = 0; + } + } + } + else if (grub_strcmp (name, "icon_width") == 0) + { + self->icon_width = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "icon_height") == 0) + { + self->icon_height = grub_strtol (value, 0, 10); + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + } + else if (grub_strcmp (name, "item_height") == 0) + { + self->item_height = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_padding") == 0) + { + self->item_padding = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_icon_space") == 0) + { + self->item_icon_space = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "item_spacing") == 0) + { + self->item_spacing = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "visible") == 0) + { + self->visible = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "menu_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->menu_box_pattern); + self->menu_box_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "item_pixmap_style") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->item_box_pattern); + self->item_box_pattern = value ? grub_strdup (value) : 0; + if (self->selected_item_box_pattern_inherit) + { + grub_free (self->selected_item_box_pattern); + self->selected_item_box_pattern = value ? grub_strdup (value) : 0; + } + } + else if (grub_strcmp (name, "selected_item_pixmap_style") == 0) + { + if (!value || grub_strcmp (value, "inherit") == 0) + { + grub_free (self->selected_item_box_pattern); + char *tmp = self->item_box_pattern; + self->selected_item_box_pattern = tmp ? grub_strdup (tmp) : 0; + self->selected_item_box_pattern_inherit = 1; + } + else + { + self->need_to_recreate_boxes = 1; + grub_free (self->selected_item_box_pattern); + self->selected_item_box_pattern = grub_strdup (value); + self->selected_item_box_pattern_inherit = 0; + } + } + else if (grub_strcmp (name, "scrollbar_frame") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_frame_pattern); + self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_thumb") == 0) + { + self->need_to_recreate_scrollbar = 1; + grub_free (self->scrollbar_thumb_pattern); + self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "scrollbar_thumb_overlay") == 0) + { + self->scrollbar_thumb_overlay = grub_strcmp (value, "true") == 0; + } + else if (grub_strcmp (name, "scrollbar_width") == 0) + { + self->scrollbar_width = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar_slice") == 0) + { + if (grub_strcmp (value, "west") == 0) + self->scrollbar_slice = SCROLLBAR_SLICE_WEST; + else if (grub_strcmp (value, "center") == 0) + self->scrollbar_slice = SCROLLBAR_SLICE_CENTER; + else if (grub_strcmp (value, "east") == 0) + self->scrollbar_slice = SCROLLBAR_SLICE_EAST; + } + else if (grub_strcmp (name, "scrollbar_left_pad") == 0) + { + self->scrollbar_left_pad = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar_right_pad") == 0) + { + self->scrollbar_right_pad = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar_top_pad") == 0) + { + self->scrollbar_top_pad = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar_bottom_pad") == 0) + { + self->scrollbar_bottom_pad = grub_strtol (value, 0, 10); + } + else if (grub_strcmp (name, "scrollbar") == 0) + { + self->draw_scrollbar = grub_strcmp (value, "false") != 0; + } + else if (grub_strcmp (name, "theme_dir") == 0) + { + self->need_to_recreate_boxes = 1; + grub_free (self->theme_dir); + self->theme_dir = value ? grub_strdup (value) : 0; + } + else if (grub_strcmp (name, "id") == 0) + { + grub_free (self->id); + if (value) + self->id = grub_strdup (value); + else + self->id = 0; + } + return grub_errno; +} + +/* Set necessary information that the gfxmenu view provides. */ +static void +list_set_view_info (void *vself, + grub_gfxmenu_view_t view) +{ + list_impl_t self = vself; + grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, + view->theme_path); + self->view = view; +} + +/* Refresh list variables */ +static void +list_refresh_info (void *vself, + grub_gfxmenu_view_t view) +{ + list_impl_t self = vself; + if (view->nested) + self->first_shown_index = 0; +} + +static struct grub_gui_component_ops list_comp_ops = + { + .destroy = list_destroy, + .get_id = list_get_id, + .is_instance = list_is_instance, + .paint = list_paint, + .set_parent = list_set_parent, + .get_parent = list_get_parent, + .set_bounds = list_set_bounds, + .get_bounds = list_get_bounds, + .get_minimal_size = list_get_minimal_size, + .set_property = list_set_property + }; + +static struct grub_gui_list_ops list_ops = +{ + .set_view_info = list_set_view_info, + .refresh_list = list_refresh_info +}; + +grub_gui_component_t +grub_gui_list_new (void) +{ + list_impl_t self; + grub_font_t default_font; + grub_video_rgba_color_t default_fg_color; + + self = grub_zalloc (sizeof (*self)); + if (! self) + return 0; + + self->list.ops = &list_ops; + self->list.component.ops = &list_comp_ops; + + self->visible = 1; + + default_font = grub_font_get ("Unknown Regular 16"); + default_fg_color = grub_video_rgba_color_rgb (0, 0, 0); + + self->icon_width = 32; + self->icon_height = 32; + self->item_height = 42; + self->item_padding = 14; + self->item_icon_space = 4; + self->item_spacing = 16; + self->item_font = default_font; + self->selected_item_font_inherit = 1; /* Default to using the item_font. */ + self->selected_item_font = default_font; + self->item_color = default_fg_color; + self->selected_item_color_inherit = 1; /* Default to using the item_color. */ + self->selected_item_color = default_fg_color; + + self->draw_scrollbar = 1; + self->need_to_recreate_scrollbar = 1; + self->scrollbar_frame = 0; + self->scrollbar_thumb = 0; + self->scrollbar_frame_pattern = 0; + self->scrollbar_thumb_pattern = 0; + self->scrollbar_thumb_overlay = 0; + self->scrollbar_width = 16; + self->scrollbar_slice = SCROLLBAR_SLICE_EAST; + self->scrollbar_left_pad = 2; + self->scrollbar_right_pad = 0; + self->scrollbar_top_pad = 0; + self->scrollbar_bottom_pad = 0; + + self->first_shown_index = 0; + + self->need_to_recreate_boxes = 0; + self->theme_dir = 0; + self->menu_box_pattern = 0; + self->item_box_pattern = 0; + self->selected_item_box_pattern_inherit = 1;/*Default to using the item_box.*/ + self->selected_item_box_pattern = 0; + self->menu_box = grub_gfxmenu_create_box (0, 0); + self->item_box = grub_gfxmenu_create_box (0, 0); + self->selected_item_box = grub_gfxmenu_create_box (0, 0); + + self->icon_manager = grub_gfxmenu_icon_manager_new (); + if (! self->icon_manager) + { + self->list.component.ops->destroy (self); + return 0; + } + grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager, + self->icon_width, + self->icon_height); + return (grub_gui_component_t) self; +} diff --git a/gfxmenu/gui_progress_bar.c b/grub-core/gfxmenu/gui_progress_bar.c similarity index 70% rename from gfxmenu/gui_progress_bar.c rename to grub-core/gfxmenu/gui_progress_bar.c index d786aae31..c4cd1859b 100644 --- a/gfxmenu/gui_progress_bar.c +++ b/grub-core/gfxmenu/gui_progress_bar.c @@ -25,6 +25,7 @@ #include #include #include +#include struct grub_gui_progress_bar { @@ -37,13 +38,12 @@ struct grub_gui_progress_bar int start; int end; int value; - int show_text; char *template; grub_font_t font; - grub_gui_color_t text_color; - grub_gui_color_t border_color; - grub_gui_color_t bg_color; - grub_gui_color_t fg_color; + grub_video_rgba_color_t text_color; + grub_video_rgba_color_t border_color; + grub_video_rgba_color_t bg_color; + grub_video_rgba_color_t fg_color; char *theme_dir; int need_to_recreate_pixmaps; @@ -52,6 +52,7 @@ struct grub_gui_progress_bar char *highlight_pattern; grub_gfxmenu_box_t bar_box; grub_gfxmenu_box_t highlight_box; + int highlight_overlay; }; typedef struct grub_gui_progress_bar *grub_gui_progress_bar_t; @@ -60,6 +61,10 @@ static void progress_bar_destroy (void *vself) { grub_gui_progress_bar_t self = vself; + grub_free (self->theme_dir); + grub_free (self->template); + grub_free (self->id); + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self); } @@ -108,20 +113,26 @@ draw_filled_rect_bar (grub_gui_progress_bar_t self) f.height = self->bounds.height - 2; /* Border. */ - grub_video_fill_rect (grub_gui_map_color (self->border_color), + grub_video_fill_rect (grub_video_map_rgba_color (self->border_color), f.x - 1, f.y - 1, f.width + 2, f.height + 2); /* Bar background. */ - int barwidth = (f.width - * (self->value - self->start) - / (self->end - self->start)); - grub_video_fill_rect (grub_gui_map_color (self->bg_color), + unsigned barwidth; + + if (self->end <= self->start + || self->value <= self->start) + barwidth = 0; + else + barwidth = (f.width + * (self->value - self->start) + / (self->end - self->start)); + grub_video_fill_rect (grub_video_map_rgba_color (self->bg_color), f.x + barwidth, f.y, f.width - barwidth, f.height); /* Bar foreground. */ - grub_video_fill_rect (grub_gui_map_color (self->fg_color), + grub_video_fill_rect (grub_video_map_rgba_color (self->fg_color), f.x, f.y, barwidth, f.height); } @@ -139,28 +150,55 @@ draw_pixmap_bar (grub_gui_progress_bar_t self) int bar_b_pad = bar->get_bottom_pad (bar); int bar_h_pad = bar_l_pad + bar_r_pad; int bar_v_pad = bar_t_pad + bar_b_pad; + int hl_l_pad = hl->get_left_pad (hl); + int hl_r_pad = hl->get_right_pad (hl); + int hl_t_pad = hl->get_top_pad (hl); + int hl_b_pad = hl->get_bottom_pad (hl); + int hl_h_pad = hl_l_pad + hl_r_pad; + int hl_v_pad = hl_t_pad + hl_b_pad; int tracklen = w - bar_h_pad; int trackheight = h - bar_v_pad; int barwidth; + int hlheight = trackheight; + int hlx = bar_l_pad; + int hly = bar_t_pad; bar->set_content_size (bar, tracklen, trackheight); - - barwidth = (tracklen * (self->value - self->start) - / (self->end - self->start)); - - hl->set_content_size (hl, barwidth, h - bar_v_pad); - bar->draw (bar, 0, 0); - hl->draw (hl, bar_l_pad, bar_t_pad); + + if (self->highlight_overlay) + { + tracklen += hl_h_pad; + hlx -= hl_l_pad; + hly -= hl_t_pad; + } + else + hlheight -= hl_v_pad; + + if (self->value <= self->start + || self->end <= self->start) + barwidth = 0; + else + barwidth = ((unsigned) (tracklen * (self->value - self->start)) + / ((unsigned) (self->end - self->start))); + + if (barwidth >= hl_h_pad) + { + hl->set_content_size (hl, barwidth - hl_h_pad, hlheight); + hl->draw (hl, hlx, hly); + } } +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + static void draw_text (grub_gui_progress_bar_t self) { if (self->template) { grub_font_t font = self->font; - grub_video_color_t text_color = grub_gui_map_color (self->text_color); + grub_video_color_t text_color = + grub_video_map_rgba_color (self->text_color); int width = self->bounds.width; int height = self->bounds.height; char *text; @@ -178,9 +216,12 @@ draw_text (grub_gui_progress_bar_t self) int y = ((height - grub_font_get_descent (font)) / 2 + grub_font_get_ascent (font) / 2); grub_font_draw_string (text, font, text_color, x, y); + grub_free (text); } } +#pragma GCC diagnostic error "-Wformat-nonliteral" + static void progress_bar_paint (void *vself, const grub_video_rect_t *region) { @@ -239,29 +280,47 @@ static void progress_bar_get_minimal_size (void *vself, unsigned *width, unsigned *height) { - unsigned text_width = 0, text_height = 0; + unsigned min_width = 0; + unsigned min_height = 0; grub_gui_progress_bar_t self = vself; if (self->template) { - text_width = grub_font_get_string_width (self->font, self->template); - text_width += grub_font_get_string_width (self->font, "XXXXXXXXXX"); - text_height = grub_font_get_descent (self->font) - + grub_font_get_ascent (self->font); + min_width = grub_font_get_string_width (self->font, self->template); + min_width += grub_font_get_string_width (self->font, "XXXXXXXXXX"); + min_height = grub_font_get_descent (self->font) + + grub_font_get_ascent (self->font); + } + if (check_pixmaps (self)) + { + grub_gfxmenu_box_t bar = self->bar_box; + grub_gfxmenu_box_t hl = self->highlight_box; + min_width += bar->get_left_pad (bar) + bar->get_right_pad (bar); + min_height += bar->get_top_pad (bar) + bar->get_bottom_pad (bar); + if (!self->highlight_overlay) + { + min_width += hl->get_left_pad (hl) + hl->get_right_pad (hl); + min_height += hl->get_top_pad (hl) + hl->get_bottom_pad (hl); + } + } + else + { + min_height += 2; + min_width += 2; } *width = 200; - if (*width < text_width) - *width = text_width; + if (*width < min_width) + *width = min_width; *height = 28; - if (*height < text_height) - *height = text_height; + if (*height < min_height) + *height = min_height; } static void progress_bar_set_state (void *vself, int visible, int start, int current, int end) { - grub_gui_progress_bar_t self = vself; + grub_gui_progress_bar_t self = vself; self->visible = visible; self->start = start; self->value = current; @@ -276,7 +335,7 @@ progress_bar_set_property (void *vself, const char *name, const char *value) { grub_free (self->template); if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_LONG@") == 0) - value + value = _("The highlighted entry will be executed automatically in %ds."); else if (grub_strcmp (value, "@TIMEOUT_NOTIFICATION_MIDDLE@") == 0) /* TRANSLATORS: 's' stands for seconds. @@ -289,6 +348,9 @@ progress_bar_set_property (void *vself, const char *name, const char *value) Please use the shortest form available in you language. */ value = _("%ds"); + if (grub_printf_fmt_check(value, "%d") != GRUB_ERR_NONE) + value = ""; /* Unsupported format. */ + self->template = grub_strdup (value); } else if (grub_strcmp (name, "font") == 0) @@ -297,19 +359,19 @@ progress_bar_set_property (void *vself, const char *name, const char *value) } else if (grub_strcmp (name, "text_color") == 0) { - grub_gui_parse_color (value, &self->text_color); + grub_video_parse_color (value, &self->text_color); } else if (grub_strcmp (name, "border_color") == 0) { - grub_gui_parse_color (value, &self->border_color); + grub_video_parse_color (value, &self->border_color); } else if (grub_strcmp (name, "bg_color") == 0) { - grub_gui_parse_color (value, &self->bg_color); + grub_video_parse_color (value, &self->bg_color); } else if (grub_strcmp (name, "fg_color") == 0) { - grub_gui_parse_color (value, &self->fg_color); + grub_video_parse_color (value, &self->fg_color); } else if (grub_strcmp (name, "bar_style") == 0) { @@ -325,6 +387,10 @@ progress_bar_set_property (void *vself, const char *name, const char *value) grub_free (self->highlight_pattern); self->highlight_pattern = value ? grub_strdup (value) : 0; } + else if (grub_strcmp (name, "highlight_overlay") == 0) + { + self->highlight_overlay = grub_strcmp (value, "true") == 0; + } else if (grub_strcmp (name, "theme_dir") == 0) { self->need_to_recreate_pixmaps = 1; @@ -333,11 +399,16 @@ progress_bar_set_property (void *vself, const char *name, const char *value) } else if (grub_strcmp (name, "id") == 0) { + grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self); grub_free (self->id); if (value) self->id = grub_strdup (value); else self->id = 0; + if (self->id && grub_strcmp (self->id, GRUB_GFXMENU_TIMEOUT_COMPONENT_ID) + == 0) + grub_gfxmenu_timeout_register ((grub_gui_component_t) self, + progress_bar_set_state); } return grub_errno; } @@ -368,17 +439,19 @@ grub_gui_progress_bar_new (void) self = grub_zalloc (sizeof (*self)); if (! self) return 0; + self->progress.ops = &progress_bar_pb_ops; self->progress.component.ops = &progress_bar_ops; self->visible = 1; self->font = grub_font_get ("Unknown Regular 16"); - grub_gui_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 }; - grub_gui_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 }; - grub_gui_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 }; + grub_video_rgba_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 }; + grub_video_rgba_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 }; + grub_video_rgba_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 }; self->text_color = black; self->border_color = black; self->bg_color = gray; self->fg_color = lightgray; + self->highlight_overlay = 0; return (grub_gui_component_t) self; } diff --git a/gfxmenu/gui_string_util.c b/grub-core/gfxmenu/gui_string_util.c similarity index 62% rename from gfxmenu/gui_string_util.c rename to grub-core/gfxmenu/gui_string_util.c index 8c51e396a..ba1e1eab3 100644 --- a/gfxmenu/gui_string_util.c +++ b/grub-core/gfxmenu/gui_string_util.c @@ -55,7 +55,7 @@ canonicalize_path (const char *path) if (*p == '/') components++; - char **path_array = grub_malloc (components * sizeof (*path_array)); + char **path_array = grub_calloc (components, sizeof (*path_array)); if (! path_array) return 0; @@ -204,124 +204,3 @@ grub_get_dirname (const char *file_path) return grub_new_substring (file_path, 0, last_slash + 1); } - -static __inline int -my_isxdigit (char c) -{ - return ((c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F')); -} - -static int -parse_hex_color_component (const char *s, unsigned start, unsigned end) -{ - unsigned len; - char buf[3]; - - len = end - start; - /* Check the limits so we don't overrun the buffer. */ - if (len < 1 || len > 2) - return 0; - - if (len == 1) - { - buf[0] = s[start]; /* Get the first and only hex digit. */ - buf[1] = buf[0]; /* Duplicate the hex digit. */ - } - else if (len == 2) - { - buf[0] = s[start]; - buf[1] = s[start + 1]; - } - - buf[2] = '\0'; - - return grub_strtoul (buf, 0, 16); -} - -/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA", - "#RRGGBB", or "#RRGGBBAA". */ -grub_err_t -grub_gui_parse_color (const char *s, grub_gui_color_t *color) -{ - grub_gui_color_t c; - - /* Skip whitespace. */ - while (*s && grub_isspace (*s)) - s++; - - if (*s == '#') - { - /* HTML-style. Number if hex digits: - [6] #RRGGBB [3] #RGB - [8] #RRGGBBAA [4] #RGBA */ - - s++; /* Skip the '#'. */ - /* Count the hexits to determine the format. */ - int hexits = 0; - const char *end = s; - while (my_isxdigit (*end)) - { - end++; - hexits++; - } - - /* Parse the color components based on the format. */ - if (hexits == 3 || hexits == 4) - { - c.red = parse_hex_color_component (s, 0, 1); - c.green = parse_hex_color_component (s, 1, 2); - c.blue = parse_hex_color_component (s, 2, 3); - if (hexits == 4) - c.alpha = parse_hex_color_component (s, 3, 4); - else - c.alpha = 255; - } - else if (hexits == 6 || hexits == 8) - { - c.red = parse_hex_color_component (s, 0, 2); - c.green = parse_hex_color_component (s, 2, 4); - c.blue = parse_hex_color_component (s, 4, 6); - if (hexits == 8) - c.alpha = parse_hex_color_component (s, 6, 8); - else - c.alpha = 255; - } - else - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "invalid HTML-type color string `%s'", s); - } - else if (grub_isdigit (*s)) - { - /* Comma separated decimal values. */ - c.red = grub_strtoul (s, 0, 0); - if ((s = grub_strchr (s, ',')) == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "missing 1st comma separator in color `%s'", s); - s++; - c.green = grub_strtoul (s, 0, 0); - if ((s = grub_strchr (s, ',')) == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "missing 2nd comma separator in color `%s'", s); - s++; - c.blue = grub_strtoul (s, 0, 0); - if ((s = grub_strchr (s, ',')) == 0) - c.alpha = 255; - else - { - s++; - c.alpha = grub_strtoul (s, 0, 0); - } - } - else - { - if (! grub_gui_get_named_color (s, &c)) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "invalid named color `%s'", s); - } - - if (grub_errno == GRUB_ERR_NONE) - *color = c; - return grub_errno; -} diff --git a/gfxmenu/gui_util.c b/grub-core/gfxmenu/gui_util.c similarity index 100% rename from gfxmenu/gui_util.c rename to grub-core/gfxmenu/gui_util.c diff --git a/gfxmenu/icon_manager.c b/grub-core/gfxmenu/icon_manager.c similarity index 93% rename from gfxmenu/icon_manager.c rename to grub-core/gfxmenu/icon_manager.c index 0c304ede0..1894682ef 100644 --- a/gfxmenu/icon_manager.c +++ b/grub-core/gfxmenu/icon_manager.c @@ -106,8 +106,10 @@ grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr, const char *path) { /* Clear the cache if the theme path has changed. */ - if (((mgr->theme_path == 0) != (path == 0)) - || (grub_strcmp (mgr->theme_path, path) != 0)) + if (mgr->theme_path == 0 && path == 0) + return; + if (mgr->theme_path == 0 || path == 0 + || grub_strcmp (mgr->theme_path, path) != 0) grub_gfxmenu_icon_manager_clear_cache (mgr); grub_free (mgr->theme_path); @@ -137,23 +139,19 @@ static struct grub_video_bitmap * try_loading_icon (grub_gfxmenu_icon_manager_t mgr, const char *dir, const char *class_name) { - char *path; - int l; + char *path, *ptr; path = grub_malloc (grub_strlen (dir) + grub_strlen (class_name) + grub_strlen (icon_extension) + 3); if (! path) return 0; - grub_strcpy (path, dir); - l = grub_strlen (path); - if (path[l-1] != '/') - { - path[l] = '/'; - path[l+1] = 0; - } - grub_strcat (path, class_name); - grub_strcat (path, icon_extension); + ptr = grub_stpcpy (path, dir); + if (path == ptr || ptr[-1] != '/') + *ptr++ = '/'; + ptr = grub_stpcpy (ptr, class_name); + ptr = grub_stpcpy (ptr, icon_extension); + *ptr = '\0'; struct grub_video_bitmap *raw_bitmap; grub_video_bitmap_load (&raw_bitmap, path); @@ -169,11 +167,7 @@ try_loading_icon (grub_gfxmenu_icon_manager_t mgr, GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); grub_video_bitmap_destroy (raw_bitmap); if (! scaled_bitmap) - { - grub_error_push (); - grub_error (grub_errno, "failed to scale icon"); - return 0; - } + return 0; return scaled_bitmap; } @@ -257,7 +251,7 @@ grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr, /* Try each class in succession. */ icon = 0; - for (c = entry->classes->next; c && ! icon; c = c->next) + for (c = entry->classes; c && ! icon; c = c->next) icon = get_icon_by_class (mgr, c->name); return icon; } diff --git a/gfxmenu/theme_loader.c b/grub-core/gfxmenu/theme_loader.c similarity index 77% rename from gfxmenu/theme_loader.c rename to grub-core/gfxmenu/theme_loader.c index 3854c6c53..9eabf501a 100644 --- a/gfxmenu/theme_loader.c +++ b/grub-core/gfxmenu/theme_loader.c @@ -30,6 +30,10 @@ #include #include #include +#include + +static grub_err_t +parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop); /* Construct a new box widget using ABSPATTERN to find the pixmap files for it, storing the new box instance at *BOXPTR. @@ -112,6 +116,24 @@ grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr, return grub_errno; } +static grub_err_t +theme_get_unsigned_int_from_proportional (const char *value, + unsigned absolute_value, + unsigned int *parsed_value) +{ + grub_err_t err; + grub_fixed_signed_t frac; + signed pixels; + err = parse_proportional_spec (value, &pixels, &frac); + if (err != GRUB_ERR_NONE) + return err; + int result = grub_fixed_sfs_multiply (absolute_value, frac) + pixels; + if (result < 0) + result = 0; + *parsed_value = result; + return GRUB_ERR_NONE; +} + /* Set the specified property NAME on the view to the given string VALUE. The caller is responsible for the lifetimes of NAME and VALUE. */ static grub_err_t @@ -135,15 +157,14 @@ theme_set_string (grub_gfxmenu_view_t view, return grub_errno; } else if (! grub_strcmp ("title-color", name)) - grub_gui_parse_color (value, &view->title_color); + grub_video_parse_color (value, &view->title_color); else if (! grub_strcmp ("message-color", name)) - grub_gui_parse_color (value, &view->message_color); + grub_video_parse_color (value, &view->message_color); else if (! grub_strcmp ("message-bg-color", name)) - grub_gui_parse_color (value, &view->message_bg_color); + grub_video_parse_color (value, &view->message_bg_color); else if (! grub_strcmp ("desktop-image", name)) { struct grub_video_bitmap *raw_bitmap; - struct grub_video_bitmap *scaled_bitmap; char *path; path = grub_resolve_relative_path (theme_dir, value); if (! path) @@ -154,23 +175,59 @@ theme_set_string (grub_gfxmenu_view_t view, return grub_errno; } grub_free(path); - grub_video_bitmap_create_scaled (&scaled_bitmap, - view->screen.width, - view->screen.height, - raw_bitmap, - GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); - grub_video_bitmap_destroy (raw_bitmap); - if (! scaled_bitmap) - { - grub_error_push (); - return grub_error (grub_errno, "error scaling desktop image"); - } - - grub_video_bitmap_destroy (view->desktop_image); - view->desktop_image = scaled_bitmap; + grub_video_bitmap_destroy (view->raw_desktop_image); + view->raw_desktop_image = raw_bitmap; + } + else if (! grub_strcmp ("desktop-image-scale-method", name)) + { + if (! value || ! grub_strcmp ("stretch", value)) + view->desktop_image_scale_method = + GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH; + else if (! grub_strcmp ("crop", value)) + view->desktop_image_scale_method = + GRUB_VIDEO_BITMAP_SELECTION_METHOD_CROP; + else if (! grub_strcmp ("padding", value)) + view->desktop_image_scale_method = + GRUB_VIDEO_BITMAP_SELECTION_METHOD_PADDING; + else if (! grub_strcmp ("fitwidth", value)) + view->desktop_image_scale_method = + GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITWIDTH; + else if (! grub_strcmp ("fitheight", value)) + view->desktop_image_scale_method = + GRUB_VIDEO_BITMAP_SELECTION_METHOD_FITHEIGHT; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported scale method: %s", + value); + } + else if (! grub_strcmp ("desktop-image-h-align", name)) + { + if (! grub_strcmp ("left", value)) + view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_LEFT; + else if (! grub_strcmp ("center", value)) + view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER; + else if (! grub_strcmp ("right", value)) + view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_RIGHT; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported horizontal align method: %s", + value); + } + else if (! grub_strcmp ("desktop-image-v-align", name)) + { + if (! grub_strcmp ("top", value)) + view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_TOP; + else if (! grub_strcmp ("center", value)) + view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER; + else if (! grub_strcmp ("bottom", value)) + view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_BOTTOM; + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "Unsupported vertical align method: %s", + value); } else if (! grub_strcmp ("desktop-color", name)) - grub_gui_parse_color (value, &view->desktop_color); + grub_video_parse_color (value, &view->desktop_color); else if (! grub_strcmp ("terminal-box", name)) { grub_err_t err; @@ -178,6 +235,52 @@ theme_set_string (grub_gfxmenu_view_t view, if (err != GRUB_ERR_NONE) return err; } + else if (! grub_strcmp ("terminal-border", name)) + { + view->terminal_border = grub_strtoul (value, 0, 10); + if (grub_errno) + return grub_errno; + } + else if (! grub_strcmp ("terminal-left", name)) + { + unsigned int tmp; + int err = theme_get_unsigned_int_from_proportional (value, + view->screen.width, + &tmp); + if (err != GRUB_ERR_NONE) + return err; + view->terminal_rect.x = tmp; + } + else if (! grub_strcmp ("terminal-top", name)) + { + unsigned int tmp; + int err = theme_get_unsigned_int_from_proportional (value, + view->screen.height, + &tmp); + if (err != GRUB_ERR_NONE) + return err; + view->terminal_rect.y = tmp; + } + else if (! grub_strcmp ("terminal-width", name)) + { + unsigned int tmp; + int err = theme_get_unsigned_int_from_proportional (value, + view->screen.width, + &tmp); + if (err != GRUB_ERR_NONE) + return err; + view->terminal_rect.width = tmp; + } + else if (! grub_strcmp ("terminal-height", name)) + { + unsigned int tmp; + int err = theme_get_unsigned_int_from_proportional (value, + view->screen.height, + &tmp); + if (err != GRUB_ERR_NONE) + return err; + view->terminal_rect.height = tmp; + } else if (! grub_strcmp ("title-text", name)) { grub_free (view->title_text); @@ -316,7 +419,7 @@ read_expression (struct parsebuf *p) skip_whitespace (p); if (peek_char (p) == '"') { - /* Read as a quoted string. + /* Read as a quoted string. The quotation marks are not included in the expression value. */ /* Skip opening quotation mark. */ read_char (p); @@ -362,10 +465,10 @@ read_expression (struct parsebuf *p) } static grub_err_t -parse_proportional_spec (char *value, signed *abs, grub_fixed_signed_t *prop) +parse_proportional_spec (const char *value, signed *abs, grub_fixed_signed_t *prop) { signed num; - char *ptr; + const char *ptr; int sig = 0; *abs = 0; *prop = 0; @@ -640,7 +743,7 @@ grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) p.view = view; p.theme_dir = grub_get_dirname (theme_path); - file = grub_file_open (theme_path); + file = grub_file_open (theme_path, GRUB_FILE_TYPE_THEME); if (! file) { grub_free (p.theme_dir); @@ -671,6 +774,8 @@ grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) view->canvas->component.ops->destroy (view->canvas); view->canvas = grub_gui_canvas_new (); + if (!view->canvas) + goto fail; ((grub_gui_component_t) view->canvas) ->ops->set_bounds ((grub_gui_component_t) view->canvas, &view->screen); diff --git a/gfxmenu/view.c b/grub-core/gfxmenu/view.c similarity index 50% rename from gfxmenu/view.c rename to grub-core/gfxmenu/view.c index bf637a96d..e02eba8b0 100644 --- a/gfxmenu/view.c +++ b/grub-core/gfxmenu/view.c @@ -36,14 +36,12 @@ #include #include #include - -/* The component ID identifying GUI components to be updated as the timeout - status changes. */ -#define TIMEOUT_COMPONENT_ID "__timeout__" +#include static void init_terminal (grub_gfxmenu_view_t view); -static grub_video_rect_t term_rect; +static void +init_background (grub_gfxmenu_view_t view); static grub_gfxmenu_view_t term_view; /* Create a new view object, loading the theme specified by THEME_PATH and @@ -54,21 +52,38 @@ grub_gfxmenu_view_new (const char *theme_path, { grub_gfxmenu_view_t view; grub_font_t default_font; - grub_gui_color_t default_fg_color; - grub_gui_color_t default_bg_color; + grub_video_rgba_color_t default_fg_color; + grub_video_rgba_color_t default_bg_color; view = grub_malloc (sizeof (*view)); if (! view) return 0; + while (grub_gfxmenu_timeout_notifications) + { + struct grub_gfxmenu_timeout_notify *p; + p = grub_gfxmenu_timeout_notifications; + grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next; + grub_free (p); + } + view->screen.x = 0; view->screen.y = 0; view->screen.width = width; view->screen.height = height; + view->need_to_check_sanity = 1; + view->terminal_border = 3; + view->terminal_rect.width = view->screen.width * 7 / 10; + view->terminal_rect.height = view->screen.height * 7 / 10; + view->terminal_rect.x = view->screen.x + (view->screen.width + - view->terminal_rect.width) / 2; + view->terminal_rect.y = view->screen.y + (view->screen.height + - view->terminal_rect.height) / 2; + default_font = grub_font_get ("Unknown Regular 16"); - default_fg_color = grub_gui_color_rgb (0, 0, 0); - default_bg_color = grub_gui_color_rgb (255, 255, 255); + default_fg_color = grub_video_rgba_color_rgb (0, 0, 0); + default_bg_color = grub_video_rgba_color_rgb (255, 255, 255); view->canvas = 0; @@ -78,10 +93,14 @@ grub_gfxmenu_view_new (const char *theme_path, view->title_color = default_fg_color; view->message_color = default_bg_color; view->message_bg_color = default_fg_color; - view->desktop_image = 0; + view->raw_desktop_image = 0; + view->scaled_desktop_image = 0; + view->desktop_image_scale_method = GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH; + view->desktop_image_h_align = GRUB_VIDEO_BITMAP_H_ALIGN_CENTER; + view->desktop_image_v_align = GRUB_VIDEO_BITMAP_V_ALIGN_CENTER; view->desktop_color = default_bg_color; view->terminal_box = grub_gfxmenu_create_box (0, 0); - view->title_text = grub_strdup ("GRUB Boot Menu"); + view->title_text = grub_strdup (_("GRUB Boot Menu")); view->progress_message_text = 0; view->theme_path = 0; @@ -108,7 +127,15 @@ grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) { if (!view) return; - grub_video_bitmap_destroy (view->desktop_image); + while (grub_gfxmenu_timeout_notifications) + { + struct grub_gfxmenu_timeout_notify *p; + p = grub_gfxmenu_timeout_notifications; + grub_gfxmenu_timeout_notifications = grub_gfxmenu_timeout_notifications->next; + grub_free (p); + } + grub_video_bitmap_destroy (view->raw_desktop_image); + grub_video_bitmap_destroy (view->scaled_desktop_image); if (view->terminal_box) view->terminal_box->destroy (view->terminal_box); grub_free (view->terminal_font_name); @@ -124,9 +151,9 @@ static void redraw_background (grub_gfxmenu_view_t view, const grub_video_rect_t *bounds) { - if (view->desktop_image) + if (view->scaled_desktop_image) { - struct grub_video_bitmap *img = view->desktop_image; + struct grub_video_bitmap *img = view->scaled_desktop_image; grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, bounds->x, bounds->y, bounds->x - view->screen.x, @@ -135,7 +162,7 @@ redraw_background (grub_gfxmenu_view_t view, } else { - grub_video_fill_rect (grub_gui_map_color (view->desktop_color), + grub_video_fill_rect (grub_video_map_rgba_color (view->desktop_color), bounds->x, bounds->y, bounds->width, bounds->height); } @@ -154,7 +181,7 @@ draw_title (grub_gfxmenu_view_t view) int y = 40 + grub_font_get_ascent (view->title_font); grub_font_draw_string (view->title_text, view->title_font, - grub_gui_map_color (view->title_color), + grub_video_map_rgba_color (view->title_color), x, y); } @@ -166,84 +193,56 @@ struct progress_value_data int value; }; -static void -update_timeout_visit (grub_gui_component_t component, - void *userdata) -{ - struct progress_value_data *pv; - pv = (struct progress_value_data *) userdata; +struct grub_gfxmenu_timeout_notify *grub_gfxmenu_timeout_notifications; - ((struct grub_gui_progress *) component)->ops - ->set_state ((struct grub_gui_progress *) component, - pv->visible, pv->start, pv->value, pv->end); +static void +update_timeouts (int visible, int start, int value, int end) +{ + struct grub_gfxmenu_timeout_notify *cur; + + for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next) + cur->set_state (cur->self, visible, start, value, end); } -void +static void +redraw_timeouts (struct grub_gfxmenu_view *view) +{ + struct grub_gfxmenu_timeout_notify *cur; + + for (cur = grub_gfxmenu_timeout_notifications; cur; cur = cur->next) + { + grub_video_rect_t bounds; + cur->self->ops->get_bounds (cur->self, &bounds); + grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED); + grub_gfxmenu_view_redraw (view, &bounds); + } +} + +void grub_gfxmenu_print_timeout (int timeout, void *data) { struct grub_gfxmenu_view *view = data; - struct progress_value_data pv; - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))); - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))) - { - grub_video_rect_t bounds; - component->ops->get_bounds (component, &bounds); - grub_gfxmenu_view_redraw (view, &bounds); - } - if (view->first_timeout == -1) view->first_timeout = timeout; - pv.visible = 1; - pv.start = -(view->first_timeout + 1); - pv.end = 0; - pv.value = -timeout; - - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + update_timeouts (1, -view->first_timeout, -timeout, 0); + redraw_timeouts (view); grub_video_swap_buffers (); if (view->double_repaint) - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + redraw_timeouts (view); } -void +void grub_gfxmenu_clear_timeout (void *data) { - struct progress_value_data pv; struct grub_gfxmenu_view *view = data; - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))); - - auto void redraw_timeout_visit (grub_gui_component_t component, - void *userdata __attribute__ ((unused))) - { - grub_video_rect_t bounds; - component->ops->get_bounds (component, &bounds); - grub_gfxmenu_view_redraw (view, &bounds); - } - - pv.visible = 0; - pv.start = 1; - pv.end = 0; - pv.value = 0; - - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv); - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + update_timeouts (0, 1, 0, 0); + redraw_timeouts (view); grub_video_swap_buffers (); if (view->double_repaint) - grub_gui_find_by_id ((grub_gui_component_t) view->canvas, - TIMEOUT_COMPONENT_ID, redraw_timeout_visit, &pv); + redraw_timeouts (view); } static void @@ -268,6 +267,27 @@ update_menu_components (grub_gfxmenu_view_t view) update_menu_visit, view); } +static void +refresh_menu_visit (grub_gui_component_t component, + void *userdata) +{ + grub_gfxmenu_view_t view; + view = userdata; + if (component->ops->is_instance (component, "list")) + { + grub_gui_list_t list = (grub_gui_list_t) component; + list->ops->refresh_list (list, view); + } +} + +/* Refresh list information (useful for submenus) */ +static void +refresh_menu_components (grub_gfxmenu_view_t view) +{ + grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas, + refresh_menu_visit, view); +} + static void draw_message (grub_gfxmenu_view_t view) { @@ -277,13 +297,13 @@ draw_message (grub_gfxmenu_view_t view) return; grub_font_t font = view->message_font; - grub_video_color_t color = grub_gui_map_color (view->message_color); + grub_video_color_t color = grub_video_map_rgba_color (view->message_color); /* Border. */ grub_video_fill_rect (color, f.x-1, f.y-1, f.width+2, f.height+2); /* Fill. */ - grub_video_fill_rect (grub_gui_map_color (view->message_bg_color), + grub_video_fill_rect (grub_video_map_rgba_color (view->message_bg_color), f.x, f.y, f.width, f.height); /* Center the text. */ @@ -298,10 +318,15 @@ void grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, const grub_video_rect_t *region) { - if (grub_video_have_common_points (&term_rect, region)) + if (grub_video_have_common_points (&view->terminal_rect, region)) grub_gfxterm_schedule_repaint (); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_area_status_t area_status; + grub_video_get_area_status (&area_status); + if (area_status == GRUB_VIDEO_AREA_ENABLED) + grub_video_set_region (region->x, region->y, + region->width, region->height); redraw_background (view, region); if (view->canvas) @@ -309,6 +334,9 @@ grub_gfxmenu_view_redraw (grub_gfxmenu_view_t view, draw_title (view); if (grub_video_have_common_points (&view->progress_message_frame, region)) draw_message (view); + + if (area_status == GRUB_VIDEO_AREA_ENABLED) + grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED); } void @@ -316,6 +344,8 @@ grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) { init_terminal (view); + init_background (view); + /* Clear the screen; there may be garbage left over in video memory. */ grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), view->screen.x, view->screen.y, @@ -326,12 +356,18 @@ grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) view->screen.x, view->screen.y, view->screen.width, view->screen.height); + refresh_menu_components (view); update_menu_components (view); + grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED); grub_gfxmenu_view_redraw (view, &view->screen); grub_video_swap_buffers (); if (view->double_repaint) - grub_gfxmenu_view_redraw (view, &view->screen); + { + grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED); + grub_gfxmenu_view_redraw (view, &view->screen); + } + } static void @@ -342,11 +378,10 @@ redraw_menu_visit (grub_gui_component_t component, view = userdata; if (component->ops->is_instance (component, "list")) { - grub_gui_list_t list; grub_video_rect_t bounds; - list = (grub_gui_list_t) component; component->ops->get_bounds (component, &bounds); + grub_video_set_area_status (GRUB_VIDEO_AREA_ENABLED); grub_gfxmenu_view_redraw (view, &bounds); } } @@ -366,7 +401,7 @@ grub_gfxmenu_redraw_menu (grub_gfxmenu_view_t view) } } -void +void grub_gfxmenu_set_chosen_entry (int entry, void *data) { grub_gfxmenu_view_t view = data; @@ -383,41 +418,175 @@ grub_gfxmenu_draw_terminal_box (void) term_box = term_view->terminal_box; if (!term_box) return; - - term_box->set_content_size (term_box, term_rect.width, - term_rect.height); - + + grub_video_set_area_status (GRUB_VIDEO_AREA_DISABLED); + + term_box->set_content_size (term_box, term_view->terminal_rect.width, + term_view->terminal_rect.height); + term_box->draw (term_box, - term_rect.x - term_box->get_left_pad (term_box), - term_rect.y - term_box->get_top_pad (term_box)); - grub_video_swap_buffers (); - if (term_view->double_repaint) - term_box->draw (term_box, - term_rect.x - term_box->get_left_pad (term_box), - term_rect.y - term_box->get_top_pad (term_box)); + term_view->terminal_rect.x - term_box->get_left_pad (term_box), + term_view->terminal_rect.y - term_box->get_top_pad (term_box)); +} + +static void +get_min_terminal (grub_font_t terminal_font, + unsigned int border_width, + unsigned int *min_terminal_width, + unsigned int *min_terminal_height) +{ + struct grub_font_glyph *glyph; + glyph = grub_font_get_glyph (terminal_font, 'M'); + *min_terminal_width = (glyph? glyph->device_width : 8) * 80 + + 2 * border_width; + *min_terminal_height = grub_font_get_max_char_height (terminal_font) * 24 + + 2 * border_width; +} + +static void +terminal_sanity_check (grub_gfxmenu_view_t view) +{ + if (!view->need_to_check_sanity) + return; + + /* terminal_font was checked before in the init_terminal function. */ + grub_font_t terminal_font = grub_font_get (view->terminal_font_name); + + /* Non-negative numbers below. */ + int scr_x = view->screen.x; + int scr_y = view->screen.y; + int scr_width = view->screen.width; + int scr_height = view->screen.height; + int term_x = view->terminal_rect.x; + int term_y = view->terminal_rect.y; + int term_width = view->terminal_rect.width; + int term_height = view->terminal_rect.height; + + /* Check that border_width isn't too big. */ + unsigned int border_width = view->terminal_border; + unsigned int min_terminal_width; + unsigned int min_terminal_height; + get_min_terminal (terminal_font, border_width, + &min_terminal_width, &min_terminal_height); + if (border_width > 3 && ((int) min_terminal_width >= scr_width + || (int) min_terminal_height >= scr_height)) + { + border_width = 3; + get_min_terminal (terminal_font, border_width, + &min_terminal_width, &min_terminal_height); + } + + /* Sanity checks. */ + if (term_width > scr_width) + term_width = scr_width; + if (term_height > scr_height) + term_height = scr_height; + + if (scr_width <= (int) min_terminal_width + || scr_height <= (int) min_terminal_height) + { + /* The screen resulution is too low. Use all space, except a small border + to show the user, that it is a window. Then center the window. */ + term_width = scr_width - 6 * border_width; + term_height = scr_height - 6 * border_width; + term_x = scr_x + (scr_width - term_width) / 2; + term_y = scr_y + (scr_height - term_height) / 2; + } + else if (term_width < (int) min_terminal_width + || term_height < (int) min_terminal_height) + { + /* The screen resolution is big enough. Make sure, that terminal screen + dimensions aren't less than minimal values. Then center the window. */ + term_width = (int) min_terminal_width; + term_height = (int) min_terminal_height; + term_x = scr_x + (scr_width - term_width) / 2; + term_y = scr_y + (scr_height - term_height) / 2; + } + + /* At this point w and h are satisfying. */ + if (term_x + term_width > scr_width) + term_x = scr_width - term_width; + if (term_y + term_height > scr_height) + term_y = scr_height - term_height; + + /* Write down corrected data. */ + view->terminal_rect.x = (unsigned int) term_x; + view->terminal_rect.y = (unsigned int) term_y; + view->terminal_rect.width = (unsigned int) term_width; + view->terminal_rect.height = (unsigned int) term_height; + view->terminal_border = border_width; + + view->need_to_check_sanity = 0; } static void init_terminal (grub_gfxmenu_view_t view) { - term_rect.width = view->screen.width * 7 / 10; - term_rect.height = view->screen.height * 7 / 10; + grub_font_t terminal_font; - term_rect.x = view->screen.x + view->screen.width * (10 - 7) / 10 / 2; - term_rect.y = view->screen.y + view->screen.height * (10 - 7) / 10 / 2; + terminal_font = grub_font_get (view->terminal_font_name); + if (!terminal_font) + { + grub_error (GRUB_ERR_BAD_FONT, "no font loaded"); + return; + } + + /* Check that terminal window size and position are sane. */ + terminal_sanity_check (view); term_view = view; /* Note: currently there is no API for changing the gfxterm font on the fly, so whatever font the initially loaded theme specifies will be permanent. */ - grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, term_rect.x, - term_rect.y, - term_rect.width, term_rect.height, - view->double_repaint, view->terminal_font_name, 3); + grub_gfxterm_set_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, + view->terminal_rect.x, + view->terminal_rect.y, + view->terminal_rect.width, + view->terminal_rect.height, + view->double_repaint, + terminal_font, + view->terminal_border); grub_gfxterm_decorator_hook = grub_gfxmenu_draw_terminal_box; } +static void +init_background (grub_gfxmenu_view_t view) +{ + struct grub_video_bitmap *scaled_bitmap; + + /* + * You don't have to scale a raw image if it's not present. This prevents + * setting grub_errno and disrupting a command execution. + */ + if (view->raw_desktop_image == NULL) + return; + + if (view->scaled_desktop_image) + return; + + if (view->desktop_image_scale_method == + GRUB_VIDEO_BITMAP_SELECTION_METHOD_STRETCH) + grub_video_bitmap_create_scaled (&scaled_bitmap, + view->screen.width, + view->screen.height, + view->raw_desktop_image, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + else + grub_video_bitmap_scale_proportional (&scaled_bitmap, + view->screen.width, + view->screen.height, + view->raw_desktop_image, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST, + view->desktop_image_scale_method, + view->desktop_image_v_align, + view->desktop_image_h_align); + if (! scaled_bitmap) + return; + view->scaled_desktop_image = scaled_bitmap; + +} + /* FIXME: previously notifications were displayed in special case. Is it necessary? */ diff --git a/gfxmenu/widget-box.c b/grub-core/gfxmenu/widget-box.c similarity index 80% rename from gfxmenu/widget-box.c rename to grub-core/gfxmenu/widget-box.c index 079fd66d4..470597ded 100644 --- a/gfxmenu/widget-box.c +++ b/grub-core/gfxmenu/widget-box.c @@ -79,22 +79,26 @@ static void draw (grub_gfxmenu_box_t self, int x, int y) { int height_n; - int height_s; - int height_e; - int height_w; - int width_n; - int width_s; - int width_e; int width_w; + int tmp; - height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); - height_s = get_height (self->scaled_pixmaps[BOX_PIXMAP_S]); - height_e = get_height (self->scaled_pixmaps[BOX_PIXMAP_E]); - height_w = get_height (self->scaled_pixmaps[BOX_PIXMAP_W]); - width_n = get_width (self->scaled_pixmaps[BOX_PIXMAP_N]); - width_s = get_width (self->scaled_pixmaps[BOX_PIXMAP_S]); - width_e = get_width (self->scaled_pixmaps[BOX_PIXMAP_E]); - width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + /* Count maximum height of NW, N, NE. */ + height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_NW]); + tmp = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); + if (tmp > height_n) + height_n = tmp; + tmp = get_height (self->scaled_pixmaps[BOX_PIXMAP_NE]); + if (tmp > height_n) + height_n = tmp; + + /* Count maximum width of NW, W, SW. */ + width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_NW]); + tmp = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + if (tmp > width_w) + width_w = tmp; + tmp = get_width (self->scaled_pixmaps[BOX_PIXMAP_SW]); + if (tmp > width_w) + width_w = tmp; /* Draw sides. */ blit (self, BOX_PIXMAP_N, x + width_w, y); @@ -142,12 +146,6 @@ scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h) if (w != 0 && h != 0) grub_video_bitmap_create_scaled (scaled, w, h, raw, GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); - if (grub_errno != GRUB_ERR_NONE) - { - grub_error_push (); - grub_error (grub_errno, - "failed to scale bitmap for styled box pixmap #%d", i); - } } return grub_errno; @@ -190,28 +188,75 @@ set_content_size (grub_gfxmenu_box_t self, return; } +static int +get_border_width (grub_gfxmenu_box_t self) +{ + return (get_width (self->raw_pixmaps[BOX_PIXMAP_E]) + + get_width (self->raw_pixmaps[BOX_PIXMAP_W])); +} + static int get_left_pad (grub_gfxmenu_box_t self) { - return get_width (self->raw_pixmaps[BOX_PIXMAP_W]); + int v, c; + + v = get_width (self->raw_pixmaps[BOX_PIXMAP_W]); + c = get_width (self->raw_pixmaps[BOX_PIXMAP_NW]); + if (c > v) + v = c; + c = get_width (self->raw_pixmaps[BOX_PIXMAP_SW]); + if (c > v) + v = c; + + return v; } static int get_top_pad (grub_gfxmenu_box_t self) { - return get_height (self->raw_pixmaps[BOX_PIXMAP_N]); + int v, c; + + v = get_height (self->raw_pixmaps[BOX_PIXMAP_N]); + c = get_height (self->raw_pixmaps[BOX_PIXMAP_NW]); + if (c > v) + v = c; + c = get_height (self->raw_pixmaps[BOX_PIXMAP_NE]); + if (c > v) + v = c; + + return v; } static int get_right_pad (grub_gfxmenu_box_t self) { - return get_width (self->raw_pixmaps[BOX_PIXMAP_E]); + int v, c; + + v = get_width (self->raw_pixmaps[BOX_PIXMAP_E]); + c = get_width (self->raw_pixmaps[BOX_PIXMAP_NE]); + if (c > v) + v = c; + c = get_width (self->raw_pixmaps[BOX_PIXMAP_SE]); + if (c > v) + v = c; + + return v; } static int get_bottom_pad (grub_gfxmenu_box_t self) { - return get_height (self->raw_pixmaps[BOX_PIXMAP_S]); + int v, c; + + v = get_height (self->raw_pixmaps[BOX_PIXMAP_S]); + c = get_height (self->raw_pixmaps[BOX_PIXMAP_SW]); + if (c > v) + v = c; + c = get_height (self->raw_pixmaps[BOX_PIXMAP_SE]); + if (c > v) + v = c; + + return v; } static void @@ -258,10 +303,10 @@ grub_gfxmenu_create_box (const char *pixmaps_prefix, box->content_height = 0; box->raw_pixmaps = (struct grub_video_bitmap **) - grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *)); box->scaled_pixmaps = (struct grub_video_bitmap **) - grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *)); /* Initialize all pixmap pointers to NULL so that proper destruction can be performed if an error is encountered partway through construction. */ @@ -300,6 +345,8 @@ grub_gfxmenu_create_box (const char *pixmaps_prefix, box->draw = draw; box->set_content_size = set_content_size; + box->get_border_width = get_border_width; + box->get_left_pad = get_left_pad; box->get_top_pad = get_top_pad; box->get_right_pad = get_right_pad; diff --git a/hello/hello.c b/grub-core/hello/hello.c similarity index 83% rename from hello/hello.c rename to grub-core/hello/hello.c index eff07d941..456b7c322 100644 --- a/hello/hello.c +++ b/grub-core/hello/hello.c @@ -26,12 +26,14 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static grub_err_t -grub_cmd_hello (struct grub_extcmd *cmd __attribute__ ((unused)), +grub_cmd_hello (grub_extcmd_context_t ctxt __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - grub_printf ("Hello World\n"); + grub_printf ("%s\n", _("Hello World")); return 0; } @@ -39,8 +41,8 @@ static grub_extcmd_t cmd; GRUB_MOD_INIT(hello) { - cmd = grub_register_extcmd ("hello", grub_cmd_hello, GRUB_COMMAND_FLAG_BOTH, - 0, N_("Say \"Hello World\"."), 0); + cmd = grub_register_extcmd ("hello", grub_cmd_hello, 0, 0, + N_("Say `Hello World'."), 0); } GRUB_MOD_FINI(hello) diff --git a/hook/datehook.c b/grub-core/hook/datehook.c similarity index 81% rename from hook/datehook.c rename to grub-core/hook/datehook.c index 4876e1198..ac75908ef 100644 --- a/hook/datehook.c +++ b/grub-core/hook/datehook.c @@ -24,7 +24,9 @@ #include #include -static char *grub_datetime_names[] = +GRUB_MOD_LICENSE ("GPLv3+"); + +static const char *grub_datetime_names[] = { "YEAR", "MONTH", @@ -35,7 +37,7 @@ static char *grub_datetime_names[] = "WEEKDAY", }; -static char * +static const char * grub_read_hook_datetime (struct grub_env_var *var, const char *val __attribute__ ((unused))) { @@ -48,7 +50,7 @@ grub_read_hook_datetime (struct grub_env_var *var, int i; for (i = 0; i < 7; i++) - if (! grub_strcmp (var->name, grub_datetime_names[i])) + if (grub_strcmp (var->name, grub_datetime_names[i]) == 0) { int n; @@ -84,20 +86,23 @@ grub_read_hook_datetime (struct grub_env_var *var, return buf; } -GRUB_MOD_INIT(datetime) +GRUB_MOD_INIT(datehook) { - int i; + unsigned i; - for (i = 0; i < 7; i++) - grub_register_variable_hook (grub_datetime_names[i], - grub_read_hook_datetime, 0); + for (i = 0; i < ARRAY_SIZE (grub_datetime_names); i++) + { + grub_register_variable_hook (grub_datetime_names[i], + grub_read_hook_datetime, 0); + grub_env_export (grub_datetime_names[i]); + } } -GRUB_MOD_FINI(datetime) +GRUB_MOD_FINI(datehook) { - int i; + unsigned i; - for (i = 0; i < 7; i++) + for (i = 0; i < ARRAY_SIZE (grub_datetime_names); i++) { grub_register_variable_hook (grub_datetime_names[i], 0, 0); grub_env_unset (grub_datetime_names[i]); diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c new file mode 100644 index 000000000..a458c3aca --- /dev/null +++ b/grub-core/io/bufio.c @@ -0,0 +1,214 @@ +/* bufio.c - buffered io access */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_BUFIO_DEF_SIZE 8192 +#define GRUB_BUFIO_MAX_SIZE 1048576 + +struct grub_bufio +{ + grub_file_t file; + grub_size_t block_size; + grub_size_t buffer_len; + grub_off_t buffer_at; + char buffer[0]; +}; +typedef struct grub_bufio *grub_bufio_t; + +static struct grub_fs grub_bufio_fs; + +grub_file_t +grub_bufio_open (grub_file_t io, grub_size_t size) +{ + grub_file_t file; + grub_bufio_t bufio = 0; + + file = (grub_file_t) grub_zalloc (sizeof (*file)); + if (! file) + return 0; + + if (size == 0) + size = GRUB_BUFIO_DEF_SIZE; + else if (size > GRUB_BUFIO_MAX_SIZE) + size = GRUB_BUFIO_MAX_SIZE; + + if (size > io->size) + size = ((io->size > GRUB_BUFIO_MAX_SIZE) ? GRUB_BUFIO_MAX_SIZE : + io->size); + + /* + * Round up size to power of 2 which the binary math to + * calculate next_buf in grub_bufio_read() requires. + */ + while (size & (size - 1)) + size = (size | (size - 1)) + 1; + + bufio = grub_zalloc (sizeof (struct grub_bufio) + size); + if (! bufio) + { + grub_free (file); + return 0; + } + + bufio->file = io; + bufio->block_size = size; + + file->device = io->device; + file->size = io->size; + file->data = bufio; + file->fs = &grub_bufio_fs; + file->not_easily_seekable = io->not_easily_seekable; + + return file; +} + +grub_file_t +grub_buffile_open (const char *name, enum grub_file_type type, grub_size_t size) +{ + grub_file_t io, file; + + io = grub_file_open (name, type); + if (! io) + return 0; + + file = grub_bufio_open (io, size); + if (! file) + { + grub_file_close (io); + return 0; + } + + return file; +} + +static grub_ssize_t +grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_size_t res = 0; + grub_off_t next_buf; + grub_bufio_t bufio = file->data; + grub_ssize_t really_read; + + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + /* First part: use whatever we already have in the buffer. */ + if ((file->offset >= bufio->buffer_at) && + (file->offset < bufio->buffer_at + bufio->buffer_len)) + { + grub_size_t n; + grub_uint64_t pos; + + pos = file->offset - bufio->buffer_at; + n = bufio->buffer_len - pos; + if (n > len) + n = len; + + grub_memcpy (buf, &bufio->buffer[pos], n); + len -= n; + res += n; + + buf += n; + } + if (len == 0) + return res; + + /* Need to read some more. */ + next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1); + /* Now read between file->offset + res and bufio->buffer_at. */ + if (file->offset + res < next_buf) + { + grub_size_t read_now; + read_now = next_buf - (file->offset + res); + grub_file_seek (bufio->file, file->offset + res); + really_read = grub_file_read (bufio->file, buf, read_now); + if (really_read < 0) + return -1; + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + len -= really_read; + buf += really_read; + res += really_read; + + /* Partial read. File ended unexpectedly. Save the last chunk in buffer. + */ + if (really_read != (grub_ssize_t) read_now) + { + bufio->buffer_len = really_read; + if (bufio->buffer_len > bufio->block_size) + bufio->buffer_len = bufio->block_size; + bufio->buffer_at = file->offset + res - bufio->buffer_len; + grub_memcpy (&bufio->buffer[0], buf - bufio->buffer_len, + bufio->buffer_len); + return res; + } + } + + /* Read into buffer. */ + grub_file_seek (bufio->file, next_buf); + really_read = grub_file_read (bufio->file, bufio->buffer, + bufio->block_size); + if (really_read < 0) + return -1; + bufio->buffer_at = next_buf; + bufio->buffer_len = really_read; + + if (file->size == GRUB_FILE_SIZE_UNKNOWN) + file->size = bufio->file->size; + + if (len > bufio->buffer_len) + len = bufio->buffer_len; + grub_memcpy (buf, &bufio->buffer[file->offset + res - next_buf], len); + res += len; + + return res; +} + +static grub_err_t +grub_bufio_close (grub_file_t file) +{ + grub_bufio_t bufio = file->data; + + grub_file_close (bufio->file); + grub_free (bufio); + + file->device = 0; + + return grub_errno; +} + +static struct grub_fs grub_bufio_fs = + { + .name = "bufio", + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_bufio_read, + .fs_close = grub_bufio_close, + .fs_label = 0, + .next = 0 + }; diff --git a/io/gzio.c b/grub-core/io/gzio.c similarity index 73% rename from io/gzio.c rename to grub-core/io/gzio.c index 9bf609105..cb7eb1fe3 100644 --- a/io/gzio.c +++ b/grub-core/io/gzio.c @@ -40,7 +40,12 @@ #include #include #include -#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); /* * Window Size @@ -58,6 +63,9 @@ struct grub_gzio { /* The underlying file object. */ grub_file_t file; + /* If input is in memory following fields are used instead of file. */ + grub_size_t mem_input_size, mem_input_off; + grub_uint8_t *mem_input; /* The offset at which the data starts in the underlying file. */ grub_off_t data_offset; /* The type of current block. */ @@ -87,6 +95,14 @@ struct grub_gzio struct huft *tl; /* The distance code table. */ struct huft *td; + /* The checksum algorithm */ + const gcry_md_spec_t *hdesc; + /* The wanted checksum */ + grub_uint32_t orig_checksum; + /* The uncompressed length */ + grub_size_t orig_len; + /* Context for checksum calculation */ + grub_uint8_t *hcontext; /* The lookup bits for the literal/length code table. */ int bl; /* The lookup bits for the distance code table. */ @@ -100,7 +116,7 @@ typedef struct grub_gzio *grub_gzio_t; static struct grub_fs grub_gzio_fs; /* Function prototypes */ -static void initialize_tables (grub_file_t file); +static void initialize_tables (grub_gzio_t); /* Eat variable-length header fields. */ static int @@ -133,24 +149,24 @@ eat_field (grub_file_t file, int len) #define OLD_GZIP_MAGIC grub_le_to_cpu16 (0x9E1F) /* Compression methods (see algorithm.doc) */ -#define STORED 0 -#define COMPRESSED 1 -#define PACKED 2 -#define LZHED 3 +#define GRUB_GZ_STORED 0 +#define GRUB_GZ_COMPRESSED 1 +#define GRUB_GZ_PACKED 2 +#define GRUB_GZ_LZHED 3 /* methods 4 to 7 reserved */ -#define DEFLATED 8 -#define MAX_METHODS 9 +#define GRUB_GZ_DEFLATED 8 +#define GRUB_GZ_MAX_METHODS 9 /* gzip flag byte */ -#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ -#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ -#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ -#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ -#define COMMENT 0x10 /* bit 4 set: file comment present */ -#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ -#define RESERVED 0xC0 /* bit 6,7: reserved */ +#define GRUB_GZ_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define GRUB_GZ_CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define GRUB_GZ_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define GRUB_GZ_ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define GRUB_GZ_COMMENT 0x10 /* bit 4 set: file comment present */ +#define GRUB_GZ_ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define GRUB_GZ_RESERVED 0xC0 /* bit 6,7: reserved */ -#define UNSUPPORTED_FLAGS (CONTINUATION | ENCRYPTED | RESERVED) +#define GRUB_GZ_UNSUPPORTED_FLAGS (GRUB_GZ_CONTINUATION | GRUB_GZ_ENCRYPTED | GRUB_GZ_RESERVED) /* inflate block codes */ #define INFLATE_STORED 0 @@ -162,7 +178,7 @@ typedef unsigned short ush; typedef unsigned long ulg; static int -test_header (grub_file_t file) +test_gzip_header (grub_file_t file) { struct { grub_uint16_t magic; @@ -173,7 +189,7 @@ test_header (grub_file_t file) grub_uint8_t os_type; } hdr; grub_uint16_t extra_len; - grub_uint32_t orig_len; + grub_uint32_t crc32; grub_gzio_t gzio = file->data; if (grub_file_tell (gzio->file) != 0) @@ -187,44 +203,39 @@ test_header (grub_file_t file) if (grub_file_read (gzio->file, &hdr, 10) != 10 || ((hdr.magic != GZIP_MAGIC) && (hdr.magic != OLD_GZIP_MAGIC))) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "no gzip magic found"); - return 0; - } + return 0; /* * This does consistency checking on the header data. If a * problem occurs from here on, then we have corrupt or otherwise * bad data, and the error should be reported to the user. */ - if (hdr.method != DEFLATED - || (hdr.flags & UNSUPPORTED_FLAGS) - || ((hdr.flags & EXTRA_FIELD) + if (hdr.method != GRUB_GZ_DEFLATED + || (hdr.flags & GRUB_GZ_UNSUPPORTED_FLAGS) + || ((hdr.flags & GRUB_GZ_EXTRA_FIELD) && (grub_file_read (gzio->file, &extra_len, 2) != 2 || eat_field (gzio->file, grub_le_to_cpu16 (extra_len)))) - || ((hdr.flags & ORIG_NAME) && eat_field (gzio->file, -1)) - || ((hdr.flags & COMMENT) && eat_field (gzio->file, -1))) - { - grub_error (GRUB_ERR_BAD_GZIP_DATA, "unsupported gzip format"); - return 0; - } + || ((hdr.flags & GRUB_GZ_ORIG_NAME) && eat_field (gzio->file, -1)) + || ((hdr.flags & GRUB_GZ_COMMENT) && eat_field (gzio->file, -1))) + return 0; gzio->data_offset = grub_file_tell (gzio->file); - grub_file_seek (gzio->file, grub_file_size (gzio->file) - 4); - - if (grub_file_read (gzio->file, &orig_len, 4) != 4) - { - grub_error (GRUB_ERR_BAD_FILE_TYPE, "unsupported gzip format"); + /* FIXME: don't do this on not easily seekable files. */ + { + grub_file_seek (gzio->file, grub_file_size (gzio->file) - 8); + if (grub_file_read (gzio->file, &crc32, 4) != 4) return 0; - } + gzio->orig_checksum = grub_le_to_cpu32 (crc32); + if (grub_file_read (gzio->file, &gzio->orig_len, 4) != 4) + return 0; + /* FIXME: this does not handle files whose original size is over 4GB. + But how can we know the real original size? */ + file->size = grub_le_to_cpu32 (gzio->orig_len); + } - /* FIXME: this does not handle files whose original size is over 4GB. - But how can we know the real original size? */ - file->size = grub_le_to_cpu32 (orig_len); - - initialize_tables (file); + initialize_tables (gzio); return 1; } @@ -355,6 +366,10 @@ static int dbits = 6; /* bits in base distance lookup table */ lookup of seven bits, the EOB code will be found in that first lookup, and so will not require that too many bits be pulled from the stream. + + Note that NEEDBITS() can fail if the file is corrupt/truncated. + The GOTOFAILIFERROR, RETURNIFERROR and RETURN1IFERROR macros can + help. */ static ush mask_bits[] = @@ -364,29 +379,63 @@ static ush mask_bits[] = 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; -#define NEEDBITS(n) do {while(k<(n)){b|=((ulg)get_byte(file))<>=(n);k-=(n);} while (0) +#define RETURNIFERROR if (grub_errno != GRUB_ERR_NONE) return +#define RETURN1IFERROR if (grub_errno != GRUB_ERR_NONE) return 1 +#define GOTOFAILIFERROR if (grub_errno != GRUB_ERR_NONE) goto fail static int -get_byte (grub_file_t file) +get_byte (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; + grub_ssize_t bytes_read; + if (gzio->mem_input) + { + if (gzio->mem_input_off < gzio->mem_input_size) + return gzio->mem_input[gzio->mem_input_off++]; + return 0; + } - if (grub_file_tell (gzio->file) == (grub_off_t) gzio->data_offset - || gzio->inbuf_d == INBUFSIZ) + if (gzio->file && (grub_file_tell (gzio->file) + == (grub_off_t) gzio->data_offset + || gzio->inbuf_d == INBUFSIZ)) { gzio->inbuf_d = 0; - grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); + bytes_read = grub_file_read (gzio->file, gzio->inbuf, INBUFSIZ); + /* + * Only trigger if we get 0 bytes. If we get negative bytes there should + * be an existing GRUB error code that we don't want to overwrite. + */ + if (bytes_read == 0) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "File is too short"); + return 0; + } } return gzio->inbuf[gzio->inbuf_d++]; } +static void +gzio_seek (grub_gzio_t gzio, grub_off_t off) +{ + if (gzio->mem_input) + { + if (off > gzio->mem_input_size) + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to seek outside of the file")); + else + gzio->mem_input_off = off; + } + else + grub_file_seek (gzio->file, off); +} + /* more function prototypes */ static int huft_build (unsigned *, unsigned, unsigned, ush *, ush *, struct huft **, int *); static int huft_free (struct huft *); -static int inflate_codes_in_window (grub_file_t); +static int inflate_codes_in_window (grub_gzio_t); /* Given a list of code lengths and a maximum table size, make a set of @@ -415,7 +464,7 @@ huft_build (unsigned *b, /* code lengths in bits (all assumed <= BMAX) */ int l; /* bits per table (returned in m) */ register unsigned *p; /* pointer into c[], b[], or v[] */ register struct huft *q; /* points to current table */ - struct huft r; /* table entry for structure assignment */ + struct huft r = {0}; /* table entry for structure assignment */ struct huft *u[BMAX]; /* table stack */ unsigned v[N_MAX]; /* values in order of bit length */ register int w; /* bits before this table == (l * h) */ @@ -475,6 +524,7 @@ huft_build (unsigned *b, /* code lengths in bits (all assumed <= BMAX) */ } /* Make a table of values in order of bit lengths */ + grub_memset (v, N_MAX, sizeof (v)); p = b; i = 0; do @@ -522,7 +572,7 @@ huft_build (unsigned *b, /* code lengths in bits (all assumed <= BMAX) */ z = 1 << j; /* table entries for j-bit table */ /* allocate and link in new table */ - q = (struct huft *) grub_malloc ((z + 1) * sizeof (struct huft)); + q = (struct huft *) grub_calloc (z + 1, sizeof (struct huft)); if (! q) { if (h) @@ -556,11 +606,18 @@ huft_build (unsigned *b, /* code lengths in bits (all assumed <= BMAX) */ r.v.n = (ush) (*p); /* simple code is just the value */ p++; /* one compiler does not like *p++ */ } - else + else if (*p < N_MAX) { r.e = (uch) e[*p - s]; /* non-simple--look up in lists */ r.v.n = d[*p++ - s]; } + else + { + /* Detected an uninitialised value, abort. */ + if (h) + huft_free (u[0]); + return 2; + } /* fill code-like entries with r */ f = 1 << (k - w); @@ -613,7 +670,7 @@ huft_free (struct huft *t) */ static int -inflate_codes_in_window (grub_file_t file) +inflate_codes_in_window (grub_gzio_t gzio) { register unsigned e; /* table entry flag/number of extra bits */ unsigned n, d; /* length and index for copy */ @@ -622,7 +679,6 @@ inflate_codes_in_window (grub_file_t file) unsigned ml, md; /* masks for bl and bd bits */ register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local copies of globals */ d = gzio->inflate_d; @@ -638,19 +694,26 @@ inflate_codes_in_window (grub_file_t file) { if (! gzio->code_state) { - NEEDBITS ((unsigned) gzio->bl); + + if (gzio->tl == NULL) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "NULL gzio->tl"); + return 1; + } + + NEEDBITS ((unsigned) gzio->bl); RETURN1IFERROR; if ((e = (t = gzio->tl + ((unsigned) b & ml))->e) > 16) do { if (e == 99) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "an unused code found"); return 1; } DUMPBITS (t->b); e -= 16; - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); DUMPBITS (t->b); @@ -672,29 +735,35 @@ inflate_codes_in_window (grub_file_t file) } /* get length of block to copy */ - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; n = t->v.n + ((unsigned) b & mask_bits[e]); DUMPBITS (e); + if (gzio->td == NULL) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "NULL gzio->td"); + return 1; + } + /* decode distance of block to copy */ - NEEDBITS ((unsigned) gzio->bd); + NEEDBITS ((unsigned) gzio->bd); RETURN1IFERROR; if ((e = (t = gzio->td + ((unsigned) b & md))->e) > 16) do { if (e == 99) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "an unused code found"); return 1; } DUMPBITS (t->b); e -= 16; - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; } while ((e = (t = t->v.t + ((unsigned) b & mask_bits[e]))->e) > 16); DUMPBITS (t->b); - NEEDBITS (e); + NEEDBITS (e); RETURN1IFERROR; d = w - t->v.n - ((unsigned) b & mask_bits[e]); DUMPBITS (e); gzio->code_state++; @@ -750,11 +819,10 @@ inflate_codes_in_window (grub_file_t file) /* get header for an inflated type 0 (stored) block. */ static void -init_stored_block (grub_file_t file) +init_stored_block (grub_gzio_t gzio) { register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local copies of globals */ b = gzio->bb; /* initialize bit buffer */ @@ -764,12 +832,12 @@ init_stored_block (grub_file_t file) DUMPBITS (k & 7); /* get the length and its complement */ - NEEDBITS (16); + NEEDBITS (16); RETURNIFERROR; gzio->block_len = ((unsigned) b & 0xffff); DUMPBITS (16); - NEEDBITS (16); + NEEDBITS (16); RETURNIFERROR; if (gzio->block_len != (int) ((~b) & 0xffff)) - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "the length of a stored block does not match"); DUMPBITS (16); @@ -784,11 +852,10 @@ init_stored_block (grub_file_t file) Huffman tables. */ static void -init_fixed_block (grub_file_t file) +init_fixed_block (grub_gzio_t gzio) { int i; /* temporary variable */ unsigned l[288]; /* length list for huft_build */ - grub_gzio_t gzio = file->data; /* set up literal table */ for (i = 0; i < 144; i++) @@ -803,7 +870,7 @@ init_fixed_block (grub_file_t file) if (huft_build (l, 288, 257, cplens, cplext, &gzio->tl, &gzio->bl) != 0) { if (grub_errno == GRUB_ERR_NONE) - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "failed in building a Huffman code table"); return; } @@ -815,7 +882,7 @@ init_fixed_block (grub_file_t file) if (huft_build (l, 30, 0, cpdist, cpdext, &gzio->td, &gzio->bd) > 1) { if (grub_errno == GRUB_ERR_NONE) - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "failed in building a Huffman code table"); huft_free (gzio->tl); gzio->tl = 0; @@ -831,7 +898,7 @@ init_fixed_block (grub_file_t file) /* get header for an inflated type 2 (dynamic Huffman codes) block. */ static void -init_dynamic_block (grub_file_t file) +init_dynamic_block (grub_gzio_t gzio) { int i; /* temporary variables */ unsigned j; @@ -844,32 +911,31 @@ init_dynamic_block (grub_file_t file) unsigned ll[286 + 30]; /* literal/length and distance code lengths */ register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local bit buffer */ b = gzio->bb; k = gzio->bk; /* read in table lengths */ - NEEDBITS (5); + NEEDBITS (5); RETURNIFERROR; nl = 257 + ((unsigned) b & 0x1f); /* number of literal/length codes */ DUMPBITS (5); - NEEDBITS (5); + NEEDBITS (5); RETURNIFERROR; nd = 1 + ((unsigned) b & 0x1f); /* number of distance codes */ DUMPBITS (5); - NEEDBITS (4); + NEEDBITS (4); RETURNIFERROR; nb = 4 + ((unsigned) b & 0xf); /* number of bit length codes */ DUMPBITS (4); if (nl > 286 || nd > 30) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, "too much data"); + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "too much data"); return; } /* read in bit-length-code lengths */ for (j = 0; j < nb; j++) { - NEEDBITS (3); + NEEDBITS (3); RETURNIFERROR; ll[bitorder[j]] = (unsigned) b & 7; DUMPBITS (3); } @@ -880,7 +946,7 @@ init_dynamic_block (grub_file_t file) gzio->bl = 7; if (huft_build (ll, 19, 19, NULL, NULL, &gzio->tl, &gzio->bl) != 0) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "failed in building a Huffman code table"); return; } @@ -889,9 +955,16 @@ init_dynamic_block (grub_file_t file) n = nl + nd; m = mask_bits[gzio->bl]; i = l = 0; + + if (gzio->tl == NULL) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "NULL gzio->tl"); + return; + } + while ((unsigned) i < n) { - NEEDBITS ((unsigned) gzio->bl); + NEEDBITS ((unsigned) gzio->bl); GOTOFAILIFERROR; j = (gzio->td = gzio->tl + ((unsigned) b & m))->b; DUMPBITS (j); j = gzio->td->v.n; @@ -899,26 +972,26 @@ init_dynamic_block (grub_file_t file) ll[i++] = l = j; /* save last length in l */ else if (j == 16) /* repeat last length 3 to 6 times */ { - NEEDBITS (2); + NEEDBITS (2); GOTOFAILIFERROR; j = 3 + ((unsigned) b & 3); DUMPBITS (2); if ((unsigned) i + j > n) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, "too many codes found"); - return; + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "too many codes found"); + goto fail; } while (j--) ll[i++] = l; } else if (j == 17) /* 3 to 10 zero length codes */ { - NEEDBITS (3); + NEEDBITS (3); GOTOFAILIFERROR; j = 3 + ((unsigned) b & 7); DUMPBITS (3); if ((unsigned) i + j > n) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, "too many codes found"); - return; + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "too many codes found"); + goto fail; } while (j--) ll[i++] = 0; @@ -927,13 +1000,13 @@ init_dynamic_block (grub_file_t file) else /* j == 18: 11 to 138 zero length codes */ { - NEEDBITS (7); + NEEDBITS (7); GOTOFAILIFERROR; j = 11 + ((unsigned) b & 0x7f); DUMPBITS (7); if ((unsigned) i + j > n) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, "too many codes found"); - return; + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "too many codes found"); + goto fail; } while (j--) ll[i++] = 0; @@ -954,7 +1027,8 @@ init_dynamic_block (grub_file_t file) gzio->bl = lbits; if (huft_build (ll, nl, 257, cplens, cplext, &gzio->tl, &gzio->bl) != 0) { - grub_error (GRUB_ERR_BAD_GZIP_DATA, + gzio->tl = 0; + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "failed in building a Huffman code table"); return; } @@ -963,7 +1037,8 @@ init_dynamic_block (grub_file_t file) { huft_free (gzio->tl); gzio->tl = 0; - grub_error (GRUB_ERR_BAD_GZIP_DATA, + gzio->td = 0; + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "failed in building a Huffman code table"); return; } @@ -971,27 +1046,32 @@ init_dynamic_block (grub_file_t file) /* indicate we're now working on a block */ gzio->code_state = 0; gzio->block_len++; + return; + + fail: + huft_free (gzio->tl); + gzio->td = NULL; + gzio->tl = NULL; } static void -get_new_block (grub_file_t file) +get_new_block (grub_gzio_t gzio) { register ulg b; /* bit buffer */ register unsigned k; /* number of bits in bit buffer */ - grub_gzio_t gzio = file->data; /* make local bit buffer */ b = gzio->bb; k = gzio->bk; /* read in last block bit */ - NEEDBITS (1); + NEEDBITS (1); RETURNIFERROR; gzio->last_block = (int) b & 1; DUMPBITS (1); /* read in block type */ - NEEDBITS (2); + NEEDBITS (2); RETURNIFERROR; gzio->block_type = (unsigned) b & 3; DUMPBITS (2); @@ -1002,13 +1082,13 @@ get_new_block (grub_file_t file) switch (gzio->block_type) { case INFLATE_STORED: - init_stored_block (file); + init_stored_block (gzio); break; case INFLATE_FIXED: - init_fixed_block (file); + init_fixed_block (gzio); break; case INFLATE_DYNAMIC: - init_dynamic_block (file); + init_dynamic_block (gzio); break; default: break; @@ -1017,10 +1097,8 @@ get_new_block (grub_file_t file) static void -inflate_window (grub_file_t file) +inflate_window (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; - /* initialize window */ gzio->wp = 0; @@ -1035,11 +1113,11 @@ inflate_window (grub_file_t file) if (gzio->last_block) break; - get_new_block (file); + get_new_block (gzio); } if (gzio->block_type > INFLATE_DYNAMIC) - grub_error (GRUB_ERR_BAD_GZIP_DATA, + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "unknown block type %d", gzio->block_type); if (grub_errno != GRUB_ERR_NONE) @@ -1058,7 +1136,7 @@ inflate_window (grub_file_t file) while (gzio->block_len && w < WSIZE && grub_errno == GRUB_ERR_NONE) { - gzio->slide[w++] = get_byte (file); + gzio->slide[w++] = get_byte (gzio); gzio->block_len--; } @@ -1071,7 +1149,7 @@ inflate_window (grub_file_t file) * Expand other kind of block. */ - if (inflate_codes_in_window (file)) + if (inflate_codes_in_window (gzio)) { huft_free (gzio->tl); huft_free (gzio->td); @@ -1080,19 +1158,33 @@ inflate_window (grub_file_t file) } } - gzio->saved_offset += WSIZE; + gzio->saved_offset += gzio->wp; - /* XXX do CRC calculation here! */ + if (gzio->hcontext) + { + gzio->hdesc->write (gzio->hcontext, gzio->slide, gzio->wp); + + if (gzio->saved_offset == gzio->orig_len) + { + grub_uint32_t csum; + + gzio->hdesc->final (gzio->hcontext); + csum = grub_get_unaligned32 (gzio->hdesc->read (gzio->hcontext)); + csum = grub_be_to_cpu32 (csum); + if (csum != gzio->orig_checksum) + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + "checksum mismatch %08x/%08x", + gzio->orig_checksum, csum); + } + } } static void -initialize_tables (grub_file_t file) +initialize_tables (grub_gzio_t gzio) { - grub_gzio_t gzio = file->data; - gzio->saved_offset = 0; - grub_file_seek (gzio->file, gzio->data_offset); + gzio_seek (gzio, gzio->data_offset); /* Initialize the bit buffer. */ gzio->bk = 0; @@ -1105,19 +1197,28 @@ initialize_tables (grub_file_t file) /* Reset memory allocation stuff. */ huft_free (gzio->tl); huft_free (gzio->td); + gzio->tl = NULL; + gzio->td = NULL; + + if (gzio->hcontext) + gzio->hdesc->init(gzio->hcontext); } -/* Open a new decompressing object on the top of IO. If TRANSPARENT is true, - even if IO does not contain data compressed by gzip, return a valid file - object. Note that this function won't close IO, even if an error occurs. */ -grub_file_t -grub_gzio_open (grub_file_t io, int transparent) +/* + * Open a new decompressing object on the top of IO. + * Note that this function won't close IO, even if an error occurs. + */ +static grub_file_t +grub_gzio_open (grub_file_t io, enum grub_file_type type) { grub_file_t file; grub_gzio_t gzio = 0; - file = (grub_file_t) grub_malloc (sizeof (*file)); + if (type & GRUB_FILE_TYPE_NO_DECOMPRESS) + return io; + + file = (grub_file_t) grub_zalloc (sizeof (*file)); if (! file) return 0; @@ -1130,60 +1231,89 @@ grub_gzio_open (grub_file_t io, int transparent) gzio->file = io; - file->device = io->device; - file->offset = 0; - file->data = gzio; - file->read_hook = 0; - file->fs = &grub_gzio_fs; + gzio->hdesc = GRUB_MD_CRC32; + gzio->hcontext = grub_malloc(gzio->hdesc->contextsize); - if (! test_header (file)) + file->device = io->device; + file->data = gzio; + file->fs = &grub_gzio_fs; + file->not_easily_seekable = 1; + + if (! test_gzip_header (file)) { + grub_errno = GRUB_ERR_NONE; + grub_free (gzio->hcontext); grub_free (gzio); grub_free (file); grub_file_seek (io, 0); - if (grub_errno == GRUB_ERR_BAD_FILE_TYPE && transparent) - { - grub_errno = GRUB_ERR_NONE; - return io; - } - else - return 0; + return io; } return file; } -/* This is similar to grub_gzio_open, but takes a file name as an argument. */ -grub_file_t -grub_gzfile_open (const char *name, int transparent) +static grub_uint8_t +mod_31 (grub_uint16_t v) { - grub_file_t io, file; - - io = grub_file_open (name); - if (! io) + /* At most 2 iterations for any number that + we can get here. + In any case faster than real division. */ + while (v > 0x1f) + v = (v & 0x1f) + (v >> 5); + if (v == 0x1f) return 0; + return v; +} - file = grub_gzio_open (io, transparent); - if (! file) +static int +test_zlib_header (grub_gzio_t gzio) +{ + grub_uint8_t cmf, flg; + + cmf = get_byte (gzio); + flg = get_byte (gzio); + + /* Check that compression method is DEFLATE. */ + if ((cmf & 0xf) != GRUB_GZ_DEFLATED) { - grub_file_close (io); + /* TRANSLATORS: It's about given file having some strange format, not + complete lack of gzip support. */ + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("unsupported gzip format")); return 0; } - return file; + /* Usually it would be: (cmf * 256 + flg) % 31 != 0. */ + /* But 256 == 8 (31). */ + /* By multiplying by 4 and using 32 == 1 (31). We get our formula. */ + if (mod_31 (cmf + flg * 4) != 0) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("unsupported gzip format")); + return 0; + } + + /* Dictionary isn't supported. */ + if (flg & 0x20) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("unsupported gzip format")); + return 0; + } + + gzio->data_offset = 2; + initialize_tables (gzio); + + return 1; } static grub_ssize_t -grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) +grub_gzio_read_real (grub_gzio_t gzio, grub_off_t offset, + char *buf, grub_size_t len) { grub_ssize_t ret = 0; - grub_gzio_t gzio = file->data; - grub_off_t offset; /* Do we reset decompression to the beginning of the file? */ - if (gzio->saved_offset > file->offset + WSIZE) - initialize_tables (file); + if (gzio->saved_offset > offset + WSIZE) + initialize_tables (gzio); /* * This loop operates upon uncompressed data only. The only @@ -1191,15 +1321,20 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) * window is within the range of data it needs. */ - offset = file->offset; - while (len > 0 && grub_errno == GRUB_ERR_NONE) { register grub_size_t size; register char *srcaddr; while (offset >= gzio->saved_offset) - inflate_window (file); + { + inflate_window (gzio); + if (gzio->wp == 0) + goto out; + } + + if (gzio->wp == 0) + goto out; srcaddr = (char *) ((offset & (WSIZE - 1)) + gzio->slide); size = gzio->saved_offset - offset; @@ -1214,12 +1349,27 @@ grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) offset += size; } + out: if (grub_errno != GRUB_ERR_NONE) ret = -1; return ret; } +static grub_ssize_t +grub_gzio_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_ssize_t ret; + ret = grub_gzio_read_real (file->data, file->offset, buf, len); + + if (!grub_errno && ret != (grub_ssize_t) len) + { + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, "premature end of compressed"); + ret = -1; + } + return ret; +} + /* Release everything, including the underlying file object. */ static grub_err_t grub_gzio_close (grub_file_t file) @@ -1229,23 +1379,84 @@ grub_gzio_close (grub_file_t file) grub_file_close (gzio->file); huft_free (gzio->tl); huft_free (gzio->td); + grub_free (gzio->hcontext); grub_free (gzio); /* No need to close the same device twice. */ file->device = 0; + file->name = 0; return grub_errno; } +grub_ssize_t +grub_zlib_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize) +{ + grub_gzio_t gzio = 0; + grub_ssize_t ret; + + gzio = grub_zalloc (sizeof (*gzio)); + if (! gzio) + return -1; + gzio->mem_input = (grub_uint8_t *) inbuf; + gzio->mem_input_size = insize; + gzio->mem_input_off = 0; + + if (!test_zlib_header (gzio)) + { + grub_free (gzio); + return -1; + } + + ret = grub_gzio_read_real (gzio, off, outbuf, outsize); + grub_free (gzio); + + /* FIXME: Check Adler. */ + return ret; +} + +grub_ssize_t +grub_deflate_decompress (char *inbuf, grub_size_t insize, grub_off_t off, + char *outbuf, grub_size_t outsize) +{ + grub_gzio_t gzio = 0; + grub_ssize_t ret; + + gzio = grub_zalloc (sizeof (*gzio)); + if (! gzio) + return -1; + gzio->mem_input = (grub_uint8_t *) inbuf; + gzio->mem_input_size = insize; + gzio->mem_input_off = 0; + + initialize_tables (gzio); + + ret = grub_gzio_read_real (gzio, off, outbuf, outsize); + grub_free (gzio); + + return ret; +} + static struct grub_fs grub_gzio_fs = { .name = "gzio", - .dir = 0, - .open = 0, - .read = grub_gzio_read, - .close = grub_gzio_close, - .label = 0, + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_gzio_read, + .fs_close = grub_gzio_close, + .fs_label = 0, .next = 0 }; + +GRUB_MOD_INIT(gzio) +{ + grub_file_filter_register (GRUB_FILE_FILTER_GZIO, grub_gzio_open); +} + +GRUB_MOD_FINI(gzio) +{ + grub_file_filter_unregister (GRUB_FILE_FILTER_GZIO); +} diff --git a/grub-core/io/lzopio.c b/grub-core/io/lzopio.c new file mode 100644 index 000000000..a7d442543 --- /dev/null +++ b/grub-core/io/lzopio.c @@ -0,0 +1,546 @@ +/* lzopio.c - decompression support for lzop */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define LZOP_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a" +#define LZOP_MAGIC_SIZE 9 +#define LZOP_CHECK_SIZE 4 +#define LZOP_NEW_LIB 0x0940 + +/* Header flags - copied from conf.h of LZOP source code. */ +#define F_ADLER32_D 0x00000001L +#define F_ADLER32_C 0x00000002L +#define F_STDIN 0x00000004L +#define F_STDOUT 0x00000008L +#define F_NAME_DEFAULT 0x00000010L +#define F_DOSISH 0x00000020L +#define F_H_EXTRA_FIELD 0x00000040L +#define F_H_GMTDIFF 0x00000080L +#define F_CRC32_D 0x00000100L +#define F_CRC32_C 0x00000200L +#define F_MULTIPART 0x00000400L +#define F_H_FILTER 0x00000800L +#define F_H_CRC32 0x00001000L +#define F_H_PATH 0x00002000L +#define F_MASK 0x00003FFFL + +struct block_header +{ + grub_uint32_t usize; + grub_uint32_t csize; + grub_uint32_t ucheck; + grub_uint32_t ccheck; + unsigned char *cdata; + unsigned char *udata; +}; + +struct grub_lzopio +{ + grub_file_t file; + int has_ccheck; + int has_ucheck; + const gcry_md_spec_t *ucheck_fun; + const gcry_md_spec_t *ccheck_fun; + grub_off_t saved_off; /* Rounded down to block boundary. */ + grub_off_t start_block_off; + struct block_header block; +}; + +typedef struct grub_lzopio *grub_lzopio_t; +static struct grub_fs grub_lzopio_fs; + +/* Some helper functions. On errors memory allocated by those function is free + * either on close() so no risk of leaks. This makes functions simpler. */ + +/* Read block header from file, after successful exit file points to + * beginning of block data. */ +static int +read_block_header (struct grub_lzopio *lzopio) +{ + lzopio->saved_off += lzopio->block.usize; + + /* Free cached block data if any. */ + grub_free (lzopio->block.udata); + grub_free (lzopio->block.cdata); + lzopio->block.udata = NULL; + lzopio->block.cdata = NULL; + + if (grub_file_read (lzopio->file, &lzopio->block.usize, + sizeof (lzopio->block.usize)) != + sizeof (lzopio->block.usize)) + return -1; + + lzopio->block.usize = grub_be_to_cpu32 (lzopio->block.usize); + + /* Last block has uncompressed data size == 0 and no other fields. */ + if (lzopio->block.usize == 0) + { + if (grub_file_tell (lzopio->file) == grub_file_size (lzopio->file)) + return 0; + else + return -1; + } + + /* Read compressed data block size. */ + if (grub_file_read (lzopio->file, &lzopio->block.csize, + sizeof (lzopio->block.csize)) != + sizeof (lzopio->block.csize)) + return -1; + + lzopio->block.csize = grub_be_to_cpu32 (lzopio->block.csize); + + /* Corrupted. */ + if (lzopio->block.csize > lzopio->block.usize) + return -1; + + /* Read checksum of uncompressed data. */ + if (lzopio->has_ucheck) + { + if (grub_file_read (lzopio->file, &lzopio->block.ucheck, + sizeof (lzopio->block.ucheck)) != + sizeof (lzopio->block.ucheck)) + return -1; + } + + /* Read checksum of compressed data. */ + if (lzopio->has_ccheck) + { + /* Incompressible data block. */ + if (lzopio->block.csize == lzopio->block.usize) + { + lzopio->block.ccheck = lzopio->block.ucheck; + } + else + { + if (grub_file_read (lzopio->file, &lzopio->block.ccheck, + sizeof (lzopio->block.ccheck)) != + sizeof (lzopio->block.ccheck)) + return -1; + } + } + + return 0; +} + +/* Read block data into memory. File must be set to beginning of block data. + * Can't be called on last block. */ +static int +read_block_data (struct grub_lzopio *lzopio) +{ + lzopio->block.cdata = grub_malloc (lzopio->block.csize); + if (!lzopio->block.cdata) + return -1; + + if (grub_file_read (lzopio->file, lzopio->block.cdata, lzopio->block.csize) + != (grub_ssize_t) lzopio->block.csize) + return -1; + + if (lzopio->ccheck_fun) + { + grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN]; + + if (lzopio->ccheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return -1; + + grub_crypto_hash (lzopio->ccheck_fun, computed_hash, + lzopio->block.cdata, + lzopio->block.csize); + + if (grub_memcmp + (computed_hash, &lzopio->block.ccheck, + sizeof (lzopio->block.ccheck)) != 0) + return -1; + } + + return 0; +} + +/* Read block data, uncompressed and also store it in memory. */ +/* XXX Investigate possibility of in-place decompression to reduce memory + * footprint. Or try to uncompress directly to buf if possible. */ +static int +uncompress_block (struct grub_lzopio *lzopio) +{ + lzo_uint usize = lzopio->block.usize; + + if (read_block_data (lzopio) < 0) + return -1; + + /* Incompressible data. */ + if (lzopio->block.csize == lzopio->block.usize) + { + lzopio->block.udata = lzopio->block.cdata; + lzopio->block.cdata = NULL; + } + else + { + lzopio->block.udata = grub_malloc (lzopio->block.usize); + if (!lzopio->block.udata) + return -1; + + if (lzo1x_decompress_safe (lzopio->block.cdata, lzopio->block.csize, + lzopio->block.udata, &usize, NULL) + != LZO_E_OK) + return -1; + + if (lzopio->ucheck_fun) + { + grub_uint8_t computed_hash[GRUB_CRYPTO_MAX_MDLEN]; + + if (lzopio->ucheck_fun->mdlen > GRUB_CRYPTO_MAX_MDLEN) + return -1; + + grub_crypto_hash (lzopio->ucheck_fun, computed_hash, + lzopio->block.udata, + lzopio->block.usize); + + if (grub_memcmp + (computed_hash, &lzopio->block.ucheck, + sizeof (lzopio->block.ucheck)) != 0) + return -1; + } + + /* Compressed data can be free now. */ + grub_free (lzopio->block.cdata); + lzopio->block.cdata = NULL; + } + + return 0; +} + +/* Jump to next block and read its header. */ +static int +jump_block (struct grub_lzopio *lzopio) +{ + /* only jump if block was not decompressed (and read from disk) */ + if (!lzopio->block.udata) + { + grub_off_t off = grub_file_tell (lzopio->file) + lzopio->block.csize; + + if (grub_file_seek (lzopio->file, off) == ((grub_off_t) - 1)) + return -1; + } + + return read_block_header (lzopio); +} + +static int +calculate_uncompressed_size (grub_file_t file) +{ + grub_lzopio_t lzopio = file->data; + grub_off_t usize_total = 0; + + if (read_block_header (lzopio) < 0) + return -1; + + /* FIXME: Don't do this for not easily seekable files. */ + while (lzopio->block.usize != 0) + { + usize_total += lzopio->block.usize; + + if (jump_block (lzopio) < 0) + return -1; + } + + file->size = usize_total; + + return 0; +} + +struct lzop_header +{ + grub_uint8_t magic[LZOP_MAGIC_SIZE]; + grub_uint16_t lzop_version; + grub_uint16_t lib_version; + grub_uint16_t lib_version_ext; + grub_uint8_t method; + grub_uint8_t level; + grub_uint32_t flags; + /* grub_uint32_t filter; */ /* No filters support. Rarely used anyway. */ + grub_uint32_t mode; + grub_uint32_t mtime_lo; + grub_uint32_t mtime_hi; + grub_uint8_t name_len; +} GRUB_PACKED; + +static int +test_header (grub_file_t file) +{ + grub_lzopio_t lzopio = file->data; + struct lzop_header header; + grub_uint32_t flags, checksum; + const gcry_md_spec_t *hcheck; + grub_uint8_t *context = NULL; + grub_uint8_t *name = NULL; + + if (grub_file_read (lzopio->file, &header, sizeof (header)) != sizeof (header)) + return 0; + + if (grub_memcmp (header.magic, LZOP_MAGIC, LZOP_MAGIC_SIZE) != 0) + return 0; + + if (grub_be_to_cpu16(header.lib_version) < LZOP_NEW_LIB) + return 0; + + /* Too new version, should upgrade minilzo? */ + if (grub_be_to_cpu16 (header.lib_version_ext) > MINILZO_VERSION) + return 0; + + flags = grub_be_to_cpu32 (header.flags); + + if (flags & F_CRC32_D) + { + lzopio->has_ucheck = 1; + lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("crc32"); + } + else if (flags & F_ADLER32_D) + { + lzopio->has_ucheck = 1; + lzopio->ucheck_fun = grub_crypto_lookup_md_by_name ("adler32"); + } + + if (flags & F_CRC32_C) + { + lzopio->has_ccheck = 1; + lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("crc32"); + } + else if (flags & F_ADLER32_C) + { + lzopio->has_ccheck = 1; + lzopio->ccheck_fun = grub_crypto_lookup_md_by_name ("adler32"); + } + + if (flags & F_H_CRC32) + hcheck = grub_crypto_lookup_md_by_name ("crc32"); + else + hcheck = grub_crypto_lookup_md_by_name ("adler32"); + + if (hcheck) { + context = grub_malloc(hcheck->contextsize); + if (! context) + return 0; + + hcheck->init(context); + + /* MAGIC is not included in check calculation. */ + hcheck->write(context, &header.lzop_version, sizeof(header)- LZOP_MAGIC_SIZE); + } + + if (header.name_len != 0) + { + name = grub_malloc (header.name_len); + if (! name) + { + grub_free (context); + return 0; + } + + if (grub_file_read (lzopio->file, name, header.name_len) != + header.name_len) + { + grub_free(name); + goto CORRUPTED; + } + + if (hcheck) + hcheck->write(context, name, header.name_len); + + grub_free(name); + } + + if (hcheck) + hcheck->final(context); + + if (grub_file_read (lzopio->file, &checksum, sizeof (checksum)) != + sizeof (checksum)) + goto CORRUPTED; + + if (hcheck && grub_memcmp (&checksum, hcheck->read(context), sizeof(checksum)) != 0) + goto CORRUPTED; + + lzopio->start_block_off = grub_file_tell (lzopio->file); + + if (calculate_uncompressed_size (file) < 0) + goto CORRUPTED; + + /* Get back to start block. */ + grub_file_seek (lzopio->file, lzopio->start_block_off); + + /* Read first block - grub_lzopio_read() expects valid block. */ + if (read_block_header (lzopio) < 0) + goto CORRUPTED; + + lzopio->saved_off = 0; + return 1; + +CORRUPTED: + return 0; +} + +static grub_file_t +grub_lzopio_open (grub_file_t io, enum grub_file_type type) +{ + grub_file_t file; + grub_lzopio_t lzopio; + + if (type & GRUB_FILE_TYPE_NO_DECOMPRESS) + return io; + + file = (grub_file_t) grub_zalloc (sizeof (*file)); + if (!file) + return 0; + + lzopio = grub_zalloc (sizeof (*lzopio)); + if (!lzopio) + { + grub_free (file); + return 0; + } + + lzopio->file = io; + + file->device = io->device; + file->data = lzopio; + file->fs = &grub_lzopio_fs; + file->size = GRUB_FILE_SIZE_UNKNOWN; + file->not_easily_seekable = 1; + + if (grub_file_tell (lzopio->file) != 0) + grub_file_seek (lzopio->file, 0); + + if (!test_header (file)) + { + grub_errno = GRUB_ERR_NONE; + grub_file_seek (io, 0); + grub_free (lzopio); + grub_free (file); + + return io; + } + + return file; +} + +static grub_ssize_t +grub_lzopio_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_lzopio_t lzopio = file->data; + grub_ssize_t ret = 0; + grub_off_t off; + + /* Backward seek before last read block. */ + if (lzopio->saved_off > grub_file_tell (file)) + { + grub_file_seek (lzopio->file, lzopio->start_block_off); + + if (read_block_header (lzopio) < 0) + goto CORRUPTED; + + lzopio->saved_off = 0; + } + + /* Forward to first block with requested data. */ + while (lzopio->saved_off + lzopio->block.usize <= grub_file_tell (file)) + { + /* EOF, could be possible files with unknown size. */ + if (lzopio->block.usize == 0) + return 0; + + if (jump_block (lzopio) < 0) + goto CORRUPTED; + } + + off = grub_file_tell (file) - lzopio->saved_off; + + while (len != 0 && lzopio->block.usize != 0) + { + grub_size_t to_copy; + + /* Block not decompressed yet. */ + if (!lzopio->block.udata && uncompress_block (lzopio) < 0) + goto CORRUPTED; + + /* Copy requested data into buffer. */ + to_copy = lzopio->block.usize - off; + if (to_copy > len) + to_copy = len; + grub_memcpy (buf, lzopio->block.udata + off, to_copy); + + len -= to_copy; + buf += to_copy; + ret += to_copy; + off = 0; + + /* Read next block if needed. */ + if (len > 0 && read_block_header (lzopio) < 0) + goto CORRUPTED; + } + + return ret; + +CORRUPTED: + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, N_("lzop file corrupted")); + return -1; +} + +/* Release everything, including the underlying file object. */ +static grub_err_t +grub_lzopio_close (grub_file_t file) +{ + grub_lzopio_t lzopio = file->data; + + grub_file_close (lzopio->file); + grub_free (lzopio->block.cdata); + grub_free (lzopio->block.udata); + grub_free (lzopio); + + /* Device must not be closed twice. */ + file->device = 0; + file->name = 0; + return grub_errno; +} + +static struct grub_fs grub_lzopio_fs = { + .name = "lzopio", + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_lzopio_read, + .fs_close = grub_lzopio_close, + .fs_label = 0, + .next = 0 +}; + +GRUB_MOD_INIT (lzopio) +{ + grub_file_filter_register (GRUB_FILE_FILTER_LZOPIO, grub_lzopio_open); +} + +GRUB_MOD_FINI (lzopio) +{ + grub_file_filter_unregister (GRUB_FILE_FILTER_LZOPIO); +} diff --git a/grub-core/io/offset.c b/grub-core/io/offset.c new file mode 100644 index 000000000..7e2db4a3a --- /dev/null +++ b/grub-core/io/offset.c @@ -0,0 +1,112 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_offset_file +{ + grub_file_t parent; + grub_off_t off; +}; + +static grub_ssize_t +grub_offset_read (grub_file_t file, char *buf, grub_size_t len) +{ + struct grub_offset_file *data = file->data; + if (grub_file_seek (data->parent, data->off + file->offset) == (grub_off_t) -1) + return -1; + return grub_file_read (data->parent, buf, len); +} + +static grub_err_t +grub_offset_close (grub_file_t file) +{ + struct grub_offset_file *data = file->data; + + if (data->parent) + grub_file_close (data->parent); + + /* No need to close the same device twice. */ + file->device = 0; + + return 0; +} + +static struct grub_fs grub_offset_fs = { + .name = "offset", + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_offset_read, + .fs_close = grub_offset_close, + .fs_label = 0, + .next = 0 +}; + +void +grub_file_offset_close (grub_file_t file) +{ + struct grub_offset_file *off_data = file->data; + off_data->parent = NULL; + grub_file_close (file); +} + +grub_file_t +grub_file_offset_open (grub_file_t parent, enum grub_file_type type, + grub_off_t start, grub_off_t size) +{ + struct grub_offset_file *off_data; + grub_file_t off_file, last_off_file; + grub_file_filter_id_t filter; + + off_file = grub_zalloc (sizeof (*off_file)); + off_data = grub_zalloc (sizeof (*off_data)); + if (!off_file || !off_data) + { + grub_free (off_file); + grub_free (off_data); + return 0; + } + + off_data->off = start; + off_data->parent = parent; + + off_file->device = parent->device; + off_file->data = off_data; + off_file->fs = &grub_offset_fs; + off_file->size = size; + + last_off_file = NULL; + for (filter = GRUB_FILE_FILTER_COMPRESSION_FIRST; + off_file && filter <= GRUB_FILE_FILTER_COMPRESSION_LAST; filter++) + if (grub_file_filters[filter]) + { + last_off_file = off_file; + off_file = grub_file_filters[filter] (off_file, type); + } + + if (!off_file) + { + off_data->parent = NULL; + grub_file_close (last_off_file); + return 0; + } + return off_file; +} diff --git a/grub-core/io/xzio.c b/grub-core/io/xzio.c new file mode 100644 index 000000000..63d8cda6a --- /dev/null +++ b/grub-core/io/xzio.c @@ -0,0 +1,346 @@ +/* xzio.c - decompression support for xz */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "xz.h" +#include "xz_stream.h" + +#define XZBUFSIZ 0x2000 +#define VLI_MAX_DIGITS 9 +#define XZ_STREAM_FOOTER_SIZE 12 + +struct grub_xzio +{ + grub_file_t file; + struct xz_buf buf; + struct xz_dec *dec; + grub_uint8_t inbuf[XZBUFSIZ]; + grub_uint8_t outbuf[XZBUFSIZ]; + grub_off_t saved_offset; +}; + +typedef struct grub_xzio *grub_xzio_t; +static struct grub_fs grub_xzio_fs; + +static grub_size_t +decode_vli (const grub_uint8_t buf[], grub_size_t size_max, + grub_uint64_t *num) +{ + if (size_max == 0) + return 0; + + if (size_max > VLI_MAX_DIGITS) + size_max = VLI_MAX_DIGITS; + + *num = buf[0] & 0x7F; + grub_size_t i = 0; + + while (buf[i++] & 0x80) + { + if (i >= size_max || buf[i] == 0x00) + return 0; + + *num |= (uint64_t) (buf[i] & 0x7F) << (i * 7); + } + + return i; +} + +static grub_ssize_t +read_vli (grub_file_t file, grub_uint64_t *num) +{ + grub_uint8_t buf[VLI_MAX_DIGITS]; + grub_ssize_t read_bytes; + grub_size_t dec; + + read_bytes = grub_file_read (file, buf, VLI_MAX_DIGITS); + if (read_bytes < 0) + return -1; + + dec = decode_vli (buf, read_bytes, num); + grub_file_seek (file, file->offset - (read_bytes - dec)); + return dec; +} + +/* Function xz_dec_run() should consume header and ask for more (XZ_OK) + * else file is corrupted (or options not supported) or not xz. */ +static int +test_header (grub_file_t file) +{ + grub_xzio_t xzio = file->data; + enum xz_ret ret; + + xzio->buf.in_size = grub_file_read (xzio->file, xzio->inbuf, + STREAM_HEADER_SIZE); + + if (xzio->buf.in_size != STREAM_HEADER_SIZE) + return 0; + + ret = xz_dec_run (xzio->dec, &xzio->buf); + + if (ret == XZ_FORMAT_ERROR) + return 0; + + if (ret != XZ_OK) + return 0; + + return 1; +} + +/* Try to find out size of uncompressed data, + * also do some footer sanity checks. */ +static int +test_footer (grub_file_t file) +{ + grub_xzio_t xzio = file->data; + grub_uint8_t footer[FOOTER_MAGIC_SIZE]; + grub_uint32_t backsize; + grub_uint8_t imarker; + grub_uint64_t uncompressed_size_total = 0; + grub_uint64_t uncompressed_size; + grub_uint64_t records; + + grub_file_seek (xzio->file, xzio->file->size - FOOTER_MAGIC_SIZE); + if (grub_file_read (xzio->file, footer, FOOTER_MAGIC_SIZE) + != FOOTER_MAGIC_SIZE + || grub_memcmp (footer, FOOTER_MAGIC, FOOTER_MAGIC_SIZE) != 0) + goto ERROR; + + grub_file_seek (xzio->file, xzio->file->size - 8); + if (grub_file_read (xzio->file, &backsize, sizeof (backsize)) + != sizeof (backsize)) + goto ERROR; + + /* Calculate real backward size. */ + backsize = (grub_le_to_cpu32 (backsize) + 1) * 4; + + /* Set file to the beginning of stream index. */ + grub_file_seek (xzio->file, + xzio->file->size - XZ_STREAM_FOOTER_SIZE - backsize); + + /* Test index marker. */ + if (grub_file_read (xzio->file, &imarker, sizeof (imarker)) + != sizeof (imarker) && imarker != 0x00) + goto ERROR; + + if (read_vli (xzio->file, &records) <= 0) + goto ERROR; + + for (; records != 0; records--) + { + if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Ignore unpadded. */ + goto ERROR; + if (read_vli (xzio->file, &uncompressed_size) <= 0) /* Uncompressed. */ + goto ERROR; + + uncompressed_size_total += uncompressed_size; + } + + file->size = uncompressed_size_total; + grub_file_seek (xzio->file, STREAM_HEADER_SIZE); + return 1; + +ERROR: + return 0; +} + +static grub_file_t +grub_xzio_open (grub_file_t io, enum grub_file_type type) +{ + grub_file_t file; + grub_xzio_t xzio; + + if (type & GRUB_FILE_TYPE_NO_DECOMPRESS) + return io; + + file = (grub_file_t) grub_zalloc (sizeof (*file)); + if (!file) + return 0; + + xzio = grub_zalloc (sizeof (*xzio)); + if (!xzio) + { + grub_free (file); + return 0; + } + + xzio->file = io; + + file->device = io->device; + file->data = xzio; + file->fs = &grub_xzio_fs; + file->size = GRUB_FILE_SIZE_UNKNOWN; + file->not_easily_seekable = 1; + + if (grub_file_tell (xzio->file) != 0) + grub_file_seek (xzio->file, 0); + + /* Allocated 64KiB for dictionary. + * Decoder will relocate if bigger is needed. */ + xzio->dec = xz_dec_init (1 << 16); + if (!xzio->dec) + { + grub_free (file); + grub_free (xzio); + return 0; + } + + xzio->buf.in = xzio->inbuf; + xzio->buf.out = xzio->outbuf; + xzio->buf.out_size = XZBUFSIZ; + + /* FIXME: don't test footer on not easily seekable files. */ + if (!test_header (file) || !test_footer (file)) + { + grub_errno = GRUB_ERR_NONE; + grub_file_seek (io, 0); + xz_dec_end (xzio->dec); + grub_free (xzio); + grub_free (file); + + return io; + } + + return file; +} + +static grub_ssize_t +grub_xzio_read (grub_file_t file, char *buf, grub_size_t len) +{ + grub_ssize_t ret = 0; + grub_ssize_t readret; + enum xz_ret xzret; + grub_xzio_t xzio = file->data; + grub_off_t current_offset; + + /* If seek backward need to reset decoder and start from beginning of file. + TODO Possible improvement by jumping blocks. */ + if (file->offset < xzio->saved_offset) + { + xz_dec_reset (xzio->dec); + xzio->saved_offset = 0; + xzio->buf.out_pos = 0; + xzio->buf.in_pos = 0; + xzio->buf.in_size = 0; + grub_file_seek (xzio->file, 0); + } + + current_offset = xzio->saved_offset; + + while (len > 0) + { + xzio->buf.out_size = file->offset + ret + len - current_offset; + if (xzio->buf.out_size > XZBUFSIZ) + xzio->buf.out_size = XZBUFSIZ; + /* Feed input. */ + if (xzio->buf.in_pos == xzio->buf.in_size) + { + readret = grub_file_read (xzio->file, xzio->inbuf, XZBUFSIZ); + if (readret < 0) + return -1; + xzio->buf.in_size = readret; + xzio->buf.in_pos = 0; + } + + xzret = xz_dec_run (xzio->dec, &xzio->buf); + switch (xzret) + { + case XZ_MEMLIMIT_ERROR: + case XZ_FORMAT_ERROR: + case XZ_OPTIONS_ERROR: + case XZ_DATA_ERROR: + case XZ_BUF_ERROR: + grub_error (GRUB_ERR_BAD_COMPRESSED_DATA, + N_("xz file corrupted or unsupported block options")); + return -1; + default: + break; + } + + { + grub_off_t new_offset = current_offset + xzio->buf.out_pos; + + if (file->offset <= new_offset) + /* Store first chunk of data in buffer. */ + { + grub_size_t delta = new_offset - (file->offset + ret); + grub_memmove (buf, xzio->buf.out + (xzio->buf.out_pos - delta), + delta); + len -= delta; + buf += delta; + ret += delta; + } + current_offset = new_offset; + } + xzio->buf.out_pos = 0; + + if (xzret == XZ_STREAM_END) /* Stream end, EOF. */ + break; + } + + if (ret >= 0) + xzio->saved_offset = file->offset + ret; + + return ret; +} + +/* Release everything, including the underlying file object. */ +static grub_err_t +grub_xzio_close (grub_file_t file) +{ + grub_xzio_t xzio = file->data; + + xz_dec_end (xzio->dec); + + grub_file_close (xzio->file); + grub_free (xzio); + + /* Device must not be closed twice. */ + file->device = 0; + file->name = 0; + return grub_errno; +} + +static struct grub_fs grub_xzio_fs = { + .name = "xzio", + .fs_dir = 0, + .fs_open = 0, + .fs_read = grub_xzio_read, + .fs_close = grub_xzio_close, + .fs_label = 0, + .next = 0 +}; + +GRUB_MOD_INIT (xzio) +{ + grub_file_filter_register (GRUB_FILE_FILTER_XZIO, grub_xzio_open); +} + +GRUB_MOD_FINI (xzio) +{ + grub_file_filter_unregister (GRUB_FILE_FILTER_XZIO); +} diff --git a/grub-core/kern/acpi.c b/grub-core/kern/acpi.c new file mode 100644 index 000000000..8ff0835d5 --- /dev/null +++ b/grub-core/kern/acpi.c @@ -0,0 +1,135 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include + +/* Simple checksum by summing all bytes. Used by ACPI and SMBIOS. */ +grub_uint8_t +grub_byte_checksum (void *base, grub_size_t size) +{ + grub_uint8_t *ptr; + grub_uint8_t ret = 0; + for (ptr = (grub_uint8_t *) base; ptr < ((grub_uint8_t *) base) + size; + ptr++) + ret += *ptr; + return ret; +} + +static void * +grub_acpi_rsdt_find_table (struct grub_acpi_table_header *rsdt, const char *sig) +{ + grub_size_t s; + grub_unaligned_uint32_t *ptr; + + if (!rsdt) + return 0; + + if (grub_memcmp (rsdt->signature, "RSDT", 4) != 0) + return 0; + + ptr = (grub_unaligned_uint32_t *) (rsdt + 1); + s = (rsdt->length - sizeof (*rsdt)) / sizeof (grub_uint32_t); + for (; s; s--, ptr++) + { + struct grub_acpi_table_header *tbl; + + /* Skip NULL entries in RSDT/XSDT. */ + if (!ptr->val) + continue; + tbl = (struct grub_acpi_table_header *) (grub_addr_t) ptr->val; + if (grub_memcmp (tbl->signature, sig, 4) == 0) + return tbl; + } + return 0; +} + +static void * +grub_acpi_xsdt_find_table (struct grub_acpi_table_header *xsdt, const char *sig) +{ + grub_size_t s; + grub_unaligned_uint64_t *ptr; + + if (!xsdt) + return 0; + + if (grub_memcmp (xsdt->signature, "XSDT", 4) != 0) + return 0; + + ptr = (grub_unaligned_uint64_t *) (xsdt + 1); + s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint64_t); + for (; s; s--, ptr++) + { + struct grub_acpi_table_header *tbl; + + /* Skip NULL entries in RSDT/XSDT. */ + if (!ptr->val) + continue; +#if GRUB_CPU_SIZEOF_VOID_P != 8 + if (ptr->val >> 32) + continue; +#endif + tbl = (struct grub_acpi_table_header *) (grub_addr_t) ptr->val; + if (grub_memcmp (tbl->signature, sig, 4) == 0) + return tbl; + } + return 0; +} + +void * +grub_acpi_find_table (const char *sig) +{ + struct grub_acpi_fadt *r = NULL; + struct grub_acpi_rsdp_v10 *rsdpv1; + struct grub_acpi_rsdp_v20 *rsdpv2; + + rsdpv1 = grub_machine_acpi_get_rsdpv1 (); + if (rsdpv1) + r = grub_acpi_rsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv1->rsdt_addr, + sig); + if (r) + return r; + rsdpv2 = grub_machine_acpi_get_rsdpv2 (); + if (rsdpv2 +#if GRUB_CPU_SIZEOF_VOID_P != 8 + && !(rsdpv2->xsdt_addr >> 32) +#endif + ) + r = grub_acpi_xsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv2->xsdt_addr, + sig); + if (r) + return r; + if (rsdpv2) + r = grub_acpi_rsdt_find_table ((struct grub_acpi_table_header *) + (grub_addr_t) rsdpv2->rsdpv1.rsdt_addr, + sig); + if (r) + return r; + return 0; +} + +struct grub_acpi_fadt * +grub_acpi_find_fadt (void) +{ + return grub_acpi_find_table (GRUB_ACPI_FADT_SIGNATURE); +} diff --git a/grub-core/kern/arm/cache.S b/grub-core/kern/arm/cache.S new file mode 100644 index 000000000..354a069fe --- /dev/null +++ b/grub-core/kern/arm/cache.S @@ -0,0 +1,123 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include + + .file "cache.S" + .text + .syntax unified +#if !defined (__thumb2__) || !defined (ARMV7) + .arm +#else + .thumb +#endif + +#if !defined (ARMV6) && !defined (ARMV7) +# error Unsupported architecture version! +#endif + + .align 2 + +/* + * Simple cache maintenance functions + */ + +@ r0 - *beg (inclusive) +@ r1 - *end (exclusive) +@void grub_arm_clean_dcache_range (grub_addr_t start, grub_addr_t end, grub_addr_t dlinesz) +#ifdef ARMV6 +FUNCTION(grub_arm_clean_dcache_range_armv6) +#else +FUNCTION(grub_arm_clean_dcache_range_armv7) +#endif + DSB + @ Clean data cache for range to point-of-unification +1: cmp r0, r1 + bge 2f +#ifdef ARMV6 + mcr p15, 0, r0, c7, c10, 1 @ Clean data cache line by MVA +#else + mcr p15, 0, r0, c7, c11, 1 @ DCCMVAU +#endif + add r0, r0, r2 @ Next line + b 1b +2: DSB + bx lr + +@ r0 - *beg (inclusive) +@ r1 - *end (exclusive) +#ifdef ARMV6 +FUNCTION(grub_arm_invalidate_icache_range_armv6) +#else +FUNCTION(grub_arm_invalidate_icache_range_armv7) +#endif + @ Invalidate instruction cache for range to point-of-unification +1: cmp r0, r1 + bge 2f + mcr p15, 0, r0, c7, c5, 1 @ ICIMVAU + add r0, r0, r2 @ Next line + b 1b + @ Branch predictor invalidate all +2: mcr p15, 0, r0, c7, c5, 6 @ BPIALL + DSB + ISB + bx lr + +#ifdef ARMV6 +FUNCTION(grub_arm_disable_caches_mmu_armv6) +#else +FUNCTION(grub_arm_disable_caches_mmu_armv7) +#endif + + push {r4, lr} + + @ disable D-cache + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 2) + mcr p15, 0, r0, c1, c0, 0 + DSB + ISB + + @ clean/invalidate D-cache + bl clean_invalidate_dcache + + @ disable I-cache + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 12) + mcr p15, 0, r0, c1, c0, 0 + DSB + ISB + + @ invalidate I-cache (also invalidates branch predictors) + mcr p15, 0, r0, c7, c5, 0 + DSB + ISB + + @ clear SCTLR M bit + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 0) + mcr p15, 0, r0, c1, c0, 0 + + mcr p15, 0, r0, c8, c7, 0 @ invalidate TLB + mcr p15, 0, r0, c7, c5, 6 @ invalidate branch predictor + DSB + ISB + + pop {r4, lr} + bx lr + diff --git a/grub-core/kern/arm/cache.c b/grub-core/kern/arm/cache.c new file mode 100644 index 000000000..6c75193e4 --- /dev/null +++ b/grub-core/kern/arm/cache.c @@ -0,0 +1,311 @@ +#include +#include +#include +#ifdef GRUB_MACHINE_UBOOT +#include +#include +#include +#endif + +/* This is only about cache architecture. It doesn't imply + the CPU architecture. */ +static enum + { + ARCH_UNKNOWN, + ARCH_ARMV5_WRITE_THROUGH, + ARCH_ARMV6, + ARCH_ARMV6_UNIFIED, + ARCH_ARMV7 + } type = ARCH_UNKNOWN; + +static int is_v6_mmu; + +static grub_uint32_t grub_arch_cache_dlinesz; +static grub_uint32_t grub_arch_cache_ilinesz; +static grub_uint32_t grub_arch_cache_max_linesz; + +/* Prototypes for asm functions. */ +void grub_arm_clean_dcache_range_armv6 (grub_addr_t start, grub_addr_t end, + grub_addr_t dlinesz); +void grub_arm_clean_dcache_range_armv7 (grub_addr_t start, grub_addr_t end, + grub_addr_t dlinesz); +void grub_arm_clean_dcache_range_poc_armv7 (grub_addr_t start, grub_addr_t end, + grub_addr_t dlinesz); +void grub_arm_invalidate_icache_range_armv6 (grub_addr_t start, grub_addr_t end, + grub_addr_t dlinesz); +void grub_arm_invalidate_icache_range_armv7 (grub_addr_t start, grub_addr_t end, + grub_addr_t dlinesz); +void grub_arm_disable_caches_mmu_armv6 (void); +void grub_arm_disable_caches_mmu_armv7 (void); +grub_uint32_t grub_arm_main_id (void); +grub_uint32_t grub_arm_cache_type (void); + +static void +probe_caches (void) +{ + grub_uint32_t main_id, cache_type; + + /* Read main ID Register */ + main_id = grub_arm_main_id (); + + switch ((main_id >> 16) & 0xf) + { + case 0x3: + case 0x4: + case 0x5: + case 0x6: + is_v6_mmu = 0; + break; + case 0x7: + case 0xf: + is_v6_mmu = 1; + break; + default: + grub_fatal ("Unsupported ARM ID 0x%x", main_id); + } + + /* Read Cache Type Register */ + cache_type = grub_arm_cache_type (); + + switch (cache_type >> 24) + { + case 0x00: + case 0x01: + grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); + grub_arch_cache_ilinesz = 8 << (cache_type & 3); + type = ARCH_ARMV5_WRITE_THROUGH; + break; + case 0x04: + case 0x0a: + case 0x0c: + case 0x0e: + case 0x1c: + grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); + grub_arch_cache_ilinesz = 8 << (cache_type & 3); + type = ARCH_ARMV6_UNIFIED; + break; + case 0x05: + case 0x0b: + case 0x0d: + case 0x0f: + case 0x1d: + grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3); + grub_arch_cache_ilinesz = 8 << (cache_type & 3); + type = ARCH_ARMV6; + break; + default: + /* + * The CTR register is pretty much unchanged from v7 onwards, + * and is guaranteed to be backward compatible (the IDC/DIC bits + * allow certain CMOs to be elided, but performing them is never + * wrong), hence handling it like its AArch64 equivalent. + */ + grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf); + grub_arch_cache_ilinesz = 4 << (cache_type & 0xf); + type = ARCH_ARMV7; + } + if (grub_arch_cache_dlinesz > grub_arch_cache_ilinesz) + grub_arch_cache_max_linesz = grub_arch_cache_dlinesz; + else + grub_arch_cache_max_linesz = grub_arch_cache_ilinesz; +} + +#ifdef GRUB_MACHINE_UBOOT + +static void subdivide (grub_uint32_t *table, grub_uint32_t *subtable, + grub_uint32_t addr) +{ + grub_uint32_t j; + addr = addr >> 20 << 20; + table[addr >> 20] = (grub_addr_t) subtable | 1; + for (j = 0; j < 256; j++) + subtable[j] = addr | (j << 12) + | (3 << 4) | (3 << 6) | (3 << 8) | (3 << 10) + | (0 << 3) | (1 << 2) | 2; +} + +void +grub_arm_enable_caches_mmu (void) +{ + grub_uint32_t *table; + grub_uint32_t i; + grub_uint32_t border_crossing = 0; + grub_uint32_t *subtable; + struct sys_info *si = grub_uboot_get_sys_info (); + + if (!si || (si->mr_no == 0)) + { + grub_printf ("couldn't get memory map, not enabling caches"); + grub_errno = GRUB_ERR_NONE; + return; + } + + if (type == ARCH_UNKNOWN) + probe_caches (); + + for (i = 0; (signed) i < si->mr_no; i++) + { + if (si->mr[i].start & ((1 << 20) - 1)) + border_crossing++; + if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1)) + border_crossing++; + } + + grub_printf ("%d crossers\n", border_crossing); + + table = grub_memalign (1 << 14, (1 << 14) + (border_crossing << 10)); + if (!table) + { + grub_printf ("couldn't allocate place for MMU table, not enabling caches"); + grub_errno = GRUB_ERR_NONE; + return; + } + + subtable = table + (1 << 12); + /* Map all unknown as device. */ + for (i = 0; i < (1 << 12); i++) + table[i] = (i << 20) | (3 << 10) | (0 << 3) | (1 << 2) | 2; + /* + Device: TEX= 0, C=0, B=1 + normal: TEX= 0, C=1, B=1 + AP = 3 + IMP = 0 + Domain = 0 +*/ + + for (i = 0; (signed) i < si->mr_no; i++) + { + if (si->mr[i].start & ((1 << 20) - 1)) + { + subdivide (table, subtable, si->mr[i].start); + subtable += (1 << 8); + } + if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1)) + { + subdivide (table, subtable, si->mr[i].start + si->mr[i].size); + subtable += (1 << 8); + } + } + + for (i = 0; (signed) i < si->mr_no; i++) + if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM + || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_SRAM + || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_FLASH) + { + grub_uint32_t cur, end; + cur = si->mr[i].start; + end = si->mr[i].start + si->mr[i].size; + while (cur < end) + { + grub_uint32_t *st; + if ((table[cur >> 20] & 3) == 2) + { + cur = cur >> 20 << 20; + table[cur >> 20] = cur | (3 << 10) | (1 << 3) | (1 << 2) | 2; + cur += (1 << 20); + continue; + } + cur = cur >> 12 << 12; + st = (grub_uint32_t *) (table[cur >> 20] & ~0x3ff); + st[(cur >> 12) & 0xff] = cur | (3 << 4) | (3 << 6) + | (3 << 8) | (3 << 10) + | (1 << 3) | (1 << 2) | 2; + cur += (1 << 12); + } + } + + grub_printf ("MMU tables generated\n"); + if (is_v6_mmu) + grub_arm_clear_mmu_v6 (); + + grub_printf ("enabling MMU\n"); + grub_arm_enable_mmu (table); + grub_printf ("MMU enabled\n"); +} + +#endif + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + grub_addr_t start = (grub_addr_t) address; + grub_addr_t end = start + len; + + if (type == ARCH_UNKNOWN) + probe_caches (); + start = ALIGN_DOWN (start, grub_arch_cache_max_linesz); + end = ALIGN_UP (end, grub_arch_cache_max_linesz); + switch (type) + { + case ARCH_ARMV6: + grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); + grub_arm_invalidate_icache_range_armv6 (start, end, + grub_arch_cache_ilinesz); + break; + case ARCH_ARMV7: + grub_arm_clean_dcache_range_armv7 (start, end, grub_arch_cache_dlinesz); + grub_arm_invalidate_icache_range_armv7 (start, end, + grub_arch_cache_ilinesz); + break; + /* Nothing to do. */ + case ARCH_ARMV5_WRITE_THROUGH: + case ARCH_ARMV6_UNIFIED: + break; + /* Pacify GCC. */ + case ARCH_UNKNOWN: + break; + } +} + +void +grub_arch_sync_dma_caches (volatile void *address, grub_size_t len) +{ + grub_addr_t start = (grub_addr_t) address; + grub_addr_t end = start + len; + + if (type == ARCH_UNKNOWN) + probe_caches (); + start = ALIGN_DOWN (start, grub_arch_cache_max_linesz); + end = ALIGN_UP (end, grub_arch_cache_max_linesz); + switch (type) + { + case ARCH_ARMV6: + grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); + grub_arm_invalidate_icache_range_armv6 (start, end, + grub_arch_cache_ilinesz); + break; + case ARCH_ARMV5_WRITE_THROUGH: + case ARCH_ARMV6_UNIFIED: + grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz); + break; + case ARCH_ARMV7: + grub_arm_clean_dcache_range_poc_armv7 (start, end, grub_arch_cache_dlinesz); + grub_arm_invalidate_icache_range_armv7 (start, end, + grub_arch_cache_ilinesz); + break; + /* Pacify GCC. */ + case ARCH_UNKNOWN: + break; + } +} + +void +grub_arm_disable_caches_mmu (void) +{ + if (type == ARCH_UNKNOWN) + probe_caches (); + switch (type) + { + case ARCH_ARMV5_WRITE_THROUGH: + case ARCH_ARMV6_UNIFIED: + case ARCH_ARMV6: + grub_arm_disable_caches_mmu_armv6 (); + break; + case ARCH_ARMV7: + grub_arm_disable_caches_mmu_armv7 (); + break; + /* Pacify GCC. */ + case ARCH_UNKNOWN: + break; + } +} diff --git a/grub-core/kern/arm/cache_armv6.S b/grub-core/kern/arm/cache_armv6.S new file mode 100644 index 000000000..dfaded0eb --- /dev/null +++ b/grub-core/kern/arm/cache_armv6.S @@ -0,0 +1,72 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include + + .file "cache_armv6.S" + .text + .syntax unified + .arm + +# define DMB mcr p15, 0, r0, c7, c10, 5 +# define DSB mcr p15, 0, r0, c7, c10, 4 +# define ISB mcr p15, 0, r0, c7, c5, 4 +#define ARMV6 1 + +clean_invalidate_dcache: + mcr p15, 0, r0, c7, c14, 0 @ Clean/Invalidate D-cache + bx lr + +#include "cache.S" + +FUNCTION(grub_arm_main_id) + mrc p15, 0, r0, c0, c0, 0 + bx lr + +FUNCTION(grub_arm_cache_type) + mrc p15, 0, r0, c0, c0, 1 + bx lr + +FUNCTION(grub_arm_clear_mmu_v6) + mov r0, #0 + mcr p15, 0, r0, c2, c0, 2 + bx lr + +FUNCTION(grub_arm_enable_mmu) + mcr p15, 0, r0, c2, c0, 0 + + mvn r0, #0 + mcr p15, 0, r0, c3, c0, 0 + + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 23) + mcr p15, 0, r0, c1, c0, 0 + + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 0) + mcr p15, 0, r0, c1, c0, 0 + + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 2) + mcr p15, 0, r0, c1, c0, 0 + + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 12) + mcr p15, 0, r0, c1, c0, 0 + + bx lr diff --git a/grub-core/kern/arm/cache_armv7.S b/grub-core/kern/arm/cache_armv7.S new file mode 100644 index 000000000..5ae76a3d8 --- /dev/null +++ b/grub-core/kern/arm/cache_armv7.S @@ -0,0 +1,138 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include + + .file "cache_armv7.S" + .text + .syntax unified +#if !defined (__thumb2__) + .arch armv7a + .arm +#else + .arch armv7 + .thumb +#endif +# define DMB dmb +# define DSB dsb +# define ISB isb +#define ARMV7 1 + +FUNCTION(grub_arm_clean_dcache_range_poc_armv7) + DSB + @ Clean data cache for range to point-of-coherence +1: cmp r0, r1 + bge 2f + mcr p15, 0, r0, c7, c14, 1 @ DCCMVAC + add r0, r0, r2 @ Next line + b 1b +2: DSB + bx lr + + + @ r0 - CLIDR + @ r1 - LoC + @ r2 - current level + @ r3 - num sets + @ r4 - num ways + @ r5 - current set + @ r6 - current way + @ r7 - line size + @ r8 - scratch + @ r9 - scratch + @ r10 - scratch + @ r11 - scratch +clean_invalidate_dcache: + push {r4-r12, lr} + mrc p15, 1, r0, c0, c0, 1 @ Read CLIDR + lsr r1, r0, #24 @ Extract LoC + and r1, r1, #0x7 + + mov r2, #0 @ First level, L1 +2: and r8, r0, #7 @ cache type at current level + cmp r8, #2 + blt 5f @ instruction only, or none, skip level + + @ set current cache level/type (for CCSIDR read) + lsl r8, r2, #1 + mcr p15, 2, r8, c0, c0, 0 @ Write CSSELR (level, type: data/uni) + + @ read current cache information + mrc p15, 1, r8, c0, c0, 0 @ Read CCSIDR + lsr r3, r8, #13 @ Number of sets -1 + + @ Keep only 14 bits of r3 + lsl r3, r3, #18 + lsr r3, r3, #18 + + lsr r4, r8, #3 @ Number of ways -1 + + @ Keep only 9 bits of r4 + lsl r4, r4, #23 + lsr r4, r4, #23 + + and r7, r8, #7 @ log2(line size in words) - 2 + add r7, r7, #2 @ adjust + mov r8, #1 + lsl r7, r8, r7 @ -> line size in words + lsl r7, r7, #2 @ -> bytes + + @ set loop + mov r5, #0 @ current set = 0 +3: lsl r8, r2, #1 @ insert level + clz r9, r7 @ calculate set field offset + mov r10, #31 + sub r9, r10, r9 + lsl r10, r5, r9 + orr r8, r8, r10 @ insert set field + + @ way loop + @ calculate way field offset + mov r6, #0 @ current way = 0 + add r10, r4, #1 + clz r9, r10 @ r9 = way field offset + add r9, r9, #1 +4: lsl r10, r6, r9 + orr r11, r8, r10 @ insert way field + + @ clean and invalidate line by set/way + mcr p15, 0, r11, c7, c14, 2 @ DCCISW + + @ next way + add r6, r6, #1 + cmp r6, r4 + ble 4b + + @ next set + add r5, r5, #1 + cmp r5, r3 + ble 3b + + @ next level +5: lsr r0, r0, #3 @ align next level CLIDR 'type' field + add r2, r2, #1 @ increment cache level counter + cmp r2, r1 + blt 2b @ outer loop + + @ return +6: DSB + ISB + pop {r4-r12, lr} + bx lr + +#include "cache.S" \ No newline at end of file diff --git a/grub-core/kern/arm/compiler-rt.S b/grub-core/kern/arm/compiler-rt.S new file mode 100644 index 000000000..645b42f50 --- /dev/null +++ b/grub-core/kern/arm/compiler-rt.S @@ -0,0 +1,86 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + + .file "misc.S" + .text + .syntax unified +#if !defined (__thumb2__) + .arm +#else + .thumb +#endif + + .align 2 + +FUNCTION(__muldi3) +FUNCTION(__aeabi_lmul) + stmfd sp!, {r4, fp} + add fp, sp, #4 + sub sp, sp, #16 + str r0, [fp, #-12] + str r1, [fp, #-8] + str r2, [fp, #-20] + str r3, [fp, #-16] + ldr r3, [fp, #-8] + ldr r2, [fp, #-20] + mul r2, r3, r2 + ldr r3, [fp, #-16] + ldr r1, [fp, #-12] + mul r3, r1, r3 + add r2, r2, r3 + ldr r0, [fp, #-12] + ldr r1, [fp, #-20] + umull r3, r4, r0, r1 + add r2, r2, r4 + mov r4, r2 + mov r0, r3 + mov r1, r4 + mov sp, fp + sub sp, sp, #4 + ldmfd sp!, {r4, fp} + bx lr + + .macro division32 parent + + sub sp, sp, #8 @ Allocate naturally aligned 64-bit space + stmfd sp!, {r3,lr} @ Dummy r3 to maintain stack alignment + add r2, sp, #8 @ Set r2 to address of 64-bit space + bl \parent + ldr r1, [sp, #8] @ Extract remainder + ldmfd sp!, {r3,lr} @ Pop into an unused arg/scratch register + add sp, sp, #8 + bx lr + .endm + +FUNCTION(__aeabi_uidivmod) + division32 grub_divmod32 +FUNCTION(__aeabi_idivmod) + division32 grub_divmod32s + +/* + * Null divide-by-zero handler + */ +FUNCTION(__aeabi_unwind_cpp_pr0) +FUNCTION(raise) + mov r0, #0 + bx lr + + END diff --git a/grub-core/kern/arm/coreboot/cbtable.c b/grub-core/kern/arm/coreboot/cbtable.c new file mode 100644 index 000000000..8a655bb5c --- /dev/null +++ b/grub-core/kern/arm/coreboot/cbtable.c @@ -0,0 +1,40 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2013 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_linuxbios_table_header_t +grub_linuxbios_get_tables (void) +{ + grub_linuxbios_table_header_t table_header + = (grub_linuxbios_table_header_t) grub_arm_saved_registers.r[0]; + + if (!grub_linuxbios_check_signature (table_header)) + return 0; + + return table_header; +} diff --git a/grub-core/kern/arm/coreboot/coreboot.S b/grub-core/kern/arm/coreboot/coreboot.S new file mode 100644 index 000000000..a1104526c --- /dev/null +++ b/grub-core/kern/arm/coreboot/coreboot.S @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include + + .file "coreboot.S" + .text + .syntax unified +#if !defined (__thumb2__) + .arch armv7a + .arm +#else + .arch armv7 + .thumb +#endif + +FUNCTION(grub_arm_pfr1) + mrc p15, 0, r0, c0, c1, 1 + bx lr + +FUNCTION(grub_armv7_get_timer_value) + isb + mrrc p15, 1, r0, r1, c14 + bx lr + +FUNCTION(grub_armv7_get_timer_frequency) + mrc p15, 0, r0, c14, c0, 0 + bx lr + diff --git a/grub-core/kern/arm/coreboot/dma.c b/grub-core/kern/arm/coreboot/dma.c new file mode 100644 index 000000000..2c2a62789 --- /dev/null +++ b/grub-core/kern/arm/coreboot/dma.c @@ -0,0 +1,59 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +struct grub_pci_dma_chunk * +grub_memalign_dma32 (grub_size_t align, grub_size_t size) +{ + void *ret; + if (align < 64) + align = 64; + size = ALIGN_UP (size, align); + ret = grub_memalign (align, size); + if (!ret) + return 0; + grub_arch_sync_dma_caches (ret, size); + return ret; +} + +void +grub_dma_free (struct grub_pci_dma_chunk *ch) +{ + grub_size_t size = (((struct grub_mm_header *) ch) - 1)->size * GRUB_MM_ALIGN; + grub_arch_sync_dma_caches (ch, size); + grub_free (ch); +} + +volatile void * +grub_dma_get_virt (struct grub_pci_dma_chunk *ch) +{ + return (void *) ch; +} + +grub_uint32_t +grub_dma_get_phys (struct grub_pci_dma_chunk *ch) +{ + return (grub_uint32_t) (grub_addr_t) ch; +} + diff --git a/grub-core/kern/arm/coreboot/init.c b/grub-core/kern/arm/coreboot/init.c new file mode 100644 index 000000000..8d8c5b829 --- /dev/null +++ b/grub-core/kern/arm/coreboot/init.c @@ -0,0 +1,151 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern grub_uint8_t _start[]; +extern grub_uint8_t _end[]; +extern grub_uint8_t _edata[]; +grub_addr_t start_of_ram = ~(grub_addr_t)0; + +void __attribute__ ((noreturn)) +grub_exit (void) +{ + /* We can't use grub_fatal() in this function. This would create an infinite + loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ + while (1) + grub_cpu_idle (); +} + +static grub_uint64_t modend; +static int have_memory = 0; + +/* Helper for grub_machine_init. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + grub_uint64_t begin = addr, end = addr + size; + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + /* Restrict ourselves to 32-bit memory space. */ + if (begin > GRUB_ULONG_MAX) + return 0; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; +#endif + + if (start_of_ram > begin) + start_of_ram = begin; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (modend && begin < modend) + { + if (begin < (grub_addr_t)_start) + { + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) ((grub_addr_t)_start - begin)); + have_memory = 1; + } + begin = modend; + } + + /* Avoid DMA problems. */ + if (end >= 0xfe000000) + end = 0xfe000000; + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); + + have_memory = 1; + + return 0; +} + +void +grub_machine_init (void) +{ + struct grub_module_header *header; + void *dtb = 0; + grub_size_t dtb_size = 0; + + modend = grub_modules_get_end (); + + grub_video_coreboot_fb_early_init (); + + grub_machine_mmap_iterate (heap_init, NULL); + if (!have_memory) + grub_fatal ("No memory found"); + + grub_video_coreboot_fb_late_init (); + + grub_font_init (); + grub_gfxterm_init (); + + FOR_MODULES (header) + if (header->type == OBJ_TYPE_DTB) + { + char *dtb_orig_addr, *dtb_copy; + dtb_orig_addr = (char *) header + sizeof (struct grub_module_header); + + dtb_size = header->size - sizeof (struct grub_module_header); + dtb = dtb_copy = grub_malloc (dtb_size); + grub_memmove (dtb_copy, dtb_orig_addr, dtb_size); + break; + } + if (!dtb) + grub_fatal ("No DTB found"); + grub_fdtbus_init (dtb, dtb_size); + + grub_rk3288_spi_init (); + + grub_machine_timer_init (); + grub_cros_init (); + grub_pl050_init (); +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} diff --git a/grub-core/kern/arm/coreboot/timer.c b/grub-core/kern/arm/coreboot/timer.c new file mode 100644 index 000000000..d97b844f8 --- /dev/null +++ b/grub-core/kern/arm/coreboot/timer.c @@ -0,0 +1,101 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +grub_uint64_t +grub_armv7_get_timer_value(void); + +grub_uint32_t +grub_armv7_get_timer_frequency(void); + +grub_uint32_t +grub_arm_pfr1(void); + +static int have_timer = 0; +static volatile grub_uint32_t *sp804_regs; + +static grub_uint64_t +sp804_get_time_ms (void) +{ + static grub_uint32_t high, last_low; + grub_uint32_t low = ~sp804_regs[1]; + if (last_low > low) + high++; + last_low = low; + return grub_divmod64 ((((grub_uint64_t) high) << 32) | low, + 1000, 0); +} + +static grub_err_t +sp804_attach(const struct grub_fdtbus_dev *dev) +{ + if (have_timer) + return GRUB_ERR_NONE; + sp804_regs = grub_fdtbus_map_reg (dev, 0, 0); + if (!grub_fdtbus_is_mapping_valid (sp804_regs)) + return grub_error (GRUB_ERR_IO, "could not map sp804: %p", sp804_regs); + grub_install_get_time_ms (sp804_get_time_ms); + have_timer = 1; + return GRUB_ERR_NONE; +} + +struct grub_fdtbus_driver sp804 = +{ + .compatible = "arm,sp804", + .attach = sp804_attach +}; + +static grub_uint32_t timer_frequency_in_khz; + +static grub_uint64_t +generic_get_time_ms (void) +{ + return grub_divmod64 (grub_armv7_get_timer_value(), timer_frequency_in_khz, 0); +} + +static int +try_generic_timer (void) +{ + if (((grub_arm_pfr1 () >> 16) & 0xf) != 1) + return 0; + grub_printf ("freq = %x\n", grub_armv7_get_timer_frequency()); + timer_frequency_in_khz = 0x016e3600 / 1000; //grub_armv7_get_timer_frequency() / 1000; + if (timer_frequency_in_khz == 0) + return 0; + grub_install_get_time_ms (generic_get_time_ms); + have_timer = 1; + return 1; +} + +void +grub_machine_timer_init (void) +{ + grub_fdtbus_register (&sp804); + + if (!have_timer) + try_generic_timer (); + if (!have_timer) + grub_fatal ("No timer found"); +} diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c new file mode 100644 index 000000000..eab9d17ff --- /dev/null +++ b/grub-core/kern/arm/dl.c @@ -0,0 +1,280 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +struct trampoline_arm +{ +#define ARM_LOAD_IP 0xe59fc000 +#define ARM_BX 0xe12fff1c +#define ARM_MOV_PC 0xe1a0f00c + grub_uint32_t load_ip; /* ldr ip, [pc] */ + grub_uint32_t bx; /* bx ip or mov pc, ip*/ + grub_uint32_t addr; +}; + +static grub_uint16_t thumb_template[8] = + { + 0x468c, /* mov ip, r1 */ + 0x4903, /* ldr r1, [pc, #12] ; (10 <.text+0x10>) */ + /* Exchange R1 and IP in limited Thumb instruction set. + IP gets negated but we compensate it by C code. */ + /* R1 IP */ + /* -A R1 */ + 0x4461, /* add r1, ip */ /* R1-A R1 */ + 0x4249, /* negs r1, r1 */ /* A-R1 R1 */ + 0x448c, /* add ip, r1 */ /* A-R1 A */ + 0x4249, /* negs r1, r1 */ /* R1-A A */ + 0x4461, /* add r1, ip */ /* R1 A */ + 0x4760 /* bx ip */ + }; + +struct trampoline_thumb +{ + grub_uint16_t template[8]; + grub_uint32_t neg_addr; +}; + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize)) + if (s->sh_type == SHT_REL) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset), + max = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_size); + rel + 1 <= max; + rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize)) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_ARM_CALL: + case R_ARM_JUMP24: + { + *tramp += sizeof (struct trampoline_arm); + break; + } + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + case R_ARM_THM_JUMP19: + { + *tramp += sizeof (struct trampoline_thumb); + break; + } + } + } + + grub_dprintf ("dl", "trampoline size %x\n", *tramp); + + return GRUB_ERR_NONE; +} + +/************************************************* + * Runtime dynamic linker with helper functions. * + *************************************************/ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Addr *target, sym_addr; + grub_err_t retval; + Elf_Sym *sym; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + target = (void *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + sym_addr = sym->st_value; + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_ARM_ABS32: + { + /* Data will be naturally aligned */ + retval = grub_arm_reloc_abs32 (target, sym_addr); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + case R_ARM_CALL: + case R_ARM_JUMP24: + { + grub_int32_t offset; + + sym_addr += grub_arm_jump24_get_offset (target); + offset = sym_addr - (grub_uint32_t) target; + + if ((sym_addr & 1) || !grub_arm_jump24_check_offset (offset)) + { + struct trampoline_arm *tp = mod->trampptr; + mod->trampptr = tp + 1; + tp->load_ip = ARM_LOAD_IP; + tp->bx = (sym_addr & 1) ? ARM_BX : ARM_MOV_PC; + tp->addr = sym_addr + 8; + offset = (grub_uint8_t *) tp - (grub_uint8_t *) target - 8; + } + if (!grub_arm_jump24_check_offset (offset)) + return grub_error (GRUB_ERR_BAD_MODULE, + "trampoline out of range"); + grub_arm_jump24_set_offset (target, offset); + } + break; + case R_ARM_THM_CALL: + case R_ARM_THM_JUMP24: + { + /* Thumb instructions can be 16-bit aligned */ + grub_int32_t offset; + + sym_addr += grub_arm_thm_call_get_offset ((grub_uint16_t *) target); + + grub_dprintf ("dl", " sym_addr = 0x%08x\n", sym_addr); + if (ELF_ST_TYPE (sym->st_info) != STT_FUNC) + sym_addr |= 1; + + offset = sym_addr - (grub_uint32_t) target; + + grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n", + target, sym_addr, offset); + + if (!(sym_addr & 1) || (offset < -0x200000 || offset >= 0x200000)) + { + struct trampoline_thumb *tp = mod->trampptr; + mod->trampptr = tp + 1; + grub_memcpy (tp->template, thumb_template, sizeof (tp->template)); + tp->neg_addr = -sym_addr - 4; + offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1; + } + + if (offset < -0x200000 || offset >= 0x200000) + return grub_error (GRUB_ERR_BAD_MODULE, + "trampoline out of range"); + + grub_dprintf ("dl", " relative destination = %p\n", + (char *) target + offset); + + retval = grub_arm_thm_call_set_offset ((grub_uint16_t *) target, offset); + if (retval != GRUB_ERR_NONE) + return retval; + } + break; + /* Happens when compiled with -march=armv4. Since currently we need + at least armv5, keep bx as-is. + */ + case R_ARM_V4BX: + break; + case R_ARM_THM_MOVW_ABS_NC: + case R_ARM_THM_MOVT_ABS: + { + grub_uint32_t offset; + offset = grub_arm_thm_movw_movt_get_value((grub_uint16_t *) target); + offset += sym_addr; + + if (ELF_R_TYPE (rel->r_info) == R_ARM_THM_MOVT_ABS) + offset >>= 16; + else + offset &= 0xffff; + + grub_arm_thm_movw_movt_set_value((grub_uint16_t *) target, offset); + } + break; + case R_ARM_THM_JUMP19: + { + /* Thumb instructions can be 16-bit aligned */ + grub_int32_t offset; + + sym_addr += grub_arm_thm_jump19_get_offset ((grub_uint16_t *) target); + + if (ELF_ST_TYPE (sym->st_info) != STT_FUNC) + sym_addr |= 1; + + offset = sym_addr - (grub_uint32_t) target; + + if (!grub_arm_thm_jump19_check_offset (offset) + || !(sym_addr & 1)) + { + struct trampoline_thumb *tp = mod->trampptr; + mod->trampptr = tp + 1; + grub_memcpy (tp->template, thumb_template, sizeof (tp->template)); + tp->neg_addr = -sym_addr - 4; + offset = ((grub_uint8_t *) tp - (grub_uint8_t *) target - 4) | 1; + } + + if (!grub_arm_thm_jump19_check_offset (offset)) + return grub_error (GRUB_ERR_BAD_MODULE, + "trampoline out of range"); + + grub_arm_thm_jump19_set_offset ((grub_uint16_t *) target, offset); + } + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } + + return GRUB_ERR_NONE; +} + + +/* + * Check if EHDR is a valid ELF header. + */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_ARM) + return grub_error (GRUB_ERR_BAD_OS, + N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm/dl_helper.c b/grub-core/kern/arm/dl_helper.c new file mode 100644 index 000000000..21d77f763 --- /dev/null +++ b/grub-core/kern/arm/dl_helper.c @@ -0,0 +1,245 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static inline grub_uint32_t +thumb_get_instruction_word (grub_uint16_t *target) +{ + /* Extract instruction word in alignment-safe manner */ + return grub_le_to_cpu16 ((*target)) << 16 | grub_le_to_cpu16 (*(target + 1)); +} + +static inline void +thumb_set_instruction_word (grub_uint16_t *target, grub_uint32_t insword) +{ + *target = grub_cpu_to_le16 (insword >> 16); + *(target + 1) = grub_cpu_to_le16 (insword & 0xffff); +} + +/* + * R_ARM_ABS32 + * + * Simple relocation of 32-bit value (in literal pool) + */ +grub_err_t +grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr) +{ + Elf32_Addr tmp; + + tmp = grub_le_to_cpu32 (*target); + tmp += sym_addr; + *target = grub_cpu_to_le32 (tmp); + + return GRUB_ERR_NONE; +} + +/******************************************************************** + * Thumb (T32) relocations: * + * * + * 32-bit Thumb instructions can be 16-bit aligned, and are fetched * + * little-endian, requiring some additional fiddling. * + ********************************************************************/ + +grub_int32_t +grub_arm_thm_call_get_offset (grub_uint16_t *target) +{ + grub_uint32_t sign, j1, j2; + grub_uint32_t insword; + grub_int32_t offset; + + insword = thumb_get_instruction_word (target); + + /* Extract bitfields from instruction words */ + sign = (insword >> 26) & 1; + j1 = (insword >> 13) & 1; + j2 = (insword >> 11) & 1; + offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) | + ((~(j2 ^ sign) & 1) << 22) | + ((insword & 0x03ff0000) >> 4) | ((insword & 0x000007ff) << 1); + + /* Sign adjust and calculate offset */ + if (offset & (1 << 24)) + offset -= (1 << 25); + + return offset; +} + +grub_err_t +grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset) +{ + grub_uint32_t sign, j1, j2; + const grub_uint32_t insmask = 0xf800d000; + grub_uint32_t insword; + int is_blx; + + insword = thumb_get_instruction_word (target); + + if (((insword >> 12) & 0xd) == 0xc) + is_blx = 1; + else + is_blx = 0; + + if (!is_blx && !(offset & 1)) + return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM"); + + /* Transform blx into bl if necessarry. */ + if (is_blx && (offset & 1)) + insword |= (1 << 12); + + /* Reassemble instruction word */ + sign = (offset >> 24) & 1; + j1 = sign ^ (~(offset >> 23) & 1); + j2 = sign ^ (~(offset >> 22) & 1); + insword = (insword & insmask) | + (sign << 26) | + (((offset >> 12) & 0x03ff) << 16) | + (j1 << 13) | (j2 << 11) | ((offset >> 1) & 0x07ff); + + thumb_set_instruction_word (target, insword); + + grub_dprintf ("dl", " *insword = 0x%08x", insword); + + return GRUB_ERR_NONE; +} + +grub_int32_t +grub_arm_thm_jump19_get_offset (grub_uint16_t *target) +{ + grub_int32_t offset; + grub_uint32_t insword; + + insword = thumb_get_instruction_word (target); + + /* Extract and sign extend offset */ + offset = ((insword >> 26) & 1) << 19 + | ((insword >> 11) & 1) << 18 + | ((insword >> 13) & 1) << 17 + | ((insword >> 16) & 0x3f) << 11 + | (insword & 0x7ff); + offset <<= 1; + if (offset & (1 << 20)) + offset -= (1 << 21); + + return offset; +} + +void +grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset) +{ + grub_uint32_t insword; + const grub_uint32_t insmask = 0xfbc0d000; + + offset >>= 1; + offset &= 0xfffff; + + insword = thumb_get_instruction_word (target); + + /* Reassemble instruction word and write back */ + insword &= insmask; + insword |= ((offset >> 19) & 1) << 26 + | ((offset >> 18) & 1) << 11 + | ((offset >> 17) & 1) << 13 + | ((offset >> 11) & 0x3f) << 16 + | (offset & 0x7ff); + thumb_set_instruction_word (target, insword); +} + +int +grub_arm_thm_jump19_check_offset (grub_int32_t offset) +{ + if ((offset > 1048574) || (offset < -1048576)) + return 0; + return 1; +} + +grub_uint16_t +grub_arm_thm_movw_movt_get_value (grub_uint16_t *target) +{ + grub_uint32_t insword; + + insword = thumb_get_instruction_word (target); + + return ((insword & 0xf0000) >> 4) | ((insword & 0x04000000) >> 15) | \ + ((insword & 0x7000) >> 4) | (insword & 0xff); +} + +void +grub_arm_thm_movw_movt_set_value (grub_uint16_t *target, grub_uint16_t value) +{ + grub_uint32_t insword; + const grub_uint32_t insmask = 0xfbf08f00; + + insword = thumb_get_instruction_word (target); + insword &= insmask; + + insword |= ((value & 0xf000) << 4) | ((value & 0x0800) << 15) | \ + ((value & 0x0700) << 4) | (value & 0xff); + + thumb_set_instruction_word (target, insword); +} + + +/*********************************************************** + * ARM (A32) relocations: * + * * + * ARM instructions are 32-bit in size and 32-bit aligned. * + ***********************************************************/ + +grub_int32_t +grub_arm_jump24_get_offset (grub_uint32_t *target) +{ + grub_int32_t offset; + grub_uint32_t insword; + + insword = grub_le_to_cpu32 (*target); + + offset = (insword & 0x00ffffff) << 2; + if (offset & 0x02000000) + offset -= 0x04000000; + return offset; +} + +int +grub_arm_jump24_check_offset (grub_int32_t offset) +{ + if (offset >= 0x02000000 || offset < -0x02000000) + return 0; + return 1; +} + +void +grub_arm_jump24_set_offset (grub_uint32_t *target, + grub_int32_t offset) +{ + grub_uint32_t insword; + + insword = grub_le_to_cpu32 (*target); + + insword &= 0xff000000; + insword |= (offset >> 2) & 0x00ffffff; + + *target = grub_cpu_to_le32 (insword); +} diff --git a/grub-core/kern/arm/efi/init.c b/grub-core/kern/arm/efi/init.c new file mode 100644 index 000000000..809f69c8c --- /dev/null +++ b/grub-core/kern/arm/efi/init.c @@ -0,0 +1,77 @@ +/* init.c - initialize an arm-based EFI system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static grub_uint64_t tmr; +static grub_efi_event_t tmr_evt; + +static grub_uint64_t +grub_efi_get_time_ms (void) +{ + return tmr; +} + +static void __grub_efi_api +increment_timer (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + tmr += 10; +} + +void +grub_machine_init (void) +{ + grub_efi_boot_services_t *b; + + grub_efi_init (); + + b = grub_efi_system_table->boot_services; + + b->create_event (GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL, + GRUB_EFI_TPL_CALLBACK, increment_timer, NULL, &tmr_evt); + b->set_timer (tmr_evt, GRUB_EFI_TIMER_PERIODIC, 100000); + + grub_install_get_time_ms (grub_efi_get_time_ms); +} + +void +grub_machine_fini (int flags) +{ + grub_efi_boot_services_t *b; + + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + b = grub_efi_system_table->boot_services; + + b->set_timer (tmr_evt, GRUB_EFI_TIMER_CANCEL, 0); + b->close_event (tmr_evt); + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S new file mode 100644 index 000000000..9f8265315 --- /dev/null +++ b/grub-core/kern/arm/efi/startup.S @@ -0,0 +1,36 @@ +/* + * (C) Copyright 2013 Free Software Foundation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ + +#include + + .file "startup.S" + .text + .arm +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0. + */ + ldr ip, =EXT_C(grub_efi_image_handle) + str r0, [ip] + ldr ip, =EXT_C(grub_efi_system_table) + str r1, [ip] + ldr ip, =EXT_C(grub_main) + bx ip + END diff --git a/grub-core/kern/arm/startup.S b/grub-core/kern/arm/startup.S new file mode 100644 index 000000000..3946fe8e1 --- /dev/null +++ b/grub-core/kern/arm/startup.S @@ -0,0 +1,177 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include + +/* + * GRUB is called from U-Boot as a Linux Kernel type image, which + * means among other things that it always enters in ARM state. + * + * coreboot starts in ARM mode as well. + * + * Overview of GRUB image layout: + * + * _start: + * Entry point (1 ARM branch instruction, to "codestart") + * grub_total_module_size: + * Data field: Size of included module blob + * (when generated by grub-mkimage) + * codestart: + * Remainder of statically-linked executable code and data. + * __bss_start: + * Start of included module blob. + * Also where global/static variables are located. + * _end: + * End of bss region (but not necessarily module blob). + * : + * : + * Loadable modules, post relocation. + * : + */ + + .text + .arm +FUNCTION(_start) + b codestart + + @ Size of final image integrated module blob - set by grub-mkimage + .org _start + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE +VARIABLE(grub_total_module_size) + .long 0 + +VARIABLE(grub_modbase) + .long 0 +bss_start_ptr: + .long EXT_C(__bss_start) +end_ptr: + .long EXT_C(_end) + + @ Memory map at start: + @ * text+data + @ * list relocations + @ * modules + @ Before we enter C, we need to apply the relocations + @ and get following map: + @ * text+data + @ * BSS (cleared) + @ * stack + @ * modules + @ + @ To make things easier we ensure + @ that BSS+stack is larger than list of relocations + @ by increasing stack if necessarry. + @ This allows us to always unconditionally copy backwards + @ Currently list of relocations is ~5K and stack is set + @ to be at least 256K + +FUNCTION(codestart) + @ Store context: Machine ID, atags/dtb, ... + @ U-Boot API signature is stored on the U-Boot heap + @ Stack pointer used as start address for signature probing + mov r12, sp + adr sp, entry_state + push {r0-r12,lr} @ store U-Boot context (sp in r12) + + adr r1, _start + ldr r0, bss_start_ptr @ src + add r0, r0, r1 + + add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + mvn r2, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + and r0, r0, r2 +1: + ldr r3, [r0], #4 @load next offset + @ both -2 and -1 are treated the same as we have only one type of relocs + @ -2 means "end of this type of relocs" and -1 means "end of all relocs" + add r2, r3, #2 + cmp r2, #1 + bls reloc_done + @ Adjust next offset + ldr r2, [r3, r1] + add r2, r2, r1 + str r2, [r3, r1] + b 1b + +reloc_done: + + @ Modules have been stored as a blob + @ they need to be manually relocated to _end + add r0, r0, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + mvn r1, #(GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + and r0, r0, r1 @ src = aligned end of relocations + + ldr r1, end_ptr @ dst = End of BSS + ldr r2, grub_total_module_size @ blob size + + add r1, r1, #GRUB_KERNEL_MACHINE_STACK_SIZE + and r1, r1, #~0x7 @ Ensure 8-byte alignment + + sub sp, r1, #8 + add r1, r1, #1024 + + str r1, EXT_C(grub_modbase) + + /* Coreboot already places modules at right place. */ +#ifndef GRUB_MACHINE_COREBOOT + add r1, r1, r2 + add r0, r0, r2 + sub r1, r1, #4 + sub r0, r0, #4 + +1: ldr r3, [r0], #-4 @ r3 = *src-- + str r3, [r1], #-4 @ *dst-- = r3 + subs r2, #4 @ remaining -= 4 + bne 1b @ while remaining != 0 +#endif + + @ Since we _are_ the C run-time, we need to manually zero the BSS + @ region before continuing + ldr r0, bss_start_ptr @ zero from here + @ If unaligned, bytewise zero until base address aligned. + mov r2, #0 +1: tst r0, #3 + beq 2f + strb r2, [r0], #1 + b 1b +2: ldr r1, end_ptr @ to here +1: str r2, [r0], #4 + cmp r0, r1 + bne 1b + + b EXT_C(grub_main) + + .align 3 +@ U-boot/coreboot context stack space +VARIABLE(grub_arm_saved_registers) + .long 0 @ r0 + .long 0 @ r1 + .long 0 @ r2 + .long 0 @ r3 + .long 0 @ r4 + .long 0 @ r5 + .long 0 @ r6 + .long 0 @ r7 + .long 0 @ r8 + .long 0 @ r9 + .long 0 @ r10 + .long 0 @ r11 + .long 0 @ sp + .long 0 @ lr +entry_state: diff --git a/grub-core/kern/arm/uboot/init.c b/grub-core/kern/arm/uboot/init.c new file mode 100644 index 000000000..2a6aa3fdd --- /dev/null +++ b/grub-core/kern/arm/uboot/init.c @@ -0,0 +1,70 @@ +/* init.c - generic U-Boot initialization and finalization */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include +#include +#include + +extern int (*grub_uboot_syscall_ptr) (int, int *, ...); + +grub_uint32_t +grub_uboot_get_machine_type (void) +{ + return grub_arm_saved_registers.r[1]; +} + +grub_addr_t +grub_uboot_get_boot_data (void) +{ + return grub_arm_saved_registers.r[2]; +} + +int +grub_uboot_api_init (void) +{ + struct api_signature *start, *end; + struct api_signature *p; + grub_addr_t grub_uboot_search_hint = grub_arm_saved_registers.sp; + if (grub_uboot_search_hint) + { + /* Extended search range to work around Trim Slice U-Boot issue */ + start = (struct api_signature *) ((grub_uboot_search_hint & ~0x000fffff) + - 0x00500000); + end = + (struct api_signature *) ((grub_addr_t) start + UBOOT_API_SEARCH_LEN - + API_SIG_MAGLEN + 0x00500000); + } + else + { + start = 0; + end = (struct api_signature *) (256 * 1024 * 1024); + } + + /* Structure alignment is (at least) 8 bytes */ + for (p = start; p < end; p = (void *) ((grub_addr_t) p + 8)) + { + if (grub_memcmp (&(p->magic), API_SIG_MAGIC, API_SIG_MAGLEN) == 0) + { + grub_uboot_syscall_ptr = p->syscall; + return p->version; + } + } + + return 0; +} diff --git a/grub-core/kern/arm/uboot/uboot.S b/grub-core/kern/arm/uboot/uboot.S new file mode 100644 index 000000000..d128775f1 --- /dev/null +++ b/grub-core/kern/arm/uboot/uboot.S @@ -0,0 +1,73 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include + + /* + * uboot_syscall(): + * This function is effectively a veneer, so it cannot + * modify the stack or corrupt any registers other than + * r12 (ip). Furthermore it needs to restore r8 for + * U-Boot (Global Data Pointer) and preserve it for Grub. + */ +FUNCTION(grub_uboot_syscall) + str r8, transition_space + str lr, transition_space + 4 + str r9, transition_space + 8 + + ldr ip, saved_registers_ptr + ldr r8, [ip, #4 * 8] + ldr r9, [ip, #4 * 9] + + bl do_syscall + + ldr r8, transition_space + ldr lr, transition_space + 4 + ldr r9, transition_space + 8 + + bx lr +do_syscall: + + ldr ip, grub_uboot_syscall_ptr + bx ip + +FUNCTION(grub_uboot_return) + ldr ip, saved_registers_ptr + ldr sp, [ip, #4 * 4] + pop {r4-r12, lr} + mov sp, r12 + bx lr + + + .align 3 + +@ GRUB context stack space +transition_space: + .long 0 @ r8 + .long 0 @ lr + .long 0 @ r9 + +saved_registers_ptr: + .long EXT_C(grub_arm_saved_registers) + +VARIABLE(grub_uboot_syscall_ptr) + .long 0 @ + + END diff --git a/grub-core/kern/arm64/cache.c b/grub-core/kern/arm64/cache.c new file mode 100644 index 000000000..b84383da1 --- /dev/null +++ b/grub-core/kern/arm64/cache.c @@ -0,0 +1,63 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + +static grub_int64_t dlinesz; +static grub_int64_t ilinesz; + +/* Prototypes for asm functions. */ +void grub_arch_clean_dcache_range (grub_addr_t beg, grub_addr_t end, + grub_uint64_t line_size); +void grub_arch_invalidate_icache_range (grub_addr_t beg, grub_addr_t end, + grub_uint64_t line_size); + +static void +probe_caches (void) +{ + grub_uint64_t cache_type; + + /* Read Cache Type Register */ + asm volatile ("mrs %0, ctr_el0": "=r"(cache_type)); + + dlinesz = 4 << ((cache_type >> 16) & 0xf); + ilinesz = 4 << (cache_type & 0xf); + + grub_dprintf("cache", "D$ line size: %lld\n", (long long) dlinesz); + grub_dprintf("cache", "I$ line size: %lld\n", (long long) ilinesz); +} + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + grub_uint64_t start, end, max_align; + + if (dlinesz == 0) + probe_caches(); + if (dlinesz == 0) + grub_fatal ("Unknown cache line size!"); + + max_align = dlinesz > ilinesz ? dlinesz : ilinesz; + + start = ALIGN_DOWN ((grub_uint64_t) address, max_align); + end = ALIGN_UP ((grub_uint64_t) address + len, max_align); + + grub_arch_clean_dcache_range (start, end, dlinesz); + grub_arch_invalidate_icache_range (start, end, ilinesz); +} diff --git a/grub-core/kern/arm64/cache_flush.S b/grub-core/kern/arm64/cache_flush.S new file mode 100644 index 000000000..e064f7ece --- /dev/null +++ b/grub-core/kern/arm64/cache_flush.S @@ -0,0 +1,55 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include + + .file "cache_flush.S" + .text + +/* + * Simple cache maintenance functions + */ + +// x0 - *beg (inclusive) +// x1 - *end (exclusive) +// x2 - line size +FUNCTION(grub_arch_clean_dcache_range) + // Clean data cache for range to point-of-unification +1: cmp x0, x1 + b.ge 2f + dc cvau, x0 // Clean Virtual Address to PoU + add x0, x0, x2 // Next line + b 1b +2: dsb ish + isb + ret + +// x0 - *beg (inclusive) +// x1 - *end (exclusive) +// x2 - line size +FUNCTION(grub_arch_invalidate_icache_range) + // Invalidate instruction cache for range to point-of-unification +1: cmp x0, x1 + b.ge 2f + ic ivau, x0 // Invalidate Virtual Address to PoU + add x0, x0, x2 // Next line + b 1b + // Branch predictor invalidation not needed on AArch64 +2: dsb ish + isb + ret diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c new file mode 100644 index 000000000..a2b5789a9 --- /dev/null +++ b/grub-core/kern/arm64/dl.c @@ -0,0 +1,198 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LDR 0x58000050 +#define BR 0xd61f0200 + + +/* + * Check if EHDR is a valid ELF header. + */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_AARCH64) + return grub_error (GRUB_ERR_BAD_OS, + N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* + * Unified function for both REL and RELA + */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + unsigned unmatched_adr_got_page = 0; + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Sym *sym; + void *place; + grub_uint64_t sym_addr; + + if (rel->r_offset >= seg->size) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + sym_addr = sym->st_value; + if (s->sh_type == SHT_RELA) + sym_addr += ((Elf_Rela *) rel)->r_addend; + + place = (void *) ((grub_addr_t) seg->addr + rel->r_offset); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_AARCH64_ABS64: + { + grub_uint64_t *abs_place = place; + + grub_dprintf ("dl", " reloc_abs64 %p => 0x%016llx\n", + place, (unsigned long long) sym_addr); + + *abs_place = (grub_uint64_t) sym_addr; + } + break; + case R_AARCH64_ADD_ABS_LO12_NC: + grub_arm64_set_abs_lo12 (place, sym_addr); + break; + case R_AARCH64_LDST64_ABS_LO12_NC: + grub_arm64_set_abs_lo12_ldst64 (place, sym_addr); + break; + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + { + grub_int64_t offset = sym_addr - (grub_uint64_t) place; + + if (!grub_arm_64_check_xxxx26_offset (offset)) + { + struct grub_arm64_trampoline *tp = mod->trampptr; + mod->trampptr = tp + 1; + tp->ldr = LDR; + tp->br = BR; + tp->addr = sym_addr; + offset = (grub_uint8_t *) tp - (grub_uint8_t *) place; + } + + if (!grub_arm_64_check_xxxx26_offset (offset)) + return grub_error (GRUB_ERR_BAD_MODULE, + "trampoline out of range"); + + grub_arm64_set_xxxx26_offset (place, offset); + } + break; + case R_AARCH64_PREL32: + { + grub_int64_t value; + Elf64_Word *addr32 = place; + value = ((grub_int32_t) *addr32) + sym_addr - + (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset; + if (value != (grub_int32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + grub_dprintf("dl", " reloc_prel32 %p => 0x%016llx\n", + place, (unsigned long long) sym_addr); + *addr32 = value; + } + break; + case R_AARCH64_ADR_GOT_PAGE: + { + grub_uint64_t *gp = mod->gotptr; + Elf_Rela *rel2; + grub_int64_t gpoffset = ((grub_uint64_t) gp & ~0xfffULL) - (((grub_uint64_t) place) & ~0xfffULL); + *gp = (grub_uint64_t) sym_addr; + mod->gotptr = gp + 1; + unmatched_adr_got_page++; + grub_dprintf("dl", " reloc_got %p => 0x%016llx (0x%016llx)\n", + place, (unsigned long long) sym_addr, (unsigned long long) gp); + if (!grub_arm64_check_hi21_signed (gpoffset)) + return grub_error (GRUB_ERR_BAD_MODULE, + "HI21 out of range"); + grub_arm64_set_hi21(place, gpoffset); + for (rel2 = (Elf_Rela *) ((char *) rel + s->sh_entsize); + rel2 < (Elf_Rela *) max; + rel2 = (Elf_Rela *) ((char *) rel2 + s->sh_entsize)) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ((Elf_Rela *) rel)->r_addend == rel2->r_addend + && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC) + { + grub_arm64_set_abs_lo12_ldst64 ((void *) ((grub_addr_t) seg->addr + rel2->r_offset), + (grub_uint64_t)gp); + break; + } + if (rel2 >= (Elf_Rela *) max) + return grub_error (GRUB_ERR_BAD_MODULE, + "ADR_GOT_PAGE without matching LD64_GOT_LO12_NC"); + } + break; + case R_AARCH64_LD64_GOT_LO12_NC: + if (unmatched_adr_got_page == 0) + return grub_error (GRUB_ERR_BAD_MODULE, + "LD64_GOT_LO12_NC without matching ADR_GOT_PAGE"); + unmatched_adr_got_page--; + break; + case R_AARCH64_ADR_PREL_PG_HI21: + { + grub_int64_t offset = (sym_addr & ~0xfffULL) - (((grub_uint64_t) place) & ~0xfffULL); + + if (!grub_arm64_check_hi21_signed (offset)) + return grub_error (GRUB_ERR_BAD_MODULE, + "HI21 out of range"); + + grub_arm64_set_hi21 (place, offset); + } + break; + + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm64/dl_helper.c b/grub-core/kern/arm64/dl_helper.c new file mode 100644 index 000000000..10e3d1ec2 --- /dev/null +++ b/grub-core/kern/arm64/dl_helper.c @@ -0,0 +1,134 @@ +/* dl_helper.c - relocation helper functions for modules and grub-mkimage */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * grub_arm64_reloc_xxxx26(): + * + * JUMP26/CALL26 relocations for B and BL instructions. + */ + +int +grub_arm_64_check_xxxx26_offset (grub_int64_t offset) +{ + const grub_ssize_t offset_low = -(1 << 27), offset_high = (1 << 27) - 1; + + if ((offset < offset_low) || (offset > offset_high)) + return 0; + return 1; +} + +void +grub_arm64_set_xxxx26_offset (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000); + + grub_dprintf ("dl", " reloc_xxxx64 %p %c= 0x%" PRIxGRUB_INT64_T "\n", + place, offset > 0 ? '+' : '-', + offset < 0 ? -offset : offset); + + *place &= insmask; + *place |= grub_cpu_to_le32 (offset >> 2) & ~insmask; +} + +int +grub_arm64_check_hi21_signed (grub_int64_t offset) +{ + if (offset != (grub_int64_t)(grub_int32_t)offset) + return 0; + return 1; +} + +void +grub_arm64_set_hi21 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0x9f00001f); + grub_uint32_t val; + + offset >>= 12; + + val = ((offset & 3) << 29) | (((offset >> 2) & 0x7ffff) << 5); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +void +grub_arm64_set_abs_lo12 (grub_uint32_t *place, grub_int64_t target) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff); + + *place &= insmask; + *place |= grub_cpu_to_le32 (target << 10) & ~insmask; +} + +void +grub_arm64_set_abs_lo12_ldst64 (grub_uint32_t *place, grub_int64_t target) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfff803ff); + + *place &= insmask; + *place |= grub_cpu_to_le32 (target << 7) & ~insmask; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_err_t +grub_arm64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf64_Ehdr *e = ehdr; + const Elf64_Shdr *s; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff)); + i < grub_le_to_cpu16 (e->e_shnum); + i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize))) + if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_REL) + || s->sh_type == grub_cpu_to_le32_compile_time (SHT_RELA)) + { + const Elf64_Rela *rel, *max; + + for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 (s->sh_offset)), + max = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_size)); + rel < max; rel = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_entsize))) + switch (ELF64_R_TYPE (rel->r_info)) + { + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + *tramp += sizeof (struct grub_arm64_trampoline); + break; + case R_AARCH64_ADR_GOT_PAGE: + *got += 8; + break; + } + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/arm64/efi/init.c b/grub-core/kern/arm64/efi/init.c new file mode 100644 index 000000000..5010caefd --- /dev/null +++ b/grub-core/kern/arm64/efi/init.c @@ -0,0 +1,63 @@ +/* init.c - initialize an arm-based EFI system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static grub_uint64_t timer_frequency_in_khz; + +static grub_uint64_t +grub_efi_get_time_ms (void) +{ + grub_uint64_t tmr; + asm volatile("mrs %0, cntvct_el0" : "=r" (tmr)); + + return tmr / timer_frequency_in_khz; +} + + +void +grub_machine_init (void) +{ + grub_uint64_t timer_frequency; + + grub_efi_init (); + + asm volatile("mrs %0, cntfrq_el0" : "=r" (timer_frequency)); + timer_frequency_in_khz = timer_frequency / 1000; + + grub_install_get_time_ms (grub_efi_get_time_ms); +} + +void +grub_machine_fini (int flags) +{ + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/grub-core/kern/arm64/efi/startup.S b/grub-core/kern/arm64/efi/startup.S new file mode 100644 index 000000000..666a7ee3c --- /dev/null +++ b/grub-core/kern/arm64/efi/startup.S @@ -0,0 +1,39 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include + + .file "startup.S" + .text +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0. + */ + ldr x2, efi_image_handle_val + str x0, [x2] + ldr x2, efi_system_table_val + str x1, [x2] + ldr x2, grub_main_val + br x2 +grub_main_val: + .quad EXT_C(grub_main) +efi_system_table_val: + .quad EXT_C(grub_efi_system_table) +efi_image_handle_val: + .quad EXT_C(grub_efi_image_handle) + diff --git a/grub-core/kern/buffer.c b/grub-core/kern/buffer.c new file mode 100644 index 000000000..a2587729c --- /dev/null +++ b/grub-core/kern/buffer.c @@ -0,0 +1,120 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2021 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 . + */ + +#include +#include +#include +#include +#include +#include + +grub_buffer_t +grub_buffer_new (grub_size_t sz) +{ + struct grub_buffer *ret; + + ret = (struct grub_buffer *) grub_malloc (sizeof (*ret)); + if (ret == NULL) + return NULL; + + ret->data = (grub_uint8_t *) grub_malloc (sz); + if (ret->data == NULL) + { + grub_free (ret); + return NULL; + } + + ret->sz = sz; + ret->pos = 0; + ret->used = 0; + + return ret; +} + +void +grub_buffer_free (grub_buffer_t buf) +{ + if (buf != NULL) + { + grub_free (buf->data); + grub_free (buf); + } +} + +grub_err_t +grub_buffer_ensure_space (grub_buffer_t buf, grub_size_t req) +{ + grub_uint8_t *d; + grub_size_t newsz = 1; + + /* Is the current buffer size adequate? */ + if (buf->sz >= req) + return GRUB_ERR_NONE; + + /* Find the smallest power-of-2 size that satisfies the request. */ + while (newsz < req) + { + if (newsz == 0) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("requested buffer size is too large")); + newsz <<= 1; + } + + d = (grub_uint8_t *) grub_realloc (buf->data, newsz); + if (d == NULL) + return grub_errno; + + buf->data = d; + buf->sz = newsz; + + return GRUB_ERR_NONE; +} + +void * +grub_buffer_take_data (grub_buffer_t buf) +{ + void *data = buf->data; + + buf->data = NULL; + buf->sz = buf->pos = buf->used = 0; + + return data; +} + +void +grub_buffer_reset (grub_buffer_t buf) +{ + buf->pos = buf->used = 0; +} + +grub_err_t +grub_buffer_advance_read_pos (grub_buffer_t buf, grub_size_t n) +{ + grub_size_t newpos; + + if (grub_add (buf->pos, n, &newpos)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + if (newpos > buf->used) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("new read is position beyond the end of the written data")); + + buf->pos = newpos; + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/command.c b/grub-core/kern/command.c new file mode 100644 index 000000000..5812e131c --- /dev/null +++ b/grub-core/kern/command.c @@ -0,0 +1,111 @@ +/* command.c - support basic command */ +/* + * 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 . + */ + +#include +#include +#include + +grub_command_t grub_command_list; + +grub_command_t +grub_register_command_prio (const char *name, + grub_command_func_t func, + const char *summary, + const char *description, + int prio) +{ + grub_command_t cmd; + int inactive = 0; + + grub_command_t *p, q; + + cmd = (grub_command_t) grub_zalloc (sizeof (*cmd)); + if (! cmd) + return 0; + + cmd->name = name; + cmd->func = func; + cmd->summary = (summary) ? summary : ""; + cmd->description = description; + + cmd->flags = 0; + cmd->prio = prio; + + for (p = &grub_command_list, q = *p; q; p = &(q->next), q = q->next) + { + int r; + + r = grub_strcmp (cmd->name, q->name); + if (r < 0) + break; + if (r > 0) + continue; + + if (cmd->prio >= (q->prio & GRUB_COMMAND_PRIO_MASK)) + { + q->prio &= ~GRUB_COMMAND_FLAG_ACTIVE; + break; + } + + inactive = 1; + } + + *p = cmd; + cmd->next = q; + if (q) + q->prev = &cmd->next; + cmd->prev = p; + + if (! inactive) + cmd->prio |= GRUB_COMMAND_FLAG_ACTIVE; + + return cmd; +} + +static grub_err_t +grub_cmd_lockdown (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **argv __attribute__ ((unused))) + +{ + return grub_error (GRUB_ERR_ACCESS_DENIED, + N_("%s: the command is not allowed when lockdown is enforced"), + cmd->name); +} + +grub_command_t +grub_register_command_lockdown (const char *name, + grub_command_func_t func, + const char *summary, + const char *description) +{ + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + func = grub_cmd_lockdown; + + return grub_register_command_prio (name, func, summary, description, 0); +} + +void +grub_unregister_command (grub_command_t cmd) +{ + if ((cmd->prio & GRUB_COMMAND_FLAG_ACTIVE) && (cmd->next)) + cmd->next->prio |= GRUB_COMMAND_FLAG_ACTIVE; + grub_list_remove (GRUB_AS_LIST (cmd)); + grub_free (cmd); +} diff --git a/grub-core/kern/compiler-rt.c b/grub-core/kern/compiler-rt.c new file mode 100644 index 000000000..eda689a0c --- /dev/null +++ b/grub-core/kern/compiler-rt.c @@ -0,0 +1,454 @@ +/* compiler-rt.c - compiler helpers. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010-2014 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 . + */ + +#include +#include + +#ifndef GRUB_EMBED_DECOMPRESSOR +void * GRUB_BUILTIN_ATTR +memcpy (void *dest, const void *src, grub_size_t n) +{ + return grub_memmove (dest, src, n); +} +void * GRUB_BUILTIN_ATTR +memmove (void *dest, const void *src, grub_size_t n) +{ + return grub_memmove (dest, src, n); +} +int GRUB_BUILTIN_ATTR +memcmp (const void *s1, const void *s2, grub_size_t n) +{ + return grub_memcmp (s1, s2, n); +} +void * GRUB_BUILTIN_ATTR +memset (void *s, int c, grub_size_t n) +{ + return grub_memset (s, c, n); +} + +#ifdef __APPLE__ + +void GRUB_BUILTIN_ATTR +__bzero (void *s, grub_size_t n) +{ + grub_memset (s, 0, n); +} + +#endif + +#if GRUB_DIVISION_IN_SOFTWARE + +grub_uint32_t +__udivsi3 (grub_uint32_t a, grub_uint32_t b) +{ + return grub_divmod64 (a, b, 0); +} + +grub_int32_t +__divsi3 (grub_int32_t a, grub_int32_t b) +{ + return grub_divmod64s (a, b, 0); +} + +grub_uint32_t +__umodsi3 (grub_uint32_t a, grub_uint32_t b) +{ + grub_uint64_t ret; + grub_divmod64 (a, b, &ret); + return ret; +} + +grub_int32_t +__modsi3 (grub_int32_t a, grub_int32_t b) +{ + grub_int64_t ret; + grub_divmod64s (a, b, &ret); + return ret; +} + +grub_uint64_t +__udivdi3 (grub_uint64_t a, grub_uint64_t b) +{ + return grub_divmod64 (a, b, 0); +} + +grub_uint64_t +__umoddi3 (grub_uint64_t a, grub_uint64_t b) +{ + grub_uint64_t ret; + grub_divmod64 (a, b, &ret); + return ret; +} + +grub_int64_t +__divdi3 (grub_int64_t a, grub_int64_t b) +{ + return grub_divmod64s (a, b, 0); +} + +grub_int64_t +__moddi3 (grub_int64_t a, grub_int64_t b) +{ + grub_int64_t ret; + grub_divmod64s (a, b, &ret); + return ret; +} + +#endif + +#endif + +#ifdef NEED_CTZDI2 + +unsigned +__ctzdi2 (grub_uint64_t x) +{ + unsigned ret = 0; + if (!x) + return 64; + if (!(x & 0xffffffff)) + { + x >>= 32; + ret |= 32; + } + if (!(x & 0xffff)) + { + x >>= 16; + ret |= 16; + } + if (!(x & 0xff)) + { + x >>= 8; + ret |= 8; + } + if (!(x & 0xf)) + { + x >>= 4; + ret |= 4; + } + if (!(x & 0x3)) + { + x >>= 2; + ret |= 2; + } + if (!(x & 0x1)) + { + x >>= 1; + ret |= 1; + } + return ret; +} +#endif + +#ifdef NEED_CTZSI2 +unsigned +__ctzsi2 (grub_uint32_t x) +{ + unsigned ret = 0; + if (!x) + return 32; + + if (!(x & 0xffff)) + { + x >>= 16; + ret |= 16; + } + if (!(x & 0xff)) + { + x >>= 8; + ret |= 8; + } + if (!(x & 0xf)) + { + x >>= 4; + ret |= 4; + } + if (!(x & 0x3)) + { + x >>= 2; + ret |= 2; + } + if (!(x & 0x1)) + { + x >>= 1; + ret |= 1; + } + return ret; +} + +#endif + +#if (defined (__MINGW32__) || defined (__CYGWIN__)) +void __register_frame_info (void) +{ +} + +void __deregister_frame_info (void) +{ +} + +void ___chkstk_ms (void) +{ +} + +void __chkstk_ms (void) +{ +} +#endif + +union component64 +{ + grub_uint64_t full; + struct + { +#ifdef GRUB_CPU_WORDS_BIGENDIAN + grub_uint32_t high; + grub_uint32_t low; +#else + grub_uint32_t low; + grub_uint32_t high; +#endif + }; +}; + +#if defined (__powerpc__) || defined (__arm__) || defined(__mips__) || \ + (defined(__riscv) && (__riscv_xlen == 32)) + +/* Based on libgcc2.c from gcc suite. */ +grub_uint64_t +__lshrdi3 (grub_uint64_t u, int b) +{ + if (b == 0) + return u; + + const union component64 uu = {.full = u}; + const int bm = 32 - b; + union component64 w; + + if (bm <= 0) + { + w.high = 0; + w.low = (grub_uint32_t) uu.high >> -bm; + } + else + { + const grub_uint32_t carries = (grub_uint32_t) uu.high << bm; + + w.high = (grub_uint32_t) uu.high >> b; + w.low = ((grub_uint32_t) uu.low >> b) | carries; + } + + return w.full; +} + +/* Based on libgcc2.c from gcc suite. */ +grub_uint64_t +__ashrdi3 (grub_uint64_t u, int b) +{ + if (b == 0) + return u; + + const union component64 uu = {.full = u}; + const int bm = 32 - b; + union component64 w; + + if (bm <= 0) + { + /* w.high = 1..1 or 0..0 */ + w.high = ((grub_int32_t) uu.high) >> (32 - 1); + w.low = ((grub_int32_t) uu.high) >> -bm; + } + else + { + const grub_uint32_t carries = ((grub_uint32_t) uu.high) << bm; + + w.high = ((grub_int32_t) uu.high) >> b; + w.low = ((grub_uint32_t) uu.low >> b) | carries; + } + + return w.full; +} + +/* Based on libgcc2.c from gcc suite. */ +grub_uint64_t +__ashldi3 (grub_uint64_t u, int b) +{ + if (b == 0) + return u; + + const union component64 uu = {.full = u}; + const int bm = 32 - b; + union component64 w; + + if (bm <= 0) + { + w.low = 0; + w.high = (grub_uint32_t) uu.low << -bm; + } + else + { + const grub_uint32_t carries = (grub_uint32_t) uu.low >> bm; + + w.low = (grub_uint32_t) uu.low << b; + w.high = ((grub_uint32_t) uu.high << b) | carries; + } + + return w.full; +} + +/* Based on libgcc2.c from gcc suite. */ +int +__ucmpdi2 (grub_uint64_t a, grub_uint64_t b) +{ + union component64 ac, bc; + ac.full = a; + bc.full = b; + + if (ac.high < bc.high) + return 0; + else if (ac.high > bc.high) + return 2; + + if (ac.low < bc.low) + return 0; + else if (ac.low > bc.low) + return 2; + return 1; +} + +#endif + +#if defined (__powerpc__) || defined(__mips__) || defined(__sparc__) || \ + defined(__arm__) || defined(__riscv) + +/* Based on libgcc2.c from gcc suite. */ +grub_uint32_t +__bswapsi2 (grub_uint32_t u) +{ + return ((((u) & 0xff000000) >> 24) + | (((u) & 0x00ff0000) >> 8) + | (((u) & 0x0000ff00) << 8) + | (((u) & 0x000000ff) << 24)); +} + +/* Based on libgcc2.c from gcc suite. */ +grub_uint64_t +__bswapdi2 (grub_uint64_t u) +{ + return ((((u) & 0xff00000000000000ull) >> 56) + | (((u) & 0x00ff000000000000ull) >> 40) + | (((u) & 0x0000ff0000000000ull) >> 24) + | (((u) & 0x000000ff00000000ull) >> 8) + | (((u) & 0x00000000ff000000ull) << 8) + | (((u) & 0x0000000000ff0000ull) << 24) + | (((u) & 0x000000000000ff00ull) << 40) + | (((u) & 0x00000000000000ffull) << 56)); +} + + +#endif + +#ifdef __arm__ +grub_uint32_t +__aeabi_uidiv (grub_uint32_t a, grub_uint32_t b) + __attribute__ ((alias ("__udivsi3"))); +grub_int32_t +__aeabi_idiv (grub_int32_t a, grub_int32_t b) + __attribute__ ((alias ("__divsi3"))); +void *__aeabi_memcpy (void *dest, const void *src, grub_size_t n) + __attribute__ ((alias ("grub_memcpy"))); +void *__aeabi_memcpy4 (void *dest, const void *src, grub_size_t n) + __attribute__ ((alias ("grub_memcpy"))); +void *__aeabi_memcpy8 (void *dest, const void *src, grub_size_t n) + __attribute__ ((alias ("grub_memcpy"))); +void *__aeabi_memset (void *s, int c, grub_size_t n) + __attribute__ ((alias ("memset"))); + +void +__aeabi_memclr (void *s, grub_size_t n) +{ + grub_memset (s, 0, n); +} + +void __aeabi_memclr4 (void *s, grub_size_t n) + __attribute__ ((alias ("__aeabi_memclr"))); +void __aeabi_memclr8 (void *s, grub_size_t n) + __attribute__ ((alias ("__aeabi_memclr"))); + +int +__aeabi_ulcmp (grub_uint64_t a, grub_uint64_t b) +{ + return __ucmpdi2 (a, b) - 1; +} + +grub_uint64_t +__aeabi_lasr (grub_uint64_t u, int b) + __attribute__ ((alias ("__ashrdi3"))); +grub_uint64_t +__aeabi_llsr (grub_uint64_t u, int b) + __attribute__ ((alias ("__lshrdi3"))); + +grub_uint64_t +__aeabi_llsl (grub_uint64_t u, int b) + __attribute__ ((alias ("__ashldi3"))); + +#endif + +#if defined(__mips__) || defined(__riscv) || defined(__sparc__) +/* Based on libgcc from gcc suite. */ +int +__clzsi2 (grub_uint32_t val) +{ + int i = 32; + int j = 16; + int temp; + + for (; j; j >>= 1) + { + if ((temp = val >> j)) + { + if (j == 1) + { + return (i - 2); + } + else + { + i -= j; + val = temp; + } + } + } + return (i - val); +} +#endif + +#if defined(__mips__) || defined(__riscv) || defined(__sparc__) +int +__clzdi2 (grub_uint64_t val) +{ + if (val >> 32) + { + return __clzsi2 (val >> 32); + } + else + { + return __clzsi2 (val) + 32; + } +} +#endif diff --git a/grub-core/kern/coreboot/cbtable.c b/grub-core/kern/coreboot/cbtable.c new file mode 100644 index 000000000..b6d0801c1 --- /dev/null +++ b/grub-core/kern/coreboot/cbtable.c @@ -0,0 +1,72 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2013 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 . + */ + +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Helper for grub_linuxbios_table_iterate. */ +int +grub_linuxbios_check_signature (grub_linuxbios_table_header_t tbl_header) +{ + if (! grub_memcmp (tbl_header->signature, "LBIO", 4)) + return 1; + + return 0; +} + +grub_err_t +grub_linuxbios_table_iterate (int (*hook) (grub_linuxbios_table_item_t, + void *), + void *hook_data) +{ + grub_linuxbios_table_header_t table_header = grub_linuxbios_get_tables (); + grub_linuxbios_table_item_t table_item; + + if (!table_header) + return 0; + +signature_found: + + table_item = + (grub_linuxbios_table_item_t) ((char *) table_header + + table_header->header_size); + for (; table_item < (grub_linuxbios_table_item_t) ((char *) table_header + + table_header->header_size + + table_header->table_size); + table_item = (grub_linuxbios_table_item_t) ((char *) table_item + table_item->size)) + { + if (table_item->tag == GRUB_LINUXBIOS_MEMBER_LINK + && grub_linuxbios_check_signature ((grub_linuxbios_table_header_t) (grub_addr_t) + *(grub_uint64_t *) (table_item + 1))) + { + table_header = (grub_linuxbios_table_header_t) (grub_addr_t) + *(grub_uint64_t *) (table_item + 1); + goto signature_found; + } + if (hook (table_item, hook_data)) + return 1; + } + + return 0; +} diff --git a/grub-core/kern/coreboot/mmap.c b/grub-core/kern/coreboot/mmap.c new file mode 100644 index 000000000..caf8f7cef --- /dev/null +++ b/grub-core/kern/coreboot/mmap.c @@ -0,0 +1,100 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2013 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 . + */ + +#include +#include +#include +#include +#include + +/* Context for grub_machine_mmap_iterate. */ +struct grub_machine_mmap_iterate_ctx +{ + grub_memory_hook_t hook; + void *hook_data; +}; + +#define GRUB_MACHINE_MEMORY_BADRAM 5 + +/* Helper for grub_machine_mmap_iterate. */ +static int +iterate_linuxbios_table (grub_linuxbios_table_item_t table_item, void *data) +{ + struct grub_machine_mmap_iterate_ctx *ctx = data; + mem_region_t mem_region; + + if (table_item->tag != GRUB_LINUXBIOS_MEMBER_MEMORY) + return 0; + + mem_region = + (mem_region_t) ((long) table_item + + sizeof (struct grub_linuxbios_table_item)); + for (; (long) mem_region < (long) table_item + (long) table_item->size; + mem_region++) + { + grub_uint64_t start = mem_region->addr; + grub_uint64_t end = mem_region->addr + mem_region->size; +#ifdef __i386__ + /* Mark region 0xa0000 - 0x100000 as reserved. */ + if (start < 0x100000 && end >= 0xa0000 + && mem_region->type == GRUB_MACHINE_MEMORY_AVAILABLE) + { + if (start < 0xa0000 + && ctx->hook (start, 0xa0000 - start, + /* Multiboot mmaps match with the coreboot mmap + definition. Therefore, we can just pass type + through. */ + mem_region->type, + ctx->hook_data)) + return 1; + if (start < 0xa0000) + start = 0xa0000; + if (start >= end) + continue; + + if (ctx->hook (start, (end > 0x100000 ? 0x100000 : end) - start, + GRUB_MEMORY_RESERVED, + ctx->hook_data)) + return 1; + start = 0x100000; + + if (end <= start) + continue; + } +#endif + if (ctx->hook (start, end - start, + /* Multiboot mmaps match with the coreboot mmap + definition. Therefore, we can just pass type + through. */ + mem_region->type, + ctx->hook_data)) + return 1; + } + + return 0; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + struct grub_machine_mmap_iterate_ctx ctx = { hook, hook_data }; + + grub_linuxbios_table_iterate (iterate_linuxbios_table, &ctx); + + return 0; +} diff --git a/kern/corecmd.c b/grub-core/kern/corecmd.c similarity index 73% rename from kern/corecmd.c rename to grub-core/kern/corecmd.c index 8b8df63cb..62d434ba9 100644 --- a/kern/corecmd.c +++ b/grub-core/kern/corecmd.c @@ -26,6 +26,7 @@ #include #include #include +#include /* set ENVVAR=VALUE */ static grub_err_t @@ -35,17 +36,14 @@ grub_core_cmd_set (struct grub_command *cmd __attribute__ ((unused)), char *var; char *val; - auto int print_env (struct grub_env_var *env); - - int print_env (struct grub_env_var *env) - { - grub_printf ("%s=%s\n", env->name, env->value); - return 0; - } - if (argc < 1) { - grub_env_iterate (print_env); + struct grub_env_var *env; + FOR_SORTED_ENV (env) + { + val = (char *) grub_env_get (env->name); + grub_printf ("%s='%s'\n", env->name, val == NULL ? "" : val); + } return 0; } @@ -67,7 +65,7 @@ grub_core_cmd_unset (struct grub_command *cmd __attribute__ ((unused)), { if (argc < 1) return grub_error (GRUB_ERR_BAD_ARGUMENT, - "no environment variable specified"); + N_("one argument expected")); grub_env_unset (argv[0]); return 0; @@ -78,17 +76,15 @@ static grub_err_t grub_core_cmd_insmod (struct grub_command *cmd __attribute__ ((unused)), int argc, char *argv[]) { - char *p; grub_dl_t mod; if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified"); + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); - p = grub_strchr (argv[0], '/'); - if (! p) - mod = grub_dl_load (argv[0]); - else + if (argv[0][0] == '/' || argv[0][0] == '(' || argv[0][0] == '+') mod = grub_dl_load_file (argv[0]); + else + mod = grub_dl_load (argv[0]); if (mod) grub_dl_ref (mod); @@ -97,7 +93,7 @@ grub_core_cmd_insmod (struct grub_command *cmd __attribute__ ((unused)), } static int -grub_mini_print_devices (const char *name) +grub_mini_print_devices (const char *name, void *data __attribute__ ((unused))) { grub_printf ("(%s) ", name); @@ -106,7 +102,8 @@ grub_mini_print_devices (const char *name) static int grub_mini_print_files (const char *filename, - const struct grub_dirhook_info *info) + const struct grub_dirhook_info *info, + void *data __attribute__ ((unused))) { grub_printf ("%s%s ", filename, info->dir ? "/" : ""); @@ -120,18 +117,20 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)), { if (argc < 1) { - grub_device_iterate (grub_mini_print_devices); - grub_putchar ('\n'); + grub_device_iterate (grub_mini_print_devices, NULL); + grub_xputs ("\n"); grub_refresh (); } else { char *device_name; - grub_device_t dev; + grub_device_t dev = 0; grub_fs_t fs; char *path; device_name = grub_file_get_device_name (argv[0]); + if (grub_errno) + goto fail; dev = grub_device_open (device_name); if (! dev) goto fail; @@ -143,13 +142,13 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)), else path++; - if (! path && ! device_name) + if (! *path && ! device_name) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); goto fail; } - if (! path) + if (! *path) { if (grub_errno == GRUB_ERR_UNKNOWN_FS) grub_errno = GRUB_ERR_NONE; @@ -159,8 +158,8 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)), } else if (fs) { - (fs->dir) (dev, path, grub_mini_print_files); - grub_putchar ('\n'); + (fs->fs_dir) (dev, path, grub_mini_print_files, NULL); + grub_xputs ("\n"); grub_refresh (); } @@ -177,12 +176,17 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)), void grub_register_core_commands (void) { - grub_register_command ("set", grub_core_cmd_set, - "[ENVVAR=VALUE]", "Set an environment variable."); + grub_command_t cmd; + cmd = grub_register_command ("set", grub_core_cmd_set, + N_("[ENVVAR=VALUE]"), + N_("Set an environment variable.")); + if (cmd) + cmd->flags |= GRUB_COMMAND_FLAG_EXTRACTOR; grub_register_command ("unset", grub_core_cmd_unset, - "ENVVAR", "Remove an environment variable."); + N_("ENVVAR"), + N_("Remove an environment variable.")); grub_register_command ("ls", grub_core_cmd_ls, - "[ARG]", "List devices or files."); + N_("[ARG]"), N_("List devices or files.")); grub_register_command ("insmod", grub_core_cmd_insmod, - "MODULE", "Insert a module."); + N_("MODULE"), N_("Insert a module.")); } diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c new file mode 100644 index 000000000..670e213cf --- /dev/null +++ b/grub-core/kern/device.c @@ -0,0 +1,194 @@ +/* device.c - device manager */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_net_t (*grub_net_open) (const char *name) = NULL; + +grub_device_t +grub_device_open (const char *name) +{ + grub_device_t dev = 0; + + if (! name) + { + name = grub_env_get ("root"); + if (name == NULL || *name == '\0') + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("variable `%s' isn't set"), "root"); + goto fail; + } + } + + dev = grub_malloc (sizeof (*dev)); + if (! dev) + goto fail; + + dev->net = NULL; + /* Try to open a disk. */ + dev->disk = grub_disk_open (name); + if (dev->disk) + return dev; + if (grub_net_open && grub_errno == GRUB_ERR_UNKNOWN_DEVICE) + { + grub_errno = GRUB_ERR_NONE; + dev->net = grub_net_open (name); + } + + if (dev->net) + return dev; + + fail: + grub_free (dev); + + return 0; +} + +grub_err_t +grub_device_close (grub_device_t device) +{ + if (device == NULL) + return GRUB_ERR_NONE; + + if (device->disk) + grub_disk_close (device->disk); + + if (device->net) + { + grub_free (device->net->server); + grub_free (device->net); + } + + grub_free (device); + + return grub_errno; +} + +struct part_ent +{ + struct part_ent *next; + char *name; +}; + +/* Context for grub_device_iterate. */ +struct grub_device_iterate_ctx +{ + grub_device_iterate_hook_t hook; + void *hook_data; + struct part_ent *ents; +}; + +/* Helper for grub_device_iterate. */ +static int +iterate_partition (grub_disk_t disk, const grub_partition_t partition, + void *data) +{ + struct grub_device_iterate_ctx *ctx = data; + struct part_ent *p; + char *part_name; + + p = grub_malloc (sizeof (*p)); + if (!p) + { + return 1; + } + + part_name = grub_partition_get_name (partition); + if (!part_name) + { + grub_free (p); + return 1; + } + p->name = grub_xasprintf ("%s,%s", disk->name, part_name); + grub_free (part_name); + if (!p->name) + { + grub_free (p); + return 1; + } + + p->next = ctx->ents; + ctx->ents = p; + + return 0; +} + +/* Helper for grub_device_iterate. */ +static int +iterate_disk (const char *disk_name, void *data) +{ + struct grub_device_iterate_ctx *ctx = data; + grub_device_t dev; + + if (ctx->hook (disk_name, ctx->hook_data)) + return 1; + + dev = grub_device_open (disk_name); + if (! dev) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + + if (dev->disk) + { + struct part_ent *p; + int ret = 0; + + ctx->ents = NULL; + (void) grub_partition_iterate (dev->disk, iterate_partition, ctx); + grub_device_close (dev); + + grub_errno = GRUB_ERR_NONE; + + p = ctx->ents; + while (p != NULL) + { + struct part_ent *next = p->next; + + if (!ret) + ret = ctx->hook (p->name, ctx->hook_data); + grub_free (p->name); + grub_free (p); + p = next; + } + + return ret; + } + + grub_device_close (dev); + return 0; +} + +int +grub_device_iterate (grub_device_iterate_hook_t hook, void *hook_data) +{ + struct grub_device_iterate_ctx ctx = { hook, hook_data, NULL }; + + /* Only disk devices are supported at the moment. */ + return grub_disk_dev_iterate (iterate_disk, &ctx); +} diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c new file mode 100644 index 000000000..82e04fd00 --- /dev/null +++ b/grub-core/kern/disk.c @@ -0,0 +1,561 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRUB_CACHE_TIMEOUT 2 + +/* Disk reads may trigger other disk reads. So, limit recursion depth. */ +#define MAX_READ_RECURSION_DEPTH 16 +static unsigned int read_recursion_depth = 0; + +/* The last time the disk was used. */ +static grub_uint64_t grub_last_time = 0; + +struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM]; + +void (*grub_disk_firmware_fini) (void); +int grub_disk_firmware_is_tainted; + +#if DISK_CACHE_STATS +static unsigned long grub_disk_cache_hits; +static unsigned long grub_disk_cache_misses; + +void +grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses) +{ + *hits = grub_disk_cache_hits; + *misses = grub_disk_cache_misses; +} +#endif + +grub_err_t (*grub_disk_write_weak) (grub_disk_t disk, + grub_disk_addr_t sector, + grub_off_t offset, + grub_size_t size, + const void *buf); +#include "disk_common.c" + +void +grub_disk_cache_invalidate_all (void) +{ + unsigned i; + + for (i = 0; i < GRUB_DISK_CACHE_NUM; i++) + { + struct grub_disk_cache *cache = grub_disk_cache_table + i; + + if (cache->data && ! cache->lock) + { + grub_free (cache->data); + cache->data = 0; + } + } +} + +static char * +grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + struct grub_disk_cache *cache; + unsigned cache_index; + + cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); + cache = grub_disk_cache_table + cache_index; + + if (cache->dev_id == dev_id && cache->disk_id == disk_id + && cache->sector == sector) + { + cache->lock = 1; +#if DISK_CACHE_STATS + grub_disk_cache_hits++; +#endif + return cache->data; + } + +#if DISK_CACHE_STATS + grub_disk_cache_misses++; +#endif + + return 0; +} + +static void +grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + struct grub_disk_cache *cache; + unsigned cache_index; + + cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); + cache = grub_disk_cache_table + cache_index; + + if (cache->dev_id == dev_id && cache->disk_id == disk_id + && cache->sector == sector) + cache->lock = 0; +} + +static grub_err_t +grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector, const char *data) +{ + unsigned cache_index; + struct grub_disk_cache *cache; + + cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); + cache = grub_disk_cache_table + cache_index; + + cache->lock = 1; + grub_free (cache->data); + cache->data = 0; + cache->lock = 0; + + cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); + if (! cache->data) + return grub_errno; + + grub_memcpy (cache->data, data, + GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); + cache->dev_id = dev_id; + cache->disk_id = disk_id; + cache->sector = sector; + + return GRUB_ERR_NONE; +} + + + +grub_disk_dev_t grub_disk_dev_list; + +void +grub_disk_dev_register (grub_disk_dev_t dev) +{ + dev->next = grub_disk_dev_list; + grub_disk_dev_list = dev; +} + +void +grub_disk_dev_unregister (grub_disk_dev_t dev) +{ + grub_disk_dev_t *p, q; + + for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next) + if (q == dev) + { + *p = q->next; + break; + } +} + +/* Return the location of the first ',', if any, which is not + escaped by a '\'. */ +static const char * +find_part_sep (const char *name) +{ + const char *p = name; + char c; + + while ((c = *p++) != '\0') + { + if (c == '\\' && *p == ',') + p++; + else if (c == ',') + return p - 1; + } + return NULL; +} + +grub_disk_t +grub_disk_open (const char *name) +{ + const char *p; + grub_disk_t disk; + grub_disk_dev_t dev; + char *raw = (char *) name; + grub_uint64_t current_time; + + grub_dprintf ("disk", "Opening `%s'...\n", name); + + disk = (grub_disk_t) grub_zalloc (sizeof (*disk)); + if (! disk) + return 0; + disk->log_sector_size = GRUB_DISK_SECTOR_BITS; + /* Default 1MiB of maximum agglomerate. */ + disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS + + GRUB_DISK_CACHE_BITS); + + p = find_part_sep (name); + if (p) + { + grub_size_t len = p - name; + + raw = grub_malloc (len + 1); + if (! raw) + goto fail; + + grub_memcpy (raw, name, len); + raw[len] = '\0'; + disk->name = grub_strdup (raw); + } + else + disk->name = grub_strdup (name); + if (! disk->name) + goto fail; + + for (dev = grub_disk_dev_list; dev; dev = dev->next) + { + if ((dev->disk_open) (raw, disk) == GRUB_ERR_NONE) + break; + else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE) + grub_errno = GRUB_ERR_NONE; + else + goto fail; + } + + if (! dev) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"), + name); + goto fail; + } + if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS + || disk->log_sector_size < GRUB_DISK_SECTOR_BITS) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "sector sizes of %d bytes aren't supported yet", + (1 << disk->log_sector_size)); + goto fail; + } + + disk->dev = dev; + + if (p) + { + disk->partition = grub_partition_probe (disk, p + 1); + if (! disk->partition) + { + /* TRANSLATORS: It means that the specified partition e.g. + hd0,msdos1=/dev/sda1 doesn't exist. */ + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition")); + goto fail; + } + } + + /* The cache will be invalidated about 2 seconds after a device was + closed. */ + current_time = grub_get_time_ms (); + + if (current_time > (grub_last_time + + GRUB_CACHE_TIMEOUT * 1000)) + grub_disk_cache_invalidate_all (); + + grub_last_time = current_time; + + fail: + + if (raw && raw != name) + grub_free (raw); + + if (grub_errno != GRUB_ERR_NONE) + { + grub_error_push (); + grub_dprintf ("disk", "Opening `%s' failed.\n", name); + grub_error_pop (); + + grub_disk_close (disk); + return 0; + } + + return disk; +} + +void +grub_disk_close (grub_disk_t disk) +{ + grub_partition_t part; + + if (disk == NULL) + return; + + grub_dprintf ("disk", "Closing `%s'.\n", disk->name); + + if (disk->dev && disk->dev->disk_close) + (disk->dev->disk_close) (disk); + + /* Reset the timer. */ + grub_last_time = grub_get_time_ms (); + + while (disk->partition) + { + part = disk->partition->parent; + grub_free (disk->partition); + disk->partition = part; + } + grub_free ((void *) disk->name); + grub_free (disk); +} + +/* Small read (less than cache size and not pass across cache unit boundaries). + sector is already adjusted and is divisible by cache unit size. + */ +static grub_err_t +grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, void *buf) +{ + char *data; + char *tmp_buf; + + /* Fetch the cache. */ + data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector); + if (data) + { + /* Just copy it! */ + grub_memcpy (buf, data + offset, size); + grub_disk_cache_unlock (disk->dev->id, disk->id, sector); + return GRUB_ERR_NONE; + } + + /* Allocate a temporary buffer. */ + tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); + if (! tmp_buf) + return grub_errno; + + /* Otherwise read data from the disk actually. */ + if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN + || sector + GRUB_DISK_CACHE_SIZE + < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS))) + { + grub_err_t err; + err = (disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, sector), + 1U << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size), tmp_buf); + if (!err) + { + /* Copy it and store it in the disk cache. */ + grub_memcpy (buf, tmp_buf + offset, size); + grub_disk_cache_store (disk->dev->id, disk->id, + sector, tmp_buf); + grub_free (tmp_buf); + return GRUB_ERR_NONE; + } + } + + grub_free (tmp_buf); + grub_errno = GRUB_ERR_NONE; + + { + /* Uggh... Failed. Instead, just read necessary data. */ + unsigned num; + grub_disk_addr_t aligned_sector; + + sector += (offset >> GRUB_DISK_SECTOR_BITS); + offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1); + aligned_sector = (sector & ~((1ULL << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) + - 1)); + offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS); + num = ((size + offset + (1ULL << (disk->log_sector_size)) + - 1) >> (disk->log_sector_size)); + + tmp_buf = grub_malloc (num << disk->log_sector_size); + if (!tmp_buf) + return grub_errno; + + if ((disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, aligned_sector), + num, tmp_buf)) + { + grub_error_push (); + grub_dprintf ("disk", "%s read failed\n", disk->name); + grub_error_pop (); + grub_free (tmp_buf); + return grub_errno; + } + grub_memcpy (buf, tmp_buf + offset, size); + grub_free (tmp_buf); + return GRUB_ERR_NONE; + } +} + +static grub_err_t +grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, void *buf) +{ + grub_err_t err; + + err = grub_disk_read_small_real (disk, sector, offset, size, buf); + if (err) + return err; + if (disk->read_hook) + err = (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS), + offset & (GRUB_DISK_SECTOR_SIZE - 1), + size, buf, disk->read_hook_data); + return err; +} + +/* Read data from the disk. */ +grub_err_t +grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, void *buf) +{ + grub_err_t err = GRUB_ERR_NONE; + + /* First of all, check if the region is within the disk. */ + if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) + { + grub_error_push (); + grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n", + (unsigned long long) sector, grub_errmsg); + grub_error_pop (); + return grub_errno; + } + + if (++read_recursion_depth >= MAX_READ_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_RECURSION_DEPTH, "grub_disk_read(): Maximum recursion depth exceeded"); + goto error; + } + + /* First read until first cache boundary. */ + if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1))) + { + grub_disk_addr_t start_sector; + grub_size_t pos; + grub_size_t len; + + start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1); + pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS; + len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS) + - pos - offset); + if (len > size) + len = size; + err = grub_disk_read_small (disk, start_sector, + offset + pos, len, buf); + if (err) + goto error; + buf = (char *) buf + len; + size -= len; + offset += len; + sector += (offset >> GRUB_DISK_SECTOR_BITS); + offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1); + } + + /* Until SIZE is zero... */ + while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS)) + { + char *data = NULL; + grub_disk_addr_t agglomerate; + + /* agglomerate read until we find a first cached entry. */ + for (agglomerate = 0; agglomerate + < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS)) + && agglomerate < disk->max_agglomerate; + agglomerate++) + { + data = grub_disk_cache_fetch (disk->dev->id, disk->id, + sector + (agglomerate + << GRUB_DISK_CACHE_BITS)); + if (data) + break; + } + + if (data) + { + grub_memcpy ((char *) buf + + (agglomerate << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS)), + data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + grub_disk_cache_unlock (disk->dev->id, disk->id, + sector + (agglomerate + << GRUB_DISK_CACHE_BITS)); + } + + if (agglomerate) + { + grub_disk_addr_t i; + + err = (disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, sector), + agglomerate << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size), + buf); + if (err) + goto error; + + for (i = 0; i < agglomerate; i ++) + grub_disk_cache_store (disk->dev->id, disk->id, + sector + (i << GRUB_DISK_CACHE_BITS), + (char *) buf + + (i << (GRUB_DISK_CACHE_BITS + + GRUB_DISK_SECTOR_BITS))); + + + if (disk->read_hook) + (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS), + buf, disk->read_hook_data); + + sector += agglomerate << GRUB_DISK_CACHE_BITS; + size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS); + buf = (char *) buf + + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS)); + } + + if (data) + { + if (disk->read_hook) + (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS), + buf, disk->read_hook_data); + sector += GRUB_DISK_CACHE_SIZE; + buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS); + } + } + + /* And now read the last part. */ + if (size) + { + err = grub_disk_read_small (disk, sector, 0, size, buf); + if (err) + goto error; + } + + err = grub_errno; + + error: + read_recursion_depth--; + return err; +} + +grub_uint64_t +grub_disk_native_sectors (grub_disk_t disk) +{ + if (disk->partition) + return grub_partition_get_len (disk->partition); + else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN) + return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); + else + return GRUB_DISK_SIZE_UNKNOWN; +} diff --git a/grub-core/kern/disk_common.c b/grub-core/kern/disk_common.c new file mode 100644 index 000000000..ab6cd0c2b --- /dev/null +++ b/grub-core/kern/disk_common.c @@ -0,0 +1,60 @@ +/* This function performs three tasks: + - Make sectors disk relative from partition relative. + - Normalize offset to be less than the sector size. + - Verify that the range is inside the partition. */ +static grub_err_t +grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector, + grub_off_t *offset, grub_size_t size) +{ + grub_partition_t part; + grub_disk_addr_t total_sectors; + + *sector += *offset >> GRUB_DISK_SECTOR_BITS; + *offset &= GRUB_DISK_SECTOR_SIZE - 1; + + for (part = disk->partition; part; part = part->parent) + { + grub_disk_addr_t start; + grub_uint64_t len; + + start = part->start; + len = part->len; + + if (*sector >= len + || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) + >> GRUB_DISK_SECTOR_BITS)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of partition")); + + *sector += start; + } + + /* Transform total_sectors to number of 512B blocks. */ + total_sectors = disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS); + + /* + * Some drivers have problems with disks above reasonable sizes. + * Clamp the size to GRUB_DISK_MAX_SECTORS. Just one condition is enough + * since GRUB_DISK_SIZE_UNKNOWN is always above GRUB_DISK_MAX_SECTORS, + * assuming a maximum 4 KiB sector size. + */ + if (total_sectors > GRUB_DISK_MAX_SECTORS) + total_sectors = GRUB_DISK_MAX_SECTORS; + + if ((total_sectors <= *sector + || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) + >> GRUB_DISK_SECTOR_BITS) > total_sectors - *sector)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read or write outside of disk `%s'"), disk->name); + + return GRUB_ERR_NONE; +} + +static unsigned +grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + return ((dev_id * 524287UL + disk_id * 2606459UL + + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS))) + % GRUB_DISK_CACHE_NUM); +} diff --git a/kern/dl.c b/grub-core/kern/dl.c similarity index 50% rename from kern/dl.c rename to grub-core/kern/dl.c index 4735a004a..de8c3aa8d 100644 --- a/kern/dl.c +++ b/grub-core/kern/dl.c @@ -31,79 +31,55 @@ #include #include #include +#include +#include + +#ifdef GRUB_MACHINE_EFI +#include +#endif /* Platforms where modules are in a readonly area of memory. */ #if defined(GRUB_MACHINE_QEMU) #define GRUB_MODULES_MACHINE_READONLY #endif - +#ifdef GRUB_MACHINE_EFI +#define DL_ALIGN GRUB_EFI_PAGE_SIZE +#else +#define DL_ALIGN 1 +#endif -struct grub_dl_list -{ - struct grub_dl_list *next; - grub_dl_t mod; -}; -typedef struct grub_dl_list *grub_dl_list_t; +#pragma GCC diagnostic ignored "-Wcast-align" -static grub_dl_list_t grub_dl_head; +grub_dl_t grub_dl_head = 0; -static grub_err_t +grub_err_t +grub_dl_add (grub_dl_t mod); + +/* Keep global so that GDB scripts work. */ +grub_err_t grub_dl_add (grub_dl_t mod) { - grub_dl_list_t l; - if (grub_dl_get (mod->name)) return grub_error (GRUB_ERR_BAD_MODULE, "`%s' is already loaded", mod->name); - l = (grub_dl_list_t) grub_malloc (sizeof (*l)); - if (! l) - return grub_errno; - - l->mod = mod; - l->next = grub_dl_head; - grub_dl_head = l; - return GRUB_ERR_NONE; } static void grub_dl_remove (grub_dl_t mod) { - grub_dl_list_t *p, q; + grub_dl_t *p, q; for (p = &grub_dl_head, q = *p; q; p = &q->next, q = *p) - if (q->mod == mod) + if (q == mod) { *p = q->next; - grub_free (q); return; } } -grub_dl_t -grub_dl_get (const char *name) -{ - grub_dl_list_t l; - - for (l = grub_dl_head; l; l = l->next) - if (grub_strcmp (name, l->mod->name) == 0) - return l->mod; - - return 0; -} - -void -grub_dl_iterate (int (*hook) (grub_dl_t mod)) -{ - grub_dl_list_t l; - - for (l = grub_dl_head; l; l = l->next) - if (hook (l->mod)) - break; -} - struct grub_symbol @@ -111,6 +87,7 @@ struct grub_symbol struct grub_symbol *next; const char *name; void *addr; + int isfunc; grub_dl_t mod; /* The module to which this symbol belongs. */ }; typedef struct grub_symbol *grub_symbol_t; @@ -135,21 +112,22 @@ grub_symbol_hash (const char *s) /* Resolve the symbol name NAME and return the address. Return NULL, if not found. */ -static void * +static grub_symbol_t grub_dl_resolve_symbol (const char *name) { grub_symbol_t sym; for (sym = grub_symtab[grub_symbol_hash (name)]; sym; sym = sym->next) if (grub_strcmp (sym->name, name) == 0) - return sym->addr; + return sym; return 0; } /* Register a symbol with the name NAME and the address ADDR. */ grub_err_t -grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod) +grub_dl_register_symbol (const char *name, void *addr, int isfunc, + grub_dl_t mod) { grub_symbol_t sym; unsigned k; @@ -172,6 +150,7 @@ grub_dl_register_symbol (const char *name, void *addr, grub_dl_t mod) sym->addr = addr; sym->mod = mod; + sym->isfunc = isfunc; k = grub_symbol_hash (name); sym->next = grub_symtab[k]; @@ -226,20 +205,24 @@ static grub_err_t grub_dl_check_header (void *ehdr, grub_size_t size) { Elf_Ehdr *e = ehdr; + grub_err_t err; /* Check the header size. */ if (size < sizeof (Elf_Ehdr)) return grub_error (GRUB_ERR_BAD_OS, "ELF header smaller than expected"); /* Check the magic numbers. */ - if (grub_arch_dl_check_header (ehdr) - || e->e_ident[EI_MAG0] != ELFMAG0 + if (e->e_ident[EI_MAG0] != ELFMAG0 || e->e_ident[EI_MAG1] != ELFMAG1 || e->e_ident[EI_MAG2] != ELFMAG2 || e->e_ident[EI_MAG3] != ELFMAG3 || e->e_ident[EI_VERSION] != EV_CURRENT || e->e_version != EV_CURRENT) - return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic"); + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-independent ELF magic")); + + err = grub_arch_dl_check_header (ehdr); + if (err) + return err; return GRUB_ERR_NONE; } @@ -249,12 +232,67 @@ static grub_err_t grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { unsigned i; - Elf_Shdr *s; + const Elf_Shdr *s; + grub_size_t tsize = 0, talign = 1, arch_addralign = 1; +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + grub_size_t tramp; + grub_size_t tramp_align; + grub_size_t got; + grub_size_t got_align; + grub_err_t err; +#endif + char *ptr; + + arch_addralign = DL_ALIGN; + + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) + { + grub_size_t sh_addralign; + grub_size_t sh_size; + + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); + sh_size = ALIGN_UP (s->sh_size, sh_addralign); + + tsize = ALIGN_UP (tsize, sh_addralign) + sh_size; + talign = grub_max (talign, sh_addralign); + } + +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); + if (err) + return err; + tramp_align = grub_max (GRUB_ARCH_DL_TRAMP_ALIGN, arch_addralign); + tsize += ALIGN_UP (tramp, tramp_align); + talign = grub_max (talign, tramp_align); + got_align = grub_max (GRUB_ARCH_DL_GOT_ALIGN, arch_addralign); + tsize += ALIGN_UP (got, got_align); + talign = grub_max (talign, got_align); +#endif + +#ifdef GRUB_MACHINE_EMU + mod->base = grub_osdep_dl_memalign (talign, tsize); +#else + mod->base = grub_memalign (talign, tsize); +#endif + if (!mod->base) + return grub_errno; + mod->sz = tsize; + ptr = mod->base; for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) { + grub_size_t sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign); + grub_size_t sh_size = ALIGN_UP (s->sh_size, sh_addralign); + if (s->sh_flags & SHF_ALLOC) { grub_dl_segment_t seg; @@ -267,20 +305,18 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { void *addr; - addr = grub_memalign (s->sh_addralign, s->sh_size); - if (! addr) - { - grub_free (seg); - return grub_errno; - } + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign); + addr = ptr; + ptr += sh_size; switch (s->sh_type) { case SHT_PROGBITS: grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); + grub_memset ((char *) addr + s->sh_size, 0, sh_size - s->sh_size); break; case SHT_NOBITS: - grub_memset (addr, 0, s->sh_size); + grub_memset (addr, 0, sh_size); break; } @@ -289,12 +325,23 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) else seg->addr = 0; - seg->size = s->sh_size; + seg->size = sh_size; seg->section = i; seg->next = mod->segment; mod->segment = seg; } } +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, tramp_align); + mod->tramp = ptr; + mod->trampptr = ptr; + ptr += tramp; + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, got_align); + mod->got = ptr; + mod->gotptr = ptr; + ptr += got; +#endif return GRUB_ERR_NONE; } @@ -314,15 +361,21 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) if (s->sh_type == SHT_SYMTAB) break; + /* Module without symbol table may still be used to pull in dependencies. + We verify at build time that such modules do not contain any relocations + that may reference symbol table. */ if (i == e->e_shnum) - return grub_error (GRUB_ERR_BAD_MODULE, "no symbol table"); + return GRUB_ERR_NONE; #ifdef GRUB_MODULES_MACHINE_READONLY mod->symtab = grub_malloc (s->sh_size); - memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size); + if (!mod->symtab) + return grub_errno; + grub_memcpy (mod->symtab, (char *) e + s->sh_offset, s->sh_size); #else mod->symtab = (Elf_Sym *) ((char *) e + s->sh_offset); #endif + mod->symsize = s->sh_entsize; sym = mod->symtab; size = s->sh_size; entsize = s->sh_entsize; @@ -345,17 +398,20 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) /* Resolve a global symbol. */ if (sym->st_name != 0 && sym->st_shndx == 0) { - sym->st_value = (Elf_Addr) grub_dl_resolve_symbol (name); - if (! sym->st_value) + grub_symbol_t nsym = grub_dl_resolve_symbol (name); + if (! nsym) return grub_error (GRUB_ERR_BAD_MODULE, - "the symbol `%s' not found", name); + N_("symbol `%s' not found"), name); + sym->st_value = (Elf_Addr) nsym->addr; + if (nsym->isfunc) + sym->st_info = ELF_ST_INFO (bind, STT_FUNC); } else { sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod, sym->st_shndx); if (bind != STB_LOCAL) - if (grub_dl_register_symbol (name, (void *) sym->st_value, mod)) + if (grub_dl_register_symbol (name, (void *) sym->st_value, 0, mod)) return grub_errno; } break; @@ -363,10 +419,21 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) case STT_FUNC: sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod, sym->st_shndx); +#ifdef __ia64__ + { + /* FIXME: free descriptor once it's not used anymore. */ + char **desc; + desc = grub_malloc (2 * sizeof (char *)); + if (!desc) + return grub_errno; + desc[0] = (void *) sym->st_value; + desc[1] = mod->base; + sym->st_value = (grub_addr_t) desc; + } +#endif if (bind != STB_LOCAL) - if (grub_dl_register_symbol (name, (void *) sym->st_value, mod)) + if (grub_dl_register_symbol (name, (void *) sym->st_value, 1, mod)) return grub_errno; - if (grub_strcmp (name, "grub_mod_init") == 0) mod->init = (void (*) (grub_dl_t)) sym->st_value; else if (grub_strcmp (name, "grub_mod_fini") == 0) @@ -391,15 +458,8 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) return GRUB_ERR_NONE; } -static void -grub_dl_call_init (grub_dl_t mod) -{ - if (mod->init) - (mod->init) (mod); -} - -static grub_err_t -grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e) +static Elf_Shdr * +grub_dl_find_section (Elf_Ehdr *e, const char *name) { Elf_Shdr *s; const char *str; @@ -411,17 +471,51 @@ grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e) for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (grub_strcmp (str + s->sh_name, ".modname") == 0) - { - mod->name = grub_strdup ((char *) e + s->sh_offset); - if (! mod->name) - return grub_errno; - break; - } + if (grub_strcmp (str + s->sh_name, name) == 0) + return s; + return NULL; +} - if (i == e->e_shnum) +/* Me, Vladimir Serbinenko, hereby I add this module check as per new + GNU module policy. Note that this license check is informative only. + Modules have to be licensed under GPLv3 or GPLv3+ (optionally + multi-licensed under other licences as well) independently of the + presence of this check and solely by linking (module loading in GRUB + constitutes linking) and GRUB core being licensed under GPLv3+. + Be sure to understand your license obligations. +*/ +static grub_err_t +grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e) +{ + Elf_Shdr *s = grub_dl_find_section (e, ".module_license"); + + if (s == NULL) + return grub_error (GRUB_ERR_BAD_MODULE, + "no license section in module %.63s", mod->name); + + if (grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3") == 0 + || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv3+") == 0 + || grub_strcmp ((char *) e + s->sh_offset, "LICENSE=GPLv2+") == 0) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_BAD_MODULE, + "incompatible license in module %.63s: %.63s", mod->name, + (char *) e + s->sh_offset); +} + +static grub_err_t +grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e) +{ + Elf_Shdr *s; + + s = grub_dl_find_section (e, ".modname"); + if (!s) return grub_error (GRUB_ERR_BAD_MODULE, "no module name found"); + mod->name = grub_strdup ((char *) e + s->sh_offset); + if (! mod->name) + return grub_errno; + return GRUB_ERR_NONE; } @@ -429,87 +523,219 @@ static grub_err_t grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) { Elf_Shdr *s; - const char *str; - unsigned i; - s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); - str = (char *) e + s->sh_offset; + s = grub_dl_find_section (e, ".moddeps"); + + if (!s) + return GRUB_ERR_NONE; + + const char *name = (char *) e + s->sh_offset; + const char *max = name + s->sh_size; + + while ((name < max) && (*name)) + { + grub_dl_t m; + grub_dl_dep_t dep; + + m = grub_dl_load (name); + if (! m) + return grub_errno; + + grub_dl_ref (m); + + dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep)); + if (! dep) + return grub_errno; + + dep->mod = m; + dep->next = mod->dep; + mod->dep = dep; + + name += grub_strlen (name) + 1; + } + + return GRUB_ERR_NONE; +} + +grub_uint64_t +grub_dl_ref (grub_dl_t mod) +{ + grub_dl_dep_t dep; + + if (!mod) + return 0; + + for (dep = mod->dep; dep; dep = dep->next) + grub_dl_ref (dep->mod); + + if (grub_add (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count overflow"); + + return mod->ref_count; +} + +grub_uint64_t +grub_dl_unref (grub_dl_t mod) +{ + grub_dl_dep_t dep; + + if (!mod) + return 0; + + for (dep = mod->dep; dep; dep = dep->next) + grub_dl_unref (dep->mod); + + if (grub_sub (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count underflow"); + + return mod->ref_count; +} + +grub_uint64_t +grub_dl_ref_count (grub_dl_t mod) +{ + if (mod == NULL) + return 0; + + return mod->ref_count; +} + +static void +grub_dl_flush_cache (grub_dl_t mod) +{ + grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n", + (unsigned long) mod->sz, mod->base); + grub_arch_sync_caches (mod->base, mod->sz); +} + +static grub_err_t +grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) +{ + Elf_Ehdr *e = ehdr; + Elf_Shdr *s; + unsigned i; for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) - if (grub_strcmp (str + s->sh_name, ".moddeps") == 0) + if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) { - const char *name = (char *) e + s->sh_offset; - const char *max = name + s->sh_size; + grub_dl_segment_t seg; + grub_err_t err; - while ((name < max) && (*name)) + if (!(s->sh_flags & SHF_INFO_LINK)) + continue; + + /* Find the target segment. */ + for (seg = mod->segment; seg; seg = seg->next) + if (seg->section == s->sh_info) + break; + + if (seg) { - grub_dl_t m; - grub_dl_dep_t dep; + if (!mod->symtab) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); - m = grub_dl_load (name); - if (! m) - return grub_errno; - - grub_dl_ref (m); - - dep = (grub_dl_dep_t) grub_malloc (sizeof (*dep)); - if (! dep) - return grub_errno; - - dep->mod = m; - dep->next = mod->dep; - mod->dep = dep; - - name += grub_strlen (name) + 1; + err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); + if (err) + return err; } } return GRUB_ERR_NONE; } -#ifndef GRUB_UTIL -int -grub_dl_ref (grub_dl_t mod) +/* Only define this on EFI to save space in core. */ +#ifdef GRUB_MACHINE_EFI +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) { - grub_dl_dep_t dep; + unsigned i; + const Elf_Shdr *s; + const Elf_Ehdr *e = ehdr; + grub_err_t err; +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + grub_size_t arch_addralign = DL_ALIGN; + grub_addr_t tgaddr; + grub_size_t tgsz; +#endif - for (dep = mod->dep; dep; dep = dep->next) - grub_dl_ref (dep->mod); + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + { + grub_dl_segment_t seg; + grub_uint64_t set_attrs = GRUB_MEM_ATTR_R; + grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; - return ++mod->ref_count; + for (seg = mod->segment; seg; seg = seg->next) + /* Does this ELF section's index match GRUB DL segment? */ + if (seg->section == i) + break; + + /* No GRUB DL segment found for this ELF section, skip it. */ + if (!seg) + continue; + + if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + if (s->sh_flags & SHF_WRITE) + { + set_attrs |= GRUB_MEM_ATTR_W; + clear_attrs &= ~GRUB_MEM_ATTR_W; + } + + if (s->sh_flags & SHF_EXECINSTR) + { + set_attrs |= GRUB_MEM_ATTR_X; + clear_attrs &= ~GRUB_MEM_ATTR_X; + } + + err = grub_update_mem_attrs ((grub_addr_t) seg->addr, seg->size, + set_attrs, clear_attrs); + if (err != GRUB_ERR_NONE) + return err; + } + +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined (__loongarch__) + tgaddr = grub_min ((grub_addr_t) mod->tramp, (grub_addr_t) mod->got); + tgsz = grub_max ((grub_addr_t) mod->trampptr, (grub_addr_t) mod->gotptr) - tgaddr; + + if (tgsz) + { + tgsz = ALIGN_UP (tgsz, arch_addralign); + + if (tgaddr < (grub_addr_t) mod->base || + tgsz > (grub_addr_t) -1 - tgaddr || + tgaddr + tgsz > (grub_addr_t) mod->base + mod->sz) + return grub_error (GRUB_ERR_BUG, + "BUG: trying to protect pages outside of module " + "allocation (\"%s\"): module base %p, size 0x%" + PRIxGRUB_SIZE "; tramp/GOT base 0x%" PRIxGRUB_ADDR + ", size 0x%" PRIxGRUB_SIZE, + mod->name, mod->base, mod->sz, tgaddr, tgsz); + err = grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_X, GRUB_MEM_ATTR_W); + if (err != GRUB_ERR_NONE) + return err; + } +#endif + + return GRUB_ERR_NONE; } - -int -grub_dl_unref (grub_dl_t mod) +#else +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod __attribute__ ((unused)), void *ehdr __attribute__ ((unused))) { - grub_dl_dep_t dep; - - for (dep = mod->dep; dep; dep = dep->next) - grub_dl_unref (dep->mod); - - return --mod->ref_count; + return GRUB_ERR_NONE; } #endif -static void -grub_dl_flush_cache (grub_dl_t mod) -{ - grub_dl_segment_t seg; - - for (seg = mod->segment; seg; seg = seg->next) { - if (seg->size) { - grub_dprintf ("modules", "flushing 0x%lx bytes at %p\n", - (unsigned long) seg->size, seg->addr); - grub_arch_sync_caches (seg->addr, seg->size); - } - } -} - /* Load a module from core memory. */ grub_dl_t -grub_dl_load_core (void *addr, grub_size_t size) +grub_dl_load_core_noinit (void *addr, grub_size_t size) { Elf_Ehdr *e; grub_dl_t mod; @@ -522,12 +748,12 @@ grub_dl_load_core (void *addr, grub_size_t size) if (e->e_type != ET_REL) { - grub_error (GRUB_ERR_BAD_MODULE, "invalid ELF file type"); + grub_error (GRUB_ERR_BAD_MODULE, N_("this ELF file is not of the right type")); return 0; } /* Make sure that every section is within the core. */ - if (size < e->e_shoff + e->e_shentsize * e->e_shnum) + if (size < e->e_shoff + (grub_uint32_t) e->e_shentsize * e->e_shnum) { grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core"); return 0; @@ -540,11 +766,21 @@ grub_dl_load_core (void *addr, grub_size_t size) mod->ref_count = 1; grub_dprintf ("modules", "relocating to %p\n", mod); + /* Me, Vladimir Serbinenko, hereby I add this module check as per new + GNU module policy. Note that this license check is informative only. + Modules have to be licensed under GPLv3 or GPLv3+ (optionally + multi-licensed under other licences as well) independently of the + presence of this check and solely by linking (module loading in GRUB + constitutes linking) and GRUB core being licensed under GPLv3+. + Be sure to understand your license obligations. + */ if (grub_dl_resolve_name (mod, e) + || grub_dl_check_license (mod, e) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) - || grub_arch_dl_relocate_symbols (mod, e)) + || grub_dl_relocate_symbols (mod, e) + || grub_dl_set_mem_attrs (mod, e)) { mod->fini = 0; grub_dl_unload (mod); @@ -555,7 +791,6 @@ grub_dl_load_core (void *addr, grub_size_t size) grub_dprintf ("modules", "module name: %s\n", mod->name); grub_dprintf ("modules", "init function: %p\n", mod->init); - grub_dl_call_init (mod); if (grub_dl_add (mod)) { @@ -566,6 +801,25 @@ grub_dl_load_core (void *addr, grub_size_t size) return mod; } +grub_dl_t +grub_dl_load_core (void *addr, grub_size_t size) +{ + grub_dl_t mod; + + grub_boot_time ("Parsing module"); + + mod = grub_dl_load_core_noinit (addr, size); + + if (!mod) + return NULL; + + grub_boot_time ("Initing module %s", mod->name); + grub_dl_init (mod); + grub_boot_time ("Module %s inited", mod->name); + + return mod; +} + /* Load a module from the file FILENAME. */ grub_dl_t grub_dl_load_file (const char *filename) @@ -575,7 +829,9 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; - file = grub_file_open (filename); + grub_boot_time ("Loading module %s", filename); + + file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); if (! file) return 0; @@ -600,13 +856,11 @@ grub_dl_load_file (const char *filename) grub_file_close (file); mod = grub_dl_load_core (core, size); + grub_free (core); if (! mod) - { - grub_free (core); - return 0; - } + return 0; - mod->ref_count = 0; + mod->ref_count--; return mod; } @@ -616,18 +870,22 @@ grub_dl_load (const char *name) { char *filename; grub_dl_t mod; - char *grub_dl_dir = grub_env_get ("prefix"); + const char *grub_dl_dir = grub_env_get ("prefix"); mod = grub_dl_get (name); if (mod) return mod; + if (grub_no_modules) + return 0; + if (! grub_dl_dir) { - grub_error (GRUB_ERR_FILE_NOT_FOUND, "\"prefix\" is not set"); + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"), "prefix"); return 0; } - filename = grub_xasprintf ("%s/%s.mod", grub_dl_dir, name); + filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM "/%s.mod", + grub_dl_dir, name); if (! filename) return 0; @@ -648,7 +906,6 @@ int grub_dl_unload (grub_dl_t mod) { grub_dl_dep_t dep, depn; - grub_dl_segment_t seg, segn; if (mod->ref_count > 0) return 0; @@ -663,19 +920,16 @@ grub_dl_unload (grub_dl_t mod) { depn = dep->next; - if (! grub_dl_unref (dep->mod)) - grub_dl_unload (dep->mod); + grub_dl_unload (dep->mod); grub_free (dep); } - for (seg = mod->segment; seg; seg = segn) - { - segn = seg->next; - grub_free (seg->addr); - grub_free (seg); - } - +#ifdef GRUB_MACHINE_EMU + grub_dl_osdep_dl_free (mod->base); +#else + grub_free (mod->base); +#endif grub_free (mod->name); #ifdef GRUB_MODULES_MACHINE_READONLY grub_free (mod->symtab); @@ -683,40 +937,3 @@ grub_dl_unload (grub_dl_t mod) grub_free (mod); return 1; } - -/* Unload unneeded modules. */ -void -grub_dl_unload_unneeded (void) -{ - /* Because grub_dl_remove modifies the list of modules, this - implementation is tricky. */ - grub_dl_list_t p = grub_dl_head; - - while (p) - { - if (grub_dl_unload (p->mod)) - { - p = grub_dl_head; - continue; - } - - p = p->next; - } -} - -/* Unload all modules. */ -void -grub_dl_unload_all (void) -{ - while (grub_dl_head) - { - grub_dl_list_t p; - - grub_dl_unload_unneeded (); - - /* Force to decrement the ref count. This will purge pre-loaded - modules and manually inserted modules. */ - for (p = grub_dl_head; p; p = p->next) - p->mod->ref_count--; - } -} diff --git a/grub-core/kern/efi/acpi.c b/grub-core/kern/efi/acpi.c new file mode 100644 index 000000000..828e6dbb2 --- /dev/null +++ b/grub-core/kern/efi/acpi.c @@ -0,0 +1,37 @@ +/* acpi.c - get acpi tables. */ +/* + * 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 . + */ + +#include +#include + +struct grub_acpi_rsdp_v10 * +grub_machine_acpi_get_rsdpv1 (void) +{ + static grub_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; + + return (struct grub_acpi_rsdp_v10 *) grub_efi_find_configuration_table (&acpi_guid); +} + +struct grub_acpi_rsdp_v20 * +grub_machine_acpi_get_rsdpv2 (void) +{ + static grub_guid_t acpi20_guid = GRUB_EFI_ACPI_20_TABLE_GUID; + + return (struct grub_acpi_rsdp_v20 *) grub_efi_find_configuration_table (&acpi20_guid); +} diff --git a/grub-core/kern/efi/debug.c b/grub-core/kern/efi/debug.c new file mode 100644 index 000000000..5d2ab1a36 --- /dev/null +++ b/grub-core/kern/efi/debug.c @@ -0,0 +1,38 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 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 . + */ +/* debug.c - aides for debugging the EFI application */ + +#include +#include +#include + +static grub_err_t +grub_cmd_gdbinfo (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_efi_print_gdb_info (); + return 0; +} + +void +grub_efi_register_debug_commands (void) +{ + grub_register_command ("gdbinfo", grub_cmd_gdbinfo, 0, + N_("Print infomation useful for GDB debugging")); +} diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c new file mode 100644 index 000000000..b93ae3aba --- /dev/null +++ b/grub-core/kern/efi/efi.c @@ -0,0 +1,1051 @@ +/* efi.c - generic EFI support */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The handle of GRUB itself. Filled in by the startup code. */ +grub_efi_handle_t grub_efi_image_handle; + +/* The pointer to a system table. Filled in by the startup code. */ +grub_efi_system_table_t *grub_efi_system_table; + +static grub_guid_t console_control_guid = GRUB_EFI_CONSOLE_CONTROL_GUID; +static grub_guid_t loaded_image_guid = GRUB_EFI_LOADED_IMAGE_GUID; +static grub_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID; + +void * +grub_efi_locate_protocol (grub_guid_t *protocol, void *registration) +{ + void *interface; + grub_efi_status_t status; + + status = grub_efi_system_table->boot_services->locate_protocol (protocol, + registration, + &interface); + if (status != GRUB_EFI_SUCCESS) + return 0; + + return interface; +} + +/* Return the array of handles which meet the requirement. If successful, + the number of handles is stored in NUM_HANDLES. The array is allocated + from the heap. */ +grub_efi_handle_t * +grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, + grub_guid_t *protocol, + void *search_key, + grub_efi_uintn_t *num_handles) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_handle_t *buffer; + grub_efi_uintn_t buffer_size = 8 * sizeof (grub_efi_handle_t); + + buffer = grub_malloc (buffer_size); + if (! buffer) + return 0; + + b = grub_efi_system_table->boot_services; + status = b->locate_handle (search_type, protocol, search_key, + &buffer_size, buffer); + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (buffer); + buffer = grub_malloc (buffer_size); + if (! buffer) + return 0; + + status = b->locate_handle (search_type, protocol, search_key, + &buffer_size, buffer); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (buffer); + return 0; + } + + *num_handles = buffer_size / sizeof (grub_efi_handle_t); + return buffer; +} + +void * +grub_efi_open_protocol (grub_efi_handle_t handle, + grub_guid_t *protocol, + grub_efi_uint32_t attributes) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + void *interface; + + b = grub_efi_system_table->boot_services; + status = b->open_protocol (handle, + protocol, + &interface, + grub_efi_image_handle, + 0, + attributes); + if (status != GRUB_EFI_SUCCESS) + return 0; + + return interface; +} + +grub_efi_status_t +grub_efi_close_protocol (grub_efi_handle_t handle, grub_guid_t *protocol) +{ + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + + return b->close_protocol (handle, protocol, grub_efi_image_handle, NULL); +} + +int +grub_efi_set_text_mode (int on) +{ + grub_efi_console_control_protocol_t *c; + grub_efi_screen_mode_t mode, new_mode; + + c = grub_efi_locate_protocol (&console_control_guid, 0); + if (! c) + /* No console control protocol instance available, assume it is + already in text mode. */ + return 1; + + if (c->get_mode (c, &mode, 0, 0) != GRUB_EFI_SUCCESS) + return 0; + + new_mode = on ? GRUB_EFI_SCREEN_TEXT : GRUB_EFI_SCREEN_GRAPHICS; + if (mode != new_mode) + if (c->set_mode (c, new_mode) != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +void +grub_efi_stall (grub_efi_uintn_t microseconds) +{ + grub_efi_system_table->boot_services->stall (microseconds); +} + +grub_efi_loaded_image_t * +grub_efi_get_loaded_image (grub_efi_handle_t image_handle) +{ + return grub_efi_open_protocol (image_handle, + &loaded_image_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + +void +grub_reboot (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN | + GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY); + grub_efi_system_table->runtime_services->reset_system (GRUB_EFI_RESET_COLD, + GRUB_EFI_SUCCESS, 0, + NULL); + for (;;) ; +} + +void +grub_exit (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); + grub_efi_system_table->boot_services->exit (grub_efi_image_handle, + GRUB_EFI_SUCCESS, 0, 0); + for (;;) ; +} + +grub_err_t +grub_efi_set_virtual_address_map (grub_efi_uintn_t memory_map_size, + grub_efi_uintn_t descriptor_size, + grub_efi_uint32_t descriptor_version, + grub_efi_memory_descriptor_t *virtual_map) +{ + grub_efi_runtime_services_t *r; + grub_efi_status_t status; + + r = grub_efi_system_table->runtime_services; + status = r->set_virtual_address_map (memory_map_size, descriptor_size, + descriptor_version, virtual_map); + + if (status == GRUB_EFI_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_IO, "set_virtual_address_map failed"); +} + +grub_err_t +grub_efi_set_variable_with_attributes (const char *var, const grub_guid_t *guid, + void *data, grub_size_t datasize, grub_efi_uint32_t attributes) +{ + grub_efi_status_t status; + grub_efi_runtime_services_t *r; + grub_efi_char16_t *var16; + + grub_utf8_to_utf16_alloc (var, &var16, NULL); + + if (var16 == NULL) + return grub_errno; + + r = grub_efi_system_table->runtime_services; + + status = r->set_variable (var16, guid, attributes, datasize, data); + grub_free (var16); + if (status == GRUB_EFI_SUCCESS) + return GRUB_ERR_NONE; + + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); +} + +grub_err_t +grub_efi_set_variable (const char *var, const grub_guid_t *guid, + void *data, grub_size_t datasize) +{ + return grub_efi_set_variable_with_attributes (var, guid, data, datasize, + GRUB_EFI_VARIABLE_NON_VOLATILE + | GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS + | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); +} + +grub_efi_status_t +grub_efi_get_variable_with_attributes (const char *var, + const grub_guid_t *guid, + grub_size_t *datasize_out, + void **data_out, + grub_efi_uint32_t *attributes) +{ + grub_efi_status_t status; + grub_efi_uintn_t datasize = 0; + grub_efi_runtime_services_t *r; + grub_efi_char16_t *var16; + void *data; + + *data_out = NULL; + *datasize_out = 0; + + grub_utf8_to_utf16_alloc (var, &var16, NULL); + if (var16 == NULL) + return grub_errno; + + r = grub_efi_system_table->runtime_services; + + status = r->get_variable (var16, guid, NULL, &datasize, NULL); + + if (status != GRUB_EFI_BUFFER_TOO_SMALL || !datasize) + { + grub_free (var16); + return status; + } + + data = grub_malloc (datasize); + if (!data) + { + grub_free (var16); + return GRUB_EFI_OUT_OF_RESOURCES; + } + + status = r->get_variable (var16, guid, attributes, &datasize, data); + grub_free (var16); + + if (status == GRUB_EFI_SUCCESS) + { + *data_out = data; + *datasize_out = datasize; + return status; + } + + grub_free (data); + return status; +} + +grub_err_t +grub_efi_set_variable_to_string (const char *name, const grub_guid_t *guid, + const char *value, grub_efi_uint32_t attributes) +{ + grub_efi_char16_t *value_16; + grub_ssize_t len16; + grub_err_t status; + + len16 = grub_utf8_to_utf16_alloc (value, &value_16, NULL); + + if (len16 < 0) + return grub_errno; + + status = grub_efi_set_variable_with_attributes (name, guid, + (void *) value_16, (len16 + 1) * sizeof (value_16[0]), + attributes); + + grub_free (value_16); + + return status; +} + +grub_efi_status_t +grub_efi_get_variable (const char *var, const grub_guid_t *guid, + grub_size_t *datasize_out, void **data_out) +{ + return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, NULL); +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Search the mods section from the PE32/PE32+ image. This code uses + a PE32 header, but should work with PE32+ as well. */ +grub_addr_t +grub_efi_section_addr (const char *section_name) +{ + grub_efi_loaded_image_t *image; + struct grub_msdos_image_header *header; + struct grub_pe_image_header *pe_image_header; + struct grub_pe32_coff_header *coff_header; + struct grub_pe32_section_table *sections; + struct grub_pe32_section_table *section; + struct grub_module_info *info; + grub_uint16_t i; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (! image) + return 0; + + header = image->image_base; + pe_image_header + = (struct grub_pe_image_header *) ((char *) header + + header->pe_image_header_offset); + coff_header = &(pe_image_header->coff_header); + sections + = (struct grub_pe32_section_table *) ((char *) coff_header + + sizeof (*coff_header) + + coff_header->optional_header_size); + + for (i = 0, section = sections; + i < coff_header->num_sections; + i++, section++) + { + if (grub_strcmp (section->name, section_name) == 0) + break; + } + + if (i == coff_header->num_sections) + { + grub_dprintf("sections", "section %d is last section; invalid.\n", i); + return 0; + } + + info = (struct grub_module_info *) ((char *) image->image_base + + section->virtual_address); + if (section->name[0] != '.' && info->magic != GRUB_MODULE_MAGIC) + { + grub_dprintf("sections", + "section %d has bad magic %08x, should be %08x\n", + i, info->magic, GRUB_MODULE_MAGIC); + return 0; + } + + grub_dprintf("sections", "returning section info for section %d: \"%s\"\n", + i, section->name); + return (grub_addr_t) info; +} + +#pragma GCC diagnostic error "-Wcast-align" + +char * +grub_efi_get_filename (grub_efi_device_path_t *dp0) +{ + char *name = 0, *p, *pi; + grub_size_t filesize = 0; + grub_efi_device_path_t *dp; + + if (!dp0) + return NULL; + + dp = dp0; + + while (dp) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + { + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%d", len); + return NULL; + } + len = (len - 4) / sizeof (grub_efi_char16_t); + filesize += GRUB_MAX_UTF8_PER_UTF16 * len + 2; + } + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + if (!filesize) + return NULL; + + dp = dp0; + + p = name = grub_malloc (filesize); + if (!name) + return NULL; + + while (dp) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + { + grub_efi_file_path_device_path_t *fp; + grub_efi_uint16_t len; + grub_efi_char16_t *dup_name; + + *p++ = '/'; + + len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%d", len); + grub_free (name); + return NULL; + } + + len = (len - 4) / sizeof (grub_efi_char16_t); + fp = (grub_efi_file_path_device_path_t *) dp; + /* According to EFI spec Path Name is NULL terminated */ + while (len > 0 && fp->path_name[len - 1] == 0) + len--; + + dup_name = grub_calloc (len, sizeof (*dup_name)); + if (!dup_name) + { + grub_free (name); + return NULL; + } + p = (char *) grub_utf16_to_utf8 ((unsigned char *) p, + grub_memcpy (dup_name, fp->path_name, len * sizeof (*dup_name)), + len); + grub_free (dup_name); + } + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + *p = '\0'; + + for (pi = name, p = name; *pi;) + { + /* EFI breaks paths with backslashes. */ + if (*pi == '\\' || *pi == '/') + { + *p++ = '/'; + while (*pi == '\\' || *pi == '/') + pi++; + continue; + } + *p++ = *pi++; + } + *p = '\0'; + + return name; +} + +grub_efi_device_path_t * +grub_efi_get_device_path (grub_efi_handle_t handle) +{ + return grub_efi_open_protocol (handle, &device_path_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); +} + +/* Return the device path node right before the end node. */ +grub_efi_device_path_t * +grub_efi_find_last_device_path (const grub_efi_device_path_t *dp) +{ + grub_efi_device_path_t *next, *p; + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + return 0; + + for (p = (grub_efi_device_path_t *) dp, next = GRUB_EFI_NEXT_DEVICE_PATH (p); + ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (next); + p = next, next = GRUB_EFI_NEXT_DEVICE_PATH (next)) + ; + + return p; +} + +/* Duplicate a device path. */ +grub_efi_device_path_t * +grub_efi_duplicate_device_path (const grub_efi_device_path_t *dp) +{ + grub_efi_device_path_t *p; + grub_size_t total_size = 0; + + for (p = (grub_efi_device_path_t *) dp; + ; + p = GRUB_EFI_NEXT_DEVICE_PATH (p)) + { + grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (p); + + /* + * In the event that we find a node that's completely garbage, for + * example if we get to 0x7f 0x01 0x02 0x00 ... (EndInstance with a size + * of 2), GRUB_EFI_END_ENTIRE_DEVICE_PATH() will be true and + * GRUB_EFI_NEXT_DEVICE_PATH() will return NULL, so we won't continue, + * and neither should our consumers, but there won't be any error raised + * even though the device path is junk. + * + * This keeps us from passing junk down back to our caller. + */ + if (len < 4) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "malformed EFI Device Path node has length=%" PRIuGRUB_SIZE, len); + return NULL; + } + + total_size += len; + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p)) + break; + } + + p = grub_malloc (total_size); + if (! p) + return 0; + + grub_memcpy (p, dp, total_size); + return p; +} + +static void +dump_vendor_path (const char *type, grub_efi_vendor_device_path_t *vendor) +{ + grub_uint32_t vendor_data_len = vendor->header.length - sizeof (*vendor); + grub_printf ("/%sVendor(%pG)[%x: ", type, &vendor->vendor_guid, vendor_data_len); + if (vendor->header.length > sizeof (*vendor)) + { + grub_uint32_t i; + for (i = 0; i < vendor_data_len; i++) + grub_printf ("%02x ", vendor->vendor_defined_data[i]); + } + grub_printf ("]"); +} + + +/* Print the chain of Device Path nodes. This is mainly for debugging. */ +void +grub_efi_print_device_path (grub_efi_device_path_t *dp) +{ + while (GRUB_EFI_DEVICE_PATH_VALID (dp)) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + switch (type) + { + case GRUB_EFI_END_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE: + grub_printf ("/EndEntire\n"); + //grub_putchar ('\n'); + break; + case GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE: + grub_printf ("/EndThis\n"); + //grub_putchar ('\n'); + break; + default: + grub_printf ("/EndUnknown(%x)\n", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_PCI_DEVICE_PATH_SUBTYPE: + { + grub_efi_pci_device_path_t *pci + = (grub_efi_pci_device_path_t *) dp; + grub_printf ("/PCI(%x,%x)", + (unsigned) pci->function, (unsigned) pci->device); + } + break; + case GRUB_EFI_PCCARD_DEVICE_PATH_SUBTYPE: + { + grub_efi_pccard_device_path_t *pccard + = (grub_efi_pccard_device_path_t *) dp; + grub_printf ("/PCCARD(%x)", + (unsigned) pccard->function); + } + break; + case GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE: + { + grub_efi_memory_mapped_device_path_t *mmapped + = (grub_efi_memory_mapped_device_path_t *) dp; + grub_printf ("/MMap(%x,%llx,%llx)", + (unsigned) mmapped->memory_type, + (unsigned long long) mmapped->start_address, + (unsigned long long) mmapped->end_address); + } + break; + case GRUB_EFI_VENDOR_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Hardware", + (grub_efi_vendor_device_path_t *) dp); + break; + case GRUB_EFI_CONTROLLER_DEVICE_PATH_SUBTYPE: + { + grub_efi_controller_device_path_t *controller + = (grub_efi_controller_device_path_t *) dp; + grub_printf ("/Ctrl(%x)", + (unsigned) controller->controller_number); + } + break; + default: + grub_printf ("/UnknownHW(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_ACPI_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_ACPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_acpi_device_path_t *acpi + = (grub_efi_acpi_device_path_t *) dp; + grub_printf ("/ACPI(%x,%x)", + (unsigned) acpi->hid, + (unsigned) acpi->uid); + } + break; + case GRUB_EFI_EXPANDED_ACPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_expanded_acpi_device_path_t *eacpi + = (grub_efi_expanded_acpi_device_path_t *) dp; + grub_printf ("/ACPI("); + + if (GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)[0] == '\0') + grub_printf ("%x,", (unsigned) eacpi->hid); + else + grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_HIDSTR (dp)); + + if (GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)[0] == '\0') + grub_printf ("%x,", (unsigned) eacpi->uid); + else + grub_printf ("%s,", GRUB_EFI_EXPANDED_ACPI_UIDSTR (dp)); + + if (GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)[0] == '\0') + grub_printf ("%x)", (unsigned) eacpi->cid); + else + grub_printf ("%s)", GRUB_EFI_EXPANDED_ACPI_CIDSTR (dp)); + } + break; + default: + grub_printf ("/UnknownACPI(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_ATAPI_DEVICE_PATH_SUBTYPE: + { + grub_efi_atapi_device_path_t *atapi + = (grub_efi_atapi_device_path_t *) dp; + grub_printf ("/ATAPI(%x,%x,%x)", + (unsigned) atapi->primary_secondary, + (unsigned) atapi->slave_master, + (unsigned) atapi->lun); + } + break; + case GRUB_EFI_SCSI_DEVICE_PATH_SUBTYPE: + { + grub_efi_scsi_device_path_t *scsi + = (grub_efi_scsi_device_path_t *) dp; + grub_printf ("/SCSI(%x,%x)", + (unsigned) scsi->pun, + (unsigned) scsi->lun); + } + break; + case GRUB_EFI_FIBRE_CHANNEL_DEVICE_PATH_SUBTYPE: + { + grub_efi_fibre_channel_device_path_t *fc + = (grub_efi_fibre_channel_device_path_t *) dp; + grub_printf ("/FibreChannel(%llx,%llx)", + (unsigned long long) fc->wwn, + (unsigned long long) fc->lun); + } + break; + case GRUB_EFI_1394_DEVICE_PATH_SUBTYPE: + { + grub_efi_1394_device_path_t *firewire + = (grub_efi_1394_device_path_t *) dp; + grub_printf ("/1394(%llx)", + (unsigned long long) firewire->guid); + } + break; + case GRUB_EFI_USB_DEVICE_PATH_SUBTYPE: + { + grub_efi_usb_device_path_t *usb + = (grub_efi_usb_device_path_t *) dp; + grub_printf ("/USB(%x,%x)", + (unsigned) usb->parent_port_number, + (unsigned) usb->usb_interface); + } + break; + case GRUB_EFI_USB_CLASS_DEVICE_PATH_SUBTYPE: + { + grub_efi_usb_class_device_path_t *usb_class + = (grub_efi_usb_class_device_path_t *) dp; + grub_printf ("/USBClass(%x,%x,%x,%x,%x)", + (unsigned) usb_class->vendor_id, + (unsigned) usb_class->product_id, + (unsigned) usb_class->device_class, + (unsigned) usb_class->device_subclass, + (unsigned) usb_class->device_protocol); + } + break; + case GRUB_EFI_I2O_DEVICE_PATH_SUBTYPE: + { + grub_efi_i2o_device_path_t *i2o + = (grub_efi_i2o_device_path_t *) dp; + grub_printf ("/I2O(%x)", (unsigned) i2o->tid); + } + break; + case GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE: + { + grub_efi_mac_address_device_path_t *mac + = (grub_efi_mac_address_device_path_t *) dp; + grub_printf ("/MacAddr(%02x:%02x:%02x:%02x:%02x:%02x,%x)", + (unsigned) mac->mac_address[0], + (unsigned) mac->mac_address[1], + (unsigned) mac->mac_address[2], + (unsigned) mac->mac_address[3], + (unsigned) mac->mac_address[4], + (unsigned) mac->mac_address[5], + (unsigned) mac->if_type); + } + break; + case GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE: + { + grub_efi_ipv4_device_path_t *ipv4 + = (grub_efi_ipv4_device_path_t *) dp; + grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)", + (unsigned) ipv4->local_ip_address[0], + (unsigned) ipv4->local_ip_address[1], + (unsigned) ipv4->local_ip_address[2], + (unsigned) ipv4->local_ip_address[3], + (unsigned) ipv4->remote_ip_address[0], + (unsigned) ipv4->remote_ip_address[1], + (unsigned) ipv4->remote_ip_address[2], + (unsigned) ipv4->remote_ip_address[3], + (unsigned) ipv4->local_port, + (unsigned) ipv4->remote_port, + (unsigned) ipv4->protocol, + (unsigned) ipv4->static_ip_address); + } + break; + case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE: + { + grub_efi_ipv6_device_path_t *ipv6 + = (grub_efi_ipv6_device_path_t *) dp; + grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)", + (unsigned) ipv6->local_ip_address[0], + (unsigned) ipv6->local_ip_address[1], + (unsigned) ipv6->local_ip_address[2], + (unsigned) ipv6->local_ip_address[3], + (unsigned) ipv6->local_ip_address[4], + (unsigned) ipv6->local_ip_address[5], + (unsigned) ipv6->local_ip_address[6], + (unsigned) ipv6->local_ip_address[7], + (unsigned) ipv6->remote_ip_address[0], + (unsigned) ipv6->remote_ip_address[1], + (unsigned) ipv6->remote_ip_address[2], + (unsigned) ipv6->remote_ip_address[3], + (unsigned) ipv6->remote_ip_address[4], + (unsigned) ipv6->remote_ip_address[5], + (unsigned) ipv6->remote_ip_address[6], + (unsigned) ipv6->remote_ip_address[7], + (unsigned) ipv6->local_port, + (unsigned) ipv6->remote_port, + (unsigned) ipv6->protocol, + (unsigned) ipv6->static_ip_address); + } + break; + case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE: + { + grub_efi_infiniband_device_path_t *ib + = (grub_efi_infiniband_device_path_t *) dp; + grub_printf ("/InfiniBand(%x,%llx,%llx,%llx)", + (unsigned) ib->port_gid[0], /* XXX */ + (unsigned long long) ib->remote_id, + (unsigned long long) ib->target_port_id, + (unsigned long long) ib->device_id); + } + break; + case GRUB_EFI_UART_DEVICE_PATH_SUBTYPE: + { + grub_efi_uart_device_path_t *uart + = (grub_efi_uart_device_path_t *) dp; + grub_printf ("/UART(%llu,%u,%x,%x)", + (unsigned long long) uart->baud_rate, + uart->data_bits, + uart->parity, + uart->stop_bits); + } + break; + case GRUB_EFI_SATA_DEVICE_PATH_SUBTYPE: + { + grub_efi_sata_device_path_t *sata; + sata = (grub_efi_sata_device_path_t *) dp; + grub_printf ("/Sata(%x,%x,%x)", + sata->hba_port, + sata->multiplier_port, + sata->lun); + } + break; + case GRUB_EFI_VLAN_DEVICE_PATH_SUBTYPE: + { + grub_efi_vlan_device_path_t *vlan; + vlan = (grub_efi_vlan_device_path_t *) dp; + grub_printf ("/Vlan(%u)", vlan->vlan_id); + } + break; + + case GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Messaging", + (grub_efi_vendor_device_path_t *) dp); + break; + default: + grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_MEDIA_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_HARD_DRIVE_DEVICE_PATH_SUBTYPE: + { + grub_efi_hard_drive_device_path_t *hd = (grub_efi_hard_drive_device_path_t *) dp; + grub_printf ("/HD(%u,%llx,%llx,%02x%02x%02x%02x%02x%02x%02x%02x,%x,%x)", + hd->partition_number, + (unsigned long long) hd->partition_start, + (unsigned long long) hd->partition_size, + (unsigned) hd->partition_signature[0], + (unsigned) hd->partition_signature[1], + (unsigned) hd->partition_signature[2], + (unsigned) hd->partition_signature[3], + (unsigned) hd->partition_signature[4], + (unsigned) hd->partition_signature[5], + (unsigned) hd->partition_signature[6], + (unsigned) hd->partition_signature[7], + (unsigned) hd->partmap_type, + (unsigned) hd->signature_type); + } + break; + case GRUB_EFI_CDROM_DEVICE_PATH_SUBTYPE: + { + grub_efi_cdrom_device_path_t *cd + = (grub_efi_cdrom_device_path_t *) dp; + grub_printf ("/CD(%u,%llx,%llx)", + cd->boot_entry, + (unsigned long long) cd->partition_start, + (unsigned long long) cd->partition_size); + } + break; + case GRUB_EFI_VENDOR_MEDIA_DEVICE_PATH_SUBTYPE: + dump_vendor_path ("Media", + (grub_efi_vendor_device_path_t *) dp); + break; + case GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE: + { + grub_efi_file_path_device_path_t *fp; + grub_uint8_t *buf; + fp = (grub_efi_file_path_device_path_t *) dp; + buf = grub_malloc ((len - 4) * 2 + 1); + if (buf) + { + grub_efi_char16_t *dup_name = grub_malloc (len - 4); + if (!dup_name) + { + grub_errno = GRUB_ERR_NONE; + grub_printf ("/File((null))"); + grub_free (buf); + break; + } + *grub_utf16_to_utf8 (buf, grub_memcpy (dup_name, fp->path_name, len - 4), + (len - 4) / sizeof (grub_efi_char16_t)) + = '\0'; + grub_free (dup_name); + } + else + grub_errno = GRUB_ERR_NONE; + grub_printf ("/File(%s)", buf); + grub_free (buf); + } + break; + case GRUB_EFI_PROTOCOL_DEVICE_PATH_SUBTYPE: + { + grub_efi_protocol_device_path_t *proto + = (grub_efi_protocol_device_path_t *) dp; + grub_printf ("/Protocol(%pG)",&proto->guid); + } + break; + default: + grub_printf ("/UnknownMedia(%x)", (unsigned) subtype); + break; + } + break; + + case GRUB_EFI_BIOS_DEVICE_PATH_TYPE: + switch (subtype) + { + case GRUB_EFI_BIOS_DEVICE_PATH_SUBTYPE: + { + grub_efi_bios_device_path_t *bios + = (grub_efi_bios_device_path_t *) dp; + grub_printf ("/BIOS(%x,%x,%s)", + (unsigned) bios->device_type, + (unsigned) bios->status_flags, + (char *) (dp + 1)); + } + break; + default: + grub_printf ("/UnknownBIOS(%x)", (unsigned) subtype); + break; + } + break; + + default: + grub_printf ("/UnknownType(%x,%x)\n", + (unsigned) type, + (unsigned) subtype); + return; + break; + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } +} + +/* Compare device paths. */ +int +grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1, + const grub_efi_device_path_t *dp2) +{ + if (! dp1 || ! dp2) + /* Return non-zero. */ + return 1; + + if (dp1 == dp2) + return 0; + + while (GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2)) + { + grub_efi_uint8_t type1, type2; + grub_efi_uint8_t subtype1, subtype2; + grub_efi_uint16_t len1, len2; + int ret; + + type1 = GRUB_EFI_DEVICE_PATH_TYPE (dp1); + type2 = GRUB_EFI_DEVICE_PATH_TYPE (dp2); + + if (type1 != type2) + return (int) type2 - (int) type1; + + subtype1 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp1); + subtype2 = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp2); + + if (subtype1 != subtype2) + return (int) subtype1 - (int) subtype2; + + len1 = GRUB_EFI_DEVICE_PATH_LENGTH (dp1); + len2 = GRUB_EFI_DEVICE_PATH_LENGTH (dp2); + + if (len1 != len2) + return (int) len1 - (int) len2; + + ret = grub_memcmp (dp1, dp2, len1); + if (ret != 0) + return ret; + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp1)) + break; + + dp1 = (grub_efi_device_path_t *) ((char *) dp1 + len1); + dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2); + } + + /* + * There's no "right" answer here, but we probably don't want to call a valid + * dp and an invalid dp equal, so pick one way or the other. + */ + if (GRUB_EFI_DEVICE_PATH_VALID (dp1) && !GRUB_EFI_DEVICE_PATH_VALID (dp2)) + return 1; + else if (!GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2)) + return -1; + + return 0; +} + +void * +grub_efi_find_configuration_table (const grub_guid_t *target_guid) +{ + unsigned i; + + for (i = 0; i < grub_efi_system_table->num_table_entries; i++) + { + grub_packed_guid_t *guid = + &grub_efi_system_table->configuration_table[i].vendor_guid; + + if (! grub_memcmp (guid, target_guid, sizeof (grub_guid_t))) + return (void *) + grub_efi_system_table->configuration_table[i].vendor_table; + } + + return 0; +} diff --git a/include/grub/util/raid.h b/grub-core/kern/efi/fdt.c similarity index 61% rename from include/grub/util/raid.h rename to grub-core/kern/efi/fdt.c index 67020bb86..15a495a34 100644 --- a/include/grub/util/raid.h +++ b/grub-core/kern/efi/fdt.c @@ -1,4 +1,4 @@ -/* raid.h - RAID support for GRUB utils. */ +/* fdt.c - EFI Flattened Device Tree interaction */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2006,2007 Free Software Foundation, Inc. @@ -17,11 +17,19 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_RAID_UTIL_HEADER -#define GRUB_RAID_UTIL_HEADER 1 +#include +#include -#ifdef __linux__ -char** grub_util_raid_getmembers (char *name); -#endif +void * +grub_efi_get_firmware_fdt (void) +{ + static grub_guid_t fdt_guid = GRUB_EFI_DEVICE_TREE_GUID; + void *firmware_fdt = grub_efi_find_configuration_table (&fdt_guid); -#endif /* ! GRUB_RAID_UTIL_HEADER */ + if (firmware_fdt) { + grub_dprintf ("linux", "found registered FDT @ %p\n", firmware_fdt); + } else { + grub_dprintf ("linux", "not found registered FDT\n"); + } + return firmware_fdt; +} diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c new file mode 100644 index 000000000..1637077e1 --- /dev/null +++ b/grub-core/kern/efi/init.c @@ -0,0 +1,160 @@ +/* init.c - generic EFI initialization and finalization */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_STACK_PROTECTOR + +static grub_efi_char16_t stack_chk_fail_msg[] = + L"* GRUB: STACK SMASHING DETECTED!!! *\r\n" + L"* GRUB: ABORTED!!! *\r\n" + L"* GRUB: REBOOTING IN 5 SECONDS... *\r\n"; + +static grub_guid_t rng_protocol_guid = GRUB_EFI_RNG_PROTOCOL_GUID; + +/* Initialize canary in case there is no RNG protocol. */ +grub_addr_t __stack_chk_guard = (grub_addr_t) GRUB_STACK_PROTECTOR_INIT; + +void __attribute__ ((noreturn)) +__stack_chk_fail (void) +{ + grub_efi_simple_text_output_interface_t *o; + + /* + * Use ConOut here rather than StdErr. StdErr only goes to + * the serial console, at least on EDK2. + */ + o = grub_efi_system_table->con_out; + o->output_string (o, stack_chk_fail_msg); + + grub_efi_system_table->boot_services->stall (5000000); + grub_efi_system_table->runtime_services->reset_system (GRUB_EFI_RESET_SHUTDOWN, + GRUB_EFI_ABORTED, 0, NULL); + + /* + * We shouldn't get here. It's unsafe to return because the stack + * is compromised and this function is noreturn, so just busy + * loop forever. + */ + do + { + /* Do not optimize out the loop. */ + asm volatile (""); + } + while (1); +} + +grub_addr_t +grub_stack_protector_init (void) +{ + grub_efi_rng_protocol_t *rng; + + /* Set up the stack canary. Make errors here non-fatal for now. */ + rng = grub_efi_locate_protocol (&rng_protocol_guid, NULL); + if (rng != NULL) + { + grub_efi_status_t status; + grub_addr_t guard = 0; + + status = rng->get_rng (rng, NULL, sizeof (guard) - 1, + (grub_efi_uint8_t *) &guard); + if (status == GRUB_EFI_SUCCESS) + return guard; + } + return 0; +} +#endif + +grub_addr_t grub_modbase; + +void +grub_efi_init (void) +{ + grub_modbase = grub_efi_section_addr ("mods"); + /* First of all, initialize the console so that GRUB can display + messages. */ + grub_console_init (); + + /* Initialize the memory management system. */ + grub_efi_mm_init (); + + /* + * Lockdown the GRUB and register the shim_lock verifier + * if the UEFI Secure Boot is enabled. + */ + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + { + grub_lockdown (); + grub_shim_lock_verifier_setup (); + } + + grub_efi_system_table->boot_services->set_watchdog_timer (0, 0, 0, NULL); + + grub_efidisk_init (); + + grub_efi_register_debug_commands (); +} + +void (*grub_efi_net_config) (grub_efi_handle_t hnd, + char **device, + char **path); + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + grub_efi_loaded_image_t *image = NULL; + char *p; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return; + *device = grub_efidisk_get_device_name (image->device_handle); + if (!*device && grub_efi_net_config) + { + grub_efi_net_config (image->device_handle, device, path); + return; + } + + *path = grub_efi_get_filename (image->file_path); + if (*path) + { + /* Get the directory. */ + p = grub_strrchr (*path, '/'); + if (p) + *p = '\0'; + } +} + +void +grub_efi_fini (void) +{ + grub_efidisk_fini (); + grub_console_fini (); +} diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c new file mode 100644 index 000000000..df238b165 --- /dev/null +++ b/grub-core/kern/efi/mm.c @@ -0,0 +1,806 @@ +/* mm.c - generic EFI memory management */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include + +#if defined (__i386__) || defined (__x86_64__) +#include +#endif + +#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) + +#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) +#define BYTES_TO_PAGES_DOWN(bytes) ((bytes) >> 12) +#define PAGES_TO_BYTES(pages) ((pages) << 12) + +/* The size of a memory map obtained from the firmware. This must be + a multiplier of 4KB. */ +#define MEMORY_MAP_SIZE 0x3000 + +/* The default heap size for GRUB itself in bytes. */ +#define DEFAULT_HEAP_SIZE 0x2000000 + +static void *finish_mmap_buf = 0; +static grub_efi_uintn_t finish_mmap_size = 0; +static grub_efi_uintn_t finish_key = 0; +static grub_efi_uintn_t finish_desc_size; +static grub_efi_uint32_t finish_desc_version; +int grub_efi_is_finished = 0; + +/* + * We need to roll back EFI allocations on exit. Remember allocations that + * we'll free on exit. + */ +struct efi_allocation; +struct efi_allocation { + grub_efi_physical_address_t address; + grub_efi_uint64_t pages; + struct efi_allocation *next; +}; +static struct efi_allocation *efi_allocated_memory; + +static void +grub_efi_store_alloc (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_boot_services_t *b; + struct efi_allocation *alloc; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = b->allocate_pool (GRUB_EFI_LOADER_DATA, + sizeof(*alloc), (void**)&alloc); + + if (status == GRUB_EFI_SUCCESS) + { + alloc->next = efi_allocated_memory; + alloc->address = address; + alloc->pages = pages; + efi_allocated_memory = alloc; + } + else + grub_printf ("Could not malloc memory to remember EFI allocation. " + "Exiting GRUB won't free all memory.\n"); +} + +static void +grub_efi_drop_alloc (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + struct efi_allocation *ea, *eap; + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + + for (eap = NULL, ea = efi_allocated_memory; ea; eap = ea, ea = ea->next) + { + if (ea->address != address) + continue; + if (ea->pages != pages) + grub_fatal ("grub_efi_drop_alloc() called with wrong page count"); + + /* Remove the current entry from the list. */ + if (eap) + eap->next = ea->next; + else + efi_allocated_memory = ea->next; + + /* Then free the memory backing it. */ + b->free_pool (ea); + + /* And leave, we're done. */ + break; + } +} + +/* Allocate pages. Return the pointer to the first of allocated pages. */ +void * +grub_efi_allocate_pages_real (grub_efi_physical_address_t address, + grub_efi_uintn_t pages, + grub_efi_allocate_type_t alloctype, + grub_efi_memory_type_t memtype) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + + /* Limit the memory access to less than 4GB for 32-bit platforms. */ + if (address > GRUB_EFI_MAX_USABLE_ADDRESS) + { + char inv_addr[17], max_addr[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (inv_addr, sizeof (inv_addr) - 1, "%" PRIxGRUB_UINT64_T, + address); + grub_snprintf (max_addr, sizeof (max_addr) - 1, "%" PRIxGRUB_UINT64_T, + (grub_efi_uint64_t) GRUB_EFI_MAX_USABLE_ADDRESS); + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("invalid memory address (0x%s > 0x%s)"), inv_addr, max_addr); + return NULL; + } + + b = grub_efi_system_table->boot_services; + status = b->allocate_pages (alloctype, memtype, pages, &address); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return NULL; + } + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = GRUB_EFI_MAX_USABLE_ADDRESS; + status = b->allocate_pages (alloctype, memtype, pages, &address); + b->free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return NULL; + } + } + + grub_efi_store_alloc (address, pages); + + return (void *) ((grub_addr_t) address); +} + +void * +grub_efi_allocate_any_pages (grub_efi_uintn_t pages) +{ + return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS, + pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_LOADER_DATA); +} + +void * +grub_efi_allocate_fixed (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + return grub_efi_allocate_pages_real (address, pages, + GRUB_EFI_ALLOCATE_ADDRESS, + GRUB_EFI_LOADER_DATA); +} + +/* Free pages starting from ADDRESS. */ +void +grub_efi_free_pages (grub_efi_physical_address_t address, + grub_efi_uintn_t pages) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + b->free_pages (address, pages); + + grub_efi_drop_alloc (address, pages); +} + +#if defined (__i386__) || defined (__x86_64__) + +/* Helper for stop_broadcom. */ +static int +find_card (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_pci_address_t addr; + grub_uint8_t cap; + grub_uint16_t pm_state; + + if ((pciid & 0xffff) != GRUB_PCI_VENDOR_BROADCOM) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + if (grub_pci_read (addr) >> 24 != GRUB_PCI_CLASS_NETWORK) + return 0; + cap = grub_pci_find_capability (dev, GRUB_PCI_CAP_POWER_MANAGEMENT); + if (!cap) + return 0; + addr = grub_pci_make_address (dev, cap + 4); + pm_state = grub_pci_read_word (addr); + pm_state = pm_state | 0x03; + grub_pci_write_word (addr, pm_state); + grub_pci_read_word (addr); + return 0; +} + +static void +stop_broadcom (void) +{ + grub_pci_iterate (find_card, NULL); +} + +#endif + +grub_err_t +grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, + grub_efi_uintn_t *map_key, + grub_efi_uintn_t *efi_desc_size, + grub_efi_uint32_t *efi_desc_version) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + +#if defined (__i386__) || defined (__x86_64__) + const grub_uint16_t apple[] = { 'A', 'p', 'p', 'l', 'e' }; + int is_apple; + + is_apple = (grub_memcmp (grub_efi_system_table->firmware_vendor, + apple, sizeof (apple)) == 0); +#endif + + while (1) + { + if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key, + &finish_desc_size, &finish_desc_version) < 0) + return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); + + if (outbuf && *outbuf_size < finish_mmap_size) + return grub_error (GRUB_ERR_IO, "memory map buffer is too small"); + + finish_mmap_buf = grub_malloc (finish_mmap_size); + if (!finish_mmap_buf) + return grub_errno; + + if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key, + &finish_desc_size, &finish_desc_version) <= 0) + { + grub_free (finish_mmap_buf); + finish_mmap_buf = NULL; + return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); + } + + b = grub_efi_system_table->boot_services; + status = b->exit_boot_services (grub_efi_image_handle, finish_key); + if (status == GRUB_EFI_SUCCESS) + break; + + if (status != GRUB_EFI_INVALID_PARAMETER) + { + grub_free (finish_mmap_buf); + finish_mmap_buf = NULL; + return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services"); + } + + grub_free (finish_mmap_buf); + finish_mmap_buf = NULL; + grub_printf ("Trying to terminate EFI services again\n"); + } + grub_efi_is_finished = 1; + if (outbuf_size) + *outbuf_size = finish_mmap_size; + if (outbuf) + grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size); + if (map_key) + *map_key = finish_key; + if (efi_desc_size) + *efi_desc_size = finish_desc_size; + if (efi_desc_version) + *efi_desc_version = finish_desc_version; + + /* + * We cannot request new memory regions from the EFI Boot Services anymore. + * FIXME: Can we completely avoid memory allocations after this? + */ + grub_mm_add_region_fn = NULL; + +#if defined (__i386__) || defined (__x86_64__) + if (is_apple) + stop_broadcom (); +#endif + + return GRUB_ERR_NONE; +} + +/* + * To obtain the UEFI memory map, we must pass a buffer of sufficient size + * to hold the entire map. This function returns a sane start value for + * buffer size. + */ +grub_efi_uintn_t +grub_efi_find_mmap_size (void) +{ + grub_efi_uintn_t mmap_size = 0; + grub_efi_uintn_t desc_size; + + if (grub_efi_get_memory_map (&mmap_size, NULL, NULL, &desc_size, 0) < 0) + { + grub_error (GRUB_ERR_IO, "cannot get EFI memory map size"); + return 0; + } + + /* + * Add an extra page, since UEFI can alter the memory map itself on + * callbacks or explicit calls, including console output. + */ + return ALIGN_UP (mmap_size + GRUB_EFI_PAGE_SIZE, GRUB_EFI_PAGE_SIZE); +} + +/* Get the memory map as defined in the EFI spec. Return 1 if successful, + return 0 if partial, or return -1 if an error occurs. */ +int +grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size, + grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t *map_key, + grub_efi_uintn_t *descriptor_size, + grub_efi_uint32_t *descriptor_version) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_uintn_t key; + grub_efi_uint32_t version; + grub_efi_uintn_t size; + + if (grub_efi_is_finished) + { + int ret = 1; + + if (memory_map != NULL) + { + if (*memory_map_size < finish_mmap_size) + { + grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size); + ret = 0; + } + else + grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size); + } + else + { + /* + * Incomplete, no buffer to copy into, same as + * GRUB_EFI_BUFFER_TOO_SMALL below. + */ + ret = 0; + } + *memory_map_size = finish_mmap_size; + if (map_key) + *map_key = finish_key; + if (descriptor_size) + *descriptor_size = finish_desc_size; + if (descriptor_version) + *descriptor_version = finish_desc_version; + return ret; + } + + /* Allow some parameters to be missing. */ + if (! map_key) + map_key = &key; + if (! descriptor_version) + descriptor_version = &version; + if (! descriptor_size) + descriptor_size = &size; + + b = grub_efi_system_table->boot_services; + status = b->get_memory_map (memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version); + if (*descriptor_size == 0) + *descriptor_size = sizeof (grub_efi_memory_descriptor_t); + if (status == GRUB_EFI_SUCCESS) + return 1; + else if (status == GRUB_EFI_BUFFER_TOO_SMALL) + return 0; + else + return -1; +} + +/* Sort the memory map in place. */ +static void +sort_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *d1; + grub_efi_memory_descriptor_t *d2; + + for (d1 = memory_map; + d1 < memory_map_end; + d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size)) + { + grub_efi_memory_descriptor_t *max_desc = d1; + + for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size); + d2 < memory_map_end; + d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size)) + { + if (max_desc->num_pages < d2->num_pages) + max_desc = d2; + } + + if (max_desc != d1) + { + grub_efi_memory_descriptor_t tmp; + + tmp = *d1; + *d1 = *max_desc; + *max_desc = tmp; + } + } +} + +/* Filter the descriptors. GRUB needs only available memory. */ +static grub_efi_memory_descriptor_t * +filter_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_memory_descriptor_t *filtered_memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + grub_efi_memory_descriptor_t *filtered_desc; + + for (desc = memory_map, filtered_desc = filtered_memory_map; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY +#if 1 + && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS +#endif + && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 + && desc->num_pages != 0) + { + grub_memcpy (filtered_desc, desc, desc_size); + + /* Avoid less than 1MB, because some loaders seem to be confused. */ + if (desc->physical_start < 0x100000) + { + desc->num_pages -= BYTES_TO_PAGES (0x100000 + - desc->physical_start); + desc->physical_start = 0x100000; + } + +#if 1 + if (BYTES_TO_PAGES (filtered_desc->physical_start) + + filtered_desc->num_pages + > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)) + filtered_desc->num_pages + = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS) + - BYTES_TO_PAGES (filtered_desc->physical_start)); +#endif + + if (filtered_desc->num_pages == 0) + continue; + + filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size); + } + } + + return filtered_desc; +} + +/* Add memory regions. */ +static grub_err_t +add_memory_regions (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end, + grub_efi_uint64_t required_pages, + unsigned int flags) +{ + grub_efi_memory_descriptor_t *desc; + + for (desc = memory_map; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + grub_efi_uint64_t pages; + grub_efi_physical_address_t start; + void *addr; + + start = desc->physical_start; + pages = desc->num_pages; + + if (pages < required_pages && (flags & GRUB_MM_ADD_REGION_CONSECUTIVE)) + continue; + + if (pages > required_pages) + { + start += PAGES_TO_BYTES (pages - required_pages); + pages = required_pages; + } + + addr = grub_efi_allocate_pages_real (start, pages, + GRUB_EFI_ALLOCATE_ADDRESS, + GRUB_EFI_LOADER_CODE); + if (! addr) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Memory starting at %p (%u pages) marked as free, but EFI would not allocate", + (void *) ((grub_addr_t) start), (unsigned) pages); + + grub_mm_init_region (addr, PAGES_TO_BYTES (pages)); + + required_pages -= pages; + if (required_pages == 0) + break; + } + + if (required_pages > 0) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "could not allocate all requested memory: %" PRIuGRUB_UINT64_T " pages still required after iterating EFI memory map", + required_pages); + + return GRUB_ERR_NONE; +} + +void +grub_efi_memory_fini (void) +{ + /* + * Free all stale allocations. grub_efi_free_pages() will remove + * the found entry from the list and it will always find the first + * list entry (efi_allocated_memory is the list start). Hence we + * remove all entries from the list until none is left altogether. + */ + while (efi_allocated_memory) + grub_efi_free_pages (efi_allocated_memory->address, + efi_allocated_memory->pages); +} + +#if 0 +/* Print the memory map. */ +static void +print_memory_map (grub_efi_memory_descriptor_t *memory_map, + grub_efi_uintn_t desc_size, + grub_efi_memory_descriptor_t *memory_map_end) +{ + grub_efi_memory_descriptor_t *desc; + int i; + + for (desc = memory_map, i = 0; + desc < memory_map_end; + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++) + { + grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n", + desc->type, desc->physical_start, desc->virtual_start, + desc->num_pages, desc->attribute); + } +} +#endif + +static grub_err_t +grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) +{ + grub_efi_memory_descriptor_t *memory_map; + grub_efi_memory_descriptor_t *memory_map_end; + grub_efi_memory_descriptor_t *filtered_memory_map; + grub_efi_memory_descriptor_t *filtered_memory_map_end; + grub_efi_uintn_t alloc_size; + grub_efi_uintn_t map_size; + grub_efi_uintn_t desc_size; + grub_err_t err; + int mm_status; + + /* Prepare a memory region to store two memory maps. */ + alloc_size = 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE); + memory_map = grub_efi_allocate_any_pages (alloc_size); + if (! memory_map) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map"); + + /* Obtain descriptors for available memory. */ + map_size = MEMORY_MAP_SIZE; + + mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0); + + if (mm_status == 0) + { + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) memory_map, alloc_size); + + /* Freeing/allocating operations may increase memory map size. */ + map_size += desc_size * 32; + + alloc_size = 2 * BYTES_TO_PAGES (map_size); + memory_map = grub_efi_allocate_any_pages (alloc_size); + if (! memory_map) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map"); + + mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, + &desc_size, 0); + } + + if (mm_status < 0) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "error fetching memory map from EFI"); + + memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size); + + filtered_memory_map = memory_map_end; + + filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map, + desc_size, memory_map_end); + + /* Sort the filtered descriptors, so that GRUB can allocate pages + from smaller regions. */ + sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end); + + /* Allocate memory regions for GRUB's memory management. */ + err = add_memory_regions (filtered_memory_map, desc_size, + filtered_memory_map_end, + BYTES_TO_PAGES (required_bytes), + flags); + if (err != GRUB_ERR_NONE) + return err; + +#if 0 + /* For debug. */ + map_size = MEMORY_MAP_SIZE; + + if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0) + grub_fatal ("cannot get memory map"); + + grub_printf ("printing memory map\n"); + print_memory_map (memory_map, desc_size, + NEXT_MEMORY_DESCRIPTOR (memory_map, map_size)); + grub_fatal ("Debug. "); +#endif + + /* Release the memory maps. */ + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t) memory_map, alloc_size); + + return GRUB_ERR_NONE; +} + +void +grub_efi_mm_init (void) +{ + if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE, GRUB_MM_ADD_REGION_NONE) != GRUB_ERR_NONE) + grub_fatal ("%s", grub_errmsg); + grub_mm_add_region_fn = grub_efi_mm_add_regions; +} + +#if defined (__aarch64__) || defined (__arm__) || defined (__riscv) || \ + defined (__loongarch__) +grub_err_t +grub_efi_get_ram_base(grub_addr_t *base_addr) +{ + grub_efi_memory_descriptor_t *memory_map, *desc; + grub_efi_uintn_t memory_map_size, desc_size; + int ret; + + memory_map_size = grub_efi_find_mmap_size(); + + memory_map = grub_malloc (memory_map_size); + if (! memory_map) + return GRUB_ERR_OUT_OF_MEMORY; + ret = grub_efi_get_memory_map (&memory_map_size, memory_map, NULL, + &desc_size, NULL); + + if (ret < 1) + return GRUB_ERR_BUG; + + for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS; + (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size); + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + if (desc->attribute & GRUB_EFI_MEMORY_WB) + *base_addr = grub_min (*base_addr, desc->physical_start); + + grub_free(memory_map); + + return GRUB_ERR_NONE; +} +#endif + +static grub_uint64_t +grub_mem_attrs_to_uefi_mem_attrs (grub_mem_attr_t attrs) +{ + grub_efi_uint64_t ret = GRUB_EFI_MEMORY_RP | GRUB_EFI_MEMORY_RO | GRUB_EFI_MEMORY_XP; + + if (attrs & GRUB_MEM_ATTR_R) + ret &= ~GRUB_EFI_MEMORY_RP; + + if (attrs & GRUB_MEM_ATTR_W) + ret &= ~GRUB_EFI_MEMORY_RO; + + if (attrs & GRUB_MEM_ATTR_X) + ret &= ~GRUB_EFI_MEMORY_XP; + + return ret; +} + +static grub_mem_attr_t +uefi_mem_attrs_to_grub_mem_attrs (grub_efi_uint64_t attrs) +{ + grub_mem_attr_t ret = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + + if (attrs & GRUB_EFI_MEMORY_RP) + ret &= ~GRUB_MEM_ATTR_R; + + if (attrs & GRUB_EFI_MEMORY_RO) + ret &= ~GRUB_MEM_ATTR_W; + + if (attrs & GRUB_EFI_MEMORY_XP) + ret &= ~GRUB_MEM_ATTR_X; + + return ret; +} + +grub_err_t +grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_mem_attr_t *attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + static grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status; + grub_efi_uint64_t efi_attrs; + + if (physaddr & (GRUB_EFI_PAGE_SIZE - 1) || size & (GRUB_EFI_PAGE_SIZE - 1) || size == 0 || attrs == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (proto == NULL) + { + /* No protocol -> do nothing, all memory is RWX in boot services */ + *attrs = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + return GRUB_ERR_NONE; + } + + efi_status = proto->get_memory_attributes (proto, physaddr, size, &efi_attrs); + if (efi_status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + *attrs = uefi_mem_attrs_to_grub_mem_attrs (efi_attrs); + + grub_dprintf ("nx", "get 0x%" PRIxGRUB_ADDR "-0x%" PRIxGRUB_ADDR ":%c%c%c\n", + addr, addr + size - 1, + (*attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (*attrs & GRUB_MEM_ATTR_W) ? 'w' : '-', + (*attrs & GRUB_MEM_ATTR_X) ? 'x' : '-'); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_update_mem_attrs (grub_addr_t addr, grub_size_t size, + grub_mem_attr_t set_attrs, grub_mem_attr_t clear_attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + static grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status = GRUB_EFI_SUCCESS; + grub_efi_uint64_t uefi_set_attrs, uefi_clear_attrs; + + if (physaddr & (GRUB_EFI_PAGE_SIZE - 1) || size & (GRUB_EFI_PAGE_SIZE - 1) || size == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (proto == NULL) + /* No protocol -> do nothing, all memory is RWX in boot services */ + return GRUB_ERR_NONE; + + uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs); + uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs); + if (uefi_set_attrs) + efi_status = proto->set_memory_attributes (proto, physaddr, size, uefi_set_attrs); + if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs) + efi_status = proto->clear_memory_attributes (proto, physaddr, size, uefi_clear_attrs); + + if (efi_status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "%s() called with invalid arguments", __FUNCTION__); + + grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%" PRIxGRUB_ADDR "-0x%" PRIxGRUB_ADDR "\n", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + addr, addr + size - 1); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c new file mode 100644 index 000000000..8d3e41360 --- /dev/null +++ b/grub-core/kern/efi/sb.c @@ -0,0 +1,238 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 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 . + * + * UEFI Secure Boot related checkings. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_guid_t shim_lock_guid = GRUB_EFI_SHIM_LOCK_GUID; + +static bool shim_lock_enabled = false; + +/* + * Determine whether we're in secure boot mode. + * + * Please keep the logic in sync with the Linux kernel, + * drivers/firmware/efi/libstub/secureboot.c:efi_get_secureboot(). + */ +grub_uint8_t +grub_efi_get_secureboot (void) +{ + static grub_guid_t efi_variable_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_efi_status_t status; + grub_efi_uint32_t attr = 0; + grub_size_t size = 0; + grub_uint8_t *secboot = NULL; + grub_uint8_t *setupmode = NULL; + grub_uint8_t *moksbstate = NULL; + grub_uint8_t secureboot = GRUB_EFI_SECUREBOOT_MODE_UNKNOWN; + const char *secureboot_str = "UNKNOWN"; + + status = grub_efi_get_variable ("SecureBoot", &efi_variable_guid, + &size, (void **) &secboot); + + if (status == GRUB_EFI_NOT_FOUND) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + goto out; + } + + if (status != GRUB_EFI_SUCCESS) + goto out; + + status = grub_efi_get_variable ("SetupMode", &efi_variable_guid, + &size, (void **) &setupmode); + + if (status != GRUB_EFI_SUCCESS) + goto out; + + if ((*secboot == 0) || (*setupmode == 1)) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + goto out; + } + + /* + * See if a user has put the shim into insecure mode. If so, and if the + * variable doesn't have the runtime attribute set, we might as well + * honor that. + */ + status = grub_efi_get_variable_with_attributes ("MokSBState", &shim_lock_guid, + &size, (void **) &moksbstate, &attr); + + /* If it fails, we don't care why. Default to secure. */ + if (status != GRUB_EFI_SUCCESS) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_ENABLED; + goto out; + } + + if (!(attr & GRUB_EFI_VARIABLE_RUNTIME_ACCESS) && *moksbstate == 1) + { + secureboot = GRUB_EFI_SECUREBOOT_MODE_DISABLED; + /* + * TODO: Replace this all with shim's LoadImage protocol, delegating policy to it. + * + * We need to set shim_lock_enabled here because we disabled secure boot + * validation *inside* shim but not in the firmware, so we set this variable + * here to trigger that code path, whereas the actual verifier is not enabled. + */ + shim_lock_enabled = true; + goto out; + } + + secureboot = GRUB_EFI_SECUREBOOT_MODE_ENABLED; + + out: + grub_free (moksbstate); + grub_free (setupmode); + grub_free (secboot); + + if (secureboot == GRUB_EFI_SECUREBOOT_MODE_DISABLED) + secureboot_str = "Disabled"; + else if (secureboot == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + secureboot_str = "Enabled"; + + grub_dprintf ("efi", "UEFI Secure Boot state: %s\n", secureboot_str); + + return secureboot; +} + +static grub_err_t +shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)), + enum grub_file_type type, + void **context __attribute__ ((unused)), + enum grub_verify_flags *flags) +{ + *flags = GRUB_VERIFY_FLAGS_NONE; + + switch (type & GRUB_FILE_TYPE_MASK) + { + /* Files we check. */ + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: + case GRUB_FILE_TYPE_BSD_KERNEL: + case GRUB_FILE_TYPE_XNU_KERNEL: + case GRUB_FILE_TYPE_PLAN9_KERNEL: + case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; + + /* Files that do not affect secureboot state. */ + case GRUB_FILE_TYPE_NONE: + case GRUB_FILE_TYPE_LOOPBACK: + case GRUB_FILE_TYPE_LINUX_INITRD: + case GRUB_FILE_TYPE_OPENBSD_RAMDISK: + case GRUB_FILE_TYPE_XNU_RAMDISK: + case GRUB_FILE_TYPE_SIGNATURE: + case GRUB_FILE_TYPE_PUBLIC_KEY: + case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST: + case GRUB_FILE_TYPE_PRINT_BLOCKLIST: + case GRUB_FILE_TYPE_TESTLOAD: + case GRUB_FILE_TYPE_GET_SIZE: + case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY: + case GRUB_FILE_TYPE_CAT: + case GRUB_FILE_TYPE_HEXCAT: + case GRUB_FILE_TYPE_CMP: + case GRUB_FILE_TYPE_HASHLIST: + case GRUB_FILE_TYPE_TO_HASH: + case GRUB_FILE_TYPE_KEYBOARD_LAYOUT: + case GRUB_FILE_TYPE_PIXMAP: + case GRUB_FILE_TYPE_GRUB_MODULE_LIST: + case GRUB_FILE_TYPE_CONFIG: + case GRUB_FILE_TYPE_THEME: + case GRUB_FILE_TYPE_GETTEXT_CATALOG: + case GRUB_FILE_TYPE_FS_SEARCH: + case GRUB_FILE_TYPE_LOADENV: + case GRUB_FILE_TYPE_SAVEENV: + case GRUB_FILE_TYPE_VERIFY_SIGNATURE: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + + /* Other files. */ + default: + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy")); + } +} + +static grub_err_t +shim_lock_verifier_write (void *context __attribute__ ((unused)), void *buf, grub_size_t size) +{ + grub_efi_shim_lock_protocol_t *sl = grub_efi_locate_protocol (&shim_lock_guid, 0); + + if (!sl) + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("shim_lock protocol not found")); + + if (sl->verify (buf, size) != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad shim signature")); + + return GRUB_ERR_NONE; +} + +struct grub_file_verifier shim_lock_verifier = + { + .name = "shim_lock_verifier", + .init = shim_lock_verifier_init, + .write = shim_lock_verifier_write + }; + +void +grub_shim_lock_verifier_setup (void) +{ + struct grub_module_header *header; + grub_efi_shim_lock_protocol_t *sl = + grub_efi_locate_protocol (&shim_lock_guid, 0); + + /* shim_lock is missing, check if GRUB image is built with --disable-shim-lock. */ + if (!sl) + { + FOR_MODULES (header) + { + if (header->type == OBJ_TYPE_DISABLE_SHIM_LOCK) + return; + } + } + + /* Secure Boot is off. Do not load shim_lock. */ + if (grub_efi_get_secureboot () != GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + + /* Enforce shim_lock_verifier. */ + grub_verifier_register (&shim_lock_verifier); + + shim_lock_enabled = true; + + grub_env_set ("shim_lock", "y"); + grub_env_export ("shim_lock"); +} + +bool +grub_is_shim_lock_enabled (void) +{ + return shim_lock_enabled; +} diff --git a/grub-core/kern/elf.c b/grub-core/kern/elf.c new file mode 100644 index 000000000..077a8500c --- /dev/null +++ b/grub-core/kern/elf.c @@ -0,0 +1,230 @@ +/* elf.c - load ELF files */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#pragma GCC diagnostic ignored "-Wcast-align" + +#if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275) +#define GRUB_ELF_ENABLE_BI_ENDIAN 1 +#else +#define GRUB_ELF_ENABLE_BI_ENDIAN 0 +#endif + +#if defined(GRUB_CPU_WORDS_BIGENDIAN) +#define GRUB_ELF_NATIVE_ENDIANNESS ELFDATA2MSB +#define GRUB_ELF_OPPOSITE_ENDIANNESS ELFDATA2LSB +#else +#define GRUB_ELF_NATIVE_ENDIANNESS ELFDATA2LSB +#define GRUB_ELF_OPPOSITE_ENDIANNESS ELFDATA2MSB +#endif + +static int grub_elf32_check_endianess_and_bswap_ehdr (grub_elf_t elf); +static int grub_elf64_check_endianess_and_bswap_ehdr (grub_elf_t elf); + +/* Check if EHDR is a valid ELF header. */ +static grub_err_t +grub_elf_check_header (grub_elf_t elf) +{ + Elf32_Ehdr *e = &elf->ehdr.ehdr32; + + if (e->e_ident[EI_MAG0] != ELFMAG0 + || e->e_ident[EI_MAG1] != ELFMAG1 + || e->e_ident[EI_MAG2] != ELFMAG2 + || e->e_ident[EI_MAG3] != ELFMAG3 + || e->e_ident[EI_VERSION] != EV_CURRENT) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-independent ELF magic")); + + if (grub_elf_is_elf32 (elf)) + { + if (!grub_elf32_check_endianess_and_bswap_ehdr (elf)) { + return grub_error (GRUB_ERR_BAD_OS, "invalid ELF endianness magic"); + } + } + else if (grub_elf_is_elf64 (elf)) + { + if (!grub_elf64_check_endianess_and_bswap_ehdr (elf)) { + return grub_error (GRUB_ERR_BAD_OS, "invalid ELF endianness magic"); + } + } + else + return grub_error (GRUB_ERR_BAD_OS, "unknown ELF class"); + + if (e->e_version != EV_CURRENT) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-independent ELF magic")); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elf_close (grub_elf_t elf) +{ + grub_file_t file = elf->file; + + grub_free (elf->phdrs); + grub_free (elf->filename); + grub_free (elf); + + if (file) + grub_file_close (file); + + return grub_errno; +} + +grub_elf_t +grub_elf_file (grub_file_t file, const char *filename) +{ + grub_elf_t elf; + + elf = grub_zalloc (sizeof (*elf)); + if (! elf) + return 0; + + elf->file = file; + + if (grub_file_seek (elf->file, 0) == (grub_off_t) -1) + goto fail; + + if (grub_file_read (elf->file, &elf->ehdr, sizeof (elf->ehdr)) + != sizeof (elf->ehdr)) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + filename); + goto fail; + } + + if (grub_elf_check_header (elf)) + goto fail; + + elf->filename = grub_strdup (filename); + if (!elf->filename) + goto fail; + + return elf; + +fail: + grub_free (elf->filename); + grub_free (elf->phdrs); + grub_free (elf); + return 0; +} + +grub_elf_t +grub_elf_open (const char *name, enum grub_file_type type) +{ + grub_file_t file; + grub_elf_t elf; + + file = grub_file_open (name, type); + if (! file) + return 0; + + elf = grub_elf_file (file, name); + if (! elf) + grub_file_close (file); + + return elf; +} + + +#define grub_swap_bytes_halfXX grub_swap_bytes16 +#define grub_swap_bytes_wordXX grub_swap_bytes32 + +/* 32-bit */ +#define ehdrXX ehdr32 +#define ELFCLASSXX ELFCLASS32 +#define ElfXX_Addr Elf32_Addr +#define grub_elfXX_size grub_elf32_size +#define grub_elfXX_load grub_elf32_load +#define FOR_ELFXX_PHDRS FOR_ELF32_PHDRS +#define grub_elf_is_elfXX grub_elf_is_elf32 +#define grub_elfXX_load_phdrs grub_elf32_load_phdrs +#define ElfXX_Phdr Elf32_Phdr +#define ElfXX_Ehdr Elf32_Ehdr +#define ElfXX_Shdr Elf32_Shdr +#define ElfXX_Word Elf32_Word +#define ElfXX_Shnum Elf32_Shnum +#define grub_uintXX_t grub_uint32_t +#define grub_swap_bytes_addrXX grub_swap_bytes32 +#define grub_swap_bytes_offXX grub_swap_bytes32 +#define grub_swap_bytes_XwordXX grub_swap_bytes32 +#define grub_elfXX_check_endianess_and_bswap_ehdr grub_elf32_check_endianess_and_bswap_ehdr +#define grub_elfXX_get_shnum grub_elf32_get_shnum +#define grub_elfXX_get_shstrndx grub_elf32_get_shstrndx +#define grub_elfXX_get_phnum grub_elf32_get_phnum + +#include "elfXX.c" + +#undef ehdrXX +#undef ELFCLASSXX +#undef ElfXX_Addr +#undef grub_elfXX_size +#undef grub_elfXX_load +#undef FOR_ELFXX_PHDRS +#undef grub_elf_is_elfXX +#undef grub_elfXX_load_phdrs +#undef ElfXX_Phdr +#undef ElfXX_Ehdr +#undef ElfXX_Shdr +#undef ElfXX_Word +#undef ElfXX_Shnum +#undef grub_uintXX_t +#undef grub_swap_bytes_addrXX +#undef grub_swap_bytes_offXX +#undef grub_swap_bytes_XwordXX +#undef grub_elfXX_check_endianess_and_bswap_ehdr +#undef grub_elfXX_get_shnum +#undef grub_elfXX_get_shstrndx +#undef grub_elfXX_get_phnum + + +/* 64-bit */ +#define ehdrXX ehdr64 +#define ELFCLASSXX ELFCLASS64 +#define ElfXX_Addr Elf64_Addr +#define grub_elfXX_size grub_elf64_size +#define grub_elfXX_load grub_elf64_load +#define FOR_ELFXX_PHDRS FOR_ELF64_PHDRS +#define grub_elf_is_elfXX grub_elf_is_elf64 +#define grub_elfXX_load_phdrs grub_elf64_load_phdrs +#define ElfXX_Phdr Elf64_Phdr +#define ElfXX_Ehdr Elf64_Ehdr +#define ElfXX_Shdr Elf64_Shdr +#define ElfXX_Word Elf64_Word +#define ElfXX_Shnum Elf64_Shnum +#define grub_uintXX_t grub_uint64_t +#define grub_swap_bytes_addrXX grub_swap_bytes64 +#define grub_swap_bytes_offXX grub_swap_bytes64 +#define grub_swap_bytes_XwordXX grub_swap_bytes64 +#define grub_elfXX_check_endianess_and_bswap_ehdr grub_elf64_check_endianess_and_bswap_ehdr +#define grub_elfXX_get_shnum grub_elf64_get_shnum +#define grub_elfXX_get_shstrndx grub_elf64_get_shstrndx +#define grub_elfXX_get_phnum grub_elf64_get_phnum + +#include "elfXX.c" diff --git a/grub-core/kern/elfXX.c b/grub-core/kern/elfXX.c new file mode 100644 index 000000000..aabf4b9d7 --- /dev/null +++ b/grub-core/kern/elfXX.c @@ -0,0 +1,308 @@ +int +grub_elf_is_elfXX (grub_elf_t elf) +{ + return elf->ehdr.ehdrXX.e_ident[EI_CLASS] == ELFCLASSXX; +} + +grub_err_t +grub_elfXX_load_phdrs (grub_elf_t elf) +{ + grub_ssize_t phdrs_size; + + if (elf->phdrs) + return GRUB_ERR_NONE; + + phdrs_size = (grub_uint32_t) elf->ehdr.ehdrXX.e_phnum * elf->ehdr.ehdrXX.e_phentsize; + + grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n", + (unsigned long long) elf->ehdr.ehdrXX.e_phoff, + (unsigned long) phdrs_size); + + elf->phdrs = grub_malloc (phdrs_size); + if (! elf->phdrs) + return grub_errno; + + if ((grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_phoff) == (grub_off_t) -1) + || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size)) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + elf->filename); + return grub_errno; + } + +#if GRUB_ELF_ENABLE_BI_ENDIAN + if (elf->ehdr.ehdrXX.e_ident[EI_DATA] == GRUB_ELF_OPPOSITE_ENDIANNESS) + { + ElfXX_Phdr *phdr; + for (phdr = elf->phdrs; (char *) phdr < (char *) elf->phdrs + phdrs_size; + phdr = (ElfXX_Phdr *) ((char *) phdr + elf->ehdr.ehdrXX.e_phentsize)) + { + phdr->p_type = grub_swap_bytes_wordXX (phdr->p_type); + phdr->p_flags = grub_swap_bytes_wordXX (phdr->p_flags); + phdr->p_offset = grub_swap_bytes_offXX (phdr->p_offset); + phdr->p_vaddr = grub_swap_bytes_addrXX (phdr->p_vaddr); + phdr->p_paddr = grub_swap_bytes_addrXX (phdr->p_paddr); + phdr->p_filesz = grub_swap_bytes_XwordXX (phdr->p_filesz); + phdr->p_memsz = grub_swap_bytes_XwordXX (phdr->p_memsz); + phdr->p_align = grub_swap_bytes_XwordXX (phdr->p_align); + } + } +#endif /* GRUB_ELF_ENABLE_BI_ENDIAN */ + + return GRUB_ERR_NONE; +} + +/* Calculate the amount of memory spanned by the segments. */ +grub_size_t +grub_elfXX_size (grub_elf_t elf, + ElfXX_Addr *base, grub_uintXX_t *max_align) +{ + ElfXX_Addr segments_start = (ElfXX_Addr) -1; + ElfXX_Addr segments_end = 0; + int nr_phdrs = 0; + grub_uint32_t curr_align = 1; + ElfXX_Phdr *phdr; + + /* Run through the program headers to calculate the total memory size we + * should claim. */ + FOR_ELFXX_PHDRS (elf, phdr) + { + /* Only consider loadable segments. */ + if (phdr->p_type != PT_LOAD) + continue; + nr_phdrs++; + if (phdr->p_paddr < segments_start) + segments_start = phdr->p_paddr; + if (phdr->p_paddr + phdr->p_memsz > segments_end) + segments_end = phdr->p_paddr + phdr->p_memsz; + if (curr_align < phdr->p_align) + curr_align = phdr->p_align; + } + + if (base) + *base = 0; + + if (nr_phdrs == 0) + { + grub_error (GRUB_ERR_BAD_OS, "no program headers present"); + return 0; + } + + if (segments_end < segments_start) + { + /* Very bad addresses. */ + grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses"); + return 0; + } + + if (base) + *base = segments_start; + if (max_align) + *max_align = curr_align; + return segments_end - segments_start; +} + +grub_err_t +grub_elfXX_load (grub_elf_t elf, const char *filename, + void *load_offset, enum grub_elf_load_flags load_flags, + grub_addr_t *base, grub_size_t *size) +{ + grub_addr_t load_base = (grub_addr_t) -1ULL; + grub_size_t load_size = 0; + ElfXX_Phdr *phdr; + + FOR_ELFXX_PHDRS(elf, phdr) + { + grub_addr_t load_addr; + + if (phdr->p_type != PT_LOAD && !((load_flags & GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC) && phdr->p_type == PT_DYNAMIC)) + continue; + + load_addr = (grub_addr_t) phdr->p_paddr; + switch (load_flags & GRUB_ELF_LOAD_FLAGS_BITS) + { + case GRUB_ELF_LOAD_FLAGS_ALL_BITS: + break; + case GRUB_ELF_LOAD_FLAGS_28BITS: + load_addr &= 0xFFFFFFF; + break; + case GRUB_ELF_LOAD_FLAGS_30BITS: + load_addr &= 0x3FFFFFFF; + break; + case GRUB_ELF_LOAD_FLAGS_62BITS: + load_addr &= 0x3FFFFFFFFFFFFFFFULL; + break; + } + load_addr += (grub_addr_t) load_offset; + + if (load_addr < load_base) + load_base = load_addr; + + grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n", + (unsigned long long) load_addr, + (unsigned long long) phdr->p_memsz); + + if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1) + return grub_errno; + + if (phdr->p_filesz) + { + grub_ssize_t read; + read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz); + if (read != (grub_ssize_t) phdr->p_filesz) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + filename); + return grub_errno; + } + } + + if (phdr->p_filesz < phdr->p_memsz) + grub_memset ((void *) (grub_addr_t) (load_addr + phdr->p_filesz), + 0, phdr->p_memsz - phdr->p_filesz); + + load_size += phdr->p_memsz; + } + + if (base) + *base = load_base; + if (size) + *size = load_size; + + return grub_errno; +} + +static int +grub_elfXX_check_endianess_and_bswap_ehdr (grub_elf_t elf) +{ + ElfXX_Ehdr *e = &(elf->ehdr.ehdrXX); + if (e->e_ident[EI_DATA] == GRUB_ELF_NATIVE_ENDIANNESS) + { + return 1; + } + +#if GRUB_ELF_ENABLE_BI_ENDIAN + if (e->e_ident[EI_DATA] == GRUB_ELF_OPPOSITE_ENDIANNESS) + { + e->e_type = grub_swap_bytes_halfXX (e->e_type); + e->e_machine = grub_swap_bytes_halfXX (e->e_machine); + e->e_version = grub_swap_bytes_wordXX (e->e_version); + e->e_entry = grub_swap_bytes_addrXX (e->e_entry); + e->e_phoff = grub_swap_bytes_offXX (e->e_phoff); + e->e_shoff = grub_swap_bytes_offXX (e->e_shoff); + e->e_flags = grub_swap_bytes_wordXX (e->e_flags); + e->e_ehsize = grub_swap_bytes_halfXX (e->e_ehsize); + e->e_phentsize = grub_swap_bytes_halfXX (e->e_phentsize); + e->e_phnum = grub_swap_bytes_halfXX (e->e_phnum); + e->e_shentsize = grub_swap_bytes_halfXX (e->e_shentsize); + e->e_shnum = grub_swap_bytes_halfXX (e->e_shnum); + e->e_shstrndx = grub_swap_bytes_halfXX (e->e_shstrndx); + return 1; + } +#endif /* GRUB_ELF_ENABLE_BI_ENDIAN */ + + return 0; +} + +grub_err_t +grub_elfXX_get_shnum (ElfXX_Ehdr *e, ElfXX_Shnum *shnum) +{ + ElfXX_Shdr *s; + + if (shnum == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for shnum")); + + /* Set *shnum to 0 so that shnum doesn't return junk on error */ + *shnum = 0; + + if (e == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header")); + + *shnum = e->e_shnum; + if (*shnum == SHN_UNDEF) + { + if (e->e_shoff == 0) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff")); + + s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff); + *shnum = s->sh_size; + if (*shnum < SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in sh_size: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum); + } + else + { + if (*shnum >= SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in e_shnum: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum); + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elfXX_get_shstrndx (ElfXX_Ehdr *e, ElfXX_Word *shstrndx) +{ + ElfXX_Shdr *s; + + if (shstrndx == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for shstrndx")); + + /* Set *shstrndx to 0 so that shstrndx doesn't return junk on error */ + *shstrndx = 0; + + if (e == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header")); + + *shstrndx = e->e_shstrndx; + if (*shstrndx == SHN_XINDEX) + { + if (e->e_shoff == 0) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff")); + + s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff); + *shstrndx = s->sh_link; + if (*shstrndx < SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table index in sh_link: %d"), *shstrndx); + } + else + { + if (*shstrndx >= SHN_LORESERVE) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table index in e_shstrndx: %d"), *shstrndx); + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_elfXX_get_phnum (ElfXX_Ehdr *e, ElfXX_Word *phnum) +{ + ElfXX_Shdr *s; + + if (phnum == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for phnum")); + + /* Set *phnum to 0 so that phnum doesn't return junk on error */ + *phnum = 0; + + if (e == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header")); + + *phnum = e->e_phnum; + if (*phnum == PN_XNUM) + { + if (e->e_shoff == 0) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff")); + + s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff); + *phnum = s->sh_info; + if (*phnum < PN_XNUM) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of program header table entries in sh_info: %d"), *phnum); + } + else + { + if (*phnum >= PN_XNUM) + return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of program header table entries in e_phnum: %d"), *phnum); + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/emu/argp_common.c b/grub-core/kern/emu/argp_common.c new file mode 100644 index 000000000..8cb4608c3 --- /dev/null +++ b/grub-core/kern/emu/argp_common.c @@ -0,0 +1,41 @@ +/* grub-setup.c - make GRUB usable */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012 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 . + */ + +#include +#include + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wmissing-declarations" + +#define _GNU_SOURCE 1 +#include +#include +#include +#include + +/* Print the version information. */ +static void +print_version (FILE *stream, struct argp_state *state) +{ + fprintf (stream, "%s (%s) %s\n", program_name, PACKAGE_NAME, PACKAGE_VERSION); +} +void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version; + +/* Set the bug report address */ +const char *argp_program_bug_address = "<"PACKAGE_BUGREPORT">"; diff --git a/grub-core/kern/emu/cache.c b/grub-core/kern/emu/cache.c new file mode 100644 index 000000000..113682cc4 --- /dev/null +++ b/grub-core/kern/emu/cache.c @@ -0,0 +1,35 @@ +#ifndef GRUB_MACHINE_EMU +#error "This source is only meant for grub-emu platform" +#endif + +#include + +#if defined(__ia64__) +#include "../ia64/cache.c" +#elif defined (__arm__) || defined (__aarch64__) + +void __clear_cache (void *beg, void *end); + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + __clear_cache (address, (char *) address + len); +} + +#elif defined (__mips__) +void _flush_cache (void *address, grub_size_t len, int type); + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + return _flush_cache (address, len, 0); +} + +#elif defined(__riscv) +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ +} + +#endif + diff --git a/grub-core/kern/emu/cache_s.S b/grub-core/kern/emu/cache_s.S new file mode 100644 index 000000000..6885ffd69 --- /dev/null +++ b/grub-core/kern/emu/cache_s.S @@ -0,0 +1,20 @@ +#ifndef GRUB_MACHINE_EMU +#error "This source is only meant for grub-emu platform" +#endif + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +#if defined(__i386__) || defined(__x86_64__) +/* Nothing is necessary. */ +#elif defined(__sparc__) +#include "../sparc64/cache.S" +#elif defined(__powerpc__) +#include "../powerpc/cache.S" +#elif defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \ + defined(__mips__) || defined(__riscv) +#else +#error "No target cpu type is defined" +#endif diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c new file mode 100644 index 000000000..e8d63b1f5 --- /dev/null +++ b/grub-core/kern/emu/full.c @@ -0,0 +1,69 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +const int grub_no_modules = 1; + +void +grub_register_exported_symbols (void) +{ +} + +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + (void) ehdr; + return GRUB_ERR_BAD_MODULE; +} + +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + (void) mod; + (void) ehdr; + (void) s; + (void) seg; + return GRUB_ERR_BAD_MODULE; +} + +#if !defined (__i386__) && !defined (__x86_64__) +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr __attribute__ ((unused)), + grub_size_t *tramp, grub_size_t *got) +{ + *tramp = 0; + *got = 0; + return GRUB_ERR_BAD_MODULE; +} +#endif + +#ifdef GRUB_LINKER_HAVE_INIT +void +grub_arch_dl_init_linker (void) +{ +} +#endif + diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c new file mode 100644 index 000000000..0e6eebdc8 --- /dev/null +++ b/grub-core/kern/emu/hostdisk.c @@ -0,0 +1,686 @@ +/* hostdisk.c - emulate biosdisk */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,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 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include /* ioctl */ +# include +# ifndef BLKFLSBUF +# define BLKFLSBUF _IO (0x12,97) /* flush buffer cache */ +# endif /* ! BLKFLSBUF */ +#endif /* __linux__ */ + +static struct +{ + char *drive; + char *device; + int device_map; +} map[256]; + +static int +unescape_cmp (const char *a, const char *b_escaped) +{ + while (*a || *b_escaped) + { + if (*b_escaped == '\\' && b_escaped[1] != 0) + b_escaped++; + if (*a < *b_escaped) + return -1; + if (*a > *b_escaped) + return +1; + a++; + b_escaped++; + } + if (*a) + return +1; + if (*b_escaped) + return -1; + return 0; +} + +static int +find_grub_drive (const char *name) +{ + unsigned int i; + + if (name) + { + for (i = 0; i < ARRAY_SIZE (map); i++) + if (map[i].drive && unescape_cmp (map[i].drive, name) == 0) + return i; + } + + return -1; +} + +static int +find_free_slot (void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE (map); i++) + if (! map[i].drive) + return i; + + return -1; +} + +static int +grub_util_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, + grub_disk_pull_t pull) +{ + unsigned i; + + if (pull != GRUB_DISK_PULL_NONE) + return 0; + + for (i = 0; i < ARRAY_SIZE (map); i++) + if (map[i].drive && hook (map[i].drive, hook_data)) + return 1; + + return 0; +} + +static grub_err_t +grub_util_biosdisk_open (const char *name, grub_disk_t disk) +{ + int drive; + struct grub_util_hostdisk_data *data; + + drive = find_grub_drive (name); + grub_util_info ("drive = %d", drive); + if (drive < 0) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "no mapping exists for `%s'", name); + + disk->id = drive; + disk->data = data = xmalloc (sizeof (struct grub_util_hostdisk_data)); + data->dev = NULL; + data->access_mode = 0; + data->fd = GRUB_UTIL_FD_INVALID; + data->is_disk = 0; + data->device_map = map[drive].device_map; + + /* Get the size. */ + { + grub_util_fd_t fd; + + fd = grub_util_fd_open (map[drive].device, GRUB_UTIL_FD_O_RDONLY); + + if (!GRUB_UTIL_FD_IS_VALID(fd)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("cannot open `%s': %s"), + map[drive].device, grub_util_fd_strerror ()); + + disk->total_sectors = grub_util_get_fd_size (fd, map[drive].device, + &disk->log_sector_size); + disk->total_sectors >>= disk->log_sector_size; + disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; + +#if GRUB_UTIL_FD_STAT_IS_FUNCTIONAL + { + struct stat st; +# if GRUB_DISK_DEVS_ARE_CHAR + if (fstat (fd, &st) >= 0 && S_ISCHR (st.st_mode)) +# else + if (fstat (fd, &st) >= 0 && S_ISBLK (st.st_mode)) +# endif + data->is_disk = 1; + } +#endif + + grub_util_fd_close (fd); + + grub_util_info ("the size of %s is %" GRUB_HOST_PRIuLONG_LONG, + name, (unsigned long long) disk->total_sectors); + + return GRUB_ERR_NONE; + } +} + +const char * +grub_hostdisk_os_dev_to_grub_drive (const char *os_disk, int add) +{ + unsigned int i; + char *canon; + + canon = grub_canonicalize_file_name (os_disk); + if (!canon) + canon = xstrdup (os_disk); + + for (i = 0; i < ARRAY_SIZE (map); i++) + if (! map[i].device) + break; + else if (strcmp (map[i].device, canon) == 0) + { + free (canon); + return map[i].drive; + } + + if (!add) + { + free (canon); + return NULL; + } + + if (i == ARRAY_SIZE (map)) + /* TRANSLATORS: it refers to the lack of free slots. */ + grub_util_error ("%s", _("device count exceeds limit")); + + map[i].device = canon; + map[i].drive = xmalloc (sizeof ("hostdisk/") + strlen (os_disk)); + strcpy (map[i].drive, "hostdisk/"); + strcpy (map[i].drive + sizeof ("hostdisk/") - 1, os_disk); + map[i].device_map = 0; + + grub_hostdisk_flush_initial_buffer (os_disk); + + return map[i].drive; +} + +#ifndef __linux__ +grub_util_fd_t +grub_util_fd_open_device (const grub_disk_t disk, grub_disk_addr_t sector, int flags, + grub_disk_addr_t *max) +{ + grub_util_fd_t fd; + struct grub_util_hostdisk_data *data = disk->data; + + *max = ~0ULL; + + flags |= GRUB_UTIL_FD_O_SYNC; + + if (data->dev && strcmp (data->dev, map[disk->id].device) == 0 && + data->access_mode == (flags & O_ACCMODE)) + { + grub_dprintf ("hostdisk", "reusing open device `%s'\n", data->dev); + fd = data->fd; + } + else + { + free (data->dev); + data->dev = 0; + if (GRUB_UTIL_FD_IS_VALID(data->fd)) + { + if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) + grub_util_fd_sync (data->fd); + grub_util_fd_close (data->fd); + data->fd = GRUB_UTIL_FD_INVALID; + } + + fd = grub_util_fd_open (map[disk->id].device, flags); + if (GRUB_UTIL_FD_IS_VALID(fd)) + { + data->dev = xstrdup (map[disk->id].device); + data->access_mode = (flags & O_ACCMODE); + data->fd = fd; + } + } + + if (!GRUB_UTIL_FD_IS_VALID(data->fd)) + { + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot open `%s': %s"), + map[disk->id].device, grub_util_fd_strerror ()); + return GRUB_UTIL_FD_INVALID; + } + + if (grub_util_fd_seek (fd, sector << disk->log_sector_size)) + { + grub_util_fd_close (fd); + grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), + map[disk->id].device, grub_util_fd_strerror ()); + + return GRUB_UTIL_FD_INVALID; + } + + return fd; +} +#endif + + +static grub_err_t +grub_util_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + while (size) + { + grub_util_fd_t fd; + grub_disk_addr_t max = ~0ULL; + fd = grub_util_fd_open_device (disk, sector, GRUB_UTIL_FD_O_RDONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (fd)) + return grub_errno; + +#ifdef __linux__ + if (sector == 0) + /* Work around a bug in Linux ez remapping. Linux remaps all + sectors that are read together with the MBR in one read. It + should only remap the MBR, so we split the read in two + parts. -jochen */ + max = 1; +#endif /* __linux__ */ + + if (max > size) + max = size; + + if (grub_util_fd_read (fd, buf, max << disk->log_sector_size) + != (ssize_t) (max << disk->log_sector_size)) + return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"), + map[disk->id].device, grub_util_fd_strerror ()); + size -= max; + buf += (max << disk->log_sector_size); + sector += max; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_util_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, const char *buf) +{ + while (size) + { + grub_util_fd_t fd; + grub_disk_addr_t max = ~0ULL; + fd = grub_util_fd_open_device (disk, sector, GRUB_UTIL_FD_O_WRONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (fd)) + return grub_errno; + +#ifdef __linux__ + if (sector == 0) + /* Work around a bug in Linux ez remapping. Linux remaps all + sectors that are write together with the MBR in one write. It + should only remap the MBR, so we split the write in two + parts. -jochen */ + max = 1; +#endif /* __linux__ */ + + if (max > size) + max = size; + + if (grub_util_fd_write (fd, buf, max << disk->log_sector_size) + != (ssize_t) (max << disk->log_sector_size)) + return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write to `%s': %s"), + map[disk->id].device, grub_util_fd_strerror ()); + size -= max; + buf += (max << disk->log_sector_size); + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_util_biosdisk_flush (struct grub_disk *disk) +{ + struct grub_util_hostdisk_data *data = disk->data; + + if (disk->dev->id != GRUB_DISK_DEVICE_BIOSDISK_ID) + return GRUB_ERR_NONE; + if (!GRUB_UTIL_FD_IS_VALID (data->fd)) + { + grub_disk_addr_t max; + data->fd = grub_util_fd_open_device (disk, 0, GRUB_UTIL_FD_O_RDONLY, &max); + if (!GRUB_UTIL_FD_IS_VALID (data->fd)) + return grub_errno; + } + grub_util_fd_sync (data->fd); +#ifdef __linux__ + if (data->is_disk) + ioctl (data->fd, BLKFLSBUF, 0); +#endif + return GRUB_ERR_NONE; +} + +static void +grub_util_biosdisk_close (struct grub_disk *disk) +{ + struct grub_util_hostdisk_data *data = disk->data; + + free (data->dev); + if (GRUB_UTIL_FD_IS_VALID (data->fd)) + { + if (data->access_mode == O_RDWR || data->access_mode == O_WRONLY) + grub_util_biosdisk_flush (disk); + grub_util_fd_close (data->fd); + } + free (data); +} + +static struct grub_disk_dev grub_util_biosdisk_dev = + { + .name = "hostdisk", + .id = GRUB_DISK_DEVICE_HOSTDISK_ID, + .disk_iterate = grub_util_biosdisk_iterate, + .disk_open = grub_util_biosdisk_open, + .disk_close = grub_util_biosdisk_close, + .disk_read = grub_util_biosdisk_read, + .disk_write = grub_util_biosdisk_write, + .next = 0 + }; + +static int +grub_util_check_file_presence (const char *p) +{ +#if !GRUB_UTIL_FD_STAT_IS_FUNCTIONAL + grub_util_fd_t h; + h = grub_util_fd_open (p, GRUB_UTIL_FD_O_RDONLY); + if (!GRUB_UTIL_FD_IS_VALID(h)) + return 0; + grub_util_fd_close (h); + return 1; +#else + struct stat st; + + if (stat (p, &st) == -1) + return 0; + return 1; +#endif +} + +static void +read_device_map (const char *dev_map) +{ + FILE *fp; + char buf[1024]; /* XXX */ + int lineno = 0; + + if (!dev_map || dev_map[0] == '\0') + { + grub_util_info ("no device.map"); + return; + } + + fp = grub_util_fopen (dev_map, "r"); + if (! fp) + { + grub_util_info (_("cannot open `%s': %s"), dev_map, strerror (errno)); + return; + } + + while (fgets (buf, sizeof (buf), fp)) + { + char *p = buf; + char *e; + char *drive_e, *drive_p; + int drive; + + lineno++; + + /* Skip leading spaces. */ + while (*p && grub_isspace (*p)) + p++; + + /* If the first character is `#' or NUL, skip this line. */ + if (*p == '\0' || *p == '#') + continue; + + if (*p != '(') + { + char *tmp; + tmp = xasprintf (_("missing `%c' symbol"), '('); + grub_util_error ("%s:%d: %s", dev_map, lineno, tmp); + } + + p++; + /* Find a free slot. */ + drive = find_free_slot (); + if (drive < 0) + grub_util_error ("%s:%d: %s", dev_map, lineno, _("device count exceeds limit")); + + e = p; + p = strchr (p, ')'); + if (! p) + { + char *tmp; + tmp = xasprintf (_("missing `%c' symbol"), ')'); + grub_util_error ("%s:%d: %s", dev_map, lineno, tmp); + } + + map[drive].drive = 0; + if ((e[0] == 'f' || e[0] == 'h' || e[0] == 'c') && e[1] == 'd') + { + char *ptr; + for (ptr = e + 2; ptr < p; ptr++) + if (!grub_isdigit (*ptr)) + break; + if (ptr == p) + { + map[drive].drive = xmalloc (p - e + sizeof ('\0')); + strncpy (map[drive].drive, e, p - e + sizeof ('\0')); + map[drive].drive[p - e] = '\0'; + } + if (*ptr == ',') + { + *p = 0; + + /* TRANSLATORS: Only one entry is ignored. However the suggestion + is to correct/delete the whole file. + device.map is a file indicating which + devices are available at boot time. Fedora populated it with + entries like (hd0,1) /dev/sda1 which would mean that every + partition is a separate disk for BIOS. Such entries were + inactive in GRUB due to its bug which is now gone. Without + this additional check these entries would be harmful now. + */ + grub_util_warn (_("the device.map entry `%s' is invalid. " + "Ignoring it. Please correct or " + "delete your device.map"), e); + continue; + } + } + drive_e = e; + drive_p = p; + map[drive].device_map = 1; + + p++; + /* Skip leading spaces. */ + while (*p && grub_isspace (*p)) + p++; + + if (*p == '\0') + grub_util_error ("%s:%d: %s", dev_map, lineno, _("filename expected")); + + /* NUL-terminate the filename. */ + e = p; + while (*e && ! grub_isspace (*e)) + e++; + *e = '\0'; + + if (!grub_util_check_file_presence (p)) + { + free (map[drive].drive); + map[drive].drive = NULL; + grub_util_info ("Cannot stat `%s', skipping", p); + continue; + } + + /* On Linux, the devfs uses symbolic links horribly, and that + confuses the interface very much, so use realpath to expand + symbolic links. */ + map[drive].device = grub_canonicalize_file_name (p); + if (! map[drive].device) + map[drive].device = xstrdup (p); + + if (!map[drive].drive) + { + char c; + map[drive].drive = xmalloc (sizeof ("hostdisk/") + strlen (p)); + memcpy (map[drive].drive, "hostdisk/", sizeof ("hostdisk/") - 1); + strcpy (map[drive].drive + sizeof ("hostdisk/") - 1, p); + c = *drive_p; + *drive_p = 0; + /* TRANSLATORS: device.map is a filename. Not to be translated. + device.map specifies disk correspondance overrides. Previously + one could create any kind of device name with this. Due to + some problems we decided to limit it to just a handful + possibilities. */ + grub_util_warn (_("the drive name `%s' in device.map is incorrect. " + "Using %s instead. " + "Please use the form [hfc]d[0-9]* " + "(E.g. `hd0' or `cd')"), + drive_e, map[drive].drive); + *drive_p = c; + } + + grub_util_info ("adding `%s' -> `%s' from device.map", map[drive].drive, + map[drive].device); + + grub_hostdisk_flush_initial_buffer (map[drive].device); + } + + fclose (fp); +} + +void +grub_util_biosdisk_init (const char *dev_map) +{ + read_device_map (dev_map); + grub_disk_dev_register (&grub_util_biosdisk_dev); +} + +void +grub_util_biosdisk_fini (void) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(map); i++) + { + if (map[i].drive) + free (map[i].drive); + if (map[i].device) + free (map[i].device); + map[i].drive = map[i].device = NULL; + } + + grub_disk_dev_unregister (&grub_util_biosdisk_dev); +} + +const char * +grub_util_biosdisk_get_compatibility_hint (grub_disk_t disk) +{ + if (disk->dev != &grub_util_biosdisk_dev || map[disk->id].device_map) + return disk->name; + return 0; +} + +const char * +grub_util_biosdisk_get_osdev (grub_disk_t disk) +{ + if (disk->dev != &grub_util_biosdisk_dev) + return 0; + + return map[disk->id].device; +} + + +static char * +grub_util_path_concat_real (size_t n, int ext, va_list ap) +{ + size_t totlen = 0; + char **l = xcalloc (n + ext, sizeof (l[0])); + char *r, *p, *pi; + size_t i; + int first = 1; + + for (i = 0; i < n + ext; i++) + { + l[i] = va_arg (ap, char *); + if (l[i]) + totlen += strlen (l[i]) + 1; + } + + r = xmalloc (totlen + 10); + + p = r; + for (i = 0; i < n; i++) + { + pi = l[i]; + if (!pi) + continue; + while (*pi == '/') + pi++; + if ((p != r || (pi != l[i] && first)) && (p == r || *(p - 1) != '/')) + *p++ = '/'; + first = 0; + p = grub_stpcpy (p, pi); + while (p != r && p != r + 1 && *(p - 1) == '/') + p--; + } + + if (ext && l[i]) + p = grub_stpcpy (p, l[i]); + + *p = '\0'; + + free (l); + + return r; +} + +char * +grub_util_path_concat (size_t n, ...) +{ + va_list ap; + char *r; + + va_start (ap, n); + + r = grub_util_path_concat_real (n, 0, ap); + + va_end (ap); + + return r; +} + +char * +grub_util_path_concat_ext (size_t n, ...) +{ + va_list ap; + char *r; + + va_start (ap, n); + + r = grub_util_path_concat_real (n, 1, ap); + + va_end (ap); + + return r; +} diff --git a/util/hostfs.c b/grub-core/kern/emu/hostfs.c similarity index 57% rename from util/hostfs.c rename to grub-core/kern/emu/hostfs.c index 501ad4664..ccbe13f98 100644 --- a/util/hostfs.c +++ b/grub-core/kern/emu/hostfs.c @@ -16,21 +16,21 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ -#define _BSD_SOURCE + +#include + #include #include #include #include +#include #include #include +#include +#include -#include #include #include - - -/* dirent.d_type is a BSD extension, not part of POSIX */ -#include #include static int @@ -38,8 +38,9 @@ is_dir (const char *path, const char *name) { int len1 = strlen(path); int len2 = strlen(name); + int ret; - char pathname[len1 + 1 + len2 + 1 + 13]; + char *pathname = xmalloc (len1 + 1 + len2 + 1 + 13); strcpy (pathname, path); /* Avoid UNC-path "//name" on Cygwin. */ @@ -48,44 +49,49 @@ is_dir (const char *path, const char *name) strcat (pathname, name); - struct stat st; - if (stat (pathname, &st)) - return 0; - return S_ISDIR (st.st_mode); + ret = grub_util_is_directory (pathname); + free (pathname); + return ret; } +struct grub_hostfs_data +{ + char *filename; + grub_util_fd_t f; +}; + static grub_err_t grub_hostfs_dir (grub_device_t device, const char *path, - int (*hook) (const char *filename, - const struct grub_dirhook_info *info)) + grub_fs_dir_hook_t hook, void *hook_data) { - DIR *dir; + grub_util_fd_dir_t dir; /* Check if the disk is our dummy disk. */ if (grub_strcmp (device->disk->name, "host")) return grub_error (GRUB_ERR_BAD_FS, "not a hostfs"); - dir = opendir (path); + dir = grub_util_fd_opendir (path); if (! dir) return grub_error (GRUB_ERR_BAD_FILENAME, - "can't open the hostfs directory `%s'", path); + N_("can't open `%s': %s"), path, + grub_util_fd_strerror ()); while (1) { - struct dirent *de; + grub_util_fd_dirent_t de; struct grub_dirhook_info info; grub_memset (&info, 0, sizeof (info)); - de = readdir (dir); + de = grub_util_fd_readdir (dir); if (! de) break; info.dir = !! is_dir (path, de->d_name); - hook (de->d_name, &info); + hook (de->d_name, &info, hook_data); } - closedir (dir); + grub_util_fd_closedir (dir); return GRUB_ERR_NONE; } @@ -94,21 +100,33 @@ grub_hostfs_dir (grub_device_t device, const char *path, static grub_err_t grub_hostfs_open (struct grub_file *file, const char *name) { - FILE *f; + grub_util_fd_t f; + struct grub_hostfs_data *data; - f = fopen (name, "rb"); - if (! f) + f = grub_util_fd_open (name, GRUB_UTIL_FD_O_RDONLY); + if (! GRUB_UTIL_FD_IS_VALID (f)) return grub_error (GRUB_ERR_BAD_FILENAME, - "can't open `%s'", name); - file->data = f; + N_("can't open `%s': %s"), name, + strerror (errno)); + data = grub_malloc (sizeof (*data)); + if (!data) + { + grub_util_fd_close (f); + return grub_errno; + } + data->filename = grub_strdup (name); + if (!data->filename) + { + grub_free (data); + grub_util_fd_close (f); + return grub_errno; + } -#ifdef __MINGW32__ - file->size = grub_util_get_disk_size (name); -#else - fseeko (f, 0, SEEK_END); - file->size = ftello (f); - fseeko (f, 0, SEEK_SET); -#endif + data->f = f; + + file->data = data; + + file->size = grub_util_get_fd_size (f, name, NULL); return GRUB_ERR_NONE; } @@ -116,18 +134,20 @@ grub_hostfs_open (struct grub_file *file, const char *name) static grub_ssize_t grub_hostfs_read (grub_file_t file, char *buf, grub_size_t len) { - FILE *f; + struct grub_hostfs_data *data; - f = (FILE *) file->data; - if (fseeko (f, file->offset, SEEK_SET) != 0) + data = file->data; + if (grub_util_fd_seek (data->f, file->offset) != 0) { - grub_error (GRUB_ERR_OUT_OF_RANGE, "fseeko: %s", strerror (errno)); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("cannot seek `%s': %s"), + data->filename, grub_util_fd_strerror ()); return -1; } - unsigned int s = fread (buf, 1, len, f); + unsigned int s = grub_util_fd_read (data->f, buf, len); if (s != len) - grub_error (GRUB_ERR_FILE_READ_ERROR, "fread: %s", strerror (errno)); + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("cannot read `%s': %s"), + data->filename, grub_util_fd_strerror ()); return (signed) s; } @@ -135,10 +155,12 @@ grub_hostfs_read (grub_file_t file, char *buf, grub_size_t len) static grub_err_t grub_hostfs_close (grub_file_t file) { - FILE *f; + struct grub_hostfs_data *data; - f = (FILE *) file->data; - fclose (f); + data = file->data; + grub_util_fd_close (data->f); + grub_free (data->filename); + grub_free (data); return GRUB_ERR_NONE; } @@ -151,14 +173,17 @@ grub_hostfs_label (grub_device_t device __attribute ((unused)), return GRUB_ERR_NONE; } +#undef open +#undef close + static struct grub_fs grub_hostfs_fs = { .name = "hostfs", - .dir = grub_hostfs_dir, - .open = grub_hostfs_open, - .read = grub_hostfs_read, - .close = grub_hostfs_close, - .label = grub_hostfs_label, + .fs_dir = grub_hostfs_dir, + .fs_open = grub_hostfs_open, + .fs_read = grub_hostfs_read, + .fs_close = grub_hostfs_close, + .fs_label = grub_hostfs_label, .next = 0 }; diff --git a/grub-core/kern/emu/lite.c b/grub-core/kern/emu/lite.c new file mode 100644 index 000000000..b327d4e41 --- /dev/null +++ b/grub-core/kern/emu/lite.c @@ -0,0 +1,47 @@ +#include +#include + +#ifndef GRUB_MACHINE_EMU +#error "This source is only meant for grub-emu platform" +#endif + +#if defined(__i386__) +#include "../i386/dl.c" +#elif defined(__x86_64__) +#include "../x86_64/dl.c" +#elif defined(__sparc__) +#include "../sparc64/dl.c" +#elif defined(__mips__) +#include "../mips/dl.c" +#elif defined(__powerpc__) +#include "../powerpc/dl.c" +#elif defined(__ia64__) +#include "../ia64/dl_helper.c" +#include "../ia64/dl.c" +#elif defined(__arm__) +#include "../arm/dl_helper.c" +#include "../arm/dl.c" +#elif defined(__aarch64__) +#include "../arm64/dl_helper.c" +#include "../arm64/dl.c" +#elif defined(__riscv) +#include "../riscv/dl.c" +#else +#error "No target cpu type is defined" +#endif + +const int grub_no_modules = 0; + +/* grub-emu-lite supports dynamic module loading, so it won't have any + embedded modules. */ +void +grub_init_all (void) +{ + return; +} + +void +grub_fini_all (void) +{ + return; +} diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c new file mode 100644 index 000000000..8a70bd7d6 --- /dev/null +++ b/grub-core/kern/emu/main.c @@ -0,0 +1,299 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +#include "progname.h" +#include + +#define ENABLE_RELOCATABLE 0 + +/* Used for going back to the main function. */ +static jmp_buf main_env; + +/* Store the prefix specified by an argument. */ +static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL; + +grub_addr_t grub_modbase = 0; + +void +grub_reboot (void) +{ + longjmp (main_env, 1); + grub_fatal ("longjmp failed"); +} + +void +grub_exit (void) +{ + grub_reboot (); +} + +void +grub_machine_init (void) +{ +} + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + *device = root_dev; + *path = dir; +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_console_fini (); +} + + + +#define OPT_MEMDISK 257 + +static struct argp_option options[] = { + {"root", 'r', N_("DEVICE_NAME"), 0, N_("Set root device."), 2}, + {"device-map", 'm', N_("FILE"), 0, + /* TRANSLATORS: There are many devices in device map. */ + N_("use FILE as the device map [default=%s]"), 0}, + {"memdisk", OPT_MEMDISK, N_("FILE"), 0, + /* TRANSLATORS: There are many devices in device map. */ + N_("use FILE as memdisk"), 0}, + {"directory", 'd', N_("DIR"), 0, + N_("use GRUB files in the directory DIR [default=%s]"), 0}, + {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + {"hold", 'H', N_("SECS"), OPTION_ARG_OPTIONAL, N_("wait until a debugger will attach"), 0}, + {"kexec", 'X', 0, 0, N_("use kexec to boot Linux kernels via systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0}, + {"tpm-device", 't', N_("DEV"), 0, N_("set TPM device."), 0}, + { 0, 0, 0, 0, 0, 0 } +}; + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +static char * +help_filter (int key, const char *text, void *input __attribute__ ((unused))) +{ + switch (key) + { + case 'd': + return xasprintf (text, DEFAULT_DIRECTORY); + case 'm': + return xasprintf (text, DEFAULT_DEVICE_MAP); + default: + return (char *) text; + } +} + +#pragma GCC diagnostic error "-Wformat-nonliteral" + +struct arguments +{ + const char *dev_map; + const char *mem_disk; + int hold; +}; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case OPT_MEMDISK: + arguments->mem_disk = arg; + break; + case 'r': + free (root_dev); + root_dev = xstrdup (arg); + break; + case 'd': + free (dir); + dir = xstrdup (arg); + break; + case 'm': + arguments->dev_map = arg; + break; + case 'H': + arguments->hold = (arg ? atoi (arg) : -1); + break; + case 'v': + verbosity++; + break; + case 'X': + grub_util_set_kexecute (); + break; + case 't': + free (tpm_dev); + tpm_dev = xstrdup (arg); + break; + + case ARGP_KEY_ARG: + { + /* Too many arguments. */ + fprintf (stderr, _("Unknown extra argument `%s'."), arg); + fprintf (stderr, "\n"); + argp_usage (state); + } + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, argp_parser, NULL, + N_("GRUB emulator."), + NULL, help_filter, NULL +}; + + + +#pragma GCC diagnostic ignored "-Wmissing-prototypes" + +int +main (int argc, char *argv[]) +{ + struct arguments arguments = + { + .dev_map = DEFAULT_DEVICE_MAP, + .hold = 0, + .mem_disk = 0, + }; + volatile int hold = 0; + size_t total_module_size = sizeof (struct grub_module_info), memdisk_size = 0; + struct grub_module_info *modinfo; + void *mods; + + grub_util_host_init (&argc, &argv); + + dir = xstrdup (DEFAULT_DIRECTORY); + + if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0) + { + fprintf (stderr, "%s", _("Error in parsing command line arguments\n")); + exit(1); + } + + if (arguments.mem_disk) + { + memdisk_size = ALIGN_UP(grub_util_get_image_size (arguments.mem_disk), 512); + total_module_size += memdisk_size + sizeof (struct grub_module_header); + } + + mods = xmalloc (total_module_size); + modinfo = grub_memset (mods, 0, total_module_size); + mods = (char *) (modinfo + 1); + + modinfo->magic = GRUB_MODULE_MAGIC; + modinfo->offset = sizeof (struct grub_module_info); + modinfo->size = total_module_size; + + if (arguments.mem_disk) + { + struct grub_module_header *header = (struct grub_module_header *) mods; + header->type = OBJ_TYPE_MEMDISK; + header->size = memdisk_size + sizeof (*header); + mods = header + 1; + + grub_util_load_image (arguments.mem_disk, mods); + mods = (char *) mods + memdisk_size; + } + + grub_modbase = (grub_addr_t) modinfo; + + hold = arguments.hold; + /* Wait until the ARGS.HOLD variable is cleared by an attached debugger. */ + if (hold && verbosity > 0) + /* TRANSLATORS: In this case GRUB tells user what he has to do. */ + printf (_("Run `gdb %s %d', and set ARGS.HOLD to zero.\n"), + program_name, (int) getpid ()); + while (hold) + { + if (hold > 0) + hold--; + + sleep (1); + } + + signal (SIGINT, SIG_IGN); + grub_console_init (); + grub_host_init (); + + /* XXX: This is a bit unportable. */ + grub_util_biosdisk_init (arguments.dev_map); + + grub_init_all (); + + grub_hostfs_init (); + + /* Make sure that there is a root device. */ + if (! root_dev) + root_dev = grub_strdup ("host"); + + dir = xstrdup (dir); + + if (tpm_dev) + grub_util_tpm_open (tpm_dev); + + /* Start GRUB! */ + if (setjmp (main_env) == 0) + grub_main (); + + grub_fini_all (); + grub_hostfs_fini (); + grub_host_fini (); + grub_util_tpm_close (); + + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); + + return 0; +} diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c new file mode 100644 index 000000000..1db24fde7 --- /dev/null +++ b/grub-core/kern/emu/misc.c @@ -0,0 +1,283 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,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 . + */ + +#ifndef GRUB_BUILD +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int verbosity; +int kexecute; + +static int grub_util_tpm_fd = -1; + +void +grub_util_warn (const char *fmt, ...) +{ + va_list ap; + + fprintf (stderr, _("%s: warning:"), program_name); + fprintf (stderr, " "); + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fprintf (stderr, ".\n"); + fflush (stderr); +} + +void +grub_util_info (const char *fmt, ...) +{ + if (verbosity > 0) + { + va_list ap; + + fprintf (stderr, _("%s: info:"), program_name); + fprintf (stderr, " "); + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fprintf (stderr, ".\n"); + fflush (stderr); + } +} + +void +grub_util_error (const char *fmt, ...) +{ + va_list ap; + + fprintf (stderr, _("%s: error:"), program_name); + fprintf (stderr, " "); + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fprintf (stderr, ".\n"); + grub_exit (); +} + +void * +xcalloc (grub_size_t nmemb, grub_size_t size) +{ + void *p; + + p = calloc (nmemb, size); + if (!p) + grub_util_error ("%s", _("out of memory")); + + return p; +} + +void * +xmalloc (grub_size_t size) +{ + void *p; + + p = malloc (size); + if (! p) + grub_util_error ("%s", _("out of memory")); + + return p; +} + +void * +xrealloc (void *ptr, grub_size_t size) +{ + ptr = realloc (ptr, size); + if (! ptr) + grub_util_error ("%s", _("out of memory")); + + return ptr; +} + +char * +xstrdup (const char *str) +{ + size_t len; + char *newstr; + + len = strlen (str); + newstr = (char *) xmalloc (len + 1); + memcpy (newstr, str, len + 1); + + return newstr; +} + +#if !defined (GRUB_MKFONT) && !defined (GRUB_BUILD) +char * +xasprintf (const char *fmt, ...) +{ + va_list ap; + char *result; + + va_start (ap, fmt); + result = grub_xvasprintf (fmt, ap); + va_end (ap); + if (!result) + grub_util_error ("%s", _("out of memory")); + + return result; +} +#endif + +#if !defined (GRUB_MACHINE_EMU) || defined (GRUB_UTIL) +void +grub_exit (void) +{ +#if defined (GRUB_KERNEL) + grub_reboot (); +#endif + exit (1); +} +#endif + +grub_uint64_t +grub_get_time_ms (void) +{ + struct timeval tv; + + gettimeofday (&tv, 0); + + return (tv.tv_sec * 1000 + tv.tv_usec / 1000); +} + +size_t +grub_util_get_image_size (const char *path) +{ + FILE *f; + size_t ret; + off_t sz; + + f = grub_util_fopen (path, "rb"); + + if (!f) + grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); + + fseeko (f, 0, SEEK_END); + + sz = ftello (f); + if (sz < 0) + grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); + if (sz != (size_t) sz) + grub_util_error (_("file `%s' is too big"), path); + ret = (size_t) sz; + + fclose (f); + + return ret; +} + +void +grub_util_load_image (const char *path, char *buf) +{ + FILE *fp; + size_t size; + + grub_util_info ("reading %s", path); + + size = grub_util_get_image_size (path); + + fp = grub_util_fopen (path, "rb"); + if (! fp) + grub_util_error (_("cannot open `%s': %s"), path, + strerror (errno)); + + if (fread (buf, 1, size, fp) != size) + grub_util_error (_("cannot read `%s': %s"), path, + strerror (errno)); + + fclose (fp); +} + +void +grub_util_set_kexecute (void) +{ + kexecute++; +} + +int +grub_util_get_kexecute (void) +{ + return kexecute; +} + +grub_err_t +grub_util_tpm_open (const char *tpm_dev) +{ + if (grub_util_tpm_fd != -1) + return GRUB_ERR_NONE; + + grub_util_tpm_fd = open (tpm_dev, O_RDWR); + if (grub_util_tpm_fd == -1) + grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror (errno)); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_util_tpm_close (void) +{ + int err; + + if (grub_util_tpm_fd == -1) + return GRUB_ERR_NONE; + + err = close (grub_util_tpm_fd); + if (err != GRUB_ERR_NONE) + grub_util_error (_("cannot close TPM device: %s"), strerror (errno)); + + grub_util_tpm_fd = -1; + return GRUB_ERR_NONE; +} + +grub_size_t +grub_util_tpm_read (void *output, grub_size_t size) +{ + if (grub_util_tpm_fd == -1) + return -1; + + return read (grub_util_tpm_fd, output, size); +} + +grub_size_t +grub_util_tpm_write (const void *input, grub_size_t size) +{ + if (grub_util_tpm_fd == -1) + return -1; + + return write (grub_util_tpm_fd, input, size); +} diff --git a/grub-core/kern/emu/mm.c b/grub-core/kern/emu/mm.c new file mode 100644 index 000000000..4d1046a21 --- /dev/null +++ b/grub-core/kern/emu/mm.c @@ -0,0 +1,75 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,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 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +void * +grub_calloc (grub_size_t nmemb, grub_size_t size) +{ + void *ret; + ret = calloc (nmemb, size); + if (!ret) + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return ret; +} + +void * +grub_malloc (grub_size_t size) +{ + void *ret; + ret = malloc (size); + if (!ret) + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return ret; +} + +void * +grub_zalloc (grub_size_t size) +{ + void *ret; + + ret = grub_malloc (size); + if (!ret) + return NULL; + memset (ret, 0, size); + return ret; +} + +void +grub_free (void *ptr) +{ + if (ptr) + free (ptr); +} + +void * +grub_realloc (void *ptr, grub_size_t size) +{ + void *ret; + ret = realloc (ptr, size); + if (!ret) + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return ret; +} diff --git a/grub-core/kern/emu/time.c b/grub-core/kern/emu/time.c new file mode 100644 index 000000000..5da8092a9 --- /dev/null +++ b/grub-core/kern/emu/time.c @@ -0,0 +1,46 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + struct tm *mytm; + time_t mytime; + + mytime = time (&mytime); + mytm = gmtime (&mytime); + + datetime->year = mytm->tm_year + 1900; + datetime->month = mytm->tm_mon + 1; + datetime->day = mytm->tm_mday; + datetime->hour = mytm->tm_hour; + datetime->minute = mytm->tm_min; + datetime->second = mytm->tm_sec; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "no clock setting routine available"); +} diff --git a/kern/env.c b/grub-core/kern/env.c similarity index 82% rename from kern/env.c rename to grub-core/kern/env.c index 84b3a001d..764068896 100644 --- a/kern/env.c +++ b/grub-core/kern/env.c @@ -41,7 +41,7 @@ grub_env_hashval (const char *s) return i % HASHSZ; } -struct grub_env_var * +static struct grub_env_var * grub_env_find (const char *name) { struct grub_env_var *var; @@ -109,9 +109,6 @@ grub_env_set (const char *name, const char *val) if (! var) return grub_errno; - /* This is not necessary. But leave this for readability. */ - var->global = 0; - var->name = grub_strdup (name); if (! var->name) goto fail; @@ -132,7 +129,7 @@ grub_env_set (const char *name, const char *val) return grub_errno; } -char * +const char * grub_env_get (const char *name) { struct grub_env_var *var; @@ -147,6 +144,19 @@ grub_env_get (const char *name) return var->value; } +bool +grub_env_get_bool (const char *name, bool if_unset) +{ + const char *val = grub_env_get (name); + + if (val == NULL || grub_strlen (val) < 1) + return if_unset; + if (grub_strcmp (val, "0") == 0 || grub_strcmp (val, "false") == 0 || + grub_strcmp (val, "disable") == 0 || grub_strcmp (val, "no") == 0) + return false; + return true; +} + void grub_env_unset (const char *name) { @@ -169,11 +179,10 @@ grub_env_unset (const char *name) grub_free (var); } -void -grub_env_iterate (int (*func) (struct grub_env_var *var)) +struct grub_env_var * +grub_env_update_get_sorted (void) { - struct grub_env_sorted_var *sorted_list = 0; - struct grub_env_sorted_var *sorted_var; + struct grub_env_var *sorted_list = 0; int i; /* Add variables associated with this context into a sorted list. */ @@ -183,40 +192,20 @@ grub_env_iterate (int (*func) (struct grub_env_var *var)) for (var = grub_current_context->vars[i]; var; var = var->next) { - struct grub_env_sorted_var *p, **q; + struct grub_env_var *p, **q; - sorted_var = grub_malloc (sizeof (*sorted_var)); - if (! sorted_var) - goto fail; - - sorted_var->var = var; - - for (q = &sorted_list, p = *q; p; q = &((*q)->next), p = *q) + for (q = &sorted_list, p = *q; p; q = &((*q)->sorted_next), p = *q) { - if (grub_strcmp (p->var->name, var->name) > 0) + if (grub_strcmp (p->name, var->name) > 0) break; } - sorted_var->next = *q; - *q = sorted_var; + var->sorted_next = *q; + *q = var; } } - /* Iterate FUNC on the sorted list. */ - for (sorted_var = sorted_list; sorted_var; sorted_var = sorted_var->next) - if (func (sorted_var->var)) - break; - - fail: - - /* Free the sorted list. */ - for (sorted_var = sorted_list; sorted_var; ) - { - struct grub_env_sorted_var *tmp = sorted_var->next; - - grub_free (sorted_var); - sorted_var = tmp; - } + return sorted_list; } grub_err_t @@ -240,3 +229,23 @@ grub_register_variable_hook (const char *name, return GRUB_ERR_NONE; } + +grub_err_t +grub_env_export (const char *name) +{ + struct grub_env_var *var; + + var = grub_env_find (name); + if (! var) + { + grub_err_t err; + + err = grub_env_set (name, ""); + if (err) + return err; + var = grub_env_find (name); + } + var->global = 1; + + return GRUB_ERR_NONE; +} diff --git a/kern/err.c b/grub-core/kern/err.c similarity index 87% rename from kern/err.c rename to grub-core/kern/err.c index 8272467f5..53c734de7 100644 --- a/kern/err.c +++ b/grub-core/kern/err.c @@ -22,17 +22,13 @@ #include #include -#define GRUB_MAX_ERRMSG 256 #define GRUB_ERROR_STACK_SIZE 10 grub_err_t grub_errno; char grub_errmsg[GRUB_MAX_ERRMSG]; +int grub_err_printed_errors; -static struct -{ - grub_err_t errno; - char errmsg[GRUB_MAX_ERRMSG]; -} grub_error_stack_items[GRUB_ERROR_STACK_SIZE]; +static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE]; static int grub_error_stack_pos; static int grub_error_stack_assert; @@ -51,18 +47,6 @@ grub_error (grub_err_t n, const char *fmt, ...) return n; } -void -grub_fatal (const char *fmt, ...) -{ - va_list ap; - - va_start (ap, fmt); - grub_vprintf (_(fmt), ap); - va_end (ap); - - grub_abort (); -} - void grub_error_push (void) { @@ -70,7 +54,7 @@ grub_error_push (void) if (grub_error_stack_pos < GRUB_ERROR_STACK_SIZE) { /* Copy active error message to stack. */ - grub_error_stack_items[grub_error_stack_pos].errno = grub_errno; + grub_error_stack_items[grub_error_stack_pos].grub_errno = grub_errno; grub_memcpy (grub_error_stack_items[grub_error_stack_pos].errmsg, grub_errmsg, sizeof (grub_errmsg)); @@ -98,7 +82,7 @@ grub_error_pop (void) /* Pop error message from error stack to current active error. */ grub_error_stack_pos--; - grub_errno = grub_error_stack_items[grub_error_stack_pos].errno; + grub_errno = grub_error_stack_items[grub_error_stack_pos].grub_errno; grub_memcpy (grub_errmsg, grub_error_stack_items[grub_error_stack_pos].errmsg, sizeof (grub_errmsg)); @@ -122,7 +106,10 @@ grub_print_error (void) do { if (grub_errno != GRUB_ERR_NONE) - grub_err_printf (_("error: %s.\n"), grub_errmsg); + { + grub_err_printf (_("error: %s.\n"), grub_errmsg); + grub_err_printed_errors++; + } } while (grub_error_pop ()); diff --git a/kern/file.c b/grub-core/kern/file.c similarity index 54% rename from kern/file.c rename to grub-core/kern/file.c index e17c35f95..6e7efe89a 100644 --- a/kern/file.c +++ b/grub-core/kern/file.c @@ -20,9 +20,16 @@ #include #include #include +#include #include #include #include +#include +#include + +void (*EXPORT_VAR (grub_grubnet_fini)) (void); + +grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX]; /* Get the device part of the filename NAME. It is enclosed by parentheses. */ char * @@ -35,7 +42,7 @@ grub_file_get_device_name (const char *name) if (! p) { - grub_error (GRUB_ERR_BAD_FILENAME, "missing `)'"); + grub_error (GRUB_ERR_BAD_FILENAME, N_("missing `%c' symbol"), ')'); return 0; } @@ -52,26 +59,31 @@ grub_file_get_device_name (const char *name) } grub_file_t -grub_file_open (const char *name) +grub_file_open (const char *name, enum grub_file_type type) { - grub_device_t device; - grub_file_t file = 0; + grub_device_t device = 0; + grub_file_t file = 0, last_file = 0; char *device_name; - char *file_name; + const char *file_name; + grub_file_filter_id_t filter; + + /* Reset grub_errno before we start. */ + grub_errno = GRUB_ERR_NONE; device_name = grub_file_get_device_name (name); if (grub_errno) - return 0; + goto fail; /* Get the file part of NAME. */ - file_name = grub_strchr (name, ')'); + file_name = (name[0] == '(') ? grub_strchr (name, ')') : NULL; if (file_name) file_name++; else - file_name = (char *) name; + file_name = name; device = grub_device_open (device_name); grub_free (device_name); + device_name = NULL; if (! device) goto fail; @@ -81,7 +93,16 @@ grub_file_open (const char *name) file->device = device; - if (device->disk && file_name[0] != '/') + /* In case of relative pathnames and non-Unix systems (like Windows) + * name of host files may not start with `/'. Blocklists for host files + * are meaningless as well (for a start, host disk does not allow any direct + * access - it is just a marker). So skip host disk in this case. + */ + if (device->disk && file_name[0] != '/' +#if defined(GRUB_UTIL) || defined(GRUB_MACHINE_EMU) + && grub_strcmp (device->disk->name, "host") +#endif + ) /* This is a block list. */ file->fs = &grub_fs_blocklist; else @@ -91,12 +112,37 @@ grub_file_open (const char *name) goto fail; } - if ((file->fs->open) (file, file_name) != GRUB_ERR_NONE) + if ((file->fs->fs_open) (file, file_name) != GRUB_ERR_NONE) goto fail; + if (file->data == NULL) + goto fail; + + if (file->fs->mod) + grub_dl_ref (file->fs->mod); + + file->name = grub_strdup (name); + grub_errno = GRUB_ERR_NONE; + + for (filter = 0; file && filter < ARRAY_SIZE (grub_file_filters); + filter++) + if (grub_file_filters[filter]) + { + last_file = file; + file = grub_file_filters[filter] (file, type); + if (file && file != last_file) + { + file->name = grub_strdup (name); + grub_errno = GRUB_ERR_NONE; + } + } + if (!file) + grub_file_close (last_file); + return file; fail: + grub_free (device_name); if (device) grub_device_close (device); @@ -107,19 +153,26 @@ grub_file_open (const char *name) return 0; } +grub_disk_read_hook_t grub_file_progress_hook; + grub_ssize_t grub_file_read (grub_file_t file, void *buf, grub_size_t len) { grub_ssize_t res; + grub_disk_read_hook_t read_hook; + void *read_hook_data; if (file->offset > file->size) { grub_error (GRUB_ERR_OUT_OF_RANGE, - "attempt to read past the end of file"); + N_("attempt to read past the end of file")); return -1; } - if (len == 0 || len > file->size - file->offset) + if (len == 0) + return 0; + + if (len > file->size - file->offset) len = file->size - file->offset; /* Prevent an overflow. */ @@ -128,8 +181,17 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) if (len == 0) return 0; - - res = (file->fs->read) (file, buf, len); + read_hook = file->read_hook; + read_hook_data = file->read_hook_data; + if (!file->read_hook) + { + file->read_hook = grub_file_progress_hook; + file->read_hook_data = file; + file->progress_offset = file->offset; + } + res = (file->fs->fs_read) (file, buf, len); + file->read_hook = read_hook; + file->read_hook_data = read_hook_data; if (res > 0) file->offset += res; @@ -139,11 +201,15 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_err_t grub_file_close (grub_file_t file) { - if (file->fs->close) - (file->fs->close) (file); + if (file->fs->mod) + grub_dl_unref (file->fs->mod); + + if (file->fs->fs_close) + (file->fs->fs_close) (file); if (file->device) grub_device_close (file->device); + grub_free (file->name); grub_free (file); return grub_errno; } @@ -156,11 +222,12 @@ grub_file_seek (grub_file_t file, grub_off_t offset) if (offset > file->size) { grub_error (GRUB_ERR_OUT_OF_RANGE, - "attempt to seek outside of the file"); + N_("attempt to seek outside of the file")); return -1; } old = file->offset; file->offset = offset; + return old; } diff --git a/kern/fs.c b/grub-core/kern/fs.c similarity index 65% rename from kern/fs.c rename to grub-core/kern/fs.c index 0c456377f..80d325868 100644 --- a/kern/fs.c +++ b/grub-core/kern/fs.c @@ -26,53 +26,25 @@ #include #include #include +#include -static grub_fs_t grub_fs_list; +grub_fs_t grub_fs_list = 0; grub_fs_autoload_hook_t grub_fs_autoload_hook = 0; -void -grub_fs_register (grub_fs_t fs) +/* Helper for grub_fs_probe. */ +static int +probe_dummy_iter (const char *filename __attribute__ ((unused)), + const struct grub_dirhook_info *info __attribute__ ((unused)), + void *data __attribute__ ((unused))) { - fs->next = grub_fs_list; - grub_fs_list = fs; -} - -void -grub_fs_unregister (grub_fs_t fs) -{ - grub_fs_t *p, q; - - for (p = &grub_fs_list, q = *p; q; p = &(q->next), q = q->next) - if (q == fs) - { - *p = q->next; - break; - } -} - -void -grub_fs_iterate (int (*hook) (const grub_fs_t fs)) -{ - grub_fs_t p; - - for (p = grub_fs_list; p; p = p->next) - if (hook (p)) - break; + return 1; } grub_fs_t grub_fs_probe (grub_device_t device) { grub_fs_t p; - auto int dummy_func (const char *filename, - const struct grub_dirhook_info *info); - - int dummy_func (const char *filename __attribute__ ((unused)), - const struct grub_dirhook_info *info __attribute__ ((unused))) - { - return 1; - } if (device->disk) { @@ -82,15 +54,34 @@ grub_fs_probe (grub_device_t device) for (p = grub_fs_list; p; p = p->next) { grub_dprintf ("fs", "Detecting %s...\n", p->name); - (p->dir) (device, "/", dummy_func); + + /* This is evil: newly-created just mounted BtrFS after copying all + GRUB files has a very peculiar unrecoverable corruption which + will be fixed at sync but we'd rather not do a global sync and + syncing just files doesn't seem to help. Relax the check for + this time. */ +#ifdef GRUB_UTIL + if (grub_strcmp (p->name, "btrfs") == 0) + { + char *label = 0; + p->fs_uuid (device, &label); + if (label) + grub_free (label); + } + else +#endif + (p->fs_dir) (device, "/", probe_dummy_iter, NULL); if (grub_errno == GRUB_ERR_NONE) return p; grub_error_push (); + /* The grub_error_push() does not touch grub_errmsg. */ + grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); grub_dprintf ("fs", "%s detection failed.\n", p->name); grub_error_pop (); - if (grub_errno != GRUB_ERR_BAD_FS) + if (grub_errno != GRUB_ERR_BAD_FS + && grub_errno != GRUB_ERR_OUT_OF_RANGE) return 0; grub_errno = GRUB_ERR_NONE; @@ -105,14 +96,15 @@ grub_fs_probe (grub_device_t device) { p = grub_fs_list; - (p->dir) (device, "/", dummy_func); + (p->fs_dir) (device, "/", probe_dummy_iter, NULL); if (grub_errno == GRUB_ERR_NONE) { count--; return p; } - if (grub_errno != GRUB_ERR_BAD_FS) + if (grub_errno != GRUB_ERR_BAD_FS + && grub_errno != GRUB_ERR_OUT_OF_RANGE) { count--; return 0; @@ -124,10 +116,10 @@ grub_fs_probe (grub_device_t device) count--; } } - else if (device->net->fs) + else if (device->net && device->net->fs) return device->net->fs; - grub_error (GRUB_ERR_UNKNOWN_FS, "unknown filesystem"); + grub_error (GRUB_ERR_UNKNOWN_FS, N_("unknown filesystem")); return 0; } @@ -138,17 +130,18 @@ grub_fs_probe (grub_device_t device) struct grub_fs_block { grub_disk_addr_t offset; - unsigned long length; + grub_disk_addr_t length; }; static grub_err_t grub_fs_blocklist_open (grub_file_t file, const char *name) { - char *p = (char *) name; + const char *p = name; unsigned num = 0; unsigned i; grub_disk_t disk = file->device->disk; struct grub_fs_block *blocks; + grub_size_t max_sectors; /* First, count the number of blocks. */ do @@ -161,11 +154,12 @@ grub_fs_blocklist_open (grub_file_t file, const char *name) while (p); /* Allocate a block list. */ - blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1)); + blocks = grub_calloc (num + 1, sizeof (struct grub_fs_block)); if (! blocks) return 0; file->size = 0; + max_sectors = grub_disk_from_native_sector (disk, disk->total_sectors); p = (char *) name; for (i = 0; i < num; i++) { @@ -175,23 +169,27 @@ grub_fs_blocklist_open (grub_file_t file, const char *name) if (grub_errno != GRUB_ERR_NONE || *p != '+') { grub_error (GRUB_ERR_BAD_FILENAME, - "invalid file name `%s'", name); + N_("invalid file name `%s'"), name); goto fail; } } p++; - blocks[i].length = grub_strtoul (p, &p, 0); + if (*p == '\0' || *p == ',') + blocks[i].length = max_sectors - blocks[i].offset; + else + blocks[i].length = grub_strtoul (p, &p, 0); + if (grub_errno != GRUB_ERR_NONE || blocks[i].length == 0 || (*p && *p != ',' && ! grub_isspace (*p))) { grub_error (GRUB_ERR_BAD_FILENAME, - "invalid file name `%s'", name); + N_("invalid file name `%s'"), name); goto fail; } - if (disk->total_sectors < blocks[i].offset + blocks[i].length) + if (max_sectors < blocks[i].offset + blocks[i].length) { grub_error (GRUB_ERR_BAD_FILENAME, "beyond the total sectors"); goto fail; @@ -217,12 +215,15 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) grub_disk_addr_t sector; grub_off_t offset; grub_ssize_t ret = 0; + grub_disk_t disk = file->device->disk; if (len > file->size - file->offset) len = file->size - file->offset; sector = (file->offset >> GRUB_DISK_SECTOR_BITS); offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1)); + disk->read_hook = file->read_hook; + disk->read_hook_data = file->read_hook_data; for (p = file->data; p->length && len > 0; p++) { if (sector < p->length) @@ -234,9 +235,12 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) >> GRUB_DISK_SECTOR_BITS) > p->length - sector) size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset; - if (grub_disk_read (file->device->disk, p->offset + sector, offset, + if (grub_disk_read (disk, p->offset + sector, offset, size, buf) != GRUB_ERR_NONE) - return -1; + { + ret = -1; + break; + } ret += size; len -= size; @@ -246,6 +250,8 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) else sector -= p->length; } + disk->read_hook = NULL; + disk->read_hook_data = NULL; return ret; } @@ -253,9 +259,9 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) struct grub_fs grub_fs_blocklist = { .name = "blocklist", - .dir = 0, - .open = grub_fs_blocklist_open, - .read = grub_fs_blocklist_read, - .close = 0, + .fs_dir = 0, + .fs_open = grub_fs_blocklist_open, + .fs_read = grub_fs_blocklist_read, + .fs_close = 0, .next = 0 }; diff --git a/kern/generic/millisleep.c b/grub-core/kern/generic/millisleep.c similarity index 100% rename from kern/generic/millisleep.c rename to grub-core/kern/generic/millisleep.c diff --git a/kern/generic/rtc_get_time_ms.c b/grub-core/kern/generic/rtc_get_time_ms.c similarity index 92% rename from kern/generic/rtc_get_time_ms.c rename to grub-core/kern/generic/rtc_get_time_ms.c index 359233628..3e39c8fe1 100644 --- a/kern/generic/rtc_get_time_ms.c +++ b/grub-core/kern/generic/rtc_get_time_ms.c @@ -21,6 +21,7 @@ #include #include +#include /* Calculate the time in milliseconds since the epoch based on the RTC. */ grub_uint64_t @@ -33,5 +34,5 @@ grub_rtc_get_time_ms (void) 1 s 1 T rtc ticks */ grub_uint64_t ticks_ms_per_sec = ((grub_uint64_t) 1000) * grub_get_rtc (); - return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND, 0); + return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND ? : 1000, 0); } diff --git a/grub-core/kern/i386/coreboot/cbtable.c b/grub-core/kern/i386/coreboot/cbtable.c new file mode 100644 index 000000000..34a2b59be --- /dev/null +++ b/grub-core/kern/i386/coreboot/cbtable.c @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2013 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 . + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_linuxbios_table_header_t +grub_linuxbios_get_tables (void) +{ + grub_linuxbios_table_header_t table_header; + /* Assuming table_header is aligned to its size (8 bytes). */ + for (table_header = (grub_linuxbios_table_header_t) 0x500; + table_header < (grub_linuxbios_table_header_t) 0x1000; table_header++) + if (grub_linuxbios_check_signature (table_header)) + return table_header; + + for (table_header = (grub_linuxbios_table_header_t) 0xf0000; + table_header < (grub_linuxbios_table_header_t) 0x100000; table_header++) + if (grub_linuxbios_check_signature (table_header)) + return table_header; + + return 0; +} diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c new file mode 100644 index 000000000..4fae8b571 --- /dev/null +++ b/grub-core/kern/i386/coreboot/init.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern grub_uint8_t _start[]; +extern grub_uint8_t _end[]; +extern grub_uint8_t _edata[]; + +void __attribute__ ((noreturn)) +grub_exit (void) +{ + /* We can't use grub_fatal() in this function. This would create an infinite + loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ + while (1) + grub_cpu_idle (); +} + +grub_addr_t grub_modbase = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; +static grub_uint64_t modend; +static int have_memory = 0; + +/* Helper for grub_machine_init. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + grub_uint64_t begin = addr, end = addr + size; + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + /* Restrict ourselves to 32-bit memory space. */ + if (begin > GRUB_ULONG_MAX) + return 0; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; +#endif + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Avoid the lower memory. */ + if (begin < GRUB_MEMORY_MACHINE_LOWER_SIZE) + begin = GRUB_MEMORY_MACHINE_LOWER_SIZE; + + if (modend && begin < modend) + begin = modend; + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); + + have_memory = 1; + + return 0; +} + +#ifndef GRUB_MACHINE_MULTIBOOT + +void +grub_machine_init (void) +{ + modend = grub_modules_get_end (); + + grub_video_coreboot_fb_early_init (); + + grub_vga_text_init (); + + grub_machine_mmap_iterate (heap_init, NULL); + if (!have_memory) + grub_fatal ("No memory found"); + + grub_video_coreboot_fb_late_init (); + + grub_font_init (); + grub_gfxterm_init (); + + grub_tsc_init (); +} + +#else + +void +grub_machine_init (void) +{ + modend = grub_modules_get_end (); + + grub_vga_text_init (); + + grub_machine_mmap_init (); + grub_machine_mmap_iterate (heap_init, NULL); + + grub_tsc_init (); +} + +#endif + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); + grub_stop_floppy (); +} diff --git a/kern/i386/coreboot/startup.S b/grub-core/kern/i386/coreboot/startup.S similarity index 75% rename from kern/i386/coreboot/startup.S rename to grub-core/kern/i386/coreboot/startup.S index e94950aae..df6adbabb 100644 --- a/kern/i386/coreboot/startup.S +++ b/grub-core/kern/i386/coreboot/startup.S @@ -18,8 +18,7 @@ #include #include -#include -#include +#include #include #include @@ -36,22 +35,18 @@ .globl start, _start start: _start: - jmp codestart +#ifdef GRUB_MACHINE_MULTIBOOT + cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax + jne 0f + movl %ebx, EXT_C(startup_multiboot_info) +0: +#endif - /* - * This is a special data area at a fixed offset from the beginning. - */ + /* initialize the stack */ + movl $GRUB_MEMORY_MACHINE_PROT_STACK, %esp - . = _start + GRUB_KERNEL_CPU_PREFIX - -VARIABLE(grub_prefix) - /* to be filled by grub-mkimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = _start + GRUB_KERNEL_CPU_DATA_END + /* jump to the main body of C code */ + jmp EXT_C(grub_main) /* * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself). @@ -65,25 +60,3 @@ multiboot_header: /* checksum */ .long -0x1BADB002 - MULTIBOOT_MEMORY_INFO -codestart: - cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax - jne 0f - movl %ebx, EXT_C(startup_multiboot_info) -0: - - /* initialize the stack */ - movl $GRUB_MEMORY_MACHINE_PROT_STACK, %esp - - /* jump to the main body of C code */ - jmp EXT_C(grub_main) - -/* - * prot_to_real and associated structures (but NOT real_to_prot, that is - * only needed for BIOS gates). - */ -#include "../realmode.S" - -/* - * Routines needed by Linux and Multiboot loaders. - */ -#include "../loader.S" diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c new file mode 100644 index 000000000..1346da5cc --- /dev/null +++ b/grub-core/kern/i386/dl.c @@ -0,0 +1,81 @@ +/* dl-386.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_386) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_386_32: + *addr += sym->st_value; + break; + + case R_386_PC32: + *addr += (sym->st_value - (grub_addr_t) addr); + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } + + return GRUB_ERR_NONE; +} diff --git a/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c similarity index 84% rename from kern/i386/efi/init.c rename to grub-core/kern/i386/efi/init.c index f73f828c6..46476e27e 100644 --- a/kern/i386/efi/init.c +++ b/grub-core/kern/i386/efi/init.c @@ -26,6 +26,7 @@ #include #include #include +#include void grub_machine_init (void) @@ -35,13 +36,13 @@ grub_machine_init (void) } void -grub_machine_fini (void) +grub_machine_fini (int flags) { - grub_efi_fini (); -} + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; -void -grub_machine_set_prefix (void) -{ - grub_efi_set_prefix (); + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); } diff --git a/kern/i386/efi/startup.S b/grub-core/kern/i386/efi/startup.S similarity index 64% rename from kern/i386/efi/startup.S rename to grub-core/kern/i386/efi/startup.S index 5b464ab83..fc5ea3dac 100644 --- a/kern/i386/efi/startup.S +++ b/grub-core/kern/i386/efi/startup.S @@ -19,40 +19,12 @@ #include #include -#include .file "startup.S" .text .globl start, _start start: _start: - jmp codestart - - /* - * 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_prefix) - /* to be filled by grub-mkimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = _start + 0x50 - -codestart: /* * EFI_SYSTEM_TABLE * and EFI_HANDLE are passed on the stack. */ @@ -62,5 +34,3 @@ codestart: movl %eax, EXT_C(grub_efi_system_table) call EXT_C(grub_main) ret - -#include "../realmode.S" diff --git a/kern/i386/halt.c b/grub-core/kern/i386/efi/tsc.c similarity index 54% rename from kern/i386/halt.c rename to grub-core/kern/i386/efi/tsc.c index 10805e42b..e41dc6526 100644 --- a/kern/i386/halt.c +++ b/grub-core/kern/i386/efi/tsc.c @@ -1,4 +1,8 @@ -/* +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * * GRUB -- GRand Unified Bootloader * Copyright (C) 2008 Free Software Foundation, Inc. * @@ -16,27 +20,21 @@ * along with GRUB. If not, see . */ -#include -#include +#include +#include #include +#include +#include +#include -const char bochs_shutdown[] = "Shutdown"; - -void -grub_halt (void) +int +grub_tsc_calibrate_from_efi (void) { - unsigned int i; - - /* Disable interrupts. */ - __asm__ __volatile__ ("cli"); - - /* Bochs, QEMU, etc. */ - for (i = 0; i < sizeof (bochs_shutdown) - 1; i++) - grub_outb (bochs_shutdown[i], 0x8900); - - grub_printf ("GRUB doesn't know how to halt this machine yet!\n"); - - /* In order to return we'd have to check what the previous status of IF - flag was. But user most likely doesn't want to return anyway ... */ - grub_stop (); + grub_uint64_t start_tsc, end_tsc; + /* Use EFI Time Service to calibrate TSC */ + start_tsc = grub_get_tsc (); + grub_efi_system_table->boot_services->stall (1000); + end_tsc = grub_get_tsc (); + grub_tsc_rate = grub_divmod64 ((1ULL << 32), end_tsc - start_tsc, 0); + return 1; } diff --git a/kern/i386/ieee1275/startup.S b/grub-core/kern/i386/ieee1275/startup.S similarity index 68% rename from kern/i386/ieee1275/startup.S rename to grub-core/kern/i386/ieee1275/startup.S index 35258adb6..62cf348e0 100644 --- a/kern/i386/ieee1275/startup.S +++ b/grub-core/kern/i386/ieee1275/startup.S @@ -17,9 +17,7 @@ */ #include -#include -#include -#include +#include #include #include @@ -37,34 +35,6 @@ start: _start: - jmp codestart - - /* - * This is a special data area at a fixed offset from the beginning. - */ - - . = _start + GRUB_KERNEL_CPU_PREFIX - -VARIABLE(grub_prefix) - /* to be filled by grub-mkimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = _start + GRUB_KERNEL_CPU_DATA_END - -codestart: movl %eax, EXT_C(grub_ieee1275_entry_fn) jmp EXT_C(grub_main) -/* - * prot_to_real and associated structures (but NOT real_to_prot, that is - * only needed for BIOS gates). - */ -#include "../realmode.S" - -/* - * Routines needed by Linux and Multiboot loaders. - */ -#include "../loader.S" diff --git a/grub-core/kern/i386/int.S b/grub-core/kern/i386/int.S new file mode 100644 index 000000000..862a54202 --- /dev/null +++ b/grub-core/kern/i386/int.S @@ -0,0 +1,134 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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 . + */ + +FUNCTION(grub_bios_interrupt) + pushf + cli + popf + pushl %ebp + pushl %ecx + pushl %eax + pushl %ebx + pushl %esi + pushl %edi + pushl %edx + + movb %al, intno + movl (%edx), %eax + movl %eax, LOCAL(bios_register_eax) + movw 4(%edx), %ax + movw %ax, LOCAL(bios_register_es) + movw 6(%edx), %ax + movw %ax, LOCAL(bios_register_ds) + movw 8(%edx), %ax + movw %ax, LOCAL(bios_register_flags) + + movl 12(%edx), %ebx + movl 16(%edx), %ecx + movl 20(%edx), %edi + movl 24(%edx), %esi + movl 28(%edx), %edx + + /* + Via C3 CPUs have cache coherence problems, so we need to call + wbinvd at these 2 points. As wbinvd slows down boot, don't do + it on non-VIA. 9090 is nop nop. */ +VARIABLE(grub_bios_via_workaround1) + .byte 0x90, 0x90 + + PROT_TO_REAL + .code16 + pushf + cli + + mov %ds, %ax + push %ax + + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_es): + .short 0 + movw %ax, %es + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_ds): + .short 0 + movw %ax, %ds + + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_flags): + .short 0 + push %ax + popf + + /* movl imm32, %eax*/ + .byte 0x66, 0xb8 +LOCAL(bios_register_eax): + .long 0 + + /* int imm8. */ + .byte 0xcd +intno: + .byte 0 + + movl %eax, %cs:LOCAL(bios_register_eax) + movw %ds, %ax + movw %ax, %cs:LOCAL(bios_register_ds) + pop %ax + mov %ax, %ds + pushf + pop %ax + movw %ax, LOCAL(bios_register_flags) + mov %es, %ax + movw %ax, LOCAL(bios_register_es) + + popf + +VARIABLE(grub_bios_via_workaround2) + .byte 0x90, 0x90 + + REAL_TO_PROT + .code32 + + popl %eax + + movl %ebx, 12(%eax) + movl %ecx, 16(%eax) + movl %edi, 20(%eax) + movl %esi, 24(%eax) + movl %edx, 28(%eax) + + movl %eax, %edx + + movl LOCAL(bios_register_eax), %eax + movl %eax, (%edx) + movw LOCAL(bios_register_es), %ax + movw %ax, 4(%edx) + movw LOCAL(bios_register_ds), %ax + movw %ax, 6(%edx) + movw LOCAL(bios_register_flags), %ax + movw %ax, 8(%edx) + + popl %edi + popl %esi + popl %ebx + popl %eax + popl %ecx + popl %ebp + ret diff --git a/kern/i386/multiboot_mmap.c b/grub-core/kern/i386/multiboot_mmap.c similarity index 82% rename from kern/i386/multiboot_mmap.c rename to grub-core/kern/i386/multiboot_mmap.c index 0f463c23c..e8f4f34b9 100644 --- a/kern/i386/multiboot_mmap.c +++ b/grub-core/kern/i386/multiboot_mmap.c @@ -16,15 +16,12 @@ * along with GRUB. If not, see . */ -#include #include #include #include #include #include -grub_size_t grub_lower_mem, grub_upper_mem; - /* A pointer to the MBI in its initial location. */ struct multiboot_info *startup_multiboot_info; @@ -37,7 +34,7 @@ static struct multiboot_info kern_multiboot_info; static grub_uint8_t mmap_entries[sizeof (struct multiboot_mmap_entry) * 32]; void -grub_machine_mmap_init () +grub_machine_mmap_init (void) { if (! startup_multiboot_info) grub_fatal ("Unable to find Multiboot Information (is CONFIG_MULTIBOOT disabled in coreboot?)"); @@ -57,27 +54,16 @@ grub_machine_mmap_init () } grub_memmove (mmap_entries, (void *) kern_multiboot_info.mmap_addr, kern_multiboot_info.mmap_length); kern_multiboot_info.mmap_addr = (grub_uint32_t) mmap_entries; - - if ((kern_multiboot_info.flags & MULTIBOOT_INFO_MEMORY) == 0) - { - grub_lower_mem = GRUB_MEMORY_MACHINE_LOWER_USABLE; - grub_upper_mem = 0; - } - else - { - grub_lower_mem = kern_multiboot_info.mem_lower * 1024; - grub_upper_mem = kern_multiboot_info.mem_upper * 1024; - } } grub_err_t -grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t)) +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) { struct multiboot_mmap_entry *entry = (void *) kern_multiboot_info.mmap_addr; while ((unsigned long) entry < kern_multiboot_info.mmap_addr + kern_multiboot_info.mmap_length) { - if (hook (entry->addr, entry->len, entry->type)) + if (hook (entry->addr, entry->len, entry->type, hook_data)) break; entry = (void *) ((grub_addr_t) entry + entry->size + sizeof (entry->size)); diff --git a/commands/i386/pc/acpi.c b/grub-core/kern/i386/pc/acpi.c similarity index 79% rename from commands/i386/pc/acpi.c rename to grub-core/kern/i386/pc/acpi.c index 88e4f55cf..0a69eba7b 100644 --- a/commands/i386/pc/acpi.c +++ b/grub-core/kern/i386/pc/acpi.c @@ -27,20 +27,21 @@ grub_machine_acpi_get_rsdpv1 (void) grub_uint8_t *ebda, *ptr; grub_dprintf ("acpi", "Looking for RSDP. Scanning EBDA\n"); - ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) 0x40e)) << 4); + ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) grub_absolute_pointer (0x40e))) << 4); ebda_len = * (grub_uint16_t *) ebda; - if (! ebda_len) - return 0; + if (! ebda_len) /* FIXME do we really need this check? */ + goto scan_bios; for (ptr = ebda; ptr < ebda + 0x400; ptr += 16) - if (grub_memcmp (ptr, "RSD PTR ", 8) == 0 + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) ptr)->revision == 0) return (struct grub_acpi_rsdp_v10 *) ptr; +scan_bios: grub_dprintf ("acpi", "Looking for RSDP. Scanning BIOS\n"); for (ptr = (grub_uint8_t *) 0xe0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16) - if (grub_memcmp (ptr, "RSD PTR ", 8) == 0 + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) ptr)->revision == 0) return (struct grub_acpi_rsdp_v10 *) ptr; @@ -54,12 +55,12 @@ grub_machine_acpi_get_rsdpv2 (void) grub_uint8_t *ebda, *ptr; grub_dprintf ("acpi", "Looking for RSDP. Scanning EBDA\n"); - ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) 0x40e)) << 4); + ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) grub_absolute_pointer (0x40e))) << 4); ebda_len = * (grub_uint16_t *) ebda; - if (! ebda_len) - return 0; + if (! ebda_len) /* FIXME do we really need this check? */ + goto scan_bios; for (ptr = ebda; ptr < ebda + 0x400; ptr += 16) - if (grub_memcmp (ptr, "RSD PTR ", 8) == 0 + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) ptr)->revision != 0 && ((struct grub_acpi_rsdp_v20 *) ptr)->length < 1024 @@ -67,10 +68,11 @@ grub_machine_acpi_get_rsdpv2 (void) == 0) return (struct grub_acpi_rsdp_v20 *) ptr; +scan_bios: grub_dprintf ("acpi", "Looking for RSDP. Scanning BIOS\n"); for (ptr = (grub_uint8_t *) 0xe0000; ptr < (grub_uint8_t *) 0x100000; ptr += 16) - if (grub_memcmp (ptr, "RSD PTR ", 8) == 0 + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 && ((struct grub_acpi_rsdp_v10 *) ptr)->revision != 0 && ((struct grub_acpi_rsdp_v20 *) ptr)->length < 1024 diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c new file mode 100644 index 000000000..326d491c5 --- /dev/null +++ b/grub-core/kern/i386/pc/init.c @@ -0,0 +1,280 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mem_region +{ + grub_addr_t addr; + grub_size_t size; +}; + +#define MAX_REGIONS 32 + +static struct mem_region mem_regions[MAX_REGIONS]; +static int num_regions; + +void (*grub_pc_net_config) (char **device, char **path); + +/* + * return the real time in ticks, of which there are about + * 18-20 per second + */ +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x1a, ®s); + + return ((regs.ecx << 16) | (regs.edx & 0xffff)) * 55ULL; +} + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *ptr; + grub_uint8_t boot_drive, dos_part, bsd_part; + + boot_drive = (grub_boot_device >> 24); + dos_part = (grub_boot_device >> 16); + bsd_part = (grub_boot_device >> 8); + + /* No hardcoded root partition - make it from the boot drive and the + partition number encoded at the install time. */ + if (boot_drive == GRUB_BOOT_MACHINE_PXE_DL) + { + if (grub_pc_net_config) + grub_pc_net_config (device, path); + return; + } + + /* XXX: This should be enough. */ +#define DEV_SIZE 100 + *device = grub_malloc (DEV_SIZE); + ptr = *device; + grub_snprintf (*device, DEV_SIZE, + "%cd%u", (boot_drive & 0x80) ? 'h' : 'f', + boot_drive & 0x7f); + ptr += grub_strlen (ptr); + + if (dos_part != 0xff) + grub_snprintf (ptr, DEV_SIZE - (ptr - *device), + ",%u", dos_part + 1); + ptr += grub_strlen (ptr); + + if (bsd_part != 0xff) + grub_snprintf (ptr, DEV_SIZE - (ptr - *device), ",%u", + bsd_part + 1); + ptr += grub_strlen (ptr); + *ptr = 0; +} + +/* Add a memory region. */ +static void +add_mem_region (grub_addr_t addr, grub_size_t size) +{ + if (num_regions == MAX_REGIONS) + /* Ignore. */ + return; + + mem_regions[num_regions].addr = addr; + mem_regions[num_regions].size = size; + num_regions++; +} + +/* Compact memory regions. */ +static void +compact_mem_regions (void) +{ + int i, j; + + /* Sort them. */ + for (i = 0; i < num_regions - 1; i++) + for (j = i + 1; j < num_regions; j++) + if (mem_regions[i].addr > mem_regions[j].addr) + { + struct mem_region tmp = mem_regions[i]; + mem_regions[i] = mem_regions[j]; + mem_regions[j] = tmp; + } + + /* Merge overlaps. */ + for (i = 0; i < num_regions - 1; i++) + if (mem_regions[i].addr + mem_regions[i].size >= mem_regions[i + 1].addr) + { + j = i + 1; + + if (mem_regions[i].addr + mem_regions[i].size + < mem_regions[j].addr + mem_regions[j].size) + mem_regions[i].size = (mem_regions[j].addr + mem_regions[j].size + - mem_regions[i].addr); + + grub_memmove (mem_regions + j, mem_regions + j + 1, + (num_regions - j - 1) * sizeof (struct mem_region)); + i--; + num_regions--; + } +} + +grub_addr_t grub_modbase; +extern grub_uint8_t _start[], _edata[]; + +/* Helper for grub_machine_init. */ +static int +mmap_iterate_hook (grub_uint64_t addr, grub_uint64_t size, + grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + /* Avoid the lower memory. */ + if (addr < GRUB_MEMORY_MACHINE_UPPER_START) + { + if (size <= GRUB_MEMORY_MACHINE_UPPER_START - addr) + return 0; + + size -= GRUB_MEMORY_MACHINE_UPPER_START - addr; + addr = GRUB_MEMORY_MACHINE_UPPER_START; + } + + /* Ignore >4GB. */ + if (addr <= 0xFFFFFFFF && type == GRUB_MEMORY_AVAILABLE) + { + grub_size_t len; + + len = (grub_size_t) ((addr + size > 0xFFFFFFFF) + ? 0xFFFFFFFF - addr + : size); + add_mem_region (addr, len); + } + + return 0; +} + +extern grub_uint16_t grub_bios_via_workaround1, grub_bios_via_workaround2; + +/* Via needs additional wbinvd. */ +static void +grub_via_workaround_init (void) +{ + grub_uint32_t manufacturer[3], max_cpuid, proc_info; + if (! grub_cpu_is_cpuid_supported ()) + return; + + grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); + + if (grub_memcmp (manufacturer, "CentaurHauls", 12) != 0) + return; + + if (max_cpuid > 0) + { + grub_cpuid (1, proc_info, /* Don't care. */ manufacturer[0], + manufacturer[2], manufacturer[1]); + /* Check model, apply only to VIA C3 and lower. */ + if (((proc_info & 0xf0) >> 4 | (proc_info & 0xf0000) >> 12) > 10) + return; + } + + grub_bios_via_workaround1 = 0x090f; + grub_bios_via_workaround2 = 0x090f; + asm volatile ("wbinvd"); +} + +void +grub_machine_init (void) +{ + int i; +#if 0 + int grub_lower_mem; +#endif + grub_addr_t modend; + + /* This has to happen before any BIOS calls. */ + grub_via_workaround_init (); + + grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start); + + /* Initialize the console as early as possible. */ + grub_console_init (); + + /* This sanity check is useless since top of GRUB_MEMORY_MACHINE_RESERVED_END + is used for stack and if it's unavailable we wouldn't have gotten so far. + */ +#if 0 + grub_lower_mem = grub_get_conv_memsize () << 10; + + /* Sanity check. */ + if (grub_lower_mem < GRUB_MEMORY_MACHINE_RESERVED_END) + grub_fatal ("too small memory"); +#endif + +/* FIXME: This prevents loader/i386/linux.c from using low memory. When our + heap implements support for requesting a chunk in low memory, this should + no longer be a problem. */ +#if 0 + /* Add the lower memory into free memory. */ + if (grub_lower_mem >= GRUB_MEMORY_MACHINE_RESERVED_END) + add_mem_region (GRUB_MEMORY_MACHINE_RESERVED_END, + grub_lower_mem - GRUB_MEMORY_MACHINE_RESERVED_END); +#endif + + grub_machine_mmap_iterate (mmap_iterate_hook, NULL); + + compact_mem_regions (); + + modend = grub_modules_get_end (); + for (i = 0; i < num_regions; i++) + { + grub_addr_t beg = mem_regions[i].addr; + grub_addr_t fin = mem_regions[i].addr + mem_regions[i].size; + if (modend && beg < modend) + beg = modend; + if (beg >= fin) + continue; + grub_mm_init_region ((void *) beg, fin - beg); + } + + grub_tsc_init (); +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_console_fini (); + grub_stop_floppy (); +} diff --git a/grub-core/kern/i386/pc/mmap.c b/grub-core/kern/i386/pc/mmap.c new file mode 100644 index 000000000..53fcf45af --- /dev/null +++ b/grub-core/kern/i386/pc/mmap.c @@ -0,0 +1,193 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,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 . + */ + +#include +#include +#include +#include +#include + +struct grub_machine_mmap_entry +{ + grub_uint32_t size; + grub_uint64_t addr; + grub_uint64_t len; +#define GRUB_MACHINE_MEMORY_AVAILABLE 1 +#define GRUB_MACHINE_MEMORY_RESERVED 2 +#define GRUB_MACHINE_MEMORY_ACPI 3 +#define GRUB_MACHINE_MEMORY_NVS 4 +#define GRUB_MACHINE_MEMORY_BADRAM 5 + grub_uint32_t type; +} GRUB_PACKED; + + +/* + * + * grub_get_conv_memsize(i) : return the conventional memory size in KB. + * BIOS call "INT 12H" to get conventional memory size + * The return value in AX. + */ +static inline grub_uint16_t +grub_get_conv_memsize (void) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x12, ®s); + return regs.eax & 0xffff; +} + +/* + * grub_get_ext_memsize() : return the extended memory size in KB. + * BIOS call "INT 15H, AH=88H" to get extended memory size + * The return value in AX. + * + */ +static inline grub_uint16_t +grub_get_ext_memsize (void) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x8800; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + return regs.eax & 0xffff; +} + +/* Get a packed EISA memory map. Lower 16 bits are between 1MB and 16MB + in 1KB parts, and upper 16 bits are above 16MB in 64KB 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. +*/ + +static inline grub_uint32_t +grub_get_eisa_mmap (void) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + regs.eax = 0xe801; + grub_bios_interrupt (0x15, ®s); + + if ((regs.eax & 0xff00) == 0x8600) + return 0; + + return (regs.eax & 0xffff) | (regs.ebx << 16); +} + +/* + * + * 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. + */ +/* Get a memory map entry. Return next continuation value. Zero means + the end. */ +static grub_uint32_t +grub_get_mmap_entry (struct grub_machine_mmap_entry *entry, + grub_uint32_t cont) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + /* place address (+4) in ES:DI */ + regs.es = ((grub_addr_t) &entry->addr) >> 4; + regs.edi = ((grub_addr_t) &entry->addr) & 0xf; + + /* set continuation value */ + regs.ebx = cont; + + /* set default maximum buffer size */ + regs.ecx = sizeof (*entry) - sizeof (entry->size); + + /* set EDX to 'SMAP' */ + regs.edx = 0x534d4150; + + regs.eax = 0xe820; + grub_bios_interrupt (0x15, ®s); + + /* write length of buffer (zero if error) into ADDR */ + if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) || regs.eax != 0x534d4150 + || regs.ecx < 0x14 || regs.ecx > 0x400) + entry->size = 0; + else + entry->size = regs.ecx; + + /* return the continuation value */ + return regs.ebx; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + grub_uint32_t cont = 0; + struct grub_machine_mmap_entry *entry + = (struct grub_machine_mmap_entry *) grub_absolute_pointer (GRUB_MEMORY_MACHINE_SCRATCH_ADDR); + int e820_works = 0; + + while (1) + { + grub_memset (entry, 0, sizeof (*entry)); + + cont = grub_get_mmap_entry (entry, cont); + + if (!entry->size) + break; + + if (entry->len) + e820_works = 1; + if (entry->len + && hook (entry->addr, entry->len, + /* GRUB mmaps have been defined to match with + the E820 definition. + Therefore, we can just pass type through. */ + entry->type, hook_data)) + break; + + if (! cont) + break; + } + + if (!e820_works) + { + grub_uint32_t eisa_mmap = grub_get_eisa_mmap (); + + if (hook (0x0, ((grub_uint32_t) grub_get_conv_memsize ()) << 10, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 0; + + if (eisa_mmap) + { + if (hook (0x100000, (eisa_mmap & 0xFFFF) << 10, + GRUB_MEMORY_AVAILABLE, hook_data) == 0) + hook (0x1000000, eisa_mmap & ~0xFFFF, GRUB_MEMORY_AVAILABLE, + hook_data); + } + else + hook (0x100000, ((grub_uint32_t) grub_get_ext_memsize ()) << 10, + GRUB_MEMORY_AVAILABLE, hook_data); + } + + return 0; +} diff --git a/grub-core/kern/i386/pc/startup.S b/grub-core/kern/i386/pc/startup.S new file mode 100644 index 000000000..b8a9b33b4 --- /dev/null +++ b/grub-core/kern/i386/pc/startup.S @@ -0,0 +1,217 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009,2011 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 . + */ + + +/* + * 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 +#include +#include +#ifdef __APPLE__ +#include +#endif + + .file "startup.S" + + .text + + .globl start, _start, __start +start: +_start: +__start: +#ifdef __APPLE__ +LOCAL(start): +#endif + .code32 + + movl %ecx, (LOCAL(real_to_prot_addr) - _start) (%esi) + movl %edi, (LOCAL(prot_to_real_addr) - _start) (%esi) + movl %eax, (EXT_C(grub_realidt) - _start) (%esi) + + /* copy back the decompressed part (except the modules) */ +#ifdef __APPLE__ + movl $EXT_C(_edata), %ecx + subl $LOCAL(start), %ecx +#else + movl $(_edata - _start), %ecx +#endif + movl $(_start), %edi + rep + movsb + + movl $LOCAL (cont), %esi + jmp *%esi +LOCAL(cont): + +#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__ + /* clean out the bss */ + movl $EXT_C(_edata), %edi + + /* compute the bss length */ + movl $GRUB_MEMORY_MACHINE_SCRATCH_ADDR, %ecx +#else + /* clean out the bss */ + movl $BSS_START_SYMBOL, %edi + + /* compute the bss length */ + movl $END_SYMBOL, %ecx +#endif + subl %edi, %ecx + + /* clean out */ + xorl %eax, %eax + cld + rep + stosb + + movl %edx, EXT_C(grub_boot_device) + + /* + * Call the start of main body of C code. + */ + call EXT_C(grub_main) + +LOCAL(real_to_prot_addr): + .long 0 +LOCAL(prot_to_real_addr): + .long 0 + + .macro PROT_TO_REAL + movl LOCAL(prot_to_real_addr), %eax + call *%eax + .endm + + .macro REAL_TO_PROT + movl LOCAL(real_to_prot_addr), %eax + calll *%eax + .endm + +/* + * grub_exit() + * + * Exit the system. + */ +FUNCTION(grub_exit) + PROT_TO_REAL + .code16 + /* Tell the BIOS a boot failure. If this does not work, reboot. */ + int $0x18 + /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */ + xorw %ax, %ax + movw $0x0472, %di + movw %ax, (%di) + ljmp $0xf000, $0xfff0 + .code32 + +/* + * int grub_pxe_call (int func, void* data, grub_uint32_t pxe_rm_entry); + */ +FUNCTION(grub_pxe_call) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + movl %ecx, %ebx + movl %eax, %ecx + movl %edx, %eax + andl $0xF, %eax + shrl $4, %edx + shll $16, %edx + addl %eax, %edx + + PROT_TO_REAL + .code16 + + pushl %ebx + pushl %edx + pushw %cx + movw %sp, %bx + lcall *%ss:6(%bx) + cld + addw $10, %sp + movw %ax, %cx + + REAL_TO_PROT + .code32 + + movzwl %cx, %eax + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +#include "../int.S" + +VARIABLE(grub_realidt) + .long 0 + +#ifdef __APPLE__ + /* Older versions of objconv assume that there is the same number + of text and data sections. Hence this dummy. */ + .section __TEXT, __zz_dummy + .byte 0 + .globl EXT_C(_edata) + .globl EXT_C(grub_boot_device) + .zerofill __DATA, __aa_before_bss, EXT_C(_edata), 1, 0 + .zerofill __DATA, __bss, EXT_C(grub_boot_device), 4, 2 +#else + .bss +VARIABLE(grub_boot_device) + .long 0 +#endif diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c new file mode 100644 index 000000000..08f81d25e --- /dev/null +++ b/grub-core/kern/i386/qemu/init.c @@ -0,0 +1,276 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern grub_uint8_t _start[]; +extern grub_uint8_t _end[]; +extern grub_uint8_t _edata[]; + +void __attribute__ ((noreturn)) +grub_exit (void) +{ + /* We can't use grub_fatal() in this function. This would create an infinite + loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ + while (1) + grub_cpu_idle (); +} + +grub_addr_t grub_modbase; + +/* Helper for grub_machine_init. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + grub_uint64_t begin = addr, end = addr + size; + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + /* Restrict ourselves to 32-bit memory space. */ + if (begin > GRUB_ULONG_MAX) + return 0; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; +#endif + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Avoid the lower memory. */ + if (begin < GRUB_MEMORY_MACHINE_LOWER_SIZE) + begin = GRUB_MEMORY_MACHINE_LOWER_SIZE; + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); + + return 0; +} + +struct resource +{ + grub_pci_device_t dev; + grub_size_t size; + unsigned type:4; + unsigned bar:3; +}; + +struct iterator_ctx +{ + struct resource *resources; + grub_size_t nresources; +}; + +/* We don't support bridges, so can't have more than 32 devices. */ +#define MAX_DEVICES 32 + +static int +find_resources (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused)), + void *data) +{ + struct iterator_ctx *ctx = data; + int bar; + + if (ctx->nresources >= MAX_DEVICES * 6) + return 1; + + for (bar = 0; bar < 6; bar++) + { + grub_pci_address_t addr; + grub_uint32_t ones, zeros, mask; + struct resource *res; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0 + + 4 * bar); + grub_pci_write (addr, 0xffffffff); + grub_pci_read (addr); + ones = grub_pci_read (addr); + grub_pci_write (addr, 0); + grub_pci_read (addr); + zeros = grub_pci_read (addr); + if (ones == zeros) + continue; + res = &ctx->resources[ctx->nresources++]; + if ((zeros & GRUB_PCI_ADDR_SPACE_MASK) == GRUB_PCI_ADDR_SPACE_IO) + mask = GRUB_PCI_ADDR_SPACE_MASK; + else + mask = (GRUB_PCI_ADDR_MEM_TYPE_MASK | GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_PREFETCH); + + res->type = ones & mask; + res->dev = dev; + res->bar = bar; + res->size = (~((zeros ^ ones)) | mask) + 1; + if ((zeros & (GRUB_PCI_ADDR_MEM_TYPE_MASK | GRUB_PCI_ADDR_SPACE_MASK)) + == (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_64)) + bar++; + } + return 0; +} + +static int +enable_cards (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + grub_uint16_t cmd = 0; + grub_pci_address_t addr; + grub_uint32_t class; + int bar; + + for (bar = 0; bar < 6; bar++) + { + grub_uint32_t val; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0 + + 4 * bar); + val = grub_pci_read (addr); + if (!val) + continue; + if ((val & GRUB_PCI_ADDR_SPACE_MASK) == GRUB_PCI_ADDR_SPACE_IO) + cmd |= GRUB_PCI_COMMAND_IO_ENABLED; + else + cmd |= GRUB_PCI_COMMAND_MEM_ENABLED; + } + + class = (grub_pci_read (addr) >> 16) & 0xffff; + + if (class == GRUB_PCI_CLASS_DISPLAY_VGA) + cmd |= GRUB_PCI_COMMAND_IO_ENABLED + | GRUB_PCI_COMMAND_MEM_ENABLED; + + if (class == GRUB_PCI_CLASS_SERIAL_USB) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, cmd); + + return 0; +} + +static void +grub_pci_assign_addresses (void) +{ + struct iterator_ctx ctx; + + { + struct resource resources[MAX_DEVICES * 6]; + int done; + unsigned i; + ctx.nresources = 0; + ctx.resources = resources; + grub_uint32_t memptr = 0xf0000000; + grub_uint16_t ioptr = 0x1000; + + grub_pci_iterate (find_resources, &ctx); + /* FIXME: do we need a better sort here? */ + do + { + done = 0; + for (i = 0; i + 1 < ctx.nresources; i++) + if (resources[i].size < resources[i+1].size) + { + struct resource t; + t = resources[i]; + resources[i] = resources[i+1]; + resources[i+1] = t; + done = 1; + } + } + while (done); + + for (i = 0; i < ctx.nresources; i++) + { + grub_pci_address_t addr; + addr = grub_pci_make_address (resources[i].dev, + GRUB_PCI_REG_ADDRESS_REG0 + + 4 * resources[i].bar); + if ((resources[i].type & GRUB_PCI_ADDR_SPACE_MASK) + == GRUB_PCI_ADDR_SPACE_IO) + { + grub_pci_write (addr, ioptr | resources[i].type); + ioptr += resources[i].size; + } + else + { + grub_pci_write (addr, memptr | resources[i].type); + memptr += resources[i].size; + if ((resources[i].type & (GRUB_PCI_ADDR_MEM_TYPE_MASK + | GRUB_PCI_ADDR_SPACE_MASK)) + == (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_64)) + { + addr = grub_pci_make_address (resources[i].dev, + GRUB_PCI_REG_ADDRESS_REG0 + + 4 * resources[i].bar + 4); + grub_pci_write (addr, 0); + } + } + } + grub_pci_iterate (enable_cards, NULL); + } +} + +void +grub_machine_init (void) +{ + grub_modbase = grub_core_entry_addr + (_edata - _start); + + grub_pci_assign_addresses (); + + grub_qemu_init_cirrus (); + + grub_vga_text_init (); + + grub_machine_mmap_init (); + grub_machine_mmap_iterate (heap_init, NULL); + + + grub_tsc_init (); +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); + grub_stop_floppy (); +} diff --git a/kern/i386/qemu/mmap.c b/grub-core/kern/i386/qemu/mmap.c similarity index 54% rename from kern/i386/qemu/mmap.c rename to grub-core/kern/i386/qemu/mmap.c index c7fc4f45e..f449637ef 100644 --- a/kern/i386/qemu/mmap.c +++ b/grub-core/kern/i386/qemu/mmap.c @@ -16,58 +16,91 @@ * along with GRUB. If not, see . */ -#include +#include #include #include #include #include #include #include +#include #define QEMU_CMOS_MEMSIZE_HIGH 0x35 #define QEMU_CMOS_MEMSIZE_LOW 0x34 +#define QEMU_CMOS_MEMSIZE2_HIGH 0x31 +#define QEMU_CMOS_MEMSIZE2_LOW 0x30 + #define min(a,b) ((a) > (b) ? (b) : (a)) extern char _start[]; extern char _end[]; -grub_size_t grub_lower_mem, grub_upper_mem; -grub_uint64_t mem_size; +static grub_uint64_t mem_size, above_4g; void -grub_machine_mmap_init () +grub_machine_mmap_init (void) { - mem_size = grub_cmos_read (QEMU_CMOS_MEMSIZE_HIGH) << 24 | grub_cmos_read (QEMU_CMOS_MEMSIZE_LOW) << 16; + grub_uint8_t high, low, b, c, d; + grub_cmos_read (QEMU_CMOS_MEMSIZE_HIGH, &high); + grub_cmos_read (QEMU_CMOS_MEMSIZE_LOW, &low); + mem_size = ((grub_uint64_t) high) << 24 + | ((grub_uint64_t) low) << 16; + if (mem_size > 0) + { + /* Don't ask... */ + mem_size += (16 * 1024 * 1024); + } + else + { + grub_cmos_read (QEMU_CMOS_MEMSIZE2_HIGH, &high); + grub_cmos_read (QEMU_CMOS_MEMSIZE2_LOW, &low); + mem_size + = ((((grub_uint64_t) high) << 18) | (((grub_uint64_t) low) << 10)) + + 1024 * 1024; + } - /* Don't ask... */ - mem_size += (16 * 1024 * 1024); + grub_cmos_read (0x5b, &b); + grub_cmos_read (0x5c, &c); + grub_cmos_read (0x5d, &d); + above_4g = (((grub_uint64_t) b) << 16) + | (((grub_uint64_t) c) << 24) + | (((grub_uint64_t) d) << 32); } grub_err_t -grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t)) +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) { if (hook (0x0, (grub_addr_t) _start, - GRUB_MACHINE_MEMORY_AVAILABLE)) + GRUB_MEMORY_AVAILABLE, hook_data)) + return 1; + + if (hook ((grub_addr_t) _end, + 0xa0000 - (grub_addr_t) _end, + GRUB_MEMORY_AVAILABLE, hook_data)) return 1; if (hook (GRUB_MEMORY_MACHINE_UPPER, 0x100000 - GRUB_MEMORY_MACHINE_UPPER, - GRUB_MACHINE_MEMORY_RESERVED)) + GRUB_MEMORY_RESERVED, hook_data)) + return 1; + + /* Everything else is free. */ + if (hook (0x100000, + min (mem_size, (grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE) - 0x100000, + GRUB_MEMORY_AVAILABLE, hook_data)) return 1; /* Protect boot.img, which contains the gdt. It is mapped at the top of memory (it is also mapped below 0x100000, but we already reserved that area). */ if (hook ((grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE, GRUB_BOOT_MACHINE_SIZE, - GRUB_MACHINE_MEMORY_RESERVED)) + GRUB_MEMORY_RESERVED, hook_data)) return 1; - /* Everything else is free. */ - if (hook (0x100000, - min (mem_size, (grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE) - 0x100000, - GRUB_MACHINE_MEMORY_AVAILABLE)) + if (above_4g != 0 && hook (0x100000000ULL, above_4g, + GRUB_MEMORY_AVAILABLE, hook_data)) return 1; return 0; diff --git a/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S similarity index 71% rename from kern/i386/qemu/startup.S rename to grub-core/kern/i386/qemu/startup.S index 7484650b2..0d89858d9 100644 --- a/kern/i386/qemu/startup.S +++ b/grub-core/kern/i386/qemu/startup.S @@ -18,6 +18,7 @@ #include #include + #include #include @@ -27,19 +28,9 @@ _start: jmp codestart - . = _start + GRUB_KERNEL_MACHINE_CORE_ENTRY_ADDR + .org GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR VARIABLE(grub_core_entry_addr) .long 0 -VARIABLE(grub_kernel_image_size) - .long 0 -VARIABLE(grub_prefix) - /* to be filled by grub-mkimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = _start + GRUB_KERNEL_MACHINE_DATA_END codestart: /* Relocate to low memory. First we figure out our location. @@ -51,11 +42,7 @@ codestart: value of `grub_core_entry_addr' in %esi. */ xorw %si, %si - /* ... which allows us to access `grub_kernel_image_size' - before relocation. */ - movl (grub_kernel_image_size - _start)(%esi), %ecx - - + movl $(_edata - _start), %ecx movl $_start, %edi cld rep @@ -63,24 +50,12 @@ codestart: ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $1f 1: -#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 @@ -94,6 +69,7 @@ codestart: call EXT_C(grub_main) /* This should never happen. */ - jmp EXT_C(grub_stop) - -#include "../realmode.S" + cli +1: + hlt + jmp 1b diff --git a/kern/i386/realmode.S b/grub-core/kern/i386/realmode.S similarity index 77% rename from kern/i386/realmode.S rename to grub-core/kern/i386/realmode.S index 578c8d2a8..265cdcb9d 100644 --- a/kern/i386/realmode.S +++ b/grub-core/kern/i386/realmode.S @@ -16,7 +16,7 @@ * along with GRUB. If not, see . */ -#include +#include /* * Note: These functions defined in this file may be called from C. @@ -46,11 +46,17 @@ * This is the area for all of the special variables. */ - .p2align 2 /* force 4-byte alignment */ - protstack: .long GRUB_MEMORY_MACHINE_PROT_STACK + .macro PROT_TO_REAL + call prot_to_real + .endm + + .macro REAL_TO_PROT + calll real_to_prot + .endm + /* * This is the Global Descriptor Table * @@ -71,7 +77,7 @@ protstack: * description. */ - .p2align 2 /* force 4-byte alignment */ + .p2align 5 /* force 32-byte alignment */ gdt: .word 0, 0 .byte 0, 0, 0, 0 @@ -105,10 +111,17 @@ gdt: .byte 0, 0x92, 0, 0 + .p2align 5 /* this is the GDT descriptor */ gdtdesc: .word 0x27 /* limit */ .long gdt /* addr */ +LOCAL(realidt): + .word 0x400 + .long 0 +protidt: + .word 0 + .long 0 /* * These next two routines, "real_to_prot" and "prot_to_real" are structured @@ -124,7 +137,22 @@ real_to_prot: /* load the GDT register */ xorw %ax, %ax movw %ax, %ds - DATA32 ADDR32 lgdt gdtdesc +#ifdef GRUB_MACHINE_QEMU + /* + qemu is special: gdtdesc is in ROM. + %cs = 0xf000 + _start + GRUB_BOOT_MACHINE_SIZE = 0x100000 + So + _start + GRUB_BOOT_MACHINE_SIZE - 0x10000 points to the same point + as %cs. + gdtdesc - (_start + GRUB_BOOT_MACHINE_SIZE - 0x10000) + = gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000 + but the later can be computed by assembly. + */ + lgdtl %cs:(gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000) +#else + lgdtl gdtdesc +#endif /* turn on protected mode */ movl %cr0, %eax @@ -132,7 +160,7 @@ real_to_prot: movl %eax, %cr0 /* jump to relocation, flush prefetch queue, and reload %cs */ - DATA32 ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg + ljmpl $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg .code32 protcseg: @@ -160,13 +188,39 @@ protcseg: /* zero %eax */ xorl %eax, %eax + sidt LOCAL(realidt) + lidt protidt + /* return on the old (or initialized) stack! */ ret + /* prot_to_real assumes that this code is under 64K which is not + true for qemu. */ +#ifndef GRUB_MACHINE_QEMU +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,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 . + */ prot_to_real: /* just in case, set GDT */ lgdt gdtdesc + sidt protidt + lidt LOCAL(realidt) + /* save the protected mode stack */ movl %esp, %eax movl %eax, protstack @@ -201,7 +255,7 @@ tmpcseg: movl %eax, %cr0 /* flush prefetch queue, reload %cs */ - DATA32 ljmp $0, $realcseg + ljmpl $0, $realcseg realcseg: /* we are in real mode now @@ -222,21 +276,6 @@ realcseg: #endif /* return on new stack! */ - DATA32 ret - - .code32 - -/* - * grub_reboot() - * - * Reboot the system. At the moment, rely on BIOS. - */ -FUNCTION(grub_reboot) - call prot_to_real - .code16 -cold_reboot: - /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */ - movw $0x0472, %di - movw %ax, (%di) - ljmp $0xf000, $0xfff0 + retl +#endif .code32 diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c new file mode 100644 index 000000000..9293b161d --- /dev/null +++ b/grub-core/kern/i386/tsc.c @@ -0,0 +1,78 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module calibrates the TSC to real time. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +/* This defines the value TSC had at the epoch (that is, when we calibrated it). */ +static grub_uint64_t tsc_boot_time; + +/* Calibrated TSC rate. (In ms per 2^32 ticks) */ +/* We assume that the tick is less than 1 ms and hence this value fits + in 32-bit. */ +grub_uint32_t grub_tsc_rate; + +static grub_uint64_t +grub_tsc_get_time_ms (void) +{ + grub_uint64_t a = grub_get_tsc () - tsc_boot_time; + grub_uint64_t ah = a >> 32; + grub_uint64_t al = a & 0xffffffff; + + return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; +} + +static int +calibrate_tsc_hardcode (void) +{ + grub_tsc_rate = 5368;/* 800 MHz */ + return 1; +} + +void +grub_tsc_init (void) +{ + if (!grub_cpu_is_tsc_supported ()) + { +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_IEEE1275) + grub_install_get_time_ms (grub_rtc_get_time_ms); +#else + grub_fatal ("no TSC found"); +#endif + return; + } + + tsc_boot_time = grub_get_tsc (); + +#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH) + (void) (grub_tsc_calibrate_from_xen () || calibrate_tsc_hardcode()); +#elif defined (GRUB_MACHINE_EFI) + (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || grub_tsc_calibrate_from_efi() || calibrate_tsc_hardcode()); +#elif defined (GRUB_MACHINE_COREBOOT) + (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || calibrate_tsc_hardcode()); +#else + (void) (grub_tsc_calibrate_from_pit () || calibrate_tsc_hardcode()); +#endif + grub_install_get_time_ms (grub_tsc_get_time_ms); +} diff --git a/grub-core/kern/i386/tsc_pit.c b/grub-core/kern/i386/tsc_pit.c new file mode 100644 index 000000000..67990bfa6 --- /dev/null +++ b/grub-core/kern/i386/tsc_pit.c @@ -0,0 +1,84 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +static int +grub_pit_wait (void) +{ + int ret = 0; + + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); + + /* Set tics. */ + grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD, + GRUB_PIT_CTRL); + /* 0xffff ticks: 55ms. */ + grub_outb (0xff, GRUB_PIT_COUNTER_2); + grub_outb (0xff, GRUB_PIT_COUNTER_2); + + /* Enable timer2 gate, keep speaker disabled. */ + grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA) + | GRUB_PIT_SPK_TMR2, + GRUB_PIT_SPEAKER_PORT); + + if ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00) { + ret = 1; + /* Wait. */ + while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); + } + + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); + + return ret; +} + +/* Calibrate the TSC based on the RTC. */ +int +grub_tsc_calibrate_from_pit (void) +{ + /* First calibrate the TSC rate (relative, not absolute time). */ + grub_uint64_t start_tsc, end_tsc; + + start_tsc = grub_get_tsc (); + if (!grub_pit_wait ()) + return 0; + end_tsc = grub_get_tsc (); + + grub_tsc_rate = 0; + if (end_tsc > start_tsc) + grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - start_tsc, 0); + if (grub_tsc_rate == 0) + return 0; + return 1; +} diff --git a/grub-core/kern/i386/tsc_pmtimer.c b/grub-core/kern/i386/tsc_pmtimer.c new file mode 100644 index 000000000..dd044590d --- /dev/null +++ b/grub-core/kern/i386/tsc_pmtimer.c @@ -0,0 +1,157 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +grub_uint64_t +grub_pmtimer_wait_count_tsc (grub_port_t pmtimer, + grub_uint16_t num_pm_ticks) +{ + grub_uint32_t start; + grub_uint64_t cur, end; + grub_uint64_t start_tsc; + grub_uint64_t end_tsc; + grub_uint32_t num_iter = 0; + int bad_reads = 0; + + /* + * Some timers are 24-bit and some are 32-bit, but it doesn't make much + * difference to us. Caring which one we have isn't really worth it since + * the low-order digits will give us enough data to calibrate TSC. So just + * mask the top-order byte off. + */ + cur = start = grub_inl (pmtimer) & 0x00ffffffUL; + end = start + num_pm_ticks; + start_tsc = grub_get_tsc (); + while (1) + { + cur &= 0xffffffffff000000ULL; + + /* Only take the low-order 24-bit for the reason explained above. */ + cur |= grub_inl (pmtimer) & 0x00ffffffUL; + + end_tsc = grub_get_tsc(); + + /* + * If we get 10 reads in a row that are obviously dead pins, there's no + * reason to do this thousands of times. + */ + if (cur == 0xffffffUL || cur == 0) + { + bad_reads++; + grub_dprintf ("pmtimer", + "pmtimer: 0x%"PRIxGRUB_UINT64_T" bad_reads: %d\n", + cur, bad_reads); + + if (bad_reads == 10) + { + grub_dprintf ("pmtimer", "timer is broken; giving up.\n"); + return 0; + } + } + + if (cur < start) + cur += 0x1000000; + + if (cur >= end) + { + grub_dprintf ("pmtimer", "pmtimer delta is 0x%"PRIxGRUB_UINT64_T"\n", + cur - start); + grub_dprintf ("pmtimer", "tsc delta is 0x%"PRIxGRUB_UINT64_T"\n", + end_tsc - start_tsc); + return end_tsc - start_tsc; + } + + /* + * Check for broken PM timer. 1ms at 10GHz should be 1E+7 TSCs; at + * 250MHz it should be 2.5E5. So if after 4E+7 TSCs on a 10GHz machine, + * we should have seen pmtimer show 4ms of change (i.e. cur =~ start + 14320); + * on a 250MHz machine that should be 160ms (start + 572800). If after + * this a time we still don't have 1ms on pmtimer, then pmtimer is broken. + * + * Likewise, if our code is perfectly efficient and introduces no delays + * whatsoever, on a 10GHz system we should see a TSC delta of 3580 in + * ~3580 iterations. On a 250MHz machine that should be ~900 iterations. + * + * With those factors in mind, there are two limits here. There's a hard + * limit here at 8x our desired pm timer delta. This limit was picked as + * an arbitrarily large value that's still not a lot of time to humans, + * because if we get that far this is either an implausibly fast machine + * or the pmtimer is not running. And there is another limit on a 4 ms TSC + * delta on a 10 GHz clock, without seeing cur converge on our target value. + */ + if ((++num_iter > (grub_uint32_t) num_pm_ticks << 3UL) || end_tsc - start_tsc > 40000000) + { + grub_dprintf ("pmtimer", + "pmtimer delta is 0x%"PRIxGRUB_UINT64_T" (%"PRIxGRUB_UINT32_T" iterations)\n", + cur - start, num_iter); + grub_dprintf ("pmtimer", + "tsc delta is implausible: 0x%"PRIxGRUB_UINT64_T"\n", + end_tsc - start_tsc); + return 0; + } + } +} + +int +grub_tsc_calibrate_from_pmtimer (void) +{ + struct grub_acpi_fadt *fadt; + grub_port_t pmtimer; + grub_uint64_t tsc_diff; + + fadt = grub_acpi_find_fadt (); + if (!fadt) + { + grub_dprintf ("pmtimer", "No FADT found; not using pmtimer.\n"); + return 0; + } + pmtimer = fadt->pmtimer; + if (!pmtimer) + { + grub_dprintf ("pmtimer", "FADT does not specify pmtimer; skipping.\n"); + return 0; + } + + /* It's 3.579545 MHz clock. Wait 1 ms. */ + tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer, 3580); + if (tsc_diff == 0) + return 0; + grub_tsc_rate = grub_divmod64 ((1ULL << 32), tsc_diff, 0); + /* + * Specifically, when the tsc_diff (end_tsc - start_tsc) is greater than (1ULL << 32), + * the result of grub_divmod64() becomes zero, causing grub_tsc_rate to always be zero. + * As a result, grub_tsc_get_time_ms() consistently returns zero, and the GRUB menu + * countdown gets stuck. To resolve this, we return 0 to proceed to the next calibration + * function when grub_tsc_rate is zero. + */ + if (grub_tsc_rate == 0) + return 0; + + return 1; +} diff --git a/grub-core/kern/i386/xen/hypercall.S b/grub-core/kern/i386/xen/hypercall.S new file mode 100644 index 000000000..09d75c3a5 --- /dev/null +++ b/grub-core/kern/i386/xen/hypercall.S @@ -0,0 +1,43 @@ +/* hypercall.S - wrappers for Xen hypercalls */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + +FUNCTION(grub_xen_hypercall) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + /* call number already in %eax. */ + /* %edx -> %ebx*/ + /* %ecx -> %ecx*/ + movl %edx, %ebx + movl 8(%ebp), %edx + movl 12(%ebp), %esi + movl 16(%ebp), %edi + movl 20(%ebp), %ebp + int $0x82 + popl %ebx + popl %edi + popl %esi + popl %ebp + ret diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c new file mode 100644 index 000000000..91fbca859 --- /dev/null +++ b/grub-core/kern/i386/xen/pvh.c @@ -0,0 +1,369 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define XEN_MEMORY_MAP_SIZE 128 + +grub_uint64_t grub_rsdp_addr; + +static char hypercall_page[GRUB_XEN_PAGE_SIZE] + __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE))); + +static grub_uint32_t xen_cpuid_base; +static struct start_info grub_xen_start_page; +static struct grub_e820_mmap_entry map[XEN_MEMORY_MAP_SIZE]; +static unsigned int nr_map_entries; + +static void +grub_xen_cons_msg (const char *msg) +{ + const char *c; + + for (c = msg; *c; c++) + grub_outb (*c, XEN_HVM_DEBUGCONS_IOPORT); +} + +static void +grub_xen_panic (const char *msg) +{ + grub_xen_cons_msg (msg); + grub_xen_cons_msg ("System halted!\n"); + + asm volatile ("cli"); + + while (1) + { + asm volatile ("hlt"); + } +} + +static void +grub_xen_cpuid_base (void) +{ + grub_uint32_t base, eax, signature[3]; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) + { + grub_cpuid (base, eax, signature[0], signature[1], signature[2]); + if (!grub_memcmp ("XenVMMXenVMM", signature, 12) && (eax - base) >= 2) + { + xen_cpuid_base = base; + return; + } + } + + grub_xen_panic ("Found no Xen signature!\n"); +} + +static void +grub_xen_setup_hypercall_page (void) +{ + grub_uint32_t msr, addr, eax, ebx, ecx, edx; + + /* Get base address of Xen-specific MSRs. */ + grub_cpuid (xen_cpuid_base + 2, eax, ebx, ecx, edx); + msr = ebx; + addr = (grub_uint32_t) (&hypercall_page); + + /* Specify hypercall page address for Xen. */ + asm volatile ("wrmsr" : : "c" (msr), "a" (addr), "d" (0) : "memory"); +} + +int +grub_xen_hypercall (grub_uint32_t callno, grub_uint32_t a0, + grub_uint32_t a1, grub_uint32_t a2, + grub_uint32_t a3, grub_uint32_t a4, + grub_uint32_t a5 __attribute__ ((unused))) +{ + grub_uint32_t res; + + asm volatile ("call *%[callno]" + : "=a" (res), "+b" (a0), "+c" (a1), "+d" (a2), + "+S" (a3), "+D" (a4) + : [callno] "a" (&hypercall_page[callno * 32]) + : "memory"); + return res; +} + +static grub_uint32_t +grub_xen_get_param (int idx) +{ + struct xen_hvm_param xhv; + int r; + + xhv.domid = DOMID_SELF; + xhv.index = idx; + r = grub_xen_hypercall (__HYPERVISOR_hvm_op, HVMOP_get_param, + (grub_uint32_t) (&xhv), 0, 0, 0, 0); + if (r < 0) + grub_xen_panic ("Could not get parameter from Xen!\n"); + return xhv.value; +} + +static void * +grub_xen_add_physmap (unsigned int space, void *addr) +{ + struct xen_add_to_physmap xatp; + + xatp.domid = DOMID_SELF; + xatp.idx = 0; + xatp.space = space; + xatp.gpfn = (grub_addr_t) addr >> GRUB_XEN_LOG_PAGE_SIZE; + if (grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_add_to_physmap, + (grub_uint32_t) (&xatp), 0, 0, 0, 0)) + grub_xen_panic ("Memory_op hypercall failed!\n"); + return addr; +} + +static void +grub_xen_sort_mmap (void) +{ + grub_uint64_t from, to; + unsigned int i; + struct grub_e820_mmap_entry tmp; + + /* Align map entries to page boundaries. */ + for (i = 0; i < nr_map_entries; i++) + { + from = map[i].addr; + to = from + map[i].len; + if (map[i].type == GRUB_MEMORY_AVAILABLE) + { + from = ALIGN_UP (from, GRUB_XEN_PAGE_SIZE); + to = ALIGN_DOWN (to, GRUB_XEN_PAGE_SIZE); + } + else + { + from = ALIGN_DOWN (from, GRUB_XEN_PAGE_SIZE); + to = ALIGN_UP (to, GRUB_XEN_PAGE_SIZE); + } + map[i].addr = from; + map[i].len = to - from; + } + + again: + /* Sort entries by start address. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].addr >= map[i - 1].addr) + continue; + tmp = map[i]; + map[i] = map[i - 1]; + map[i - 1] = tmp; + i = 0; + } + + /* Detect overlapping areas. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].addr >= map[i - 1].addr + map[i - 1].len) + continue; + tmp = map[i - 1]; + map[i - 1].len = map[i].addr - map[i - 1].addr; + if (map[i].addr + map[i].len >= tmp.addr + tmp.len) + continue; + if (nr_map_entries < ARRAY_SIZE (map)) + { + map[nr_map_entries].addr = map[i].addr + map[i].len; + map[nr_map_entries].len = tmp.addr + tmp.len - map[nr_map_entries].addr; + map[nr_map_entries].type = tmp.type; + nr_map_entries++; + goto again; + } + } + + /* Merge adjacent entries. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].type == map[i - 1].type && + map[i].addr == map[i - 1].addr + map[i - 1].len) + { + map[i - 1].len += map[i].len; + map[i] = map[nr_map_entries - 1]; + nr_map_entries--; + goto again; + } + } +} + +static void +grub_xen_get_mmap (void) +{ + struct xen_memory_map memmap; + + memmap.nr_entries = ARRAY_SIZE (map); + set_xen_guest_handle (memmap.buffer, map); + if (grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_memory_map, + (grub_uint32_t) (&memmap), 0, 0, 0, 0)) + grub_xen_panic ("Could not get memory map from Xen!\n"); + nr_map_entries = memmap.nr_entries; + + grub_xen_sort_mmap (); +} + +static void +grub_xen_set_mmap (void) +{ + struct xen_foreign_memory_map memmap; + + memmap.domid = DOMID_SELF; + memmap.map.nr_entries = nr_map_entries; + set_xen_guest_handle (memmap.map.buffer, map); + grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_set_memory_map, + (grub_uint32_t) (&memmap), 0, 0, 0, 0); +} + +static void +grub_xen_mm_init_regions (void) +{ + grub_uint64_t modend, from, to; + unsigned int i; + + modend = grub_modules_get_end (); + + for (i = 0; i < nr_map_entries; i++) + { + if (map[i].type != GRUB_MEMORY_AVAILABLE) + continue; + from = map[i].addr; + to = from + map[i].len; + if (from < modend) + from = modend; + if (from >= to || from >= (1ULL << 32)) + continue; + if (to > (1ULL << 32)) + to = 1ULL << 32; + grub_mm_init_region ((void *) (grub_addr_t) from, to - from); + } +} + +static grub_uint64_t +grub_xen_find_page (grub_uint64_t start) +{ + unsigned int i, j; + grub_uint64_t last = start; + + /* + * Try to find a e820 map hole below 4G. + * Relies on page-aligned entries (addr and len) and input (start). + */ + + for (i = 0; i < nr_map_entries; i++) + { + if (last > map[i].addr + map[i].len) + continue; + if (last < map[i].addr) + return last; + if ((map[i].addr >> 32) || ((map[i].addr + map[i].len) >> 32)) + break; + last = map[i].addr + map[i].len; + } + if (i == nr_map_entries) + return last; + + /* No hole found, use the highest RAM page below 4G and reserve it. */ + if (nr_map_entries == ARRAY_SIZE (map)) + grub_xen_panic ("Memory map size limit reached!\n"); + for (i = 0, j = 0; i < nr_map_entries; i++) + { + if (map[i].type != GRUB_MEMORY_AVAILABLE) + continue; + if (map[i].addr >> 32) + break; + j = i; + if ((map[i].addr + map[i].len) >> 32) + break; + } + if (map[j].type != GRUB_MEMORY_AVAILABLE) + grub_xen_panic ("No free memory page found!\n"); + if ((map[j].addr + map[j].len) >> 32) + last = (1ULL << 32) - GRUB_XEN_PAGE_SIZE; + else + last = map[j].addr + map[j].len - GRUB_XEN_PAGE_SIZE; + map[nr_map_entries].addr = last; + map[nr_map_entries].len = GRUB_XEN_PAGE_SIZE; + map[nr_map_entries].type = GRUB_MEMORY_RESERVED; + nr_map_entries++; + grub_xen_sort_mmap (); + + return last; +} + +void +grub_xen_setup_pvh (void) +{ + grub_addr_t par; + + grub_xen_cpuid_base (); + grub_xen_setup_hypercall_page (); + grub_xen_get_mmap (); + + /* Setup Xen data. */ + grub_xen_start_page_addr = &grub_xen_start_page; + + par = grub_xen_get_param (HVM_PARAM_CONSOLE_PFN); + grub_xen_start_page_addr->console.domU.mfn = par; + grub_xen_xcons = (void *) (grub_addr_t) (par << GRUB_XEN_LOG_PAGE_SIZE); + par = grub_xen_get_param (HVM_PARAM_CONSOLE_EVTCHN); + grub_xen_start_page_addr->console.domU.evtchn = par; + + par = grub_xen_get_param (HVM_PARAM_STORE_PFN); + grub_xen_start_page_addr->store_mfn = par; + grub_xen_xenstore = (void *) (grub_addr_t) (par << GRUB_XEN_LOG_PAGE_SIZE); + par = grub_xen_get_param (HVM_PARAM_STORE_EVTCHN); + grub_xen_start_page_addr->store_evtchn = par; + + par = grub_xen_find_page (0); + grub_xen_grant_table = grub_xen_add_physmap (XENMAPSPACE_grant_table, + (void *) par); + par = grub_xen_find_page (par + GRUB_XEN_PAGE_SIZE); + grub_xen_shared_info = grub_xen_add_physmap (XENMAPSPACE_shared_info, + (void *) par); + grub_xen_set_mmap (); + + grub_xen_mm_init_regions (); + + grub_rsdp_addr = pvh_start_info->rsdp_paddr; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + unsigned int i; + + for (i = 0; i < nr_map_entries; i++) + { + if (map[i].len && hook (map[i].addr, map[i].len, map[i].type, hook_data)) + break; + } + + return GRUB_ERR_NONE; +} diff --git a/include/grub/efi/time.h b/grub-core/kern/i386/xen/startup.S similarity index 65% rename from include/grub/efi/time.h rename to grub-core/kern/i386/xen/startup.S index 540f6fc04..fbe8300a7 100644 --- a/include/grub/efi/time.h +++ b/grub-core/kern/i386/xen/startup.S @@ -1,6 +1,7 @@ +/* startup.S - bootstrap GRUB itself */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2013 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 @@ -16,15 +17,22 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_EFI_TIME_HEADER -#define GRUB_EFI_TIME_HEADER 1 - +#include #include -/* This is destined to overflow when one hour passes by. */ -#define GRUB_TICKS_PER_SECOND ((1UL << 31) / 60 / 60 * 2) + .file "startup.S" + .text + .globl start, _start + .code32 -/* Return the real time in ticks. */ -grub_uint32_t EXPORT_FUNC (grub_get_rtc) (void); +start: +_start: + leal LOCAL(stack_end), %esp + movl %esi, EXT_C(grub_xen_start_page_addr) -#endif /* ! GRUB_EFI_TIME_HEADER */ + call EXT_C(grub_main) + /* Doesn't return. */ + + .bss + .space (1 << 22) +LOCAL(stack_end): diff --git a/grub-core/kern/i386/xen/startup_pvh.S b/grub-core/kern/i386/xen/startup_pvh.S new file mode 100644 index 000000000..363c31858 --- /dev/null +++ b/grub-core/kern/i386/xen/startup_pvh.S @@ -0,0 +1,81 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include +#include + + .file "startup_pvh.S" + .text + .globl start, _start + .code32 + +start: +_start: + cld + lgdt gdtdesc + ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $1f +1: + movl $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss + leal LOCAL(stack_end), %esp + + /* Save address of start info structure. */ + mov %ebx, pvh_start_info + call EXT_C(grub_main) + /* Doesn't return. */ + + .p2align 3 +gdt: + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + + .p2align 3 +/* this is the GDT descriptor */ +gdtdesc: + .word 0x17 /* limit */ + .long gdt /* addr */ + + .p2align 2 +/* Saved pointer to start info structure. */ + .globl pvh_start_info +pvh_start_info: + .long 0 + + .bss + .space GRUB_MEMORY_MACHINE_PROT_STACK_SIZE +LOCAL(stack_end): diff --git a/grub-core/kern/i386/xen/tsc.c b/grub-core/kern/i386/xen/tsc.c new file mode 100644 index 000000000..8792b308d --- /dev/null +++ b/grub-core/kern/i386/xen/tsc.c @@ -0,0 +1,40 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * 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 . + */ + +#include +#include +#include +#include +#include + +int +grub_tsc_calibrate_from_xen (void) +{ + grub_uint64_t t; + t = grub_xen_shared_info->vcpu_info[0].time.tsc_to_system_mul; + if (grub_xen_shared_info->vcpu_info[0].time.tsc_shift > 0) + t <<= grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + else + t >>= -grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + grub_tsc_rate = grub_divmod64 (t, 1000000, 0); + return 1; +} diff --git a/grub-core/kern/ia64/cache.c b/grub-core/kern/ia64/cache.c new file mode 100644 index 000000000..13efd308f --- /dev/null +++ b/grub-core/kern/ia64/cache.c @@ -0,0 +1,35 @@ +/* init.c - initialize an ia64-based EFI system */ +/* + * 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 . + */ + +#include +#include + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + /* Cache line length is at least 32. */ + len += (grub_uint64_t)address & 0x1f; + grub_uint64_t a = (grub_uint64_t)address & ~0x1f; + + /* Flush data. */ + for (len = (len + 31) & ~0x1f; len > 0; len -= 0x20, a += 0x20) + asm volatile ("fc.i %0" : : "r" (a)); + /* Sync and serialize. Maybe extra. */ + asm volatile (";; sync.i;; srlz.i;;"); +} diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c new file mode 100644 index 000000000..db59300fe --- /dev/null +++ b/grub-core/kern/ia64/dl.c @@ -0,0 +1,150 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MASK19 ((1 << 19) - 1) +#define MASK20 ((1 << 20) - 1) + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_IA_64) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rela *rel, *max; + + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + grub_addr_t addr; + Elf_Sym *sym; + grub_uint64_t value; + + if (seg->size < (rel->r_offset & ~3)) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (grub_addr_t) seg->addr + rel->r_offset; + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + /* On the PPC the value does not have an explicit + addend, add it. */ + value = sym->st_value + rel->r_addend; + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_IA64_PCREL21B: + { + grub_int64_t noff; + if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) + { + struct grub_ia64_trampoline *tr = mod->trampptr; + grub_ia64_make_trampoline (tr, value); + noff = ((char *) tr - (char *) (addr & ~3)) >> 4; + mod->trampptr = tr + 1; + } + else + noff = ((char *) value - (char *) (addr & ~3)) >> 4; + + if ((noff & ~MASK19) && ((-noff) & ~MASK19)) + return grub_error (GRUB_ERR_BAD_MODULE, + "jump offset too big (%lx)", noff); + grub_ia64_add_value_to_slot_20b (addr, noff); + } + break; + case R_IA64_SEGREL64LSB: + *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr; + break; + case R_IA64_FPTR64LSB: + case R_IA64_DIR64LSB: + *(grub_uint64_t *) addr += value; + break; + case R_IA64_PCREL64LSB: + *(grub_uint64_t *) addr += value - addr; + break; + case R_IA64_GPREL64I: + grub_ia64_set_immu64 (addr, value - (grub_addr_t) mod->base); + break; + case R_IA64_GPREL22: + if ((value - (grub_addr_t) mod->base) & ~MASK20) + return grub_error (GRUB_ERR_BAD_MODULE, + "gprel offset too big (%lx)", + value - (grub_addr_t) mod->base); + grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) mod->base); + break; + + case R_IA64_LTOFF22X: + case R_IA64_LTOFF22: + if (ELF_ST_TYPE (sym->st_info) == STT_FUNC) + value = *(grub_uint64_t *) sym->st_value + rel->r_addend; + /* Fallthrough. */ + case R_IA64_LTOFF_FPTR22: + { + grub_uint64_t *gpptr = mod->gotptr; + *gpptr = value; + if (((grub_addr_t) gpptr - (grub_addr_t) mod->base) & ~MASK20) + return grub_error (GRUB_ERR_BAD_MODULE, + "gprel offset too big (%lx)", + (grub_addr_t) gpptr - (grub_addr_t) mod->base); + grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - (grub_addr_t) mod->base); + mod->gotptr = gpptr + 1; + break; + } + /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV. */ + case R_IA64_LDXMOV: + break; + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/ia64/dl_helper.c b/grub-core/kern/ia64/dl_helper.c new file mode 100644 index 000000000..05a0a68db --- /dev/null +++ b/grub-core/kern/ia64/dl_helper.c @@ -0,0 +1,241 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wcast-align" + +#define MASK20 ((1 << 20) - 1) +#define MASK3 (~(grub_addr_t) 3) + +void +grub_ia64_set_immu64 (grub_addr_t addr, grub_uint64_t val) +{ + /* Copied from binutils. */ + grub_uint64_t *ptr = ((grub_uint64_t *) (addr & MASK3)); + grub_uint64_t t0, t1; + + t0 = grub_le_to_cpu64 (ptr[0]); + t1 = grub_le_to_cpu64 (ptr[1]); + + /* tmpl/s: bits 0.. 5 in t0 + slot 0: bits 5..45 in t0 + slot 1: bits 46..63 in t0, bits 0..22 in t1 + slot 2: bits 23..63 in t1 */ + + /* First, clear the bits that form the 64 bit constant. */ + t0 &= ~(0x3ffffLL << 46); + t1 &= ~(0x7fffffLL + | (( (0x07fLL << 13) | (0x1ffLL << 27) + | (0x01fLL << 22) | (0x001LL << 21) + | (0x001LL << 36)) << 23)); + + t0 |= ((val >> 22) & 0x03ffffLL) << 46; /* 18 lsbs of imm41 */ + t1 |= ((val >> 40) & 0x7fffffLL) << 0; /* 23 msbs of imm41 */ + t1 |= ( (((val >> 0) & 0x07f) << 13) /* imm7b */ + | (((val >> 7) & 0x1ff) << 27) /* imm9d */ + | (((val >> 16) & 0x01f) << 22) /* imm5c */ + | (((val >> 21) & 0x001) << 21) /* ic */ + | (((val >> 63) & 0x001) << 36)) << 23; /* i */ + + ptr[0] = t0; + ptr[1] = t1; +} + +void +grub_ia64_add_value_to_slot_20b (grub_addr_t addr, grub_uint32_t value) +{ + grub_uint32_t val; + switch (addr & 3) + { + case 0: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 2))); + val = (((((val & MASK20) + value) & MASK20) << 2) + | (val & ~(MASK20 << 2))); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 2), + grub_cpu_to_le32 (val)); + break; + case 1: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 7))); + val = ((((((val >> 3) & MASK20) + value) & MASK20) << 3) + | (val & ~(MASK20 << 3))); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 7), + grub_cpu_to_le32 (val)); + break; + case 2: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 12))); + val = ((((((val >> 4) & MASK20) + value) & MASK20) << 4) + | (val & ~(MASK20 << 4))); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 12), + grub_cpu_to_le32 (val)); + break; + } +} + +#define MASKF21 ( ((1 << 23) - 1) & ~((1 << 7) | (1 << 8)) ) + +static grub_uint32_t +add_value_to_slot_21_real (grub_uint32_t a, grub_uint32_t value) +{ + grub_uint32_t high, mid, low, c; + low = (a & 0x00007f); + mid = (a & 0x7fc000) >> 7; + high = (a & 0x003e00) << 7; + c = (low | mid | high) + value; + return (c & 0x7f) | ((c << 7) & 0x7fc000) | ((c >> 7) & 0x0003e00); //0x003e00 +} + +void +grub_ia64_add_value_to_slot_21 (grub_addr_t addr, grub_uint32_t value) +{ + grub_uint32_t val; + switch (addr & 3) + { + case 0: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 2))); + val = ((add_value_to_slot_21_real (((val >> 2) & MASKF21), value) + & MASKF21) << 2) | (val & ~(MASKF21 << 2)); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 2), + grub_cpu_to_le32 (val)); + break; + case 1: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 7))); + val = ((add_value_to_slot_21_real (((val >> 3) & MASKF21), value) + & MASKF21) << 3) | (val & ~(MASKF21 << 3)); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 7), + grub_cpu_to_le32 (val)); + break; + case 2: + val = grub_le_to_cpu32 (grub_get_unaligned32 (((grub_uint8_t *) + (addr & MASK3) + 12))); + val = ((add_value_to_slot_21_real (((val >> 4) & MASKF21), value) + & MASKF21) << 4) | (val & ~(MASKF21 << 4)); + grub_set_unaligned32 (((grub_uint8_t *) (addr & MASK3) + 12), + grub_cpu_to_le32 (val)); + break; + } +} + +static const grub_uint8_t nopm[5] = + { + /* [MLX] nop.m 0x0 */ + 0x05, 0x00, 0x00, 0x00, 0x01 + }; + +#ifdef GRUB_UTIL +static grub_uint8_t jump[0x20] = + { + /* [MMI] add r15=r15,r1;; */ + 0x0b, 0x78, 0x3c, 0x02, 0x00, 0x20, + /* ld8 r16=[r15],8 */ + 0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0, + /* mov r14=r1;; */ + 0x01, 0x08, 0x00, 0x84, + /* [MIB] ld8 r1=[r15] */ + 0x11, 0x08, 0x00, 0x1e, 0x18, 0x10, + /* mov b6=r16 */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, + /* br.few b6;; */ + 0x60, 0x00, 0x80, 0x00 + }; +#else +static const grub_uint8_t jump[0x20] = + { + /* ld8 r16=[r15],8 */ + 0x02, 0x80, 0x20, 0x1e, 0x18, 0x14, + /* mov r14=r1;; */ + 0xe0, 0x00, 0x04, 0x00, 0x42, 0x00, + /* nop.i 0x0 */ + 0x00, 0x00, 0x04, 0x00, + /* ld8 r1=[r15] */ + 0x11, 0x08, 0x00, 0x1e, 0x18, 0x10, + /* mov b6=r16 */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, + /* br.few b6;; */ + 0x60, 0x00, 0x80, 0x00 + }; +#endif + +void +grub_ia64_make_trampoline (struct grub_ia64_trampoline *tr, grub_uint64_t addr) +{ + grub_memcpy (tr->nop, nopm, sizeof (tr->nop)); + tr->addr_hi[0] = ((addr & 0xc00000) >> 16); + tr->addr_hi[1] = (addr >> 24) & 0xff; + tr->addr_hi[2] = (addr >> 32) & 0xff; + tr->addr_hi[3] = (addr >> 40) & 0xff; + tr->addr_hi[4] = (addr >> 48) & 0xff; + tr->addr_hi[5] = (addr >> 56) & 0xff; + tr->e0 = 0xe0; + tr->addr_lo[0] = ((addr & 0x000f) << 4) | 0x01; + tr->addr_lo[1] = (((addr & 0x0070) >> 4) | ((addr & 0x070000) >> 11) + | ((addr & 0x200000) >> 17)); + tr->addr_lo[2] = ((addr & 0x1f80) >> 5) | ((addr & 0x180000) >> 19); + tr->addr_lo[3] = ((addr & 0xe000) >> 13) | 0x60; + grub_memcpy (tr->jump, jump, sizeof (tr->jump)); +} + +grub_err_t +grub_ia64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf64_Ehdr *e = ehdr; + grub_size_t cntt = 0, cntg = 0; + const Elf64_Shdr *s; + unsigned i; + + for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff)); + i < grub_le_to_cpu16 (e->e_shnum); + i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 (e->e_shentsize))) + if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_RELA)) + { + const Elf64_Rela *rel, *max; + + for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 (s->sh_offset)), + max = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_size)); + rel < max; rel = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 (s->sh_entsize))) + switch (ELF64_R_TYPE (grub_le_to_cpu64 (rel->r_info))) + { + case R_IA64_PCREL21B: + cntt++; + break; + case R_IA64_LTOFF_FPTR22: + case R_IA64_LTOFF22X: + case R_IA64_LTOFF22: + cntg++; + break; + } + } + *tramp = cntt * sizeof (struct grub_ia64_trampoline); + *got = cntg * sizeof (grub_uint64_t); + + return GRUB_ERR_NONE; +} + diff --git a/grub-core/kern/ia64/efi/init.c b/grub-core/kern/ia64/efi/init.c new file mode 100644 index 000000000..f8de85398 --- /dev/null +++ b/grub-core/kern/ia64/efi/init.c @@ -0,0 +1,80 @@ +/* init.c - initialize an ia64-based EFI system */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_uint64_t divisor = 1; + +static grub_uint64_t +get_itc (void) +{ + grub_uint64_t ret; + asm volatile ("mov %0=ar.itc" : "=r" (ret)); + return ret; +} + +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + return get_itc () / divisor; +} + +void +grub_machine_init (void) +{ + grub_efi_event_t event; + grub_uint64_t before, after; + grub_efi_uintn_t idx; + grub_efi_init (); + + grub_efi_system_table->boot_services->create_event (GRUB_EFI_EVT_TIMER, + GRUB_EFI_TPL_CALLBACK, + 0, 0, &event); + + before = get_itc (); + grub_efi_system_table->boot_services->set_timer (event, GRUB_EFI_TIMER_RELATIVE, + 200000); + grub_efi_system_table->boot_services->wait_for_event (1, &event, &idx); + after = get_itc (); + grub_efi_system_table->boot_services->close_event (event); + divisor = (after - before + 5) / 20; + if (divisor == 0) + divisor = 800000; + grub_install_get_time_ms (grub_rtc_get_time_ms); +} + +void +grub_machine_fini (int flags) +{ + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/grub-core/kern/ia64/efi/startup.S b/grub-core/kern/ia64/efi/startup.S new file mode 100644 index 000000000..d75c6d7cc --- /dev/null +++ b/grub-core/kern/ia64/efi/startup.S @@ -0,0 +1,44 @@ +/* + * 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 . + */ +#include +#include +#include + + .text + .psr abi64 + .psr lsb + .lsb + + .global _start + .proc _start +_start: + alloc loc0=ar.pfs,2,4,0,0 + mov loc1=rp + addl loc2=@gprel(grub_efi_image_handle),gp + addl loc3=@gprel(grub_efi_system_table),gp + ;; + st8 [loc2]=in0 + st8 [loc3]=in1 + br.call.sptk.few rp=grub_main + ;; + mov ar.pfs=loc0 + mov rp=loc1 + ;; + br.ret.sptk.few rp + + .endp _start diff --git a/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c similarity index 62% rename from kern/ieee1275/cmain.c rename to grub-core/kern/ieee1275/cmain.c index c1185f82c..e74de3248 100644 --- a/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -20,10 +20,9 @@ #include #include #include -#include #include -int (*grub_ieee1275_entry_fn) (void *); +int (*grub_ieee1275_entry_fn) (void *) GRUB_IEEE1275_ENTRY_FN_ATTRIBUTE; grub_ieee1275_phandle_t grub_ieee1275_chosen; grub_ieee1275_ihandle_t grub_ieee1275_mmu; @@ -44,21 +43,23 @@ grub_ieee1275_set_flag (enum grub_ieee1275_flag flag) grub_ieee1275_flags |= (1 << flag); } -#define SF "SmartFirmware(tm)" -#define OHW "PPC Open Hack'Ware" - static void grub_ieee1275_find_options (void) { grub_ieee1275_phandle_t root; grub_ieee1275_phandle_t options; grub_ieee1275_phandle_t openprom; - grub_ieee1275_phandle_t bootrom; int rc; grub_uint32_t realmode = 0; - char tmp[32]; + char tmp[256]; int is_smartfirmware = 0; int is_olpc = 0; + int is_qemu = 0; + grub_ssize_t actual; + +#ifdef __sparc__ + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0); +#endif grub_ieee1275_finddevice ("/", &root); grub_ieee1275_finddevice ("/options", &options); @@ -71,7 +72,8 @@ grub_ieee1275_find_options (void) rc = grub_ieee1275_get_property (openprom, "CodeGen-copyright", tmp, sizeof (tmp), 0); - if (rc >= 0 && !grub_strncmp (tmp, SF, sizeof (SF) - 1)) + if (rc >= 0 && !grub_strncmp (tmp, "SmartFirmware(tm)", + sizeof ("SmartFirmware(tm)") - 1)) is_smartfirmware = 1; rc = grub_ieee1275_get_property (root, "architecture", @@ -79,6 +81,58 @@ grub_ieee1275_find_options (void) if (rc >= 0 && !grub_strcmp (tmp, "OLPC")) is_olpc = 1; + rc = grub_ieee1275_get_property (root, "model", + tmp, sizeof (tmp), 0); + if (rc >= 0 && (!grub_strcmp (tmp, "Emulated PC") + || !grub_strcmp (tmp, "IBM pSeries (emulated by qemu)"))) { + is_qemu = 1; + } + + if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + + /* Old Macs have no key repeat, newer ones have fully working one. + The ones inbetween when repeated key generates an escaoe sequence + only the escape is repeated. With this workaround however a fast + e.g. down arrow-ESC is perceived as down arrow-down arrow which is + also annoying but is less so than the original bug of exiting from + the current window on arrow repeat. To avoid unaffected users suffering + from this workaround match only exact models known to have this bug. + */ + if (rc >= 0 && grub_strcmp (tmp, "PowerBook3,3") == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_REPEAT); + + rc = grub_ieee1275_get_property (root, "compatible", + tmp, sizeof (tmp), &actual); + if (rc >= 0) + { + char *ptr; + + if (grub_strncmp (tmp, "sun4v", 5) == 0) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_RAW_DEVNAMES); + for (ptr = tmp; ptr - tmp < actual; ptr += grub_strlen (ptr) + 1) + { + if (grub_memcmp (ptr, "MacRISC", sizeof ("MacRISC") - 1) == 0 + && (ptr[sizeof ("MacRISC") - 1] == 0 + || grub_isdigit (ptr[sizeof ("MacRISC") - 1]))) + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_OFNET_SUFFIX); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_VIRT_TO_REAL_BROKEN); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN); + break; + } + } + +#if defined(__powerpc__) + if (grub_strncmp (tmp, "IBM,", 4) == 0) + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_VM); + } +#endif + } + if (is_smartfirmware) { /* Broken in all versions */ @@ -133,25 +187,21 @@ grub_ieee1275_find_options (void) */ grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); } - if (! grub_ieee1275_finddevice ("/rom/boot-rom", &bootrom)) + if (is_qemu) { - rc = grub_ieee1275_get_property (bootrom, "model", tmp, sizeof (tmp), 0); - if (rc >= 0 && !grub_strncmp (tmp, OHW, sizeof (OHW) - 1)) - { - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_BROKEN_OUTPUT); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_SET_COLORS); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM); - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_ANSI); - } + /* OpenFirmware hangs on qemu if one requests any memory below 1.5 MiB. */ + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM); + + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_HAS_CURSORONOFF); +#if defined(__powerpc__) + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_POWER_KVM); +#endif } } -#undef SF -#undef OHW - void grub_ieee1275_init (void) { diff --git a/kern/ieee1275/ieee1275.c b/grub-core/kern/ieee1275/ieee1275.c similarity index 69% rename from kern/ieee1275/ieee1275.c rename to grub-core/kern/ieee1275/ieee1275.c index 9e2919172..36ca2dbfc 100644 --- a/kern/ieee1275/ieee1275.c +++ b/grub-core/kern/ieee1275/ieee1275.c @@ -19,6 +19,7 @@ #include #include +#include #define IEEE1275_PHANDLE_INVALID ((grub_ieee1275_cell_t) -1) #define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_cell_t) 0) @@ -27,7 +28,7 @@ int -grub_ieee1275_finddevice (char *name, grub_ieee1275_phandle_t *phandlep) +grub_ieee1275_finddevice (const char *name, grub_ieee1275_phandle_t *phandlep) { struct find_device_args { @@ -232,7 +233,7 @@ grub_ieee1275_instance_to_path (grub_ieee1275_ihandle_t ihandle, } int -grub_ieee1275_write (grub_ieee1275_ihandle_t ihandle, void *buffer, +grub_ieee1275_write (grub_ieee1275_ihandle_t ihandle, const void *buffer, grub_size_t len, grub_ssize_t *actualp) { struct write_args @@ -305,7 +306,7 @@ grub_ieee1275_seek (grub_ieee1275_ihandle_t ihandle, grub_disk_addr_t pos, args.pos_lo = pos; #else args.pos_hi = (grub_ieee1275_cell_t) (pos >> (8 * GRUB_IEEE1275_CELL_SIZEOF)); - args.pos_lo = (grub_ieee1275_cell_t) + args.pos_lo = (grub_ieee1275_cell_t) (pos & ((1ULL << (8 * GRUB_IEEE1275_CELL_SIZEOF)) - 1)); #endif @@ -397,9 +398,6 @@ grub_ieee1275_interpret (const char *command, grub_ieee1275_cell_t *catch) } args; - if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CANNOT_INTERPRET)) - return -1; - INIT_IEEE1275_COMMON (&args.common, "interpret", 1, 1); args.command = (grub_ieee1275_cell_t) command; @@ -482,6 +480,91 @@ grub_ieee1275_close (grub_ieee1275_ihandle_t ihandle) return 0; } +int +grub_ieee1275_decode_unit4 (grub_ieee1275_ihandle_t ihandle, + void *addr, grub_size_t size, + grub_uint32_t *phy_lo, grub_uint32_t *phy_hi, + grub_uint32_t *lun_lo, grub_uint32_t *lun_hi) +{ + struct decode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 5); + args.method = (grub_ieee1275_cell_t) "decode-unit"; + args.ihandle = ihandle; + args.size = size; + args.addr = (grub_ieee1275_cell_t) addr; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "decode-unit failed\n"); + return -1; + } + + *phy_lo = args.tgt_l; + *phy_hi = args.tgt_h; + *lun_lo = args.lun_l; + *lun_hi = args.lun_h; + return 0; +} + +char * +grub_ieee1275_encode_uint4 (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t phy_lo, grub_uint32_t phy_hi, + grub_uint32_t lun_lo, grub_uint32_t lun_hi, + grub_size_t *size) +{ + char *addr; + struct encode_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt_h; + grub_ieee1275_cell_t tgt_l; + grub_ieee1275_cell_t lun_h; + grub_ieee1275_cell_t lun_l; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t addr; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 6, 3); + args.method = (grub_ieee1275_cell_t) "encode-unit"; + args.ihandle = ihandle; + + args.tgt_l = phy_lo; + args.tgt_h = phy_hi; + args.lun_l = lun_lo; + args.lun_h = lun_hi; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "encode-unit failed\n"); + return 0; + } + + addr = (void *)args.addr; + *size = args.size; + addr = grub_strdup ((char *)args.addr); + return addr; +} + int grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, grub_addr_t *result) @@ -507,6 +590,9 @@ grub_ieee1275_claim (grub_addr_t addr, grub_size_t size, unsigned int align, *result = args.base; if (args.base == IEEE1275_CELL_INVALID) return -1; + grub_dprintf ("mmap", "CLAIMED: 0x%" PRIxGRUB_IEEE1275_CELL_T " (%" + PRIuGRUB_IEEE1275_CELL_T " MiB) size: %" PRIuGRUB_SIZE " MiB\n", + args.base, args.base >> 20, ALIGN_UP (size, 1 << 20) >> 20); return 0; } @@ -532,7 +618,7 @@ grub_ieee1275_release (grub_addr_t addr, grub_size_t size) int grub_ieee1275_set_property (grub_ieee1275_phandle_t phandle, - const char *propname, void *buf, + const char *propname, const void *buf, grub_size_t size, grub_ssize_t *actual) { struct set_property_args @@ -607,3 +693,117 @@ grub_ieee1275_milliseconds (grub_uint32_t *msecs) *msecs = args.msecs; return 0; } + +int +grub_ieee1275_set_address (grub_ieee1275_ihandle_t ihandle, + grub_uint32_t target, grub_uint32_t lun) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t tgt; + grub_ieee1275_cell_t lun; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 1); + + /* + * IEEE 1275-1994 Standard for Boot (Initialization Configuration) + * Firmware: Core Requirements and Practices + * E.3.2.2 Bus-specific methods for bus nodes + * + * A package implementing the scsi-2 device type shall implement the + * following bus-specific method: + * + * set-address ( unit# target# -- ) + * Sets the SCSI target number (0x0..0xf) and unit number (0..7) to which + * subsequent commands apply. + */ + args.method = (grub_ieee1275_cell_t) "set-address"; + args.ihandle = ihandle; + args.tgt = target; + args.lun = lun; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return args.catch_result; +} + +int +grub_ieee1275_no_data_command (grub_ieee1275_ihandle_t ihandle, + const void *cmd_addr, grub_ssize_t *result) +{ + struct set_address + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t cmd_addr; + grub_ieee1275_cell_t error; + grub_ieee1275_cell_t catch_result; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + + /* + * IEEE 1275-1994 Standard for Boot (Initialization Configuration) + * Firmware: Core Requirements and Practices + * + * E.3.2.2 Bus-specific methods for bus nodes + * + * A package implementing the scsi-2 device type shall implement the + * following bus-specific method: + * + * no-data-command ( cmd-addr -- error? ) + * Executes a simple SCSI command, automatically retrying under + * certain conditions. cmd-addr is the address of a 6-byte command buffer + * containing an SCSI command that does not have a data transfer phase. + * Executes the command, retrying indefinitely with the same retry criteria + * as retry-command. + * + * error? is nonzero if an error occurred, zero otherwise. + * NOTE no-data-command is a convenience function. It provides + * no capabilities that are not present in retry-command, but for + * those commands that meet its restrictions, it is easier to use. + */ + args.method = (grub_ieee1275_cell_t) "no-data-command"; + args.ihandle = ihandle; + args.cmd_addr = (grub_ieee1275_cell_t) cmd_addr; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + if (result) + *result = args.error; + + return args.catch_result; +} + +int +grub_ieee1275_get_block_size (grub_ieee1275_ihandle_t ihandle) +{ + struct size_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t result; + grub_ieee1275_cell_t size; + } args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "block-size"; + args.ihandle = ihandle; + args.result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.result)) + return 0; + + return args.size; +} diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c new file mode 100644 index 000000000..a5586f85b --- /dev/null +++ b/grub-core/kern/ieee1275/init.c @@ -0,0 +1,1047 @@ +/* init.c -- Initialize GRUB on the newworld mac (PPC). */ +/* + * 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 . + */ + +#include /* offsetof() */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __sparc__ +#include +#endif +#include +#include +#include +#include +#include +#ifdef __i386__ +#include +#endif +#ifdef __sparc__ +#include +#endif +#if defined(__powerpc__) || defined(__i386__) +#include +#endif + +/* The maximum heap size we're going to claim at boot. Not used by sparc. */ +#ifdef __i386__ +#define HEAP_MAX_SIZE (unsigned long) (64 * 1024 * 1024) +#else /* __powerpc__ */ +#define HEAP_MAX_SIZE (unsigned long) (32 * 1024 * 1024) +#endif + +/* RMO max. address at 768 MB */ +#define RMO_ADDR_MAX (grub_uint64_t) (768 * 1024 * 1024) + +/* + * The amount of OF space we will not claim here so as to leave space for + * the loader and linux to service early allocations. + * + * In 2021, Daniel Axtens claims that we should leave at least 128MB to + * ensure we can load a stock kernel and initrd on a pseries guest with + * a 512MB real memory area under PowerVM. + */ +#define RUNTIME_MIN_SPACE (128UL * 1024 * 1024) + +extern char _start[]; +extern char _end[]; + +#ifdef __sparc__ +grub_addr_t grub_ieee1275_original_stack; +#endif + +/* Options vector5 properties. */ + +#define LPAR 0x80 +#define SPLPAR 0x40 +#define DYN_RCON_MEM 0x20 +#define LARGE_PAGES 0x10 +#define DONATE_DCPU_CLS 0x02 +#define PCI_EXP 0x01 +#define BYTE2 (LPAR | SPLPAR | DYN_RCON_MEM | LARGE_PAGES | DONATE_DCPU_CLS | PCI_EXP) + +#define CMOC 0x80 +#define EXT_CMO 0x40 +#define CMO (CMOC | EXT_CMO) + +#define ASSOC_REF 0x80 +#define AFFINITY 0x40 +#define NUMA 0x20 +#define ASSOCIATIVITY (ASSOC_REF | AFFINITY | NUMA) + +#define HOTPLUG_INTRPT 0x04 +#define HPT_RESIZE 0x01 +#define BIN_OPTS (HOTPLUG_INTRPT | HPT_RESIZE) + +#define MAX_CPU 256 + +#define PFO_HWRNG 0x80000000 +#define PFO_HW_COMP 0x40000000 +#define PFO_ENCRYPT 0x20000000 +#define PLATFORM_FACILITIES (PFO_HWRNG | PFO_HW_COMP | PFO_ENCRYPT) + +#define SUB_PROCESSORS 1 + +#define DY_MEM_V2 0x80 +#define DRC_INFO 0x40 +#define BYTE22 (DY_MEM_V2 | DRC_INFO) + +/* For ibm,arch-vec-5-platform-support. */ +#define XIVE_INDEX 0x17 +#define MMU_INDEX 0x18 +#define RADIX_GTSE_INDEX 0x1a +#define RADIX_ENABLED 0x40 +#define XIVE_ENABLED 0x40 +#define HASH_ENABLED 0x00 +#define MAX_SUPPORTED 0xC0 +#define RADIX_GTSE_ENABLED 0x40 + +void +grub_exit (void) +{ + grub_ieee1275_exit (); +} + +/* Translate an OF filesystem path (separated by backslashes), into a GRUB + path (separated by forward slashes). */ +static void +grub_translate_ieee1275_path (char *filepath) +{ + char *backslash; + + backslash = grub_strchr (filepath, '\\'); + while (backslash != 0) + { + *backslash = '/'; + backslash = grub_strchr (filepath, '\\'); + } +} + +void (*grub_ieee1275_net_config) (const char *dev, char **device, char **path, + char *bootpath); +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *bootpath; + char *filename; + char *type; + + bootpath = grub_ieee1275_get_boot_dev (); + if (! bootpath) + return; + + /* Transform an OF device path to a GRUB path. */ + + type = grub_ieee1275_get_device_type (bootpath); + if (type && grub_strcmp (type, "network") == 0) + { + char *dev, *canon; + char *ptr; + dev = grub_ieee1275_get_aliasdevname (bootpath); + canon = grub_ieee1275_canonicalise_devname (dev); + if (! canon) + return; + ptr = canon + grub_strlen (canon) - 1; + while (ptr > canon && (*ptr == ',' || *ptr == ':')) + ptr--; + ptr++; + *ptr = 0; + + if (grub_ieee1275_net_config) + grub_ieee1275_net_config (canon, device, path, bootpath); + grub_free (dev); + grub_free (canon); + } + else + *device = grub_ieee1275_encode_devname (bootpath); + grub_free (type); + + filename = grub_ieee1275_get_filename (bootpath); + if (filename) + { + char *lastslash = grub_strrchr (filename, '\\'); + + /* Truncate at last directory. */ + if (lastslash) + { + *lastslash = '\0'; + grub_translate_ieee1275_path (filename); + + *path = filename; + } + } + grub_free (bootpath); +} + +/* Claim some available memory in the first /memory node. */ +#ifdef __sparc__ +static void +grub_claim_heap (void) +{ + grub_mm_init_region ((void *) (grub_modules_get_end () + + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000); +} +#else +/* Helpers for mm on powerpc. */ + +/* ibm,kernel-dump data structures */ +struct kd_section +{ + grub_uint32_t flags; + grub_uint16_t src_datatype; +#define KD_SRC_DATATYPE_REAL_MODE_REGION 0x0011 + grub_uint16_t error_flags; + grub_uint64_t src_address; + grub_uint64_t num_bytes; + grub_uint64_t act_bytes; + grub_uint64_t dst_address; +} GRUB_PACKED; + +#define MAX_KD_SECTIONS 10 + +struct kernel_dump +{ + grub_uint32_t format; + grub_uint16_t num_sections; + grub_uint16_t status_flags; + grub_uint32_t offset_1st_section; + grub_uint32_t num_blocks; + grub_uint64_t start_block; + grub_uint64_t num_blocks_avail; + grub_uint32_t offet_path_string; + grub_uint32_t max_time_allowed; + struct kd_section kds[MAX_KD_SECTIONS]; /* offset_1st_section should point to kds[0] */ +} GRUB_PACKED; + +/* + * Determine if a kernel dump exists and if it does, then determine the highest + * address that grub can use for memory allocations. + * The caller must have initialized *highest to rmo_top. *highest will not + * be modified if no kernel dump is found. + */ +static void +check_kernel_dump (grub_uint64_t *highest) +{ + struct kernel_dump kernel_dump; + grub_ssize_t kernel_dump_size; + grub_ieee1275_phandle_t rtas; + struct kd_section *kds; + grub_size_t i; + + /* If there's a kernel-dump it must have at least one section */ + if (grub_ieee1275_finddevice ("/rtas", &rtas) || + grub_ieee1275_get_property (rtas, "ibm,kernel-dump", &kernel_dump, + sizeof (kernel_dump), &kernel_dump_size) || + kernel_dump_size <= (grub_ssize_t) offsetof (struct kernel_dump, kds[1])) + return; + + kernel_dump_size = grub_min (kernel_dump_size, (grub_ssize_t) sizeof (kernel_dump)); + + if (grub_be_to_cpu32 (kernel_dump.format) != 1) + { + grub_printf (_("Error: ibm,kernel-dump has an unexpected format version '%u'\n"), + grub_be_to_cpu32 (kernel_dump.format)); + return; + } + + if (grub_be_to_cpu16 (kernel_dump.num_sections) > MAX_KD_SECTIONS) + { + grub_printf (_("Error: Too many kernel dump sections: %d\n"), + grub_be_to_cpu32 (kernel_dump.num_sections)); + return; + } + + for (i = 0; i < grub_be_to_cpu16 (kernel_dump.num_sections); i++) + { + kds = (struct kd_section *) ((grub_addr_t) &kernel_dump + + grub_be_to_cpu32 (kernel_dump.offset_1st_section) + + i * sizeof (struct kd_section)); + /* sanity check the address is within the 'kernel_dump' struct */ + if ((grub_addr_t) kds > (grub_addr_t) &kernel_dump + kernel_dump_size + sizeof (*kds)) + { + grub_printf (_("Error: 'kds' address beyond last available section\n")); + return; + } + + if ((grub_be_to_cpu16 (kds->src_datatype) == KD_SRC_DATATYPE_REAL_MODE_REGION) && + (grub_be_to_cpu64 (kds->src_address) == 0)) + { + *highest = grub_min (*highest, grub_be_to_cpu64 (kds->num_bytes)); + break; + } + } + + return; +} + +/* + * How much memory does OF believe exists in total? + * + * This isn't necessarily the true total. It can be the total memory + * accessible in real mode for a pseries guest, for example. + */ +static grub_uint64_t rmo_top; + +static int +count_free (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Do not consider memory beyond 4GB */ + if (addr > 0xffffffffULL) + return 0; + + if (addr + len > 0xffffffffULL) + len = 0xffffffffULL - addr; + + *(grub_uint32_t *) data += len; + + return 0; +} + +int +grub_regions_claim (grub_uint64_t addr, grub_uint64_t len, + grub_memory_type_t type, void *data) +{ + struct regions_claim_request *rcr = data; + grub_uint64_t linux_rmo_save; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Do not consider memory beyond 4GB */ + if (addr > 0xffffffffULL) + return 0; + + if (addr + len > 0xffffffffULL) + len = 0xffffffffULL - addr; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM)) + { + if (addr + len <= 0x180000) + return 0; + + if (addr < 0x180000) + { + len = addr + len - 0x180000; + addr = 0x180000; + } + } + + /* In theory, firmware should already prevent this from happening by not + listing our own image in /memory/available. The check below is intended + as a safeguard in case that doesn't happen. However, it doesn't protect + us from corrupting our module area, which extends up to a + yet-undetermined region above _end. */ + if ((addr < (grub_addr_t) _end) && ((addr + len) > (grub_addr_t) _start)) + { + grub_printf ("Warning: attempt to claim over our own code!\n"); + len = 0; + } + + /* + * Linux likes to claim memory at min(RMO top, 768MB) and works down + * without reference to /memory/available. (See prom_init.c::alloc_down) + * + * If this block contains min(RMO top, 768MB), do not claim below that for + * at least a few MB (this is where RTAS, SML and potentially TCEs live). + * + * We also need to leave enough space for the DT in the RMA. (See + * prom_init.c::alloc_up) + * + * Finally, we also want to make sure that when grub loads the kernel, + * it isn't going to use up all the memory we're trying to reserve! So + * enforce our entire RUNTIME_MIN_SPACE here (no fadump): + * + * | Top of memory == upper_mem_limit -| + * | | + * | available | + * | | + * |---------- 768 MB ----------| + * | | + * | reserved | + * | | + * |--- 768 MB - runtime min space ---| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * In case fadump is used, we allow the following: + * + * |---------- Top of memory ----------| + * | | + * | unavailable | + * | (kernel dump area) | + * | | + * |--------- upper_mem_limit ---------| + * | | + * | available | + * | | + * |---------- 768 MB ----------| + * | | + * | reserved | + * | | + * |--- 768 MB - runtime min space ---| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * Edge cases: + * + * - Total memory less than RUNTIME_MIN_SPACE: only claim up to HEAP_MAX_SIZE. + * (enforced elsewhere) + * + * - Total memory between RUNTIME_MIN_SPACE and 768MB: + * + * |---------- Top of memory ----------| + * | | + * | reserved | + * | | + * |---- top - runtime min space ----| + * | | + * | available | + * | | + * |---------- 0 MB ----------| + * + * This by itself would not leave us with RUNTIME_MIN_SPACE of free bytes: if + * rmo_top < 768MB, we will almost certainly have FW claims in the reserved + * region. We try to address that elsewhere: grub_ieee1275_mm_add_region will + * not call us if the resulting free space would be less than RUNTIME_MIN_SPACE. + */ + linux_rmo_save = grub_min (RMO_ADDR_MAX, rmo_top) - RUNTIME_MIN_SPACE; + if (rmo_top > RUNTIME_MIN_SPACE) + { + if (rmo_top <= RMO_ADDR_MAX) + { + if (addr > linux_rmo_save) + { + grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n", + addr); + return 0; + } + else if (addr + len > linux_rmo_save) + { + grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, addr, rmo_top - RUNTIME_MIN_SPACE); + len = linux_rmo_save - addr; + } + } + else + { + grub_uint64_t upper_mem_limit = rmo_top; + grub_uint64_t orig_addr = addr; + + check_kernel_dump (&upper_mem_limit); + + grub_dprintf ("ieee1275", "upper_mem_limit is at %llx (%lld MiB)\n", + upper_mem_limit, upper_mem_limit >> 20); + + /* + * we order these cases to prefer higher addresses and avoid some + * splitting issues + * The following shows the order of variables: + * no kernel dump: linux_rmo_save < RMO_ADDR_MAX <= upper_mem_limit == rmo_top + * with kernel dump: liuxx_rmo_save < RMO_ADDR_MAX <= upper_mem_limit <= rmo_top + */ + if (addr < RMO_ADDR_MAX && (addr + len) > RMO_ADDR_MAX && upper_mem_limit >= RMO_ADDR_MAX) + { + grub_dprintf ("ieee1275", + "adjusting region for RUNTIME_MIN_SPACE: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, RMO_ADDR_MAX, addr + len); + len = (addr + len) - RMO_ADDR_MAX; + addr = RMO_ADDR_MAX; + + /* We must not exceed the upper_mem_limit (assuming it's >= RMO_ADDR_MAX) */ + if (addr + len > upper_mem_limit) + { + /* Take the bigger chunk from either below linux_rmo_save or above RMO_ADDR_MAX. */ + len = upper_mem_limit - addr; + if (orig_addr < linux_rmo_save && linux_rmo_save - orig_addr > len) + { + /* lower part is bigger */ + addr = orig_addr; + len = linux_rmo_save - addr; + } + + grub_dprintf ("ieee1275", "re-adjusted region to: (%llx -> %llx)\n", + addr, addr + len); + + if (len == 0) + return 0; + } + } + else if ((addr < linux_rmo_save) && ((addr + len) > linux_rmo_save)) + { + grub_dprintf ("ieee1275", "capping region: (%llx -> %llx) -> (%llx -> %llx)\n", + addr, addr + len, addr, linux_rmo_save); + len = linux_rmo_save - addr; + } + else if (addr >= linux_rmo_save && (addr + len) <= RMO_ADDR_MAX) + { + grub_dprintf ("ieee1275", "rejecting region in RUNTIME_MIN_SPACE reservation (%llx)\n", + addr); + return 0; + } + } + } + + /* Honor alignment restrictions on candidate addr */ + if (rcr->align) + { + grub_uint64_t align_addr = ALIGN_UP (addr, rcr->align); + grub_uint64_t d = align_addr - addr; + + if (d > len) + return 0; + + len -= d; + addr = align_addr; + } + + if (rcr->flags & GRUB_MM_ADD_REGION_CONSECUTIVE && len < rcr->total) + return 0; + + if (len > rcr->total) + len = rcr->total; + + if (len) + { + grub_err_t err; + /* Claim and use it. */ + err = grub_claimmap (addr, len); + if (err) + return err; + if (rcr->init_region) + grub_mm_init_region ((void *) (grub_addr_t) addr, len); + rcr->total -= len; + + rcr->addr = addr; + } + + *(grub_uint32_t *) data = rcr->total; + + if (rcr->total == 0) + return 1; + + return 0; +} + +static int +heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_NONE, + .total = *(grub_uint32_t *) data, + .init_region = true, + }; + int ret; + + ret = grub_regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; +} + +static int +region_claim (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type, + void *data) +{ + struct regions_claim_request rcr = { + .flags = GRUB_MM_ADD_REGION_CONSECUTIVE, + .total = *(grub_uint32_t *) data, + .init_region = true, + }; + int ret; + + ret = grub_regions_claim (addr, len, type, &rcr); + + *(grub_uint32_t *) data = rcr.total; + + return ret; +} + +static grub_err_t +grub_ieee1275_mm_add_region (grub_size_t size, unsigned int flags) +{ + grub_uint32_t free_memory = 0; + grub_uint32_t avail = 0; + grub_uint32_t total; + + grub_dprintf ("ieee1275", "mm requested region of size %x, flags %x\n", + size, flags); + + /* + * Update free memory each time, which is a bit inefficient but guards us + * against a situation where some OF driver goes out to firmware for + * memory and we don't realise. + */ + grub_machine_mmap_iterate (count_free, &free_memory); + + /* Ensure we leave enough space to boot. */ + if (free_memory <= RUNTIME_MIN_SPACE + size) + { + grub_dprintf ("ieee1275", "Cannot satisfy allocation and retain minimum runtime space\n"); + return GRUB_ERR_OUT_OF_MEMORY; + } + + if (free_memory > RUNTIME_MIN_SPACE) + avail = free_memory - RUNTIME_MIN_SPACE; + + grub_dprintf ("ieee1275", "free = 0x%x available = 0x%x\n", free_memory, avail); + + if (flags & GRUB_MM_ADD_REGION_CONSECUTIVE) + { + /* first try rounding up hard for the sake of speed */ + total = grub_max (ALIGN_UP (size, 1024 * 1024) + 1024 * 1024, 32 * 1024 * 1024); + total = grub_min (avail, total); + + grub_dprintf ("ieee1275", "looking for %x bytes of memory (%x requested)\n", total, size); + + grub_machine_mmap_iterate (region_claim, &total); + grub_dprintf ("ieee1275", "get memory from fw %s\n", total == 0 ? "succeeded" : "failed"); + + if (total != 0) + { + total = grub_min (avail, size); + + grub_dprintf ("ieee1275", "fallback for %x bytes of memory (%x requested)\n", total, size); + + grub_machine_mmap_iterate (region_claim, &total); + grub_dprintf ("ieee1275", "fallback from fw %s\n", total == 0 ? "succeeded" : "failed"); + } + } + else + { + /* provide padding for a grub_mm_header_t and region */ + total = grub_min (avail, size); + grub_machine_mmap_iterate (heap_init, &total); + grub_dprintf ("ieee1275", "get noncontig memory from fw %s\n", total == 0 ? "succeeded" : "failed"); + } + + if (total == 0) + return GRUB_ERR_NONE; + else + return GRUB_ERR_OUT_OF_MEMORY; +} + +/* + * How much memory does OF believe it has? (regardless of whether + * it's accessible or not) + */ +static grub_err_t +grub_ieee1275_total_mem (grub_uint64_t *total) +{ + grub_ieee1275_phandle_t root; + grub_ieee1275_phandle_t memory; + grub_uint32_t reg[4]; + grub_ssize_t reg_size; + grub_uint32_t address_cells = 1; + grub_uint32_t size_cells = 1; + grub_uint64_t size; + + /* If we fail to get to the end, report 0. */ + *total = 0; + + /* Determine the format of each entry in `reg'. */ + if (grub_ieee1275_finddevice ("/", &root)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find / node"); + if (grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells, + sizeof (address_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #address-cells"); + if (grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells, + sizeof (size_cells), 0)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine #size-cells"); + + if (size_cells > address_cells) + address_cells = size_cells; + + /* Load `/memory/reg'. */ + if (grub_ieee1275_finddevice ("/memory", &memory)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't find /memory node"); + if (grub_ieee1275_get_integer_property (memory, "reg", reg, + sizeof (reg), ®_size)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /memory/reg property"); + if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "/memory response buffer exceeded"); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS)) + { + address_cells = 1; + size_cells = 1; + } + + /* Decode only the size */ + size = reg[address_cells]; + if (size_cells == 2) + size = (size << 32) | reg[address_cells + 1]; + + *total = size; + + return grub_errno; +} + +#if defined(__powerpc__) + +/* See PAPR or arch/powerpc/kernel/prom_init.c */ +struct option_vector2 +{ + grub_uint8_t byte1; + grub_uint16_t reserved; + grub_uint32_t real_base; + grub_uint32_t real_size; + grub_uint32_t virt_base; + grub_uint32_t virt_size; + grub_uint32_t load_base; + grub_uint32_t min_rma; + grub_uint32_t min_load; + grub_uint8_t min_rma_percent; + grub_uint8_t max_pft_size; +} GRUB_PACKED; + +struct option_vector5 +{ + grub_uint8_t byte1; + grub_uint8_t byte2; + grub_uint8_t byte3; + grub_uint8_t cmo; + grub_uint8_t associativity; + grub_uint8_t bin_opts; + grub_uint8_t micro_checkpoint; + grub_uint8_t reserved0; + grub_uint32_t max_cpus; + grub_uint16_t base_papr; + grub_uint16_t mem_reference; + grub_uint32_t platform_facilities; + grub_uint8_t sub_processors; + grub_uint8_t byte22; + grub_uint8_t xive; + grub_uint8_t mmu; + grub_uint8_t hpt_ext; + grub_uint8_t radix_gtse; +} GRUB_PACKED; + +struct pvr_entry +{ + grub_uint32_t mask; + grub_uint32_t entry; +}; + +struct cas_vector +{ + struct + { + struct pvr_entry terminal; + } pvr_list; + grub_uint8_t num_vecs; + grub_uint8_t vec1_size; + grub_uint8_t vec1; + grub_uint8_t vec2_size; + struct option_vector2 vec2; + grub_uint8_t vec3_size; + grub_uint16_t vec3; + grub_uint8_t vec4_size; + grub_uint16_t vec4; + grub_uint8_t vec5_size; + struct option_vector5 vec5; +} GRUB_PACKED; + +/* + * Call ibm,client-architecture-support to try to get more RMA. + * We ask for 768MB which should be enough to verify a distro kernel. + * We ignore most errors: if we don't succeed we'll proceed with whatever + * memory we have. + */ +static void +grub_ieee1275_ibm_cas (void) +{ + int rc; + grub_ieee1275_ihandle_t root; + grub_uint8_t ibm_arch_platform_support[8]; + grub_ssize_t actual; + grub_uint8_t xive_support = 0; + grub_uint8_t mmu_support = 0; + grub_uint8_t radix_gtse_support = 0; + int i = 0; + int prop_len = 8; + struct cas_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_ihandle_t ihandle; + grub_ieee1275_cell_t cas_addr; + grub_ieee1275_cell_t result; + } args; + + grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,arch-vec-5-platform-support", + (grub_uint32_t *) ibm_arch_platform_support, + sizeof (ibm_arch_platform_support), + &actual); + + for (i = 0; i < prop_len; i++) + { + switch (ibm_arch_platform_support[i]) + { + case XIVE_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + xive_support = XIVE_ENABLED; + else + xive_support = 0; + break; + + case MMU_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + mmu_support = RADIX_ENABLED; + else + mmu_support = HASH_ENABLED; + break; + + case RADIX_GTSE_INDEX: + if (mmu_support == RADIX_ENABLED) + radix_gtse_support = ibm_arch_platform_support[i + 1] & RADIX_GTSE_ENABLED; + else + radix_gtse_support = 0; + break; + + default: + /* Ignoring the other indexes of ibm,arch-vec-5-platform-support. */ + break; + } + /* Skipping the property value. */ + i++; + } + + struct cas_vector vector = + { + .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ + .num_vecs = 5 - 1, + .vec1_size = 0, + .vec1 = 0x80, /* ignore */ + .vec2_size = 1 + sizeof (struct option_vector2) - 2, + .vec2 = { + 0, 0, -1, -1, -1, -1, -1, 768, -1, 0, 48 + }, + .vec3_size = 2 - 1, + .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ + .vec4_size = 2 - 1, + .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ + .vec5_size = 1 + sizeof (struct option_vector5) - 2, + .vec5 = { + 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22, xive_support, mmu_support, 0, radix_gtse_support + } + }; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2); + args.method = (grub_ieee1275_cell_t) "ibm,client-architecture-support"; + rc = grub_ieee1275_open ("/", &root); + if (rc) + { + grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS"); + return; + } + args.ihandle = root; + args.cas_addr = (grub_ieee1275_cell_t) &vector; + + grub_printf ("Calling ibm,client-architecture-support from grub..."); + IEEE1275_CALL_ENTRY_FN (&args); + grub_printf ("done\n"); + + grub_ieee1275_close (root); +} + +#endif /* __powerpc__ */ + +static void +grub_claim_heap (void) +{ + grub_err_t err; + grub_uint32_t total = HEAP_MAX_SIZE; +#if defined(__powerpc__) + grub_uint32_t ibm_ca_support_reboot = 0; + grub_ssize_t actual; +#endif + + err = grub_ieee1275_total_mem (&rmo_top); + + /* + * If we cannot size the available memory, we can't be sure we're leaving + * space for the kernel, initrd and things Linux loads early in boot. So only + * allow further allocations from firmware on success + */ + if (err == GRUB_ERR_NONE) + grub_mm_add_region_fn = grub_ieee1275_mm_add_region; + +#if defined(__powerpc__) + /* Check if it's a CAS reboot with below property. If so, we will skip CAS call. */ + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,client-architecture-support-reboot", + &ibm_ca_support_reboot, + sizeof (ibm_ca_support_reboot), + &actual) >= 0) + grub_dprintf ("ieee1275", "ibm,client-architecture-support-reboot: %" PRIuGRUB_UINT32_T "\n", + ibm_ca_support_reboot); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) + { + /* + * If we have an error don't call CAS. Just hope for the best. + * Along with the above, if the rmo_top is 512 MB or above. We + * will skip the CAS call. However, if we call CAS, the rmo_top + * will be set to 768 MB via CAS Vector2. But we need to call + * CAS with rmo_top < 512 MB to avoid the issue on the older + * Linux kernel, which still uses rmo_top as 512 MB. If we call + * CAS with a condition rmo_top < 768 MB, it will result in an + * issue due to the IBM CAS reboot feature and we won't be able + * to boot the newer kernel. Whenever a reboot is detected as + * the CAS reboot by GRUB it will boot the machine with the + * last booted kernel by reading the variable boot-last-label + * which has the info related to the last boot and it's specific + * to IBM PowerPC. Due to this, the machine will boot with the + * last booted kernel which has rmo_top as 512 MB. Also, if the + * reboot is detected as a CAS reboot, the GRUB will skip the CAS + * call. As the CAS has already been called earlier, so it is + * not required to call CAS even if the other conditions are met. + * This condition will also prevent a scenario where the machine + * get stuck in the CAS reboot loop while booting a machine with + * an older kernel, having option_vector2 MIN_RMA as 512 MB in + * Linux prom_init.c and GRUB uses rmo_top < 768 MB condition + * for calling CAS. Due to their respective conditions, Linux + * CAS and GRUB CAS will keep doing the CAS calls and change + * the MIN_RMA from 768, changed by GRUB, to 512, changed by Linux, + * to 768, changed by GRUB, to 512, changed by Linux, and so on. + * The machine will stuck in this CAS reboot loop forever. + * + * IBM PAPR: https://openpower.foundation/specifications/linuxonpower/ + */ + if (!ibm_ca_support_reboot && err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) + grub_ieee1275_ibm_cas (); + } +#endif + + grub_machine_mmap_iterate (heap_init, &total); +} +#endif + +static void +grub_parse_cmdline (void) +{ + grub_ssize_t actual; + char args[256]; + + if (grub_ieee1275_get_property (grub_ieee1275_chosen, "bootargs", &args, + sizeof args, &actual) == 0 + && actual > 1) + { + int i = 0; + + while (i < actual) + { + char *command = &args[i]; + char *end; + char *val; + + end = grub_strchr (command, ';'); + if (end == 0) + i = actual; /* No more commands after this one. */ + else + { + *end = '\0'; + i += end - command + 1; + while (grub_isspace(args[i])) + i++; + } + + /* Process command. */ + val = grub_strchr (command, '='); + if (val) + { + *val = '\0'; + grub_env_set (command, val + 1); + } + } + } +} + +grub_addr_t grub_modbase; + +void +grub_machine_init (void) +{ + grub_modbase = ALIGN_UP((grub_addr_t) _end + + GRUB_KERNEL_MACHINE_MOD_GAP, + GRUB_KERNEL_MACHINE_MOD_ALIGN); + grub_ieee1275_init (); + + grub_console_init_early (); + grub_claim_heap (); + grub_console_init_lately (); +#ifdef __sparc__ + grub_obdisk_init (); +#else + grub_ofdisk_init (); +#endif + grub_parse_cmdline (); + +#ifdef __i386__ + grub_tsc_init (); +#else + grub_install_get_time_ms (grub_rtc_get_time_ms); +#endif +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + { +#ifdef __sparc__ + grub_obdisk_fini (); +#else + grub_ofdisk_fini (); +#endif + grub_console_fini (); + } +} + +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + grub_uint32_t msecs = 0; + + grub_ieee1275_milliseconds (&msecs); + + return msecs; +} diff --git a/kern/ieee1275/mmap.c b/grub-core/kern/ieee1275/mmap.c similarity index 81% rename from kern/ieee1275/mmap.c rename to grub-core/kern/ieee1275/mmap.c index 6f0652770..bf325eadf 100644 --- a/kern/ieee1275/mmap.c +++ b/grub-core/kern/ieee1275/mmap.c @@ -16,16 +16,16 @@ * along with GRUB. If not, see . */ -#include +#include #include #include grub_err_t -grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uint64_t, grub_uint32_t)) +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) { grub_ieee1275_phandle_t root; grub_ieee1275_phandle_t memory; - grub_uint32_t available[32]; + grub_uint32_t available[128]; grub_ssize_t available_size; grub_uint32_t address_cells = 1; grub_uint32_t size_cells = 1; @@ -49,6 +49,15 @@ grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uin sizeof available, &available_size)) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "couldn't examine /memory/available property"); + if (available_size < 0 || (grub_size_t) available_size > sizeof (available)) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "/memory response buffer exceeded"); + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS)) + { + address_cells = 1; + size_cells = 1; + } /* Decode each entry and call `hook'. */ i = 0; @@ -66,7 +75,7 @@ grub_machine_mmap_iterate (int NESTED_FUNC_ATTR (*hook) (grub_uint64_t, grub_uin if (size_cells == 2) size = (size << 32) | available[i++]; - if (hook (address, size, GRUB_MACHINE_MEMORY_AVAILABLE)) + if (hook (address, size, GRUB_MEMORY_AVAILABLE, hook_data)) break; } diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c new file mode 100644 index 000000000..11b2beb2f --- /dev/null +++ b/grub-core/kern/ieee1275/openfw.c @@ -0,0 +1,593 @@ +/* openfw.c -- Open firmware support functions. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +enum grub_ieee1275_parse_type +{ + GRUB_PARSE_FILENAME, + GRUB_PARSE_PARTITION, + GRUB_PARSE_DEVICE, + GRUB_PARSE_DEVICE_TYPE +}; + +static int +fill_alias (struct grub_ieee1275_devalias *alias) +{ + grub_ssize_t actual; + + if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type, + IEEE1275_MAX_PROP_LEN, &actual)) + alias->type[0] = 0; + + if (alias->parent_dev == alias->phandle) + return 0; + + if (grub_ieee1275_package_to_path (alias->phandle, alias->path, + IEEE1275_MAX_PATH_LEN, &actual)) + return 0; + + if (grub_strcmp (alias->parent_path, alias->path) == 0) + return 0; + + if (grub_ieee1275_get_property (alias->phandle, "name", alias->name, + IEEE1275_MAX_PROP_LEN, &actual)) + return 0; + grub_dprintf ("devalias", "device path=%s\n", alias->path); + return 1; +} + +void +grub_ieee1275_devalias_free (struct grub_ieee1275_devalias *alias) +{ + grub_free (alias->name); + grub_free (alias->type); + grub_free (alias->path); + grub_free (alias->parent_path); + alias->name = 0; + alias->type = 0; + alias->path = 0; + alias->parent_path = 0; + alias->phandle = GRUB_IEEE1275_PHANDLE_INVALID; +} + +void +grub_ieee1275_children_peer (struct grub_ieee1275_devalias *alias) +{ + while (grub_ieee1275_peer (alias->phandle, &alias->phandle) != -1) + if (fill_alias (alias)) + return; + grub_ieee1275_devalias_free (alias); +} + +void +grub_ieee1275_children_first (const char *devpath, + struct grub_ieee1275_devalias *alias) +{ + grub_ieee1275_phandle_t dev; + + grub_dprintf ("devalias", "iterating children of %s\n", + devpath); + + alias->name = 0; + alias->path = 0; + alias->parent_path = 0; + alias->type = 0; + + if (grub_ieee1275_finddevice (devpath, &dev)) + return; + + if (grub_ieee1275_child (dev, &alias->phandle)) + return; + + alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN); + if (!alias->type) + return; + alias->path = grub_malloc (IEEE1275_MAX_PATH_LEN); + if (!alias->path) + { + grub_free (alias->type); + return; + } + alias->parent_path = grub_strdup (devpath); + if (!alias->parent_path) + { + grub_free (alias->path); + grub_free (alias->type); + return; + } + + alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN); + if (!alias->name) + { + grub_free (alias->path); + grub_free (alias->type); + grub_free (alias->parent_path); + return; + } + if (!fill_alias (alias)) + grub_ieee1275_children_peer (alias); +} + +static int +iterate_recursively (const char *path, + int (*hook) (struct grub_ieee1275_devalias *alias)) +{ + struct grub_ieee1275_devalias alias; + int ret = 0; + + FOR_IEEE1275_DEVCHILDREN(path, alias) + { + ret = hook (&alias); + if (ret) + break; + ret = iterate_recursively (alias.path, hook); + if (ret) + break; + } + grub_ieee1275_devalias_free (&alias); + return ret; +} + +int +grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias)) +{ + return iterate_recursively ("/", hook); +} + +void +grub_ieee1275_devalias_init_iterator (struct grub_ieee1275_devalias *alias) +{ + alias->name = 0; + alias->path = 0; + alias->parent_path = 0; + alias->type = 0; + + grub_dprintf ("devalias", "iterating aliases\n"); + + if (grub_ieee1275_finddevice ("/aliases", &alias->parent_dev)) + return; + + alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN); + if (!alias->name) + return; + + alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN); + if (!alias->type) + { + grub_free (alias->name); + alias->name = 0; + return; + } + + alias->name[0] = '\0'; +} + +int +grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias) +{ + if (!alias->name) + return 0; + while (1) + { + grub_ssize_t pathlen; + grub_ssize_t actual; + char *tmp; + + if (alias->path) + { + grub_free (alias->path); + alias->path = 0; + } + tmp = grub_strdup (alias->name); + if (grub_ieee1275_next_property (alias->parent_dev, tmp, + alias->name) <= 0) + { + grub_free (tmp); + grub_ieee1275_devalias_free (alias); + return 0; + } + grub_free (tmp); + + grub_dprintf ("devalias", "devalias name = %s\n", alias->name); + + grub_ieee1275_get_property_length (alias->parent_dev, alias->name, &pathlen); + + /* The property `name' is a special case we should skip. */ + if (grub_strcmp (alias->name, "name") == 0) + continue; + + /* Sun's OpenBoot often doesn't zero terminate the device alias + strings, so we will add a NULL byte at the end explicitly. */ + pathlen += 1; + + alias->path = grub_malloc (pathlen + 1); + if (! alias->path) + { + grub_ieee1275_devalias_free (alias); + return 0; + } + + if (grub_ieee1275_get_property (alias->parent_dev, alias->name, alias->path, + pathlen, &actual) || actual < 0) + { + grub_dprintf ("devalias", "get_property (%s) failed\n", alias->name); + grub_free (alias->path); + continue; + } + if (actual > pathlen) + actual = pathlen; + alias->path[actual] = '\0'; + alias->path[pathlen] = '\0'; + + if (grub_ieee1275_finddevice (alias->path, &alias->phandle)) + { + grub_dprintf ("devalias", "finddevice (%s) failed\n", alias->path); + grub_free (alias->path); + alias->path = 0; + continue; + } + + if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type, + IEEE1275_MAX_PROP_LEN, &actual)) + { + /* NAND device don't have device_type property. */ + alias->type[0] = 0; + } + return 1; + } +} + +/* Call the "map" method of /chosen/mmu. */ +int +grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size, + grub_uint32_t mode) +{ + struct map_args { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t mode; + grub_ieee1275_cell_t size; + grub_ieee1275_cell_t virt; +#ifdef __sparc__ + grub_ieee1275_cell_t phys_high; +#endif + grub_ieee1275_cell_t phys_low; + grub_ieee1275_cell_t catch_result; + } args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", +#ifdef __sparc__ + 7, +#else + 6, +#endif + 1); + args.method = (grub_ieee1275_cell_t) "map"; + args.ihandle = grub_ieee1275_mmu; +#ifdef __sparc__ + args.phys_high = 0; +#endif + args.phys_low = phys; + args.virt = virt; + args.size = size; + args.mode = mode; /* Format is WIMG0PP. */ + args.catch_result = (grub_ieee1275_cell_t) -1; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return -1; + + return args.catch_result; +} + +grub_err_t +grub_claimmap (grub_addr_t addr, grub_size_t size) +{ + if (grub_ieee1275_claim (addr, size, 0, 0)) + return -1; + + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE) + && grub_ieee1275_map (addr, addr, size, 0x00)) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "map failed: address 0x%llx, size 0x%llx\n", + (long long) addr, (long long) size); + grub_ieee1275_release (addr, size); + return grub_errno; + } + + return GRUB_ERR_NONE; +} + +/* Get the device arguments of the Open Firmware node name `path'. */ +static char * +grub_ieee1275_get_devargs (const char *path) +{ + char *colon = grub_strchr (path, ':'); + + if (! colon) + return 0; + + return grub_strdup (colon + 1); +} + +/* Get the device path of the Open Firmware node name `path'. */ +char * +grub_ieee1275_get_devname (const char *path) +{ + char *colon = grub_strchr (path, ':'); + int pathlen = grub_strlen (path); + struct grub_ieee1275_devalias curalias; + if (colon) + pathlen = (int)(colon - path); + + /* Try to find an alias for this device. */ + FOR_IEEE1275_DEVALIASES (curalias) + /* briQ firmware can change capitalization in /chosen/bootpath. */ + if (grub_strncasecmp (curalias.path, path, pathlen) == 0 + && curalias.path[pathlen] == 0) + { + char *newpath; + newpath = grub_strdup (curalias.name); + grub_ieee1275_devalias_free (&curalias); + return newpath; + } + + return grub_strndup (path, pathlen); +} + +static char * +grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype) +{ + char type[64]; /* XXX check size. */ + char *device = grub_ieee1275_get_devname (path); + char *ret = 0; + grub_ieee1275_phandle_t dev; + + /* We need to know what type of device it is in order to parse the full + file path properly. */ + if (grub_ieee1275_finddevice (device, &dev)) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device); + goto fail; + } + if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0)) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, + "device %s lacks a device_type property", device); + goto fail; + } + + switch (ptype) + { + case GRUB_PARSE_DEVICE: + ret = grub_strdup (device); + break; + case GRUB_PARSE_DEVICE_TYPE: + ret = grub_strdup (type); + break; + case GRUB_PARSE_FILENAME: + { + char *comma; + char *args; + + args = grub_ieee1275_get_devargs (path); + if (!args) + /* Shouldn't happen. */ + return 0; + + /* The syntax of the device arguments is defined in the CHRP and PReP + IEEE1275 bindings: "[partition][,[filename]]". */ + comma = grub_strchr (args, ','); + + if (comma) + { + char *filepath = comma + 1; + + /* Make sure filepath has leading backslash. */ + if (filepath[0] != '\\') + ret = grub_xasprintf ("\\%s", filepath); + else + ret = grub_strdup (filepath); + } + grub_free (args); + } + break; + case GRUB_PARSE_PARTITION: + { + char *comma; + char *args; + + if (grub_strcmp ("block", type) != 0) + goto unknown; + + args = grub_ieee1275_get_devargs (path); + if (!args) + /* Shouldn't happen. */ + return 0; + + comma = grub_strchr (args, ','); + if (!comma) + ret = grub_strdup (args); + else + ret = grub_strndup (args, (grub_size_t)(comma - args)); + /* Consistently provide numbered partitions to GRUB. + OpenBOOT traditionally uses alphabetical partition + specifiers. */ + if (ret[0] >= 'a' && ret[0] <= 'z') + ret[0] = '1' + (ret[0] - 'a'); + grub_free (args); + } + break; + default: + unknown: + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "unsupported type %s for device %s", type, device); + } + +fail: + grub_free (device); + return ret; +} + +char * +grub_ieee1275_get_device_type (const char *path) +{ + return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE_TYPE); +} + +char * +grub_ieee1275_get_aliasdevname (const char *path) +{ + return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE); +} + +char * +grub_ieee1275_get_filename (const char *path) +{ + return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME); +} + +/* Convert a device name from IEEE1275 syntax to GRUB syntax. */ +char * +grub_ieee1275_encode_devname (const char *path) +{ + char *device = grub_ieee1275_get_devname (path); + char *partition; + char *encoding; + char *optr; + const char *iptr; + + if (! device) + return 0; + + encoding = grub_malloc (sizeof ("ieee1275/") + 2 * grub_strlen (device) + + sizeof (",XXXXXXXXXXXX")); + if (!encoding) + { + grub_free (device); + return 0; + } + + partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION); + + optr = grub_stpcpy (encoding, "ieee1275/"); + for (iptr = device; *iptr; ) + { + if (*iptr == ',') + *optr++ ='\\'; + *optr++ = *iptr++; + } + if (partition && partition[0]) + { + unsigned int partno = grub_strtoul (partition, 0, 0); + + *optr++ = ','; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS)) + /* GRUB partition 1 is OF partition 0. */ + partno++; + + grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno); + } + else + *optr = '\0'; + + grub_free (partition); + grub_free (device); + + return encoding; +} + +/* Resolve aliases. */ +char * +grub_ieee1275_canonicalise_devname (const char *path) +{ + struct canon_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t path; + grub_ieee1275_cell_t buf; + grub_ieee1275_cell_t inlen; + grub_ieee1275_cell_t outlen; + } + args; + char *buf = NULL; + grub_size_t bufsize = 64; + int i; + + for (i = 0; i < 2; i++) + { + grub_free (buf); + + buf = grub_malloc (bufsize); + if (!buf) + return NULL; + + INIT_IEEE1275_COMMON (&args.common, "canon", 3, 1); + args.path = (grub_ieee1275_cell_t) path; + args.buf = (grub_ieee1275_cell_t) buf; + args.inlen = (grub_ieee1275_cell_t) (bufsize - 1); + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return 0; + if (args.outlen > bufsize - 1) + { + bufsize = args.outlen + 2; + continue; + } + return buf; + } + /* Shouldn't reach here. */ + grub_free (buf); + return NULL; +} + +char * +grub_ieee1275_get_boot_dev (void) +{ + char *bootpath; + grub_ssize_t bootpath_size; + + if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", + &bootpath_size) + || bootpath_size <= 0) + { + /* Should never happen. */ + grub_printf ("/chosen/bootpath property missing!\n"); + return NULL; + } + + bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); + if (! bootpath) + { + grub_print_error (); + return NULL; + } + grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath, + (grub_size_t) bootpath_size + 1, 0); + bootpath[bootpath_size] = '\0'; + + return bootpath; +} diff --git a/grub-core/kern/list.c b/grub-core/kern/list.c new file mode 100644 index 000000000..a256bb3f8 --- /dev/null +++ b/grub-core/kern/list.c @@ -0,0 +1,55 @@ +/* list.c - grub list function */ +/* + * 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 . + */ + +#include +#include +#include + +void * +grub_named_list_find (grub_named_list_t head, const char *name) +{ + grub_named_list_t item; + + FOR_LIST_ELEMENTS (item, head) + if (grub_strcmp (item->name, name) == 0) + return item; + + return NULL; +} + +void +grub_list_push (grub_list_t *head, grub_list_t item) +{ + item->prev = head; + if (*head) + (*head)->prev = &item->next; + item->next = *head; + *head = item; +} + +void +grub_list_remove (grub_list_t item) +{ + if (item->prev) + *item->prev = item->next; + if (item->next) + item->next->prev = item->prev; + item->next = 0; + item->prev = 0; +} diff --git a/grub-core/kern/lockdown.c b/grub-core/kern/lockdown.c new file mode 100644 index 000000000..af6d493cd --- /dev/null +++ b/grub-core/kern/lockdown.c @@ -0,0 +1,85 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 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 . + * + */ + +#include +#include +#include +#include +#include + +static int lockdown = GRUB_LOCKDOWN_DISABLED; + +static grub_err_t +lockdown_verifier_init (grub_file_t io __attribute__ ((unused)), + enum grub_file_type type, + void **context __attribute__ ((unused)), + enum grub_verify_flags *flags) +{ + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_GRUB_MODULE: + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: + case GRUB_FILE_TYPE_XEN_HYPERVISOR: + case GRUB_FILE_TYPE_BSD_KERNEL: + case GRUB_FILE_TYPE_XNU_KERNEL: + case GRUB_FILE_TYPE_PLAN9_KERNEL: + case GRUB_FILE_TYPE_NTLDR: + case GRUB_FILE_TYPE_TRUECRYPT: + case GRUB_FILE_TYPE_FREEDOS: + case GRUB_FILE_TYPE_PXECHAINLOADER: + case GRUB_FILE_TYPE_PCCHAINLOADER: + case GRUB_FILE_TYPE_COREBOOT_CHAINLOADER: + case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE: + case GRUB_FILE_TYPE_ACPI_TABLE: + case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: + case GRUB_FILE_TYPE_FONT: + *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; + + /* Fall through. */ + + default: + return GRUB_ERR_NONE; + } +} + +struct grub_file_verifier lockdown_verifier = + { + .name = "lockdown_verifier", + .init = lockdown_verifier_init, + }; + +void +grub_lockdown (void) +{ + lockdown = GRUB_LOCKDOWN_ENABLED; + + grub_verifier_register (&lockdown_verifier); + + grub_env_set ("lockdown", "y"); + grub_env_export ("lockdown"); +} + +int +grub_is_lockdown (void) +{ + return lockdown; +} diff --git a/grub-core/kern/loongarch64/cache.c b/grub-core/kern/loongarch64/cache.c new file mode 100644 index 000000000..ff834dca4 --- /dev/null +++ b/grub-core/kern/loongarch64/cache.c @@ -0,0 +1,39 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 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 . + */ + +#include +#include + +/* Prototypes for asm functions. */ +void grub_arch_clean_dcache_range (void); +void grub_arch_invalidate_icache_range (void); + +void +grub_arch_sync_caches (void *address __attribute__((unused)), + grub_size_t len __attribute__((unused))) +{ + grub_arch_clean_dcache_range (); + grub_arch_invalidate_icache_range (); +} + +void +grub_arch_sync_dma_caches (volatile void *address __attribute__((unused)), + grub_size_t len __attribute__((unused))) +{ + /* DMA non-coherent devices not supported yet */ +} diff --git a/kern/i386/misc.S b/grub-core/kern/loongarch64/cache_flush.S similarity index 71% rename from kern/i386/misc.S rename to grub-core/kern/loongarch64/cache_flush.S index 7d57df9b9..a587ed58f 100644 --- a/kern/i386/misc.S +++ b/grub-core/kern/loongarch64/cache_flush.S @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2023 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 @@ -18,12 +18,16 @@ #include + .file "cache_flush.S" .text /* - * This call is special... it never returns... in fact it should simply - * hang at this point! + * No further work to do because cache consistency is maintained by hardware on + * LoongArch. */ -FUNCTION(grub_stop) - cli -1: hlt - jmp 1b +FUNCTION(grub_arch_clean_dcache_range) + dbar 0 + jr $ra + +FUNCTION(grub_arch_invalidate_icache_range) + ibar 0 + jr $ra diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c new file mode 100644 index 000000000..7f923b415 --- /dev/null +++ b/grub-core/kern/loongarch64/dl.c @@ -0,0 +1,150 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_LOONGARCH) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* + * Unified function for both REL and RELA. + */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + struct grub_loongarch64_stack stack; + grub_loongarch64_stack_init (&stack); + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Sym *sym; + void *place; + grub_uint64_t sym_addr; + + if (rel->r_offset >= seg->size) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is outside the segment"); + + sym = (Elf_Sym *) ((char*) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + sym_addr = sym->st_value; + if (s->sh_type == SHT_RELA) + sym_addr += ((Elf_Rela *) rel)->r_addend; + + place = (void *) ((grub_addr_t) seg->addr + rel->r_offset); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_LARCH_64: + { + grub_uint64_t *abs_place = place; + + grub_dprintf ("dl", "reloc_abs64 %p => 0x%016llx, %p\n", + place, (unsigned long long) sym_addr, abs_place); + + *abs_place += (grub_uint64_t) sym_addr; + } + break; + case R_LARCH_MARK_LA: + break; + case R_LARCH_SOP_PUSH_PCREL: + case R_LARCH_SOP_PUSH_PLT_PCREL: + grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place); + break; + case R_LARCH_B26: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + + grub_loongarch64_b26 (abs_place, off); + } + break; + case R_LARCH_ABS_HI20: + { + grub_uint32_t *abs_place = place; + grub_loongarch64_xxx_hi20 (abs_place, sym_addr); + } + break; + case R_LARCH_ABS64_LO20: + { + grub_uint32_t *abs_place = place; + grub_loongarch64_abs64_lo20 (abs_place, sym_addr); + } + break; + case R_LARCH_ABS64_HI12: + { + grub_uint32_t *abs_place = place; + grub_loongarch64_abs64_hi12 (abs_place, sym_addr); + } + break; + case R_LARCH_PCALA_HI20: + { + grub_uint32_t *abs_place = place; + grub_int32_t off = (((sym_addr + 0x800) & ~0xfffULL) - ((grub_addr_t)place & ~0xfffULL)); + + grub_loongarch64_xxx_hi20 (abs_place, off); + } + break; + case R_LARCH_ABS_LO12: + case R_LARCH_PCALA_LO12: + { + grub_uint32_t *abs_place = place; + grub_loongarch64_xxx_lo12 (abs_place, sym_addr); + } + break; + GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr) + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + (grub_uint64_t) ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + break; + } + } + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/loongarch64/dl_helper.c b/grub-core/kern/loongarch64/dl_helper.c new file mode 100644 index 000000000..006e8500e --- /dev/null +++ b/grub-core/kern/loongarch64/dl_helper.c @@ -0,0 +1,285 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * LoongArch relocations documentation: + * https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#relocations + */ +static void grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x); +static grub_uint64_t grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack); + +void +grub_loongarch64_stack_init (grub_loongarch64_stack_t stack) +{ + stack->top = -1; + stack->count = LOONGARCH64_STACK_MAX; +} + +static void +grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x) +{ + if (stack->top == stack->count) + return; + stack->data[++stack->top] = x; +} + +static grub_uint64_t +grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack) +{ + if (stack->top == -1) + return 0; + return stack->data[stack->top--]; +} + +void +grub_loongarch64_sop_push (grub_loongarch64_stack_t stack, grub_int64_t offset) +{ + grub_loongarch64_stack_push (stack, offset); +} + +/* opr2 = pop (), opr1 = pop (), push (opr1 - opr2) */ +void +grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b; + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + grub_loongarch64_stack_push (stack, a - b); +} + +/* opr2 = pop (), opr1 = pop (), push (opr1 << opr2) */ +void +grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b; + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + grub_loongarch64_stack_push (stack, a << b); +} + +/* opr2 = pop (), opr1 = pop (), push (opr1 >> opr2) */ +void +grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b; + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + grub_loongarch64_stack_push (stack, a >> b); +} + +/* opr2 = pop (), opr1 = pop (), push (opr1 + opr2) */ +void +grub_loongarch64_sop_add (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b; + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + grub_loongarch64_stack_push (stack, a + b); +} + +/* opr2 = pop (), opr1 = pop (), push (opr1 & opr2) */ +void +grub_loongarch64_sop_and (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b; + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + grub_loongarch64_stack_push (stack, a & b); +} + +/* opr3 = pop (), opr2 = pop (), opr1 = pop (), push (opr1 ? opr2 : opr3) */ +void +grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack) +{ + grub_uint64_t a, b, c; + c = grub_loongarch64_stack_pop (stack); + b = grub_loongarch64_stack_pop (stack); + a = grub_loongarch64_stack_pop (stack); + + if (a) { + grub_loongarch64_stack_push (stack, b); + } else { + grub_loongarch64_stack_push (stack, c); + } +} + +/* opr1 = pop (), (*(uint32_t *) PC) [14 ... 10] = opr1 [4 ... 0] */ +void +grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place |= ((a & 0x1f) << 10); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */ +void +grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place = *place | ((a & 0xfff) << 10); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */ +void +grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place = (*place) | ((a & 0xfff) << 10); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [15 ... 0] */ +void +grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place = (*place) | ((a & 0xffff) << 10); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] */ +void +grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place = (*place) | (((a >> 2) & 0xffff) << 10); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [24 ... 5] = opr1 [19 ... 0] */ +void +grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack, grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place = (*place) | ((a & 0xfffff)<<5); +} + +/* opr1 = pop (), (*(uint32_t *) PC) [4 ... 0] = opr1 [22 ... 18] */ +void +grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + + *place =(*place) | (((a >> 2) & 0xffff) << 10); + *place =(*place) | ((a >> 18) & 0x1f); +} + +/* + * opr1 = pop () + * (*(uint32_t *) PC) [9 ... 0] = opr1 [27 ... 18], + * (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] + */ +void +grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack, + grub_uint64_t *place) +{ + grub_uint64_t a = grub_loongarch64_stack_pop (stack); + *place =(*place) | (((a >> 2) & 0xffff) << 10); + *place =(*place) | ((a >> 18) & 0x3ff); +} + +/* + * B26 relocation for the 28-bit PC-relative jump + * (*(uint32_t *) PC) [9 ... 0] = (S + A - PC) [27 ... 18] + * (*(uint32_t *) PC) [25 ... 10] = (S + A - PC) [17 ... 2] + */ +void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset) +{ + grub_uint32_t val; + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000); + + grub_dprintf ("dl", " reloc_b26 %p %c= 0x%" PRIxGRUB_INT64_T "\n", + place, offset > 0 ? '+' : '-', + offset < 0 ? -offset : offset); + + val = ((offset >> 18) & 0x3ff) | (((offset >> 2) & 0xffff) << 10); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +/* + * ABS_HI20/PCALA_HI20 relocations for 32/64-bit absolute address/PC-relative offset + * (*(uint32_t *) PC) [24 ... 5] = (S + A) [31 ... 12] + */ +void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f); + grub_uint32_t val; + + offset >>= 12; + val = ((offset & 0xfffff) << 5); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +/* + * ABS_LO12/PCALA_LO12 relocations for 32/64-bit absolute address + * (*(uint32_t *) PC) [21 ... 10] = (S + A) [11 ... 0] + */ +void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff); + + *place &= insmask; + *place |= grub_cpu_to_le32 (offset << 10) & ~insmask; +} + +/* + * ABS64_HI12 relocation for the 64-bit absolute address + * (*(uint32_t *) PC) [21 ... 10] = (S + A) [63 ... 52] + */ +void grub_loongarch64_abs64_hi12 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff); + grub_uint32_t val; + + offset >>= 52; + val = ((offset & 0xfff) << 10); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} + +/* + * ABS64_LO20 relocation for the 64-bit absolute address + * (*(uint32_t *) PC) [24 ... 5] = (S + A) [51 ... 32] + */ +void grub_loongarch64_abs64_lo20 (grub_uint32_t *place, grub_int64_t offset) +{ + const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f); + grub_uint32_t val; + + offset >>= 32; + val = ((offset & 0xfffff) << 5); + + *place &= insmask; + *place |= grub_cpu_to_le32 (val) & ~insmask; +} diff --git a/grub-core/kern/loongarch64/efi/init.c b/grub-core/kern/loongarch64/efi/init.c new file mode 100644 index 000000000..924b0e87d --- /dev/null +++ b/grub-core/kern/loongarch64/efi/init.c @@ -0,0 +1,77 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2023 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define EFI_TIMER_PERIOD_MILLISECONDS(ms) ((grub_uint64_t)(ms * 10000)) + +static grub_uint64_t tmr; +static grub_efi_event_t tmr_evt; + +static grub_uint64_t +grub_efi_get_time_ms (void) +{ + return tmr; +} + +static void +grub_loongson_increment_timer (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + tmr += 10; +} + +void +grub_machine_init (void) +{ + grub_efi_boot_services_t *b; + + grub_efi_init (); + + b = grub_efi_system_table->boot_services; + b->create_event (GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL, + GRUB_EFI_TPL_CALLBACK, grub_loongson_increment_timer, NULL, &tmr_evt); + b->set_timer (tmr_evt, GRUB_EFI_TIMER_PERIODIC, EFI_TIMER_PERIOD_MILLISECONDS(10)); + + grub_install_get_time_ms (grub_efi_get_time_ms); +} + +void +grub_machine_fini (int flags) +{ + grub_efi_boot_services_t *b; + + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + b = grub_efi_system_table->boot_services; + + b->set_timer (tmr_evt, GRUB_EFI_TIMER_CANCEL, 0); + b->close_event (tmr_evt); + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/include/grub/i386/coreboot/init.h b/grub-core/kern/loongarch64/efi/startup.S similarity index 69% rename from include/grub/i386/coreboot/init.h rename to grub-core/kern/loongarch64/efi/startup.S index e67007414..87cfb23ea 100644 --- a/include/grub/i386/coreboot/init.h +++ b/grub-core/kern/loongarch64/efi/startup.S @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007 Free Software Foundation, Inc. + * Copyright (C) 2023 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 @@ -16,13 +16,19 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_INIT_I386_LINUXBIOS_HEADER -#define GRUB_INIT_I386_LINUXBIOS_HEADER 1 - #include -#include -void EXPORT_FUNC(grub_stop) (void) __attribute__ ((noreturn)); -void EXPORT_FUNC(grub_stop_floppy) (void); + .file "startup.S" + .text -#endif +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in $a1/$a0. + */ + + la $a2, EXT_C(grub_efi_image_handle) + st.d $a0, $a2, 0 + la $a2, EXT_C(grub_efi_system_table) + st.d $a1, $a2, 0 + + b EXT_C(grub_main) diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c new file mode 100644 index 000000000..143a232b8 --- /dev/null +++ b/grub-core/kern/main.c @@ -0,0 +1,370 @@ +/* main.c - the kernel main routine */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2006,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_PCBIOS +#include +#endif + +static bool cli_disabled = false; +static bool cli_need_auth = false; + +grub_addr_t +grub_modules_get_end (void) +{ + struct grub_module_info *modinfo; + + modinfo = (struct grub_module_info *) grub_modbase; + + /* Check if there are any modules. */ + if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC) + return grub_modbase; + + return grub_modbase + modinfo->size; +} + +/* Load all modules in core. */ +static void +grub_load_modules (void) +{ + struct grub_module_header *header; + FOR_MODULES (header) + { + /* Not an ELF module, skip. */ + if (header->type != OBJ_TYPE_ELF) + continue; + + if (! grub_dl_load_core ((char *) header + sizeof (struct grub_module_header), + (header->size - sizeof (struct grub_module_header)))) + grub_fatal ("%s", grub_errmsg); + + if (grub_errno) + grub_print_error (); + } +} + +static char *load_config; + +static void +grub_load_config (void) +{ + struct grub_module_header *header; + FOR_MODULES (header) + { + /* Not an embedded config, skip. */ + if (header->type != OBJ_TYPE_CONFIG) + continue; + + load_config = grub_malloc (header->size - sizeof (struct grub_module_header) + 1); + if (!load_config) + { + grub_print_error (); + break; + } + grub_memcpy (load_config, (char *) header + + sizeof (struct grub_module_header), + header->size - sizeof (struct grub_module_header)); + load_config[header->size - sizeof (struct grub_module_header)] = 0; + break; + } +} + +/* Write hook for the environment variables of root. Remove surrounding + parentheses, if any. */ +static char * +grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + /* XXX Is it better to check the existence of the device? */ + grub_size_t len = grub_strlen (val); + + if (val[0] == '(' && val[len - 1] == ')') + return grub_strndup (val + 1, len - 2); + + return grub_strdup (val); +} + +static void +grub_set_prefix_and_root (void) +{ + char *device = NULL; + char *path = NULL; + char *fwdevice = NULL; + char *fwpath = NULL; + char *prefix = NULL; + struct grub_module_header *header; + + FOR_MODULES (header) + if (header->type == OBJ_TYPE_PREFIX) + prefix = (char *) header + sizeof (struct grub_module_header); + + grub_register_variable_hook ("root", 0, grub_env_write_root); + + grub_machine_get_bootlocation (&fwdevice, &fwpath); + + if (fwdevice) + { + char *cmdpath; + + cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : ""); + if (cmdpath) + { + grub_env_set ("cmdpath", cmdpath); + grub_env_export ("cmdpath"); + grub_free (cmdpath); + } + } + + if (prefix) + { + char *pptr = NULL; + if (prefix[0] == '(') + { + pptr = grub_strrchr (prefix, ')'); + if (pptr) + { + device = grub_strndup (prefix + 1, pptr - prefix - 1); + pptr++; + } + } + if (!pptr) + pptr = prefix; + if (pptr[0]) + path = grub_strdup (pptr); + } + + if (!device && fwdevice) + device = fwdevice; + else if (fwdevice && (device[0] == ',' || !device[0])) + { + /* We have a partition, but still need to fill in the drive. */ + char *comma, *new_device; + + for (comma = fwdevice; *comma; ) + { + if (comma[0] == '\\' && comma[1] == ',') + { + comma += 2; + continue; + } + if (*comma == ',') + break; + comma++; + } + if (*comma) + { + char *drive = grub_strndup (fwdevice, comma - fwdevice); + new_device = grub_xasprintf ("%s%s", drive, device); + grub_free (drive); + } + else + new_device = grub_xasprintf ("%s%s", fwdevice, device); + + grub_free (fwdevice); + grub_free (device); + device = new_device; + } + else + grub_free (fwdevice); + if (fwpath && !path) + { + grub_size_t len = grub_strlen (fwpath); + while (len > 1 && fwpath[len - 1] == '/') + fwpath[--len] = 0; + if (len >= sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1 + && grub_memcmp (fwpath + len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1), GRUB_TARGET_CPU "-" GRUB_PLATFORM, + sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1) == 0) + fwpath[len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1)] = 0; + path = fwpath; + } + else + grub_free (fwpath); + if (device) + { + char *prefix_set; + + prefix_set = grub_xasprintf ("(%s)%s", device, path ? : ""); + if (prefix_set) + { + grub_env_set ("prefix", prefix_set); + grub_free (prefix_set); + } + grub_env_set ("root", device); + } + + grub_free (device); + grub_free (path); + grub_print_error (); +} + +/* Load the normal mode module and execute the normal mode if possible. */ +static void +grub_load_normal_mode (void) +{ + /* Load the module. */ + grub_dl_load ("normal"); + + /* Print errors if any. */ + grub_print_error (); + grub_errno = 0; + + grub_command_execute ("normal", 0, 0); +} + +bool +grub_is_cli_disabled (void) +{ + return cli_disabled; +} + +bool +grub_is_cli_need_auth (void) +{ + return cli_need_auth; +} + +void grub_cli_set_auth_needed (void) +{ + cli_need_auth = true; +} + +static void +check_is_cli_disabled (void) +{ + struct grub_module_header *header; + header = 0; + + FOR_MODULES (header) + { + if (header->type == OBJ_TYPE_DISABLE_CLI) + { + cli_disabled = true; + return; + } + } +} + +static void +reclaim_module_space (void) +{ + grub_addr_t modstart, modend; + + if (!grub_modbase) + return; + +#ifdef GRUB_MACHINE_PCBIOS + modstart = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR; +#else + modstart = grub_modbase; +#endif + modend = grub_modules_get_end (); + grub_modbase = 0; + +#if GRUB_KERNEL_PRELOAD_SPACE_REUSABLE + grub_mm_init_region ((void *) modstart, modend - modstart); +#else + (void) modstart; + (void) modend; +#endif +} + +/* The main routine. */ +void __attribute__ ((noreturn)) +grub_main (void) +{ +#ifdef GRUB_STACK_PROTECTOR + /* + * This call should only be made from a function that does not return because + * functions that return will get instrumented to check that the stack cookie + * does not change and this call will change the stack cookie. Thus a stack + * guard failure will be triggered. + */ + grub_update_stack_guard (); +#endif + + /* First of all, initialize the machine. */ + grub_machine_init (); + + grub_boot_time ("After machine init."); + + /* This breaks flicker-free boot on EFI systems, so disable it there. */ +#ifndef GRUB_MACHINE_EFI + /* Hello. */ + grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); + grub_printf ("Welcome to GRUB!\n\n"); + grub_setcolorstate (GRUB_TERM_COLOR_STANDARD); +#endif + + /* Init verifiers API. */ + grub_verifiers_init (); + + grub_load_config (); + + grub_boot_time ("Before loading embedded modules."); + + /* Load pre-loaded modules and free the space. */ + grub_register_exported_symbols (); +#ifdef GRUB_LINKER_HAVE_INIT + grub_arch_dl_init_linker (); +#endif + grub_load_modules (); + + grub_boot_time ("After loading embedded modules."); + + /* Check if the CLI should be disabled */ + check_is_cli_disabled (); + + /* It is better to set the root device as soon as possible, + for convenience. */ + grub_set_prefix_and_root (); + grub_env_export ("root"); + grub_env_export ("prefix"); + + /* Reclaim space used for modules. */ + reclaim_module_space (); + + grub_boot_time ("After reclaiming module space."); + + grub_register_core_commands (); + + grub_boot_time ("Before execution of embedded config."); + + if (load_config) + grub_parser_execute (load_config); + + grub_boot_time ("After execution of embedded config. Attempt to go to normal mode"); + + grub_load_normal_mode (); + grub_rescue_run (); +} diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c new file mode 100644 index 000000000..2ed3ff319 --- /dev/null +++ b/grub-core/kern/mips/arc/init.c @@ -0,0 +1,463 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *type_names[] = { +#ifdef GRUB_CPU_WORDS_BIGENDIAN + NULL, +#endif + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "eisa", "tc", "scsi", "dti", "multi", "disk", "tape", "cdrom", "worm", + "serial", "net", "video", "par", "point", "key", "audio", "other", + "rdisk", "fdisk", "tape", "modem", "monitor", "print", "pointer", + "keyboard", "term", +#ifndef GRUB_CPU_WORDS_BIGENDIAN + "other", +#endif + "line", "network", NULL +}; + +static int +iterate_rec (const char *prefix, const struct grub_arc_component *parent, + grub_arc_iterate_devs_hook_t hook, void *hook_data, + int alt_names) +{ + const struct grub_arc_component *comp; + FOR_ARC_CHILDREN(comp, parent) + { + char *name; + const char *cname = NULL; + if (comp->type < ARRAY_SIZE (type_names)) + cname = type_names[comp->type]; + if (!cname) + cname = "unknown"; + if (alt_names) + name = grub_xasprintf ("%s/%s%lu", prefix, cname, comp->key); + else + name = grub_xasprintf ("%s%s(%lu)", prefix, cname, comp->key); + if (!name) + return 1; + if (hook (name, comp, hook_data)) + { + grub_free (name); + return 1; + } + if (iterate_rec ((parent ? name : prefix), comp, hook, hook_data, + alt_names)) + { + grub_free (name); + return 1; + } + grub_free (name); + } + return 0; +} + +int +grub_arc_iterate_devs (grub_arc_iterate_devs_hook_t hook, void *hook_data, + int alt_names) +{ + return iterate_rec ((alt_names ? "arc" : ""), NULL, hook, hook_data, + alt_names); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + struct grub_arc_memory_descriptor *cur = NULL; + while (1) + { + grub_memory_type_t type; + cur = GRUB_ARC_FIRMWARE_VECTOR->getmemorydescriptor (cur); + if (!cur) + return GRUB_ERR_NONE; + switch (cur->type) + { + case GRUB_ARC_MEMORY_EXCEPTION_BLOCK: + case GRUB_ARC_MEMORY_SYSTEM_PARAMETER_BLOCK: + case GRUB_ARC_MEMORY_FW_PERMANENT: + default: + type = GRUB_MEMORY_RESERVED; + break; + + case GRUB_ARC_MEMORY_FW_TEMPORARY: + case GRUB_ARC_MEMORY_FREE: + case GRUB_ARC_MEMORY_LOADED: + case GRUB_ARC_MEMORY_FREE_CONTIGUOUS: + type = GRUB_MEMORY_AVAILABLE; + break; + case GRUB_ARC_MEMORY_BADRAM: + type = GRUB_MEMORY_BADRAM; + break; + } + if (hook (((grub_uint64_t) cur->start_page) << 12, + ((grub_uint64_t) cur->num_pages) << 12, type, hook_data)) + return GRUB_ERR_NONE; + } +} + +char * +grub_arc_alt_name_to_norm (const char *name, const char *suffix) +{ + char *optr; + const char *iptr; + char * ret = grub_malloc (2 * grub_strlen (name) + grub_strlen (suffix)); + int state = 0; + + if (!ret) + return NULL; + optr = ret; + for (iptr = name + 4; *iptr; iptr++) + if (state == 0) + { + if (!grub_isdigit (*iptr)) + *optr++ = *iptr; + else + { + *optr++ = '('; + *optr++ = *iptr; + state = 1; + } + } + else + { + if (grub_isdigit (*iptr)) + *optr++ = *iptr; + else + { + *optr++ = ')'; + state = 0; + } + } + if (state) + *optr++ = ')'; + grub_strcpy (optr, suffix); + return ret; +} + +static char * +norm_name_to_alt (const char *name) +{ + char *optr; + const char *iptr; + int state = 0; + char * ret = grub_malloc (grub_strlen (name) + sizeof ("arc/")); + + if (!ret) + return NULL; + optr = grub_stpcpy (ret, "arc/"); + for (iptr = name; *iptr; iptr++) + { + if (state == 3) + { + *optr++ = '/'; + state = 0; + } + if (*iptr == '(') + { + state = 1; + continue; + } + if (*iptr == ')') + { + if (state == 1) + *optr++ = '0'; + state = 3; + continue; + } + *optr++ = *iptr; + if (state == 1) + state = 2; + } + *optr = '\0'; + return ret; +} + +extern grub_uint32_t grub_total_modules_size __attribute__ ((section(".text"))); +grub_addr_t grub_modbase; + +extern char _end[]; +static char boot_location[256]; + +void +grub_machine_init (void) +{ + struct grub_arc_memory_descriptor *cur = NULL; + grub_addr_t modend; + + grub_memcpy (boot_location, + (char *) (GRUB_DECOMPRESSOR_LINK_ADDR - 256), 256); + + grub_modbase = ALIGN_UP ((grub_addr_t) _end, GRUB_KERNEL_MACHINE_MOD_ALIGN); + modend = grub_modbase + grub_total_modules_size; + grub_console_init_early (); + + /* FIXME: measure this. */ + grub_arch_cpuclock = 150000000; + grub_install_get_time_ms (grub_rtc_get_time_ms); + + while (1) + { + grub_uint64_t start, end; + cur = GRUB_ARC_FIRMWARE_VECTOR->getmemorydescriptor (cur); + if (!cur) + break; + if (cur->type != GRUB_ARC_MEMORY_FREE + && cur->type != GRUB_ARC_MEMORY_LOADED + && cur->type != GRUB_ARC_MEMORY_FREE_CONTIGUOUS) + continue; + start = ((grub_uint64_t) cur->start_page) << 12; + end = ((grub_uint64_t) cur->num_pages) << 12; + end += start; + if ((grub_uint64_t) start < (modend & 0x1fffffff)) + start = (modend & 0x1fffffff); + if ((grub_uint64_t) end > 0x20000000) + end = 0x20000000; + if (end > start) + grub_mm_init_region ((void *) (grub_addr_t) (start | 0x80000000), + end - start); + } + + grub_console_init_lately (); + + grub_arcdisk_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +void +grub_halt (void) +{ + GRUB_ARC_FIRMWARE_VECTOR->powerdown (); + + grub_millisleep (1500); + + grub_puts_ (N_("Shutdown failed")); + grub_refresh (); + while (1); +} + +void +grub_exit (void) +{ + GRUB_ARC_FIRMWARE_VECTOR->exit (); + + grub_millisleep (1500); + + grub_puts_ (N_("Exit failed")); + grub_refresh (); + while (1); +} + +static char * +get_part (char *dev) +{ + char *ptr; + if (!*dev) + return 0; + ptr = dev + grub_strlen (dev) - 1; + if (ptr == dev || *ptr != ')') + return 0; + ptr--; + while (grub_isdigit (*ptr) && ptr > dev) + ptr--; + if (*ptr != '(' || ptr == dev) + return 0; + ptr--; + if (ptr - dev < (int) sizeof ("partition") - 2) + return 0; + ptr -= sizeof ("partition") - 2; + if (grub_memcmp (ptr, "partition", sizeof ("partition") - 1) != 0) + return 0; + return ptr; +} + +static grub_disk_addr_t +get_partition_offset (char *part, grub_disk_addr_t *en) +{ + grub_arc_fileno_t handle; + grub_disk_addr_t ret = -1; + struct grub_arc_fileinfo info; + grub_arc_err_t r; + + if (GRUB_ARC_FIRMWARE_VECTOR->open (part, GRUB_ARC_FILE_ACCESS_OPEN_RO, + &handle)) + return -1; + + r = GRUB_ARC_FIRMWARE_VECTOR->getfileinformation (handle, &info); + if (!r) + { + ret = (info.start >> 9); + *en = (info.end >> 9); + } + GRUB_ARC_FIRMWARE_VECTOR->close (handle); + return ret; +} + +struct get_device_name_ctx +{ + char *partition_name; + grub_disk_addr_t poff, pend; +}; + +static int +get_device_name_iter (grub_disk_t disk __attribute__ ((unused)), + const grub_partition_t part, void *data) +{ + struct get_device_name_ctx *ctx = data; + + if (grub_partition_get_start (part) == ctx->poff + && grub_partition_get_len (part) == ctx->pend) + { + ctx->partition_name = grub_partition_get_name (part); + return 1; + } + + return 0; +} + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *loaddev = boot_location; + char *pptr, *partptr; + char *dname; + grub_disk_addr_t poff = -1, pend = -1; + struct get_device_name_ctx ctx; + grub_disk_t parent = 0; + unsigned i; + + for (i = 0; i < ARRAY_SIZE (type_names); i++) + if (type_names[i] + && grub_memcmp (loaddev, type_names[i], grub_strlen (type_names[i])) == 0 + && loaddev[grub_strlen (type_names[i])] == '(') + break; + if (i == ARRAY_SIZE (type_names)) + pptr = loaddev; + else + for (pptr = loaddev; *pptr && *pptr != '/' && *pptr != '\\'; pptr++); + if (*pptr) + { + char *iptr, *optr; + char sep = *pptr; + *path = grub_malloc (grub_strlen (pptr) + 1); + if (!*path) + return; + for (iptr = pptr, optr = *path; *iptr; iptr++, optr++) + if (*iptr == sep) + *optr = '/'; + else + *optr = *iptr; + *optr = '\0'; + *path = grub_strdup (pptr); + *pptr = '\0'; + } + + if (*loaddev == '\0') + { + const char *syspart = 0; + + if (GRUB_ARC_SYSTEM_PARAMETER_BLOCK->firmware_vector_length + >= (unsigned) ((char *) (&GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable + 1) + - (char *) GRUB_ARC_FIRMWARE_VECTOR) + && GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable) + syspart = GRUB_ARC_FIRMWARE_VECTOR->getenvironmentvariable ("SystemPartition"); + if (!syspart) + return; + loaddev = grub_strdup (syspart); + } + + partptr = get_part (loaddev); + if (partptr) + { + poff = get_partition_offset (loaddev, &pend); + *partptr = '\0'; + } + dname = norm_name_to_alt (loaddev); + if (poff == (grub_addr_t) -1) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + parent = grub_disk_open (dname); + if (!parent) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + if (poff == 0 + && pend == grub_disk_native_sectors (parent)) + { + grub_disk_close (parent); + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + ctx.partition_name = NULL; + ctx.poff = poff; + ctx.pend = pend; + + grub_partition_iterate (parent, get_device_name_iter, &ctx); + grub_disk_close (parent); + + if (! ctx.partition_name) + { + *device = dname; + if (loaddev != boot_location) + grub_free (loaddev); + return; + } + + *device = grub_xasprintf ("%s,%s", dname, + ctx.partition_name); + grub_free (ctx.partition_name); + grub_free (dname); + if (loaddev != boot_location) + grub_free (loaddev); +} diff --git a/grub-core/kern/mips/cache.S b/grub-core/kern/mips/cache.S new file mode 100644 index 000000000..fa331eca1 --- /dev/null +++ b/grub-core/kern/mips/cache.S @@ -0,0 +1,70 @@ + +#include + + .set noreorder + .set nomacro + +FUNCTION (grub_arch_sync_caches) +#include "cache_flush.S" + j $ra + nop + +FUNCTION (grub_arch_sync_dma_caches) + move $t2, $a0 + addu $t3, $a0, $a1 + srl $t2, $t2, 5 + sll $t2, $t2, 5 + addu $t3, $t3, 0x1f + srl $t3, $t3, 5 + sll $t3, $t3, 5 + move $t0, $t2 + subu $t1, $t3, $t2 +1: + cache_op 1, 0($t0) +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 1, 1($t0) + cache_op 1, 2($t0) + cache_op 1, 3($t0) + + addiu $t1, $t1, -0x20 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x20 +#else + addiu $t1, $t1, -4 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 0, 0($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + cache_op 0, 0($t0) + addiu $t1, $t1, -4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 23, 0($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + cache_op 23, 0($t0) + addiu $t1, $t1, -0x4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op + + jr $ra + nop diff --git a/grub-core/kern/mips/cache_flush.S b/grub-core/kern/mips/cache_flush.S new file mode 100644 index 000000000..89961a0f7 --- /dev/null +++ b/grub-core/kern/mips/cache_flush.S @@ -0,0 +1,54 @@ +#ifndef CACHE_OP_DEFINED +#define CACHE_OP_DEFINED 1 + .macro cache_op op addr + .set mips3 + cache \op, \addr + .set mips1 + .endm + .macro sync_op + .set mips3 + sync + .set mips1 + .endm +#endif + + move $t2, $a0 + addu $t3, $a0, $a1 + srl $t2, $t2, 5 + sll $t2, $t2, 5 + addu $t3, $t3, 0x1f + srl $t3, $t3, 5 + sll $t3, $t3, 5 + move $t0, $t2 + subu $t1, $t3, $t2 +1: + cache_op 1, 0($t0) + /* All four ways. */ +#ifdef GRUB_MACHINE_MIPS_LOONGSON + cache_op 1, 1($t0) + cache_op 1, 2($t0) + cache_op 1, 3($t0) + addiu $t1, $t1, -0x20 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x20 + +#else + addiu $t1, $t1, -0x4 + bne $t1, $zero, 1b + addiu $t0, $t0, 0x4 +#endif + sync_op + move $t0, $t2 + subu $t1, $t3, $t2 +2: + cache_op 0, 0($t0) +#ifdef GRUB_MACHINE_MIPS_LOONGSON + addiu $t1, $t1, -0x20 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x20 +#else + addiu $t1, $t1, -0x4 + bne $t1, $zero, 2b + addiu $t0, $t0, 0x4 +#endif + sync_op diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c new file mode 100644 index 000000000..5b02f97fc --- /dev/null +++ b/grub-core/kern/mips/dl.c @@ -0,0 +1,274 @@ +/* dl-386.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Dummy __gnu_local_gp. Resolved by linker. */ +static char __gnu_local_gp_dummy; +static char _gp_disp_dummy; + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ +#ifdef GRUB_CPU_WORDS_BIGENDIAN + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2MSB + || e->e_machine != EM_MIPS) +#else + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_MIPS) +#endif + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + /* FIXME: suboptimal. */ + grub_size_t gp_size = 0; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + if (s->sh_type == SHT_REL) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((const char *) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel++) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_GOT16: + case R_MIPS_CALL16: + case R_MIPS_GPREL32: + gp_size += 4; + break; + } + } + + if (gp_size > 0x08000) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n"); + + *got = gp_size; + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + grub_uint32_t gp0; + Elf_Ehdr *e = ehdr; + + if (!mod->reginfo) + { + unsigned i; + Elf_Shdr *ri; + + /* Find reginfo. */ + for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff); + i < e->e_shnum; + i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize)) + if (ri->sh_type == SHT_MIPS_REGINFO) + break; + if (i == e->e_shnum) + return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); + mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset); + } + + gp0 = mod->reginfo[5]; + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + grub_uint8_t *addr; + Elf_Sym *sym; + grub_uint32_t sym_value; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + sym_value = sym->st_value; + if (s->sh_type == SHT_RELA) + { + sym_value += ((Elf_Rela *) rel)->r_addend; + } + if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) + sym_value = (grub_addr_t) mod->got; + else if (sym_value == (grub_addr_t) &_gp_disp_dummy) + { + sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr; + if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) + /* ABI mandates +4 even if partner lui doesn't + immediately precede addiu. */ + sym_value += 4; + } + switch (ELF_R_TYPE (rel->r_info)) + { + case R_MIPS_HI16: + { + grub_uint32_t value; + Elf_Rel *rel2; + +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + value = (*(grub_uint16_t *) addr << 16) + + sym_value + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset +#ifdef GRUB_CPU_WORDS_BIGENDIAN + + 2 +#endif + ); + break; + } + *(grub_uint16_t *) addr = (value >> 16) & 0xffff; + } + break; + case R_MIPS_LO16: +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *(grub_uint16_t *) addr += sym_value & 0xffff; + break; + case R_MIPS_32: + *(grub_uint32_t *) addr += sym_value; + break; + case R_MIPS_GPREL32: + *(grub_uint32_t *) addr = sym_value + + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got; + break; + + case R_MIPS_26: + { + grub_uint32_t value; + grub_uint32_t raw; + raw = (*(grub_uint32_t *) addr) & 0x3ffffff; + value = raw << 2; + value += sym_value; + raw = (value >> 2) & 0x3ffffff; + + *(grub_uint32_t *) addr = + raw | ((*(grub_uint32_t *) addr) & 0xfc000000); + } + break; + case R_MIPS_GOT16: + if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) + { + Elf_Rel *rel2; + /* Handle partner lo16 relocation. Lower part is + treated as signed. Hence add 0x8000 to compensate. + */ + sym_value += (*(grub_uint16_t *) addr << 16) + + 0x8000; + for (rel2 = rel + 1; rel2 < max; rel2++) + if (ELF_R_SYM (rel2->r_info) + == ELF_R_SYM (rel->r_info) + && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) + { + sym_value += *(grub_int16_t *) + ((char *) seg->addr + rel2->r_offset +#ifdef GRUB_CPU_WORDS_BIGENDIAN + + 2 +#endif + ); + break; + } + sym_value &= 0xffff0000; + *(grub_uint16_t *) addr = 0; + } + /* Fallthrough. */ + case R_MIPS_CALL16: + { + grub_uint32_t *gpptr = mod->gotptr; + /* FIXME: reuse*/ +#ifdef GRUB_CPU_WORDS_BIGENDIAN + addr += 2; +#endif + *gpptr = sym_value + *(grub_uint16_t *) addr; + *(grub_uint16_t *) addr + = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got); + mod->gotptr = gpptr + 1; + break; + } + case R_MIPS_JALR: + break; + default: + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + break; + } + } + + return GRUB_ERR_NONE; +} + +void +grub_arch_dl_init_linker (void) +{ + grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0, 0); + grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0); +} + diff --git a/grub-core/kern/mips/init.c b/grub-core/kern/mips/init.c new file mode 100644 index 000000000..14b8752ec --- /dev/null +++ b/grub-core/kern/mips/init.c @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +#include +#include +#include +#include + +/* 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_MIPS_COP0_TIMER_COUNT : "=r" (low)); + if (low < last) + high++; + last = low; + + return (((grub_uint64_t) high) << 32) | low; +} diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c new file mode 100644 index 000000000..5bd721260 --- /dev/null +++ b/grub-core/kern/mips/loongson/init.c @@ -0,0 +1,320 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + hook (GRUB_ARCH_LOWMEMPSTART, grub_arch_memsize << 20, + GRUB_MEMORY_AVAILABLE, hook_data); + hook (GRUB_ARCH_HIGHMEMPSTART, grub_arch_highmemsize << 20, + GRUB_MEMORY_AVAILABLE, hook_data); + return GRUB_ERR_NONE; +} + +/* Helper for init_pci. */ +static int +set_card (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_pci_address_t addr; + /* We could use grub_pci_assign_addresses for this but we prefer to + have exactly same memory map as on pmon. */ + switch (pciid) + { + case GRUB_LOONGSON_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_LOONGSON_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; +} + +static void +init_pci (void) +{ + *((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, NULL); +} + +void +grub_machine_init (void) +{ + grub_addr_t modend; + grub_uint32_t prid; + + asm volatile ("mfc0 %0, " GRUB_CPU_LOONGSON_COP0_PRID : "=r" (prid)); + + switch (prid) + { + /* Loongson 2E. */ + case 0x6302: + grub_arch_machine = GRUB_ARCH_MACHINE_FULOONG2E; + grub_bonito_type = GRUB_BONITO_2F; + break; + /* Loongson 2F. */ + case 0x6303: + if (grub_arch_machine != GRUB_ARCH_MACHINE_FULOONG2F + && grub_arch_machine != GRUB_ARCH_MACHINE_YEELOONG) + grub_arch_machine = GRUB_ARCH_MACHINE_YEELOONG; + grub_bonito_type = GRUB_BONITO_2F; + break; + /* Loongson 3A. */ + case 0x6305: + grub_arch_machine = GRUB_ARCH_MACHINE_YEELOONG_3A; + grub_bonito_type = GRUB_BONITO_3A; + break; + } + + /* 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 and Fuloong have 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; + 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_sm712_init (); + grub_video_sis315pro_init (); + grub_video_radeon_fuloong2e_init (); + grub_video_radeon_yeeloong3a_init (); + grub_font_init (); + grub_gfxterm_init (); + + grub_keylayouts_init (); + if (grub_arch_machine == GRUB_ARCH_MACHINE_YEELOONG + || grub_arch_machine == GRUB_ARCH_MACHINE_YEELOONG_3A) + grub_at_keyboard_init (); + + grub_terminfo_init (); + grub_serial_init (); + + grub_boot_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +static int +halt_via (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + grub_uint16_t pm; + grub_pci_address_t addr; + + if (pciid != 0x30571106) + return 0; + + addr = grub_pci_make_address (dev, 0x40); + pm = grub_pci_read (addr) & ~1; + + if (pm == 0) + { + grub_pci_write (addr, 0x1801); + pm = 0x1800; + } + + addr = grub_pci_make_address (dev, 0x80); + grub_pci_write_byte (addr, 0xff); + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write_word (addr, grub_pci_read_word (addr) | GRUB_PCI_COMMAND_IO_ENABLED); + + /* FIXME: This one is derived from qemu. Check on real hardware. */ + grub_outw (0x2000, pm + 4 + GRUB_MACHINE_PCI_IO_BASE); + grub_millisleep (5000); + + return 0; +} + +void +grub_halt (void) +{ + switch (grub_arch_machine) + { + case GRUB_ARCH_MACHINE_FULOONG2E: + grub_pci_iterate (halt_via, NULL); + break; + case GRUB_ARCH_MACHINE_FULOONG2F: + { + grub_pci_device_t dev; + grub_port_t p; + if (grub_cs5536_find (&dev)) + { + p = (grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_GPIO_BAR) + & GRUB_CS5536_LBAR_ADDR_MASK) + GRUB_MACHINE_PCI_IO_BASE; + grub_outl ((1 << 13), p + 4); + grub_outl ((1 << 29), p); + grub_millisleep (5000); + } + } + break; + case GRUB_ARCH_MACHINE_YEELOONG: + grub_outb (grub_inb (GRUB_CPU_LOONGSON_GPIOCFG) + & ~GRUB_CPU_YEELOONG_SHUTDOWN_GPIO, GRUB_CPU_LOONGSON_GPIOCFG); + grub_millisleep (1500); + break; + case GRUB_ARCH_MACHINE_YEELOONG_3A: + grub_millisleep (1); + grub_outb (0x4e, GRUB_MACHINE_PCI_IO_BASE_3A | 0x66); + grub_millisleep (1); + grub_outb (2, GRUB_MACHINE_PCI_IO_BASE_3A | 0x62); + grub_millisleep (5000); + break; + } + + grub_puts_ (N_("Shutdown failed")); + grub_refresh (); + while (1); +} + +void +grub_exit (void) +{ + grub_halt (); +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +extern char _end[]; +grub_addr_t grub_modbase = (grub_addr_t) _end; + diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c new file mode 100644 index 000000000..b5477b87f --- /dev/null +++ b/grub-core/kern/mips/qemu_mips/init.c @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline int +probe_mem (grub_addr_t addr) +{ + volatile grub_uint8_t *ptr = (grub_uint8_t *) (0xa0000000 | addr); + grub_uint8_t c = *ptr; + *ptr = 0xAA; + if (*ptr != 0xAA) + return 0; + *ptr = 0x55; + if (*ptr != 0x55) + return 0; + *ptr = c; + return 1; +} + +void +grub_machine_init (void) +{ + grub_addr_t modend; + + if (grub_arch_memsize == 0) + { + int i; + + for (i = 27; i >= 0; i--) + if (probe_mem (grub_arch_memsize | (1 << i))) + grub_arch_memsize |= (1 << i); + grub_arch_memsize++; + } + + /* FIXME: measure this. */ + grub_arch_cpuclock = 200000000; + + modend = grub_modules_get_end (); + grub_mm_init_region ((void *) modend, grub_arch_memsize + - (modend - GRUB_ARCH_LOWMEMVSTART)); + + grub_install_get_time_ms (grub_rtc_get_time_ms); + + grub_keylayouts_init (); + grub_at_keyboard_init (); + + grub_qemu_init_cirrus (); + grub_vga_text_init (); + + grub_terminfo_init (); + grub_serial_init (); + + grub_boot_init (); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +void +grub_exit (void) +{ + grub_halt (); +} + +void +grub_halt (void) +{ + grub_outl (42, 0xbfbf0004); + while (1); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + hook (0, grub_arch_memsize, GRUB_MEMORY_AVAILABLE, hook_data); + return GRUB_ERR_NONE; +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +extern char _end[]; +grub_addr_t grub_modbase = (grub_addr_t) _end; + diff --git a/grub-core/kern/mips/startup.S b/grub-core/kern/mips/startup.S new file mode 100644 index 000000000..1fdb58aca --- /dev/null +++ b/grub-core/kern/mips/startup.S @@ -0,0 +1,126 @@ +/* startup.S - Startup code for the MIPS. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#define BASE_ADDR 8 + + .globl __start, _start, start + .set noreorder + .set nomacro +__start: +_start: +start: +.extern __bss_start +.extern _end + bal cont + nop + + .org GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE +VARIABLE(grub_total_modules_size) + .long 0 + +VARIABLE (grub_arch_busclock) + .long 0 +VARIABLE (grub_arch_cpuclock) + .long 0 +VARIABLE (grub_arch_memsize) + .long 0 +VARIABLE (grub_arch_highmemsize) + .long 0 +#ifdef GRUB_MACHINE_MIPS_LOONGSON +VARIABLE (grub_arch_machine) + .long GRUB_ARCH_MACHINE_FULOONG2F +#endif +cont: + /* Save our base. */ + move $s0, $ra + +#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS + lui $t1, %hi(grub_arch_busclock) + addiu $t1, %lo(grub_arch_busclock) + sw $s4, 8($t1) +#endif + +#ifdef GRUB_MACHINE_MIPS_LOONGSON + lui $t1, %hi(grub_arch_busclock) + addiu $t1, %lo(grub_arch_busclock) + sw $s2, 0($t1) + sw $s3, 4($t1) + sw $s4, 8($t1) + sw $s5, 12($t1) + sw $s7, 16($t1) +#endif + + /* Move the modules out of BSS. */ + lui $t2, %hi(__bss_start) + addiu $t2, %lo(__bss_start) + + lui $t1, %hi(_end) + addiu $t1, %lo(_end) + addiu $t1, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + li $t3, (GRUB_KERNEL_MACHINE_MOD_ALIGN - 1) + nor $t3, $t3, $0 + and $t1, $t1, $t3 + + lw $t3, (GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE - BASE_ADDR)($s0) + + /* Backward copy. */ + add $t1, $t1, $t3 + add $t2, $t2, $t3 + addiu $t1, $t1, -1 + addiu $t2, $t2, -1 + + /* $t2 is source. $t1 is destination. $t3 is size. */ +modulesmovcont: + beq $t3, $0, modulesmovdone + nop + lb GRUB_ASM_T4, 0($t2) + sb GRUB_ASM_T4, 0($t1) + addiu $t2, $t2, -1 + addiu $t1, $t1, -1 + b modulesmovcont + addiu $t3, $t3, -1 +modulesmovdone: + + /* Clean BSS. */ + + lui $t1, %hi(__bss_start) + addiu $t1, $t1, %lo(__bss_start) + lui $t2, %hi(_end) + addiu $t2, $t2, %lo(_end) +bsscont: + sb $0,0($t1) + addiu $t1, $t1, 1 + sltu $t3, $t1, $t2 + bne $t3, $0, bsscont + nop + + lui $t9, %hi(grub_main) + addiu $t9, %lo(grub_main) + + lui $sp, %hi(GRUB_MACHINE_MEMORY_STACK_HIGH) + jr $t9 + addiu $sp, $sp, %lo(GRUB_MACHINE_MEMORY_STACK_HIGH) + diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c new file mode 100644 index 000000000..2b7922393 --- /dev/null +++ b/grub-core/kern/misc.c @@ -0,0 +1,1405 @@ +/* misc.c - definitions of misc functions */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +union printf_arg +{ + /* Yes, type is also part of union as the moment we fill the value + we don't need to store its type anymore (when we'll need it, we'll + have format spec again. So save some space. */ + enum + { + INT, LONG, LONGLONG, + UNSIGNED_INT = 3, UNSIGNED_LONG, UNSIGNED_LONGLONG, + STRING, + GUID + } type; + long long ll; +}; + +struct printf_args +{ + union printf_arg prealloc[32]; + union printf_arg *ptr; + grub_size_t count; +}; + +static void +parse_printf_args (const char *fmt0, struct printf_args *args, + va_list args_in); +static int +grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, + struct printf_args *args); + +static void +free_printf_args (struct printf_args *args) +{ + if (args->ptr != args->prealloc) + grub_free (args->ptr); +} + +static int +grub_iswordseparator (int c) +{ + return (grub_isspace (c) || c == ',' || c == ';' || c == '|' || c == '&'); +} + +/* grub_gettext_dummy is not translating anything. */ +static const char * +grub_gettext_dummy (const char *s) +{ + return s; +} + +const char* (*grub_gettext) (const char *s) = grub_gettext_dummy; + +void * +grub_memmove (void *dest, const void *src, grub_size_t n) +{ + char *d = (char *) dest; + const char *s = (const char *) src; + + if (d < s) + while (n--) + *d++ = *s++; + else + { + d += n; + s += n; + + while (n--) + *--d = *--s; + } + + return dest; +} + +char * +grub_strcpy (char *dest, const char *src) +{ + char *p = dest; + + while ((*p++ = *src++) != '\0') + ; + + return dest; +} + +int +grub_printf (const char *fmt, ...) +{ + va_list ap; + int ret; + +#if defined(MM_DEBUG) && !defined(GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) + /* + * To prevent infinite recursion when grub_mm_debug is on, disable it + * when calling grub_vprintf(). One such call loop is: + * grub_vprintf() -> parse_printf_args() -> parse_printf_arg_fmt() -> + * grub_debug_calloc() -> grub_printf() -> grub_vprintf(). + */ + int grub_mm_debug_save = 0; + + if (grub_mm_debug) + { + grub_mm_debug_save = grub_mm_debug; + grub_mm_debug = 0; + } +#endif + + va_start (ap, fmt); + ret = grub_vprintf (fmt, ap); + va_end (ap); + +#if defined(MM_DEBUG) && !defined(GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) + grub_mm_debug = grub_mm_debug_save; +#endif + + return ret; +} + +int +grub_printf_ (const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start (ap, fmt); + ret = grub_vprintf (_(fmt), ap); + va_end (ap); + + return ret; +} + +int +grub_puts_ (const char *s) +{ + return grub_puts (_(s)); +} + +#if defined (__APPLE__) && ! defined (GRUB_UTIL) +int +grub_err_printf (const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start (ap, fmt); + ret = grub_vprintf (fmt, ap); + va_end (ap); + + return ret; +} +#endif + +#if ! defined (__APPLE__) && ! defined (GRUB_UTIL) +int grub_err_printf (const char *fmt, ...) +__attribute__ ((alias("grub_printf"))); +#endif + +int +grub_debug_enabled (const char * condition) +{ + const char *debug, *found; + grub_size_t clen; + int ret = 0; + + debug = grub_env_get ("debug"); + if (!debug) + return 0; + + if (grub_strword (debug, "all")) + { + if (debug[3] == '\0') + return 1; + ret = 1; + } + + clen = grub_strlen (condition); + found = debug-1; + while(1) + { + found = grub_strstr (found+1, condition); + + if (found == NULL) + break; + + /* Found condition is not a whole word, so ignore it. */ + if (*(found + clen) != '\0' && *(found + clen) != ',' + && !grub_isspace (*(found + clen))) + continue; + + /* + * If found condition is at the start of debug or the start is on a word + * boundary, then enable debug. Else if found condition is prefixed with + * '-' and the start is on a word boundary, then disable debug. If none + * of these cases, ignore. + */ + if (found == debug || *(found - 1) == ',' || grub_isspace (*(found - 1))) + ret = 1; + else if (*(found - 1) == '-' && ((found == debug + 1) || (*(found - 2) == ',' + || grub_isspace (*(found - 2))))) + ret = 0; + } + + return ret; +} + +void +grub_real_dprintf (const char *file, const int line, const char *condition, + const char *fmt, ...) +{ + va_list args; + + if (grub_debug_enabled (condition)) + { + grub_printf ("%s:%d:%s: ", file, line, condition); + va_start (args, fmt); + grub_vprintf (fmt, args); + va_end (args); + grub_refresh (); + } +} + +#define PREALLOC_SIZE 255 + +int +grub_vprintf (const char *fmt, va_list ap) +{ + grub_size_t s; + static char buf[PREALLOC_SIZE + 1]; + char *curbuf = buf; + struct printf_args args; + + parse_printf_args (fmt, &args, ap); + + s = grub_vsnprintf_real (buf, PREALLOC_SIZE, fmt, &args); + if (s > PREALLOC_SIZE) + { + curbuf = grub_malloc (s + 1); + if (!curbuf) + { + grub_errno = GRUB_ERR_NONE; + buf[PREALLOC_SIZE - 3] = '.'; + buf[PREALLOC_SIZE - 2] = '.'; + buf[PREALLOC_SIZE - 1] = '.'; + buf[PREALLOC_SIZE] = 0; + curbuf = buf; + } + else + s = grub_vsnprintf_real (curbuf, s, fmt, &args); + } + + free_printf_args (&args); + + grub_xputs (curbuf); + + if (curbuf != buf) + grub_free (curbuf); + + return s; +} + +int +grub_memcmp (const void *s1, const void *s2, grub_size_t n) +{ + const grub_uint8_t *t1 = s1; + const grub_uint8_t *t2 = s2; + + while (n--) + { + if (*t1 != *t2) + return (int) *t1 - (int) *t2; + + t1++; + t2++; + } + + return 0; +} + +int +grub_strcmp (const char *s1, const char *s2) +{ + while (*s1 && *s2) + { + if (*s1 != *s2) + break; + + s1++; + s2++; + } + + return (int) (grub_uint8_t) *s1 - (int) (grub_uint8_t) *s2; +} + +int +grub_strncmp (const char *s1, const char *s2, grub_size_t n) +{ + if (n == 0) + return 0; + + while (*s1 && *s2 && --n) + { + if (*s1 != *s2) + break; + + s1++; + s2++; + } + + return (int) (grub_uint8_t) *s1 - (int) (grub_uint8_t) *s2; +} + +char * +grub_strchr (const char *s, int c) +{ + do + { + if (*s == c) + return (char *) s; + } + while (*s++); + + return 0; +} + +char * +grub_strrchr (const char *s, int c) +{ + char *p = NULL; + + do + { + if (*s == c) + p = (char *) s; + } + while (*s++); + + return p; +} + +int +grub_strword (const char *haystack, const char *needle) +{ + const char *n_pos = needle; + + while (grub_iswordseparator (*haystack)) + haystack++; + + while (*haystack) + { + /* Crawl both the needle and the haystack word we're on. */ + while(*haystack && !grub_iswordseparator (*haystack) + && *haystack == *n_pos) + { + haystack++; + n_pos++; + } + + /* If we reached the end of both words at the same time, the word + is found. If not, eat everything in the haystack that isn't the + next word (or the end of string) and "reset" the needle. */ + if ( (!*haystack || grub_iswordseparator (*haystack)) + && (!*n_pos || grub_iswordseparator (*n_pos))) + return 1; + else + { + n_pos = needle; + while (*haystack && !grub_iswordseparator (*haystack)) + haystack++; + while (grub_iswordseparator (*haystack)) + haystack++; + } + } + + return 0; +} + +int +grub_isspace (int c) +{ + return (c == '\n' || c == '\r' || c == ' ' || c == '\t'); +} + +unsigned long +grub_strtoul (const char * restrict str, const char ** const restrict end, + int base) +{ + unsigned long long num; + + num = grub_strtoull (str, end, base); +#if GRUB_CPU_SIZEOF_LONG != 8 + if (num > ~0UL) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return ~0UL; + } +#endif + + return (unsigned long) num; +} + +unsigned long long +grub_strtoull (const char * restrict str, const char ** const restrict end, + int base) +{ + unsigned long long num = 0; + int found = 0; + + /* Skip white spaces. */ + /* grub_isspace checks that *str != '\0'. */ + while (grub_isspace (*str)) + str++; + + /* Guess the base, if not specified. The prefix `0x' means 16, and + the prefix `0' means 8. */ + if (str[0] == '0') + { + if (str[1] == 'x') + { + if (base == 0 || base == 16) + { + base = 16; + str += 2; + } + } + else if (base == 0 && str[1] >= '0' && str[1] <= '7') + base = 8; + } + + if (base == 0) + base = 10; + + while (*str) + { + unsigned long digit; + + digit = grub_tolower (*str) - '0'; + if (digit >= 'a' - '0') + digit += '0' - 'a' + 10; + else if (digit > 9) + break; + + if (digit >= (unsigned long) base) + break; + + found = 1; + + /* NUM * BASE + DIGIT > ~0ULL */ + if (num > grub_divmod64 (~0ULL - digit, base, 0)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("overflow is detected")); + + if (end) + *end = (char *) str; + + return ~0ULL; + } + + num = num * base + digit; + str++; + } + + if (! found) + { + grub_error (GRUB_ERR_BAD_NUMBER, + N_("unrecognized number")); + + if (end) + *end = (char *) str; + + return 0; + } + + if (end) + *end = (char *) str; + + return num; +} + +char * +grub_strdup (const char *s) +{ + grub_size_t len; + char *p; + + len = grub_strlen (s) + 1; + p = (char *) grub_malloc (len); + if (! p) + return 0; + + return grub_memcpy (p, s, len); +} + +char * +grub_strndup (const char *s, grub_size_t n) +{ + grub_size_t len; + char *p; + + len = grub_strlen (s); + if (len > n) + len = n; + p = (char *) grub_malloc (len + 1); + if (! p) + return 0; + + grub_memcpy (p, s, len); + p[len] = '\0'; + return p; +} + +/* clang detects that we're implementing here a memset so it decides to + optimise and calls memset resulting in infinite recursion. With volatile + we make it not optimise in this way. */ +#ifdef __clang__ +#define VOLATILE_CLANG volatile +#else +#define VOLATILE_CLANG +#endif + +void * +grub_memset (void *s, int c, grub_size_t len) +{ + void *p = s; + grub_uint8_t pattern8 = c; + + if (len >= 3 * sizeof (unsigned long)) + { + unsigned long patternl = 0; + grub_size_t i; + + for (i = 0; i < sizeof (unsigned long); i++) + patternl |= ((unsigned long) pattern8) << (8 * i); + + while (len > 0 && (((grub_addr_t) p) & (sizeof (unsigned long) - 1))) + { + *(VOLATILE_CLANG grub_uint8_t *) p = pattern8; + p = (grub_uint8_t *) p + 1; + len--; + } + while (len >= sizeof (unsigned long)) + { + *(VOLATILE_CLANG unsigned long *) p = patternl; + p = (unsigned long *) p + 1; + len -= sizeof (unsigned long); + } + } + + while (len > 0) + { + *(VOLATILE_CLANG grub_uint8_t *) p = pattern8; + p = (grub_uint8_t *) p + 1; + len--; + } + + return s; +} + +grub_size_t +grub_strlen (const char *s) +{ + const char *p = s; + + while (*p) + p++; + + return p - s; +} + +static inline void +grub_reverse (char *str) +{ + char *p = str + grub_strlen (str) - 1; + + while (str < p) + { + char tmp; + + tmp = *str; + *str = *p; + *p = tmp; + str++; + p--; + } +} + +/* Divide N by D, return the quotient, and store the remainder in *R. */ +grub_uint64_t +grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r) +{ + /* This algorithm is typically implemented by hardware. The idea + is to get the highest bit in N, 64 times, by keeping + upper(N * 2^i) = (Q * D + M), where upper + represents the high 64 bits in 128-bits space. */ + unsigned bits = 64; + grub_uint64_t q = 0; + grub_uint64_t m = 0; + + /* ARM and IA64 don't have a fast 32-bit division. + Using that code would just make us use software division routines, calling + ourselves indirectly and hence getting infinite recursion. + */ +#if !GRUB_DIVISION_IN_SOFTWARE + /* Skip the slow computation if 32-bit arithmetic is possible. */ + if (n < 0xffffffff && d < 0xffffffff) + { + if (r) + *r = ((grub_uint32_t) n) % (grub_uint32_t) d; + + return ((grub_uint32_t) n) / (grub_uint32_t) d; + } +#endif + + while (bits--) + { + m <<= 1; + + if (n & (1ULL << 63)) + m |= 1; + + q <<= 1; + n <<= 1; + + if (m >= d) + { + q |= 1; + m -= d; + } + } + + if (r) + *r = m; + + return q; +} + +/* Convert a long long value to a string. This function avoids 64-bit + modular arithmetic or divisions. */ +static inline char * +grub_lltoa (char *str, int c, unsigned long long n) +{ + unsigned base = ((c == 'x') || (c == 'X')) ? 16 : ((c == 'o') ? 8 : 10); + char *p; + + if ((long long) n < 0 && c == 'd') + { + n = (unsigned long long) (-((long long) n)); + *str++ = '-'; + } + + p = str; + + if (base == 16) + do + { + unsigned d = (unsigned) (n & 0xf); + *p++ = (d > 9) ? (d + ((c == 'x') ? 'a' : 'A') - 10) : d + '0'; + } + while (n >>= 4); + else if (base == 8) + do + { + *p++ = ((unsigned) (n & 0x7)) + '0'; + } + while (n >>= 3); + else + /* BASE == 10 */ + do + { + grub_uint64_t m; + + n = grub_divmod64 (n, 10, &m); + *p++ = m + '0'; + } + while (n); + + *p = 0; + + grub_reverse (str); + return p; +} + +/* + * Parse printf() fmt0 string into args arguments. + * + * The parsed arguments are either used by a printf() function to format the fmt0 + * string or they are used to compare a format string from an untrusted source + * against a format string with expected arguments. + * + * When the fmt_check is set to !0, e.g. 1, then this function is executed in + * printf() format check mode. This enforces stricter rules for parsing the + * fmt0 to limit exposure to possible errors in printf() handling. It also + * disables positional parameters, "$", because some formats, e.g "%s%1$d", + * cannot be validated with the current implementation. + * + * The max_args allows to set a maximum number of accepted arguments. If the fmt0 + * string defines more arguments than the max_args then the parse_printf_arg_fmt() + * function returns an error. This is currently used for format check only. + */ +static grub_err_t +parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, + int fmt_check, grub_size_t max_args) +{ + const char *fmt; + char c; + grub_size_t n = 0; + + args->count = 0; + + COMPILE_TIME_ASSERT (sizeof (int) == sizeof (grub_uint32_t)); + COMPILE_TIME_ASSERT (sizeof (int) <= sizeof (long long)); + COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long)); + COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *) + || sizeof (int) == sizeof (void *)); + + fmt = fmt0; + while ((c = *fmt++) != 0) + { + if (c != '%') + continue; + + if (*fmt =='-') + fmt++; + + while (grub_isdigit (*fmt)) + fmt++; + + if (*fmt == '$') + { + if (fmt_check) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "positional arguments are not supported"); + fmt++; + } + + if (*fmt =='-') + fmt++; + + while (grub_isdigit (*fmt)) + fmt++; + + if (*fmt =='.') + fmt++; + + while (grub_isdigit (*fmt)) + fmt++; + + c = *fmt++; + if (c == 'l') + c = *fmt++; + if (c == 'l') + c = *fmt++; + + switch (c) + { + case 'p': + if (*(fmt) == 'G') + ++fmt; + /* Fall through. */ + case 'x': + case 'X': + case 'u': + case 'd': + case 'o': + case 'c': + case 'C': + case 's': + args->count++; + break; + case '%': + /* "%%" is the escape sequence to output "%". */ + break; + default: + if (fmt_check) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "unexpected format"); + break; + } + } + + if (fmt_check && args->count > max_args) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many arguments"); + + if (args->count <= ARRAY_SIZE (args->prealloc)) + args->ptr = args->prealloc; + else + { + args->ptr = grub_calloc (args->count, sizeof (args->ptr[0])); + if (!args->ptr) + { + if (fmt_check) + return grub_errno; + + grub_errno = GRUB_ERR_NONE; + args->ptr = args->prealloc; + args->count = ARRAY_SIZE (args->prealloc); + } + } + + grub_memset (args->ptr, 0, args->count * sizeof (args->ptr[0])); + + fmt = fmt0; + n = 0; + while ((c = *fmt++) != 0) + { + int longfmt = 0; + unsigned long curn; + const char *p; + + if (c != '%') + continue; + + curn = n++; + + if (*fmt =='-') + fmt++; + + p = fmt; + + while (grub_isdigit (*fmt)) + fmt++; + + if (*fmt == '$') + { + curn = grub_strtoul (p, 0, 10); + if (curn == 0) + continue; + curn--; + fmt++; + } + + if (*fmt =='-') + fmt++; + + while (grub_isdigit (*fmt)) + fmt++; + + if (*fmt =='.') + fmt++; + + while (grub_isdigit (*fmt)) + fmt++; + + c = *fmt++; + if (c == '%') + { + n--; + continue; + } + + if (c == 'l') + { + c = *fmt++; + longfmt = 1; + } + if (c == 'l') + { + c = *fmt++; + longfmt = 2; + } + if (curn >= args->count) + continue; + switch (c) + { + case 'x': + case 'X': + case 'o': + case 'u': + args->ptr[curn].type = UNSIGNED_INT + longfmt; + break; + case 'd': + args->ptr[curn].type = INT + longfmt; + break; + case 'p': + if (sizeof (void *) == sizeof (long long)) + args->ptr[curn].type = UNSIGNED_LONGLONG; + else + args->ptr[curn].type = UNSIGNED_INT; + if (*(fmt) == 'G') { + args->ptr[curn].type = GUID; + ++fmt; + } + break; + case 's': + args->ptr[curn].type = STRING; + break; + case 'C': + case 'c': + args->ptr[curn].type = INT; + break; + } + } + + return GRUB_ERR_NONE; +} + +static void +parse_printf_args (const char *fmt0, struct printf_args *args, va_list args_in) +{ + grub_size_t n; + + parse_printf_arg_fmt (fmt0, args, 0, 0); + + for (n = 0; n < args->count; n++) + switch (args->ptr[n].type) + { + case INT: + args->ptr[n].ll = va_arg (args_in, int); + break; + case LONG: + args->ptr[n].ll = va_arg (args_in, long); + break; + case UNSIGNED_INT: + args->ptr[n].ll = va_arg (args_in, unsigned int); + break; + case UNSIGNED_LONG: + args->ptr[n].ll = va_arg (args_in, unsigned long); + break; + case LONGLONG: + case UNSIGNED_LONGLONG: + args->ptr[n].ll = va_arg (args_in, long long); + break; + case STRING: + case GUID: + if (sizeof (void *) == sizeof (long long)) + args->ptr[n].ll = va_arg (args_in, long long); + else + args->ptr[n].ll = va_arg (args_in, unsigned int); + break; + } +} + +static inline void __attribute__ ((always_inline)) +write_char (char *str, grub_size_t *count, grub_size_t max_len, unsigned char ch) +{ + if (*count < max_len) + str[*count] = ch; + + (*count)++; +} + +static void +write_number (char *str, grub_size_t *count, grub_size_t max_len, grub_size_t format1, + char rightfill, char zerofill, char c, long long value) +{ + char tmp[32]; + const char *p = tmp; + grub_size_t len; + grub_size_t fill; + + len = grub_lltoa (tmp, c, value) - tmp; + fill = len < format1 ? format1 - len : 0; + if (! rightfill) + while (fill--) + write_char (str, count, max_len, zerofill); + while (*p) + write_char (str, count, max_len, *p++); + if (rightfill) + while (fill--) + write_char (str, count, max_len, zerofill); +} + +static int +grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, + struct printf_args *args) +{ + char c; + grub_size_t n = 0; + grub_size_t count = 0; + const char *fmt; + + fmt = fmt0; + + while ((c = *fmt++) != 0) + { + unsigned int format1 = 0; + unsigned int format2 = ~ 0U; + char zerofill = ' '; + char rightfill = 0; + grub_size_t curn; + + if (c != '%') + { + write_char (str, &count, max_len, c); + continue; + } + + curn = n++; + + rescan:; + + if (*fmt =='-') + { + rightfill = 1; + fmt++; + } + + /* Read formatting parameters. */ + if (grub_isdigit (*fmt)) + { + if (fmt[0] == '0') + zerofill = '0'; + format1 = grub_strtoul (fmt, &fmt, 10); + } + + if (*fmt == '.') + fmt++; + + if (grub_isdigit (*fmt)) + format2 = grub_strtoul (fmt, &fmt, 10); + + if (*fmt == '$') + { + if (format1 == 0) + continue; + curn = format1 - 1; + fmt++; + format1 = 0; + format2 = ~ 0U; + zerofill = ' '; + rightfill = 0; + + goto rescan; + } + + c = *fmt++; + if (c == 'l') + c = *fmt++; + if (c == 'l') + c = *fmt++; + + if (c == '%') + { + write_char (str, &count, max_len, c); + n--; + continue; + } + + if (curn >= args->count) + continue; + + long long curarg = args->ptr[curn].ll; + + switch (c) + { + case 'p': + if (*(fmt) == 'G') + { + ++fmt; + grub_packed_guid_t *guid = (grub_packed_guid_t *)(grub_addr_t) curarg; + write_number (str, &count, max_len, 8, 0, '0', 'x', guid->data1); + write_char (str, &count, max_len, '-'); + write_number (str, &count, max_len, 4, 0, '0', 'x', guid->data2); + write_char (str, &count, max_len, '-'); + write_number (str, &count, max_len, 4, 0, '0', 'x', guid->data3); + write_char (str, &count, max_len, '-'); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[0]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[1]); + write_char (str, &count, max_len, '-'); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[2]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[3]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[4]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[5]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[6]); + write_number (str, &count, max_len, 2, 0, '0', 'x', guid->data4[7]); + break; + } + else + { + write_char (str, &count, max_len, '0'); + write_char (str, &count, max_len, 'x'); + c = 'x'; + } + /* Fall through. */ + case 'x': + case 'X': + case 'u': + case 'd': + case 'o': + write_number (str, &count, max_len, format1, rightfill, zerofill, c, curarg); + break; + + case 'c': + write_char (str, &count, max_len, curarg & 0xff); + break; + + case 'C': + { + grub_uint32_t code = curarg; + int shift; + unsigned mask; + + if (code <= 0x7f) + { + shift = 0; + mask = 0; + } + else if (code <= 0x7ff) + { + shift = 6; + mask = 0xc0; + } + else if (code <= 0xffff) + { + shift = 12; + mask = 0xe0; + } + else if (code <= 0x10ffff) + { + shift = 18; + mask = 0xf0; + } + else + { + code = '?'; + shift = 0; + mask = 0; + } + + write_char (str, &count, max_len, mask | (code >> shift)); + + for (shift -= 6; shift >= 0; shift -= 6) + write_char (str, &count, max_len, 0x80 | (0x3f & (code >> shift))); + } + break; + + case 's': + { + grub_size_t len = 0; + grub_size_t fill; + const char *p = ((char *) (grub_addr_t) curarg) ? : "(null)"; + grub_size_t i; + + while (len < format2 && p[len]) + len++; + + fill = len < format1 ? format1 - len : 0; + + if (!rightfill) + while (fill--) + write_char (str, &count, max_len, zerofill); + + for (i = 0; i < len; i++) + write_char (str, &count, max_len, *p++); + + if (rightfill) + while (fill--) + write_char (str, &count, max_len, zerofill); + } + + break; + + default: + write_char (str, &count, max_len, c); + break; + } + } + + if (count < max_len) + str[count] = '\0'; + else + str[max_len] = '\0'; + return count; +} + +int +grub_vsnprintf (char *str, grub_size_t n, const char *fmt, va_list ap) +{ + grub_size_t ret; + struct printf_args args; + + if (!n) + return 0; + + n--; + + parse_printf_args (fmt, &args, ap); + + ret = grub_vsnprintf_real (str, n, fmt, &args); + + free_printf_args (&args); + + return ret; +} + +int +grub_snprintf (char *str, grub_size_t n, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start (ap, fmt); + ret = grub_vsnprintf (str, n, fmt, ap); + va_end (ap); + + return ret; +} + +char * +grub_xvasprintf (const char *fmt, va_list ap) +{ + grub_size_t s, as = PREALLOC_SIZE; + char *ret; + struct printf_args args; + + parse_printf_args (fmt, &args, ap); + + while (1) + { + ret = grub_malloc (as + 1); + if (!ret) + { + free_printf_args (&args); + return NULL; + } + + s = grub_vsnprintf_real (ret, as, fmt, &args); + + if (s <= as) + { + free_printf_args (&args); + return ret; + } + + grub_free (ret); + as = s; + } +} + +char * +grub_xasprintf (const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start (ap, fmt); + ret = grub_xvasprintf (fmt, ap); + va_end (ap); + + return ret; +} + +grub_err_t +grub_printf_fmt_check (const char *fmt, const char *fmt_expected) +{ + struct printf_args args_expected, args_fmt; + grub_err_t ret; + grub_size_t n; + + if (fmt == NULL || fmt_expected == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid format"); + + ret = parse_printf_arg_fmt (fmt_expected, &args_expected, 1, GRUB_SIZE_MAX); + if (ret != GRUB_ERR_NONE) + return ret; + + /* Limit parsing to the number of expected arguments. */ + ret = parse_printf_arg_fmt (fmt, &args_fmt, 1, args_expected.count); + if (ret != GRUB_ERR_NONE) + { + free_printf_args (&args_expected); + return ret; + } + + for (n = 0; n < args_fmt.count; n++) + if (args_fmt.ptr[n].type != args_expected.ptr[n].type) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "arguments types do not match"); + break; + } + + free_printf_args (&args_expected); + free_printf_args (&args_fmt); + + return ret; +} + + +/* Abort GRUB. This function does not return. */ +void __attribute__ ((noreturn)) +grub_abort (void) +{ + grub_printf ("\nAborted."); + +#ifndef GRUB_UTIL + if (grub_term_inputs) +#endif + { + grub_printf (" Press any key to exit."); + grub_getkey (); + } + + grub_exit (); +} + +void +grub_fatal (const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + grub_vprintf (_(fmt), ap); + va_end (ap); + + grub_refresh (); + + grub_abort (); +} + +grub_ssize_t +grub_utf8_to_utf16_alloc (const char *str8, grub_uint16_t **utf16_msg, grub_uint16_t **last_position) +{ + grub_size_t len; + grub_size_t len16; + + len = grub_strlen (str8); + + /* Check for integer overflow */ + if (len > GRUB_SSIZE_MAX / GRUB_MAX_UTF16_PER_UTF8 - 1) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("string too long")); + *utf16_msg = NULL; + return -1; + } + + len16 = len * GRUB_MAX_UTF16_PER_UTF8; + + *utf16_msg = grub_calloc (len16 + 1, sizeof (*utf16_msg[0])); + if (*utf16_msg == NULL) + return -1; + + len16 = grub_utf8_to_utf16 (*utf16_msg, len16, (grub_uint8_t *) str8, len, NULL); + + if (last_position != NULL) + *last_position = *utf16_msg + len16; + + return len16; +} + + +#if BOOT_TIME_STATS + +#include + +struct grub_boot_time *grub_boot_time_head; +static struct grub_boot_time **boot_time_last = &grub_boot_time_head; + +void +grub_real_boot_time (const char *file, + const int line, + const char *fmt, ...) +{ + struct grub_boot_time *n; + va_list args; + + grub_error_push (); + n = grub_malloc (sizeof (*n)); + if (!n) + { + grub_errno = 0; + grub_error_pop (); + return; + } + n->file = file; + n->line = line; + n->tp = grub_get_time_ms (); + n->next = 0; + + va_start (args, fmt); + n->msg = grub_xvasprintf (fmt, args); + va_end (args); + + *boot_time_last = n; + boot_time_last = &n->next; + + grub_errno = 0; + grub_error_pop (); +} +#endif diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c new file mode 100644 index 000000000..027a25cd1 --- /dev/null +++ b/grub-core/kern/mm.c @@ -0,0 +1,863 @@ +/* mm.c - functions for memory manager */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +/* + The design of this memory manager. + + This is a simple implementation of malloc with a few extensions. These are + the extensions: + + - memalign is implemented efficiently. + + - multiple regions may be used as free space. They may not be + contiguous. + + - if existing regions are insufficient to satisfy an allocation, a new + region can be requested from firmware. + + Regions are managed by a singly linked list, and the meta information is + stored in the beginning of each region. Space after the meta information + is used to allocate memory. + + The memory space is used as cells instead of bytes for simplicity. This + is important for some CPUs which may not access multiple bytes at a time + when the first byte is not aligned at a certain boundary (typically, + 4-byte or 8-byte). The size of each cell is equal to the size of struct + grub_mm_header, so the header of each allocated/free block fits into one + cell precisely. One cell is 16 bytes on 32-bit platforms and 32 bytes + on 64-bit platforms. + + There are two types of blocks: allocated blocks and free blocks. + + In allocated blocks, the header of each block has only its size. Note that + this size is based on cells but not on bytes. The header is located right + before the returned pointer, that is, the header resides at the previous + cell. + + Free blocks constitutes a ring, using a singly linked list. The first free + block is pointed to by the meta information of a region. The allocator + attempts to pick up the second block instead of the first one. This is + a typical optimization against defragmentation, and makes the + implementation a bit easier. + + For safety, both allocated blocks and free ones are marked by magic + numbers. Whenever anything unexpected is detected, GRUB aborts the + operation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MM_DEBUG +# undef grub_calloc +# undef grub_malloc +# undef grub_zalloc +# undef grub_realloc +# undef grub_free +# undef grub_memalign +#endif + + + +/* + * GRUB_MM_MGMT_OVERHEAD is an upper bound of management overhead of + * each region, with any possible padding taken into account. + * + * The value must be large enough to make sure grub_memalign(align, size) + * always succeeds after a successful call to + * grub_mm_init_region(addr, size + align + GRUB_MM_MGMT_OVERHEAD), + * for any given addr, align and size (assuming no interger overflow). + * + * The worst case which has maximum overhead is shown in the figure below: + * + * +-- addr + * v |<- size + align ->| + * +---------+----------------+----------------+------------------+---------+ + * | padding | grub_mm_region | grub_mm_header | usable bytes | padding | + * +---------+----------------+----------------+------------------+---------+ + * |<- a ->|<- b ->|<- c ->|<- d ->|<- e ->| + * ^ + * b == sizeof (struct grub_mm_region) | / Assuming no other suitable + * c == sizeof (struct grub_mm_header) | | block is available, then: + * d == size + align +-| If align == 0, this will be + * | the pointer returned by next + * Assuming addr % GRUB_MM_ALIGN == 1, then: | grub_memalign(align, size). + * a == GRUB_MM_ALIGN - 1 | If align > 0, this chunk may + * | need to be split to fulfill + * Assuming d % GRUB_MM_ALIGN == 1, then: | alignment requirements, and + * e == GRUB_MM_ALIGN - 1 | the returned pointer may be + * \ inside these usable bytes. + * Therefore, the maximum overhead is: + * a + b + c + e == (GRUB_MM_ALIGN - 1) + sizeof (struct grub_mm_region) + * + sizeof (struct grub_mm_header) + (GRUB_MM_ALIGN - 1) + */ +#define GRUB_MM_MGMT_OVERHEAD ((GRUB_MM_ALIGN - 1) \ + + sizeof (struct grub_mm_region) \ + + sizeof (struct grub_mm_header) \ + + (GRUB_MM_ALIGN - 1)) + +/* The size passed to grub_mm_add_region_fn() is aligned up by this value. */ +#define GRUB_MM_HEAP_GROW_ALIGN 4096 + +/* Minimal heap growth granularity when existing heap space is exhausted. */ +#define GRUB_MM_HEAP_GROW_EXTRA 0x100000 + +grub_mm_region_t grub_mm_base; +grub_mm_add_region_func_t grub_mm_add_region_fn; + +/* Get a header from the pointer PTR, and set *P and *R to a pointer + to the header and a pointer to its region, respectively. PTR must + be allocated. */ +static void +get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r) +{ + if ((grub_addr_t) ptr & (GRUB_MM_ALIGN - 1)) + grub_fatal ("unaligned pointer %p", ptr); + + for (*r = grub_mm_base; *r; *r = (*r)->next) + if ((grub_addr_t) ptr > (grub_addr_t) ((*r) + 1) + && (grub_addr_t) ptr <= (grub_addr_t) ((*r) + 1) + (*r)->size) + break; + + if (! *r) + grub_fatal ("out of range pointer %p", ptr); + + *p = (grub_mm_header_t) ptr - 1; + if ((*p)->magic == GRUB_MM_FREE_MAGIC) + grub_fatal ("double free at %p", *p); + if ((*p)->magic != GRUB_MM_ALLOC_MAGIC) + grub_fatal ("alloc magic is broken at %p: %lx", *p, + (unsigned long) (*p)->magic); +} + +/* Initialize a region starting from ADDR and whose size is SIZE, + to use it as free space. */ +void +grub_mm_init_region (void *addr, grub_size_t size) +{ + grub_mm_header_t h; + grub_mm_region_t r, *p, q; + + grub_dprintf ("regions", "Using memory for heap: start=%p, end=%p\n", + addr, (char *) addr + (unsigned int) size); + + /* Exclude last 4K to avoid overflows. */ + /* If addr + 0x1000 overflows then whole region is in excluded zone. */ + if ((grub_addr_t) addr > ~((grub_addr_t) 0x1000)) + return; + + /* If addr + 0x1000 + size overflows then decrease size. */ + if (((grub_addr_t) addr + 0x1000) > ~(grub_addr_t) size) + size = ((grub_addr_t) -0x1000) - (grub_addr_t) addr; + + /* Attempt to merge this region with every existing region */ + for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p) + { + /* + * Is the new region immediately below an existing region? That + * is, is the address of the memory we're adding now (addr) + size + * of the memory we're adding (size) + the bytes we couldn't use + * at the start of the region we're considering (q->pre_size) + * equal to the address of q? In other words, does the memory + * looks like this? + * + * addr q + * |----size-----|-q->pre_size-|| + */ + grub_dprintf ("regions", "Can we extend into region above?" + " %p + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", + (grub_uint8_t *) addr, size, q->pre_size, (grub_uint8_t *) q); + if ((grub_uint8_t *) addr + size + q->pre_size == (grub_uint8_t *) q) + { + grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n", + q, (grub_uint8_t *) q + sizeof (*q) + q->size, + addr, (grub_uint8_t *) q + sizeof (*q) + q->size); + /* + * Yes, we can merge the memory starting at addr into the + * existing region from below. Align up addr to GRUB_MM_ALIGN + * so that our new region has proper alignment. + */ + r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); + /* Copy the region data across */ + *r = *q; + /* Consider all the new size as pre-size */ + r->pre_size += size; + + /* + * If we have enough pre-size to create a block, create a + * block with it. Mark it as allocated and pass it to + * grub_free (), which will sort out getting it into the free + * list. + */ + if (r->pre_size >> GRUB_MM_ALIGN_LOG2) + { + h = (grub_mm_header_t) (r + 1); + /* block size is pre-size converted to cells */ + h->size = (r->pre_size >> GRUB_MM_ALIGN_LOG2); + h->magic = GRUB_MM_ALLOC_MAGIC; + /* region size grows by block size converted back to bytes */ + r->size += h->size << GRUB_MM_ALIGN_LOG2; + /* adjust pre_size to be accurate */ + r->pre_size &= (GRUB_MM_ALIGN - 1); + *p = r; + grub_free (h + 1); + } + /* Replace the old region with the new region */ + *p = r; + return; + } + + /* + * Is the new region immediately above an existing region? That + * is: + * q addr + * ||-q->post_size-|----size-----| + */ + grub_dprintf ("regions", "Can we extend into region below?" + " %p + %x + %" PRIxGRUB_SIZE " + %" PRIxGRUB_SIZE " ?=? %p\n", + (grub_uint8_t *) q, (int) sizeof (*q), q->size, q->post_size, (grub_uint8_t *) addr); + if ((grub_uint8_t *) q + sizeof (*q) + q->size + q->post_size == + (grub_uint8_t *) addr) + { + grub_dprintf ("regions", "Yes: extending a region: (%p -> %p) -> (%p -> %p)\n", + q, (grub_uint8_t *) q + sizeof (*q) + q->size, + q, (grub_uint8_t *) addr + size); + /* + * Yes! Follow a similar pattern to above, but simpler. + * Our header starts at address - post_size, which should align us + * to a cell boundary. + * + * Cast to (void *) first to avoid the following build error: + * kern/mm.c: In function ‘grub_mm_init_region’: + * kern/mm.c:211:15: error: cast increases required alignment of target type [-Werror=cast-align] + * 211 | h = (grub_mm_header_t) ((grub_uint8_t *) addr - q->post_size); + * | ^ + * It is safe to do that because proper alignment is enforced in grub_mm_size_sanity_check(). + */ + h = (grub_mm_header_t)(void *) ((grub_uint8_t *) addr - q->post_size); + /* our size is the allocated size plus post_size, in cells */ + h->size = (size + q->post_size) >> GRUB_MM_ALIGN_LOG2; + h->magic = GRUB_MM_ALLOC_MAGIC; + /* region size grows by block size converted back to bytes */ + q->size += h->size << GRUB_MM_ALIGN_LOG2; + /* adjust new post_size to be accurate */ + q->post_size = (q->post_size + size) & (GRUB_MM_ALIGN - 1); + grub_free (h + 1); + return; + } + } + + grub_dprintf ("regions", "No: considering a new region at %p of size %" PRIxGRUB_SIZE "\n", + addr, size); + /* + * If you want to modify the code below, please also take a look at + * GRUB_MM_MGMT_OVERHEAD and make sure it is synchronized with the code. + */ + + /* Allocate a region from the head. */ + r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN); + + /* If this region is too small, ignore it. */ + if (size < GRUB_MM_ALIGN + (char *) r - (char *) addr + sizeof (*r)) + return; + + size -= (char *) r - (char *) addr + sizeof (*r); + + h = (grub_mm_header_t) (r + 1); + h->next = h; + h->magic = GRUB_MM_FREE_MAGIC; + h->size = (size >> GRUB_MM_ALIGN_LOG2); + + r->first = h; + r->pre_size = (grub_addr_t) r - (grub_addr_t) addr; + r->size = (h->size << GRUB_MM_ALIGN_LOG2); + r->post_size = size - r->size; + + /* Find where to insert this region. Put a smaller one before bigger ones, + to prevent fragmentation. */ + for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p) + if (q->size > r->size) + break; + + *p = r; + r->next = q; +} + +/* Allocate the number of units N with the alignment ALIGN from the ring + * buffer given in *FIRST. ALIGN must be a power of two. Both N and + * ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful, + * otherwise return NULL. + * + * Note: because in certain circumstances we need to adjust the ->next + * pointer of the previous block, we iterate over the singly linked + * list with the pair (prev, cur). *FIRST is our initial previous, and + * *FIRST->next is our initial current pointer. So we will actually + * allocate from *FIRST->next first and *FIRST itself last. + */ +static void * +grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align) +{ + grub_mm_header_t cur, prev; + + /* When everything is allocated side effect is that *first will have alloc + magic marked, meaning that there is no room in this region. */ + if ((*first)->magic == GRUB_MM_ALLOC_MAGIC) + return 0; + + /* Try to search free slot for allocation in this memory region. */ + for (prev = *first, cur = prev->next; ; prev = cur, cur = cur->next) + { + grub_off_t extra; + + extra = ((grub_addr_t) (cur + 1) >> GRUB_MM_ALIGN_LOG2) & (align - 1); + if (extra) + extra = align - extra; + + if (! cur) + grub_fatal ("null in the ring"); + + if (cur->magic != GRUB_MM_FREE_MAGIC) + grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic); + + if (cur->size >= n + extra) + { + extra += (cur->size - extra - n) & (~(align - 1)); + if (extra == 0 && cur->size == n) + { + /* There is no special alignment requirement and memory block + is complete match. + + 1. Just mark memory block as allocated and remove it from + free list. + + Result: + +---------------+ previous block's next + | alloc, size=n | | + +---------------+ v + */ + prev->next = cur->next; + } + else if (align == 1 || cur->size == n + extra) + { + /* There might be alignment requirement, when taking it into + account memory block fits in. + + 1. Allocate new area at end of memory block. + 2. Reduce size of available blocks from original node. + 3. Mark new area as allocated and "remove" it from free + list. + + Result: + +---------------+ + | free, size-=n | next --+ + +---------------+ | + | alloc, size=n | | + +---------------+ v + */ + cur->size -= n; + cur += cur->size; + } + else if (extra == 0) + { + grub_mm_header_t r; + + r = cur + extra + n; + r->magic = GRUB_MM_FREE_MAGIC; + r->size = cur->size - extra - n; + r->next = cur->next; + prev->next = r; + + if (prev == cur) + { + prev = r; + r->next = r; + } + } + else + { + /* There is alignment requirement and there is room in memory + block. Split memory block to three pieces. + + 1. Create new memory block right after section being + allocated. Mark it as free. + 2. Add new memory block to free chain. + 3. Mark current memory block having only extra blocks. + 4. Advance to aligned block and mark that as allocated and + "remove" it from free list. + + Result: + +------------------------------+ + | free, size=extra | next --+ + +------------------------------+ | + | alloc, size=n | | + +------------------------------+ | + | free, size=orig.size-extra-n | <------+, next --+ + +------------------------------+ v + */ + grub_mm_header_t r; + + r = cur + extra + n; + r->magic = GRUB_MM_FREE_MAGIC; + r->size = cur->size - extra - n; + r->next = cur; + + cur->size = extra; + prev->next = r; + cur += extra; + } + + cur->magic = GRUB_MM_ALLOC_MAGIC; + cur->size = n; + + /* Mark find as a start marker for next allocation to fasten it. + This will have side effect of fragmenting memory as small + pieces before this will be un-used. */ + /* So do it only for chunks under 32K. */ + if (n < (0x8000 >> GRUB_MM_ALIGN_LOG2) + || *first == cur) + *first = prev; + + return cur + 1; + } + + /* Search was completed without result. */ + if (cur == *first) + break; + } + + return 0; +} + +/* Allocate SIZE bytes with the alignment ALIGN and return the pointer. */ +void * +grub_memalign (grub_size_t align, grub_size_t size) +{ + grub_mm_region_t r; + grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1; + grub_size_t grow; + int count = 0; + + if (!grub_mm_base) + goto fail; + + if (size > ~(grub_size_t) align) + goto fail; + + grow = size + align; + + /* We currently assume at least a 32-bit grub_size_t, + so limiting allocations to - 1MiB + in name of sanity is beneficial. */ + if (grow > ~(grub_size_t) 0x100000) + goto fail; + + align = (align >> GRUB_MM_ALIGN_LOG2); + if (align == 0) + align = 1; + + again: + + for (r = grub_mm_base; r; r = r->next) + { + void *p; + + p = grub_real_malloc (&(r->first), n, align); + if (p) + return p; + } + + /* If failed, increase free memory somehow. */ + switch (count) + { + case 0: + /* Request additional pages, contiguous */ + count++; + + /* + * Calculate the necessary size of heap growth (if applicable), + * with region management overhead taken into account. + */ + if (grub_add (grow, GRUB_MM_MGMT_OVERHEAD, &grow)) + goto fail; + + /* Preallocate some extra space if heap growth is small. */ + grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA); + + /* Align up heap growth to make it friendly to CPU/MMU. */ + if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) + goto fail; + grow = ALIGN_UP (grow, GRUB_MM_HEAP_GROW_ALIGN); + + /* Do the same sanity check again. */ + if (grow > ~(grub_size_t) 0x100000) + goto fail; + + if (grub_mm_add_region_fn != NULL && + grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_CONSECUTIVE) == GRUB_ERR_NONE) + goto again; + + /* fallthrough */ + + case 1: + /* Request additional pages, anything at all */ + count++; + + if (grub_mm_add_region_fn != NULL) + { + /* + * Try again even if this fails, in case it was able to partially + * satisfy the request + */ + grub_mm_add_region_fn (grow, GRUB_MM_ADD_REGION_NONE); + goto again; + } + + /* fallthrough */ + + case 2: + /* Invalidate disk caches. */ + grub_disk_cache_invalidate_all (); + count++; + goto again; + + default: + break; + } + + fail: + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + return 0; +} + +/* + * Allocate NMEMB instances of SIZE bytes and return the pointer, or error on + * integer overflow. + */ +void * +grub_calloc (grub_size_t nmemb, grub_size_t size) +{ + void *ret; + grub_size_t sz = 0; + + if (grub_mul (nmemb, size, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + + ret = grub_memalign (0, sz); + if (!ret) + return NULL; + + grub_memset (ret, 0, sz); + return ret; +} + +/* Allocate SIZE bytes and return the pointer. */ +void * +grub_malloc (grub_size_t size) +{ + return grub_memalign (0, size); +} + +/* Allocate SIZE bytes, clear them and return the pointer. */ +void * +grub_zalloc (grub_size_t size) +{ + void *ret; + + ret = grub_memalign (0, size); + if (ret) + grub_memset (ret, 0, size); + + return ret; +} + +/* Deallocate the pointer PTR. */ +void +grub_free (void *ptr) +{ + grub_mm_header_t p; + grub_mm_region_t r; + + if (! ptr) + return; + + get_header_from_pointer (ptr, &p, &r); + + if (r->first->magic == GRUB_MM_ALLOC_MAGIC) + { + p->magic = GRUB_MM_FREE_MAGIC; + r->first = p->next = p; + } + else + { + grub_mm_header_t cur, prev; + +#if 0 + cur = r->first; + do + { + grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n", + GRUB_FILE, __LINE__, cur, cur->size, cur->magic); + cur = cur->next; + } + while (cur != r->first); +#endif + /* Iterate over all blocks in the free ring. + * + * The free ring is arranged from high addresses to low + * addresses, modulo wraparound. + * + * We are looking for a block with a higher address than p or + * whose next address is lower than p. + */ + for (prev = r->first, cur = prev->next; cur <= p || cur->next >= p; + prev = cur, cur = prev->next) + { + if (cur->magic != GRUB_MM_FREE_MAGIC) + grub_fatal ("free magic is broken at %p: 0x%x", cur, cur->magic); + + /* Deal with wrap-around */ + if (cur <= cur->next && (cur > p || cur->next < p)) + break; + } + + /* mark p as free and insert it between cur and cur->next */ + p->magic = GRUB_MM_FREE_MAGIC; + p->next = cur->next; + cur->next = p; + + /* + * If the block we are freeing can be merged with the next + * free block, do that. + */ + if (p->next + p->next->size == p) + { + p->magic = 0; + + p->next->size += p->size; + cur->next = p->next; + p = p->next; + } + + r->first = cur; + + /* Likewise if can be merged with the preceeding free block */ + if (cur == p + p->size) + { + cur->magic = 0; + p->size += cur->size; + if (cur == prev) + prev = p; + prev->next = p; + cur = prev; + } + + /* + * Set r->first such that the just free()d block is tried first. + * (An allocation is tried from *first->next, and cur->next == p.) + */ + r->first = cur; + } +} + +/* Reallocate SIZE bytes and return the pointer. The contents will be + the same as that of PTR. */ +void * +grub_realloc (void *ptr, grub_size_t size) +{ + grub_mm_header_t p; + grub_mm_region_t r; + void *q; + grub_size_t n; + + if (! ptr) + return grub_malloc (size); + + if (! size) + { + grub_free (ptr); + return 0; + } + + /* FIXME: Not optimal. */ + n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1; + get_header_from_pointer (ptr, &p, &r); + + if (p->size >= n) + return ptr; + + q = grub_malloc (size); + if (! q) + return q; + + /* We've already checked that p->size < n. */ + grub_memcpy (q, ptr, p->size << GRUB_MM_ALIGN_LOG2); + grub_free (ptr); + return q; +} + +#ifdef MM_DEBUG +int grub_mm_debug = 0; + +void +grub_mm_dump_free (void) +{ + grub_mm_region_t r; + + for (r = grub_mm_base; r; r = r->next) + { + grub_mm_header_t p; + + grub_printf ("Region %p (size %" PRIuGRUB_SIZE ")\n\n", r, r->size); + + /* Follow the free list. */ + p = r->first; + do + { + if (p->magic != GRUB_MM_FREE_MAGIC) + grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic); + + grub_printf ("F:%p:%u:%p\n", + p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next); + p = p->next; + } + while (p != r->first); + } + + grub_printf ("\n"); +} + +void +grub_mm_dump (unsigned lineno) +{ + grub_mm_region_t r; + + grub_printf ("called at line %u\n", lineno); + for (r = grub_mm_base; r; r = r->next) + { + grub_mm_header_t p; + + grub_printf ("Region %p (size %" PRIuGRUB_SIZE ")\n\n", r, r->size); + + for (p = (grub_mm_header_t) ALIGN_UP ((grub_addr_t) (r + 1), + GRUB_MM_ALIGN); + (grub_addr_t) p < (grub_addr_t) (r+1) + r->size; + p++) + { + switch (p->magic) + { + case GRUB_MM_FREE_MAGIC: + grub_printf ("F:%p:%u:%p\n", + p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next); + break; + case GRUB_MM_ALLOC_MAGIC: + grub_printf ("A:%p:%u\n", p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2); + break; + } + } + } + + grub_printf ("\n"); +} + +void * +grub_debug_calloc (const char *file, int line, grub_size_t nmemb, grub_size_t size) +{ + void *ptr; + + if (grub_mm_debug) + grub_printf ("%s:%d: calloc (0x%" PRIxGRUB_SIZE ", 0x%" PRIxGRUB_SIZE ") = ", + file, line, nmemb, size); + ptr = grub_calloc (nmemb, size); + if (grub_mm_debug) + grub_printf ("%p\n", ptr); + return ptr; +} + +void * +grub_debug_malloc (const char *file, int line, grub_size_t size) +{ + void *ptr; + + if (grub_mm_debug) + grub_printf ("%s:%d: malloc (0x%" PRIxGRUB_SIZE ") = ", file, line, size); + ptr = grub_malloc (size); + if (grub_mm_debug) + grub_printf ("%p\n", ptr); + return ptr; +} + +void * +grub_debug_zalloc (const char *file, int line, grub_size_t size) +{ + void *ptr; + + if (grub_mm_debug) + grub_printf ("%s:%d: zalloc (0x%" PRIxGRUB_SIZE ") = ", file, line, size); + ptr = grub_zalloc (size); + if (grub_mm_debug) + grub_printf ("%p\n", ptr); + return ptr; +} + +void +grub_debug_free (const char *file, int line, void *ptr) +{ + if (grub_mm_debug) + grub_printf ("%s:%d: free (%p)\n", file, line, ptr); + grub_free (ptr); +} + +void * +grub_debug_realloc (const char *file, int line, void *ptr, grub_size_t size) +{ + if (grub_mm_debug) + grub_printf ("%s:%d: realloc (%p, 0x%" PRIxGRUB_SIZE ") = ", file, line, ptr, size); + ptr = grub_realloc (ptr, size); + if (grub_mm_debug) + grub_printf ("%p\n", ptr); + return ptr; +} + +void * +grub_debug_memalign (const char *file, int line, grub_size_t align, + grub_size_t size) +{ + void *ptr; + + if (grub_mm_debug) + grub_printf ("%s:%d: memalign (0x%" PRIxGRUB_SIZE ", 0x%" PRIxGRUB_SIZE + ") = ", file, line, align, size); + ptr = grub_memalign (align, size); + if (grub_mm_debug) + grub_printf ("%p\n", ptr); + return ptr; +} + +#endif /* MM_DEBUG */ diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c new file mode 100644 index 000000000..9b7b31a51 --- /dev/null +++ b/grub-core/kern/parser.c @@ -0,0 +1,344 @@ +/* parser.c - the part of the parser that can return partial tokens */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2007,2009,2021 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 . + */ + +#include +#include +#include +#include +#include + + +/* All the possible state transitions on the command line. If a + transition can not be found, it is assumed that there is no + transition and keep_value is assumed to be 1. */ +static struct grub_parser_state_transition state_transitions[] = { + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_QUOTE, '\'', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_DQUOTE, '\"', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_VAR, '$', 0}, + {GRUB_PARSER_STATE_TEXT, GRUB_PARSER_STATE_ESC, '\\', 0}, + + {GRUB_PARSER_STATE_ESC, GRUB_PARSER_STATE_TEXT, 0, 1}, + + {GRUB_PARSER_STATE_QUOTE, GRUB_PARSER_STATE_TEXT, '\'', 0}, + + {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_TEXT, '\"', 0}, + {GRUB_PARSER_STATE_DQUOTE, GRUB_PARSER_STATE_QVAR, '$', 0}, + + {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME2, '{', 0}, + {GRUB_PARSER_STATE_VAR, GRUB_PARSER_STATE_VARNAME, 0, 1}, + {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, ' ', 1}, + {GRUB_PARSER_STATE_VARNAME, GRUB_PARSER_STATE_TEXT, '\t', 1}, + {GRUB_PARSER_STATE_VARNAME2, GRUB_PARSER_STATE_TEXT, '}', 0}, + + {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME2, '{', 0}, + {GRUB_PARSER_STATE_QVAR, GRUB_PARSER_STATE_QVARNAME, 0, 1}, + {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_TEXT, '\"', 0}, + {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, ' ', 1}, + {GRUB_PARSER_STATE_QVARNAME, GRUB_PARSER_STATE_DQUOTE, '\t', 1}, + {GRUB_PARSER_STATE_QVARNAME2, GRUB_PARSER_STATE_DQUOTE, '}', 0}, + + {0, 0, 0, 0} +}; + + +/* Determines the state following STATE, determined by C. */ +grub_parser_state_t +grub_parser_cmdline_state (grub_parser_state_t state, char c, char *result) +{ + struct grub_parser_state_transition *transition; + struct grub_parser_state_transition default_transition; + + default_transition.to_state = state; + default_transition.keep_value = 1; + + /* Look for a good translation. */ + for (transition = state_transitions; transition->from_state; transition++) + { + if (transition->from_state != state) + continue; + /* An exact match was found, use it. */ + if (transition->input == c) + break; + + if (grub_isspace (transition->input) && !grub_isalpha (c) + && !grub_isdigit (c) && c != '_') + break; + + /* A less perfect match was found, use this one if no exact + match can be found. */ + if (transition->input == 0) + break; + } + + if (!transition->from_state) + transition = &default_transition; + + if (transition->keep_value) + *result = c; + else + *result = 0; + return transition->to_state; +} + + +/* Helper for grub_parser_split_cmdline. */ +static inline int +check_varstate (grub_parser_state_t s) +{ + return (s == GRUB_PARSER_STATE_VARNAME + || s == GRUB_PARSER_STATE_VARNAME2 + || s == GRUB_PARSER_STATE_QVARNAME + || s == GRUB_PARSER_STATE_QVARNAME2); +} + + +static grub_err_t +add_var (grub_buffer_t varname, grub_buffer_t buf, + grub_parser_state_t state, grub_parser_state_t newstate) +{ + const char *val; + + /* Check if a variable was being read in and the end of the name + was reached. */ + if (!(check_varstate (state) && !check_varstate (newstate))) + return GRUB_ERR_NONE; + + if (grub_buffer_append_char (varname, '\0') != GRUB_ERR_NONE) + return grub_errno; + + val = grub_env_get ((const char *) grub_buffer_peek_data (varname)); + grub_buffer_reset (varname); + if (!val) + return GRUB_ERR_NONE; + + /* Insert the contents of the variable in the buffer. */ + return grub_buffer_append_data (buf, val, grub_strlen (val)); +} + +static grub_err_t +terminate_arg (grub_buffer_t buffer, int *argc) +{ + grub_size_t unread = grub_buffer_get_unread_bytes (buffer); + + if (unread == 0) + return GRUB_ERR_NONE; + + if (*(const char *) grub_buffer_peek_data_at (buffer, unread - 1) == '\0') + return GRUB_ERR_NONE; + + if (grub_buffer_append_char (buffer, '\0') != GRUB_ERR_NONE) + return grub_errno; + + (*argc)++; + + return GRUB_ERR_NONE; +} + +static grub_err_t +process_char (char c, grub_buffer_t buffer, grub_buffer_t varname, + grub_parser_state_t state, int *argc, + grub_parser_state_t *newstate) +{ + char use; + + *newstate = grub_parser_cmdline_state (state, c, &use); + + /* + * If a variable was being processed and this character does + * not describe the variable anymore, write the variable to + * the buffer. + */ + if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE) + return grub_errno; + + if (check_varstate (*newstate)) + { + if (use) + return grub_buffer_append_char (varname, use); + } + else if (*newstate == GRUB_PARSER_STATE_TEXT && + state != GRUB_PARSER_STATE_ESC && grub_isspace (use)) + { + /* + * Don't add more than one argument if multiple + * spaces are used. + */ + return terminate_arg (buffer, argc); + } + else if (use) + return grub_buffer_append_char (buffer, use); + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_parser_split_cmdline (const char *cmdline, + grub_reader_getline_t getline, void *getline_data, + int *argc, char ***argv) +{ + grub_parser_state_t state = GRUB_PARSER_STATE_TEXT; + grub_buffer_t buffer, varname; + char *rd = (char *) cmdline; + char *rp = rd; + int i; + + *argc = 0; + *argv = NULL; + + buffer = grub_buffer_new (1024); + if (buffer == NULL) + return grub_errno; + + varname = grub_buffer_new (200); + if (varname == NULL) + goto fail; + + do + { + if (rp == NULL || *rp == '\0') + { + if (rd != cmdline) + { + grub_free (rd); + rd = rp = NULL; + } + if (getline) + { + getline (&rd, 1, getline_data); + rp = rd; + } + else + break; + } + + if (!rd) + break; + + for (; *rp != '\0'; rp++) + { + grub_parser_state_t newstate; + + if (process_char (*rp, buffer, varname, state, argc, + &newstate) != GRUB_ERR_NONE) + goto fail; + + state = newstate; + } + } + while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state)); + + /* A special case for when the last character was part of a + variable. */ + if (add_var (varname, buffer, state, GRUB_PARSER_STATE_TEXT) != GRUB_ERR_NONE) + goto fail; + + /* Ensure that the last argument is terminated. */ + if (terminate_arg (buffer, argc) != GRUB_ERR_NONE) + goto fail; + + /* If there are no args, then we're done. */ + if (!*argc) + { + grub_errno = GRUB_ERR_NONE; + goto out; + } + + *argv = grub_calloc (*argc + 1, sizeof (char *)); + if (!*argv) + goto fail; + + /* The arguments are separated with 0's, setup argv so it points to + the right values. */ + for (i = 0; i < *argc; i++) + { + char *arg; + + if (i > 0) + { + if (grub_buffer_advance_read_pos (buffer, 1) != GRUB_ERR_NONE) + goto fail; + } + + arg = (char *) grub_buffer_peek_data (buffer); + if (arg == NULL || + grub_buffer_advance_read_pos (buffer, grub_strlen (arg)) != GRUB_ERR_NONE) + goto fail; + + (*argv)[i] = arg; + } + + /* Keep memory for the return values. */ + grub_buffer_take_data (buffer); + + grub_errno = GRUB_ERR_NONE; + + out: + if (rd != cmdline) + grub_free (rd); + grub_buffer_free (buffer); + grub_buffer_free (varname); + + return grub_errno; + + fail: + grub_free (*argv); + *argv = NULL; + *argc = 0; + goto out; +} + +/* Helper for grub_parser_execute. */ +static grub_err_t +grub_parser_execute_getline (char **line, int cont __attribute__ ((unused)), + void *data) +{ + char **source = data; + 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; +} + +grub_err_t +grub_parser_execute (char *source) +{ + while (source) + { + char *line; + + grub_parser_execute_getline (&line, 0, &source); + grub_rescue_parse_line (line, grub_parser_execute_getline, &source); + grub_free (line); + grub_print_error (); + } + + return grub_errno; +} diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c new file mode 100644 index 000000000..3b128e6d2 --- /dev/null +++ b/grub-core/kern/partition.c @@ -0,0 +1,295 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,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 . + */ + +#include +#include +#include +#include +#include + +#ifdef GRUB_UTIL +#include +#endif + +grub_partition_map_t grub_partition_map_list; + +#define MAX_RECURSION_DEPTH 32 +static unsigned int recursion_depth = 0; + +/* + * Checks that disk->partition contains part. This function assumes that the + * start of part is relative to the start of disk->partition. Returns 1 if + * disk->partition is null. + */ +static int +grub_partition_check_containment (const grub_disk_t disk, + const grub_partition_t part) +{ + if (disk->partition == NULL) + return 1; + + if (part->start + part->len > disk->partition->len) + { + char *partname; + + partname = grub_partition_get_name (disk->partition); + grub_dprintf ("partition", "sub-partition %s%d of (%s,%s) ends after parent.\n", + part->partmap->name, part->number + 1, disk->name, partname); +#ifdef GRUB_UTIL + grub_util_warn (_("Discarding improperly nested partition (%s,%s,%s%d)"), + disk->name, partname, part->partmap->name, part->number + 1); +#endif + grub_free (partname); + + return 0; + } + + return 1; +} + +/* Context for grub_partition_map_probe. */ +struct grub_partition_map_probe_ctx +{ + int partnum; + grub_partition_t p; +}; + +/* Helper for grub_partition_map_probe. */ +static int +probe_iter (grub_disk_t dsk, const grub_partition_t partition, void *data) +{ + struct grub_partition_map_probe_ctx *ctx = data; + + if (ctx->partnum != partition->number) + return 0; + + if (!(grub_partition_check_containment (dsk, partition))) + return 0; + + ctx->p = (grub_partition_t) grub_malloc (sizeof (*ctx->p)); + if (! ctx->p) + return 1; + + grub_memcpy (ctx->p, partition, sizeof (*ctx->p)); + return 1; +} + +static grub_partition_t +grub_partition_map_probe (const grub_partition_map_t partmap, + grub_disk_t disk, int partnum) +{ + struct grub_partition_map_probe_ctx ctx = { + .partnum = partnum, + .p = 0 + }; + + partmap->iterate (disk, probe_iter, &ctx); + if (grub_errno) + goto fail; + + return ctx.p; + + fail: + grub_free (ctx.p); + return 0; +} + +grub_partition_t +grub_partition_probe (struct grub_disk *disk, const char *str) +{ + grub_partition_t part; + grub_partition_t curpart = 0; + grub_partition_t tail; + const char *ptr; + + if (str == NULL) + return 0; + + part = tail = disk->partition; + + for (ptr = str; *ptr;) + { + grub_partition_map_t partmap; + unsigned long num; + const char *partname, *partname_end; + + partname = ptr; + while (*ptr && grub_isalpha (*ptr)) + ptr++; + partname_end = ptr; + + num = grub_strtoul (ptr, &ptr, 0); + if (num == 0 || num > GRUB_INT_MAX) + { + grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid partition number")); + return 0; + } + + num -= 1; + + curpart = 0; + /* Use the first partition map type found. */ + FOR_PARTITION_MAPS(partmap) + { + if (partname_end != partname && + (grub_strncmp (partmap->name, partname, partname_end - partname) + != 0 || partmap->name[partname_end - partname] != 0)) + continue; + + disk->partition = part; + curpart = grub_partition_map_probe (partmap, disk, num); + disk->partition = tail; + if (curpart) + break; + + if (grub_errno == GRUB_ERR_BAD_PART_TABLE) + { + /* Continue to next partition map type. */ + grub_errno = GRUB_ERR_NONE; + continue; + } + + break; + } + + if (! curpart) + { + while (part) + { + curpart = part->parent; + grub_free (part); + part = curpart; + } + return 0; + } + curpart->parent = part; + part = curpart; + if (! ptr || *ptr != ',') + break; + ptr++; + } + + return part; +} + +/* Context for grub_partition_iterate. */ +struct grub_partition_iterate_ctx +{ + int ret; + grub_partition_iterate_hook_t hook; + void *hook_data; +}; + +/* Helper for grub_partition_iterate. */ +static int +part_iterate (grub_disk_t dsk, const grub_partition_t partition, void *data) +{ + struct grub_partition_iterate_ctx *ctx = data; + struct grub_partition p = *partition; + + if (!(grub_partition_check_containment (dsk, partition))) + return 0; + + p.parent = dsk->partition; + dsk->partition = 0; + if (ctx->hook (dsk, &p, ctx->hook_data)) + { + ctx->ret = 1; + return 1; + } + if (p.start != 0) + { + const struct grub_partition_map *partmap; + dsk->partition = &p; + FOR_PARTITION_MAPS(partmap) + { + grub_err_t err; + recursion_depth++; + if (recursion_depth <= MAX_RECURSION_DEPTH) + err = partmap->iterate (dsk, part_iterate, ctx); + else + err = grub_error (GRUB_ERR_RECURSION_DEPTH, "maximum recursion depth exceeded"); + recursion_depth--; + if (err) + grub_errno = GRUB_ERR_NONE; + if (ctx->ret) + break; + } + } + dsk->partition = p.parent; + return ctx->ret; +} + +int +grub_partition_iterate (struct grub_disk *disk, + grub_partition_iterate_hook_t hook, void *hook_data) +{ + struct grub_partition_iterate_ctx ctx = { + .ret = 0, + .hook = hook, + .hook_data = hook_data + }; + const struct grub_partition_map *partmap; + + FOR_PARTITION_MAPS(partmap) + { + grub_err_t err; + err = partmap->iterate (disk, part_iterate, &ctx); + if (err) + grub_errno = GRUB_ERR_NONE; + if (ctx.ret) + break; + } + + return ctx.ret; +} + +char * +grub_partition_get_name (const grub_partition_t partition) +{ + char *out = 0, *ptr; + grub_size_t needlen = 0; + grub_partition_t part; + if (!partition) + return grub_strdup (""); + for (part = partition; part; part = part->parent) + /* Even on 64-bit machines this buffer is enough to hold + longest number. */ + needlen += grub_strlen (part->partmap->name) + 1 + 27; + out = grub_malloc (needlen + 1); + if (!out) + return NULL; + + ptr = out + needlen; + *ptr = 0; + for (part = partition; part; part = part->parent) + { + char buf[27]; + grub_size_t len; + grub_snprintf (buf, sizeof (buf), "%d", part->number + 1); + len = grub_strlen (buf); + ptr -= len; + grub_memcpy (ptr, buf, len); + len = grub_strlen (part->partmap->name); + ptr -= len; + grub_memcpy (ptr, part->partmap->name, len); + *--ptr = ','; + } + grub_memmove (out, ptr + 1, out + needlen - ptr); + return out; +} diff --git a/grub-core/kern/powerpc/cache.S b/grub-core/kern/powerpc/cache.S new file mode 100644 index 000000000..d85e68d42 --- /dev/null +++ b/grub-core/kern/powerpc/cache.S @@ -0,0 +1,26 @@ +/* cache.S - Flush the processor cache for a specific region. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2004,2007,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 . + */ + + .text + + .align 2 + .globl grub_arch_sync_caches +grub_arch_sync_caches: +#include "cache_flush.S" + blr diff --git a/kern/powerpc/cache.S b/grub-core/kern/powerpc/cache_flush.S similarity index 91% rename from kern/powerpc/cache.S rename to grub-core/kern/powerpc/cache_flush.S index da982afa0..1410f78b1 100644 --- a/kern/powerpc/cache.S +++ b/grub-core/kern/powerpc/cache_flush.S @@ -1,7 +1,7 @@ /* cache.S - Flush the processor cache for a specific region. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2004,2007 Free Software Foundation, Inc. + * Copyright (C) 2004,2007,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 @@ -17,13 +17,9 @@ * along with GRUB. If not, see . */ +#undef CACHE_LINE_BYTES #define CACHE_LINE_BYTES 32 - .text - - .align 2 - .globl grub_arch_sync_caches -grub_arch_sync_caches: /* `address' may not be CACHE_LINE_BYTES-aligned. */ andi. 6, 3, CACHE_LINE_BYTES - 1 /* Find the misalignment. */ add 4, 4, 6 /* Adjust `size' to compensate. */ @@ -45,4 +41,3 @@ grub_arch_sync_caches: sync /* Force all icbis to complete. */ isync /* Discard partially executed instructions that were loaded from the invalid icache. */ - blr diff --git a/grub-core/kern/powerpc/compiler-rt.S b/grub-core/kern/powerpc/compiler-rt.S new file mode 100644 index 000000000..b3b912db6 --- /dev/null +++ b/grub-core/kern/powerpc/compiler-rt.S @@ -0,0 +1,130 @@ +/* + * Special support for eabi and SVR4 + * + * Copyright (C) 1995-2014 Free Software Foundation, Inc. + * Written By Michael Meissner + * 64-bit support written by David Edelsohn + * + * This file 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, or (at your option) any + * later version. + * + * This file 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. + * + * Under Section 7 of GPL version 3, you are granted additional + * permissions described in the GCC Runtime Library Exception, version + * 3.1, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License and + * a copy of the GCC Runtime Library Exception along with this program; + * see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + * . + */ + +/* Do any initializations needed for the eabi environment */ + +#include +#include + + .section ".text" + +#define CFI_RESTORE(reg) .cfi_restore reg +#define CFI_OFFSET(reg, off) .cfi_offset reg, off +#define CFI_DEF_CFA_REGISTER(reg) .cfi_def_cfa_register reg +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc + +/* Routines for restoring integer registers, called by the compiler. */ +/* Called with r11 pointing to the stack header word of the caller of the */ +/* function, just beyond the end of the integer restore area. */ + +CFI_STARTPROC +CFI_DEF_CFA_REGISTER (11) +CFI_OFFSET (65, 4) +CFI_OFFSET (14, -72) +CFI_OFFSET (15, -68) +CFI_OFFSET (16, -64) +CFI_OFFSET (17, -60) +CFI_OFFSET (18, -56) +CFI_OFFSET (19, -52) +CFI_OFFSET (20, -48) +CFI_OFFSET (21, -44) +CFI_OFFSET (22, -40) +CFI_OFFSET (23, -36) +CFI_OFFSET (24, -32) +CFI_OFFSET (25, -28) +CFI_OFFSET (26, -24) +CFI_OFFSET (27, -20) +CFI_OFFSET (28, -16) +CFI_OFFSET (29, -12) +CFI_OFFSET (30, -8) +CFI_OFFSET (31, -4) +FUNCTION(_restgpr_14_x) lwz 14,-72(11) /* restore gp registers */ +CFI_RESTORE (14) +FUNCTION(_restgpr_15_x) lwz 15,-68(11) +CFI_RESTORE (15) +FUNCTION(_restgpr_16_x) lwz 16,-64(11) +CFI_RESTORE (16) +FUNCTION(_restgpr_17_x) lwz 17,-60(11) +CFI_RESTORE (17) +FUNCTION(_restgpr_18_x) lwz 18,-56(11) +CFI_RESTORE (18) +FUNCTION(_restgpr_19_x) lwz 19,-52(11) +CFI_RESTORE (19) +FUNCTION(_restgpr_20_x) lwz 20,-48(11) +CFI_RESTORE (20) +FUNCTION(_restgpr_21_x) lwz 21,-44(11) +CFI_RESTORE (21) +FUNCTION(_restgpr_22_x) lwz 22,-40(11) +CFI_RESTORE (22) +FUNCTION(_restgpr_23_x) lwz 23,-36(11) +CFI_RESTORE (23) +FUNCTION(_restgpr_24_x) lwz 24,-32(11) +CFI_RESTORE (24) +FUNCTION(_restgpr_25_x) lwz 25,-28(11) +CFI_RESTORE (25) +FUNCTION(_restgpr_26_x) lwz 26,-24(11) +CFI_RESTORE (26) +FUNCTION(_restgpr_27_x) lwz 27,-20(11) +CFI_RESTORE (27) +FUNCTION(_restgpr_28_x) lwz 28,-16(11) +CFI_RESTORE (28) +FUNCTION(_restgpr_29_x) lwz 29,-12(11) +CFI_RESTORE (29) +FUNCTION(_restgpr_30_x) lwz 30,-8(11) +CFI_RESTORE (30) +FUNCTION(_restgpr_31_x) lwz 0,4(11) + lwz 31,-4(11) +CFI_RESTORE (31) + mtlr 0 +CFI_RESTORE (65) + mr 1,11 +CFI_DEF_CFA_REGISTER (1) + blr +CFI_ENDPROC + +CFI_STARTPROC +FUNCTION(_savegpr_14) stw 14,-72(11) /* save gp registers */ +FUNCTION(_savegpr_15) stw 15,-68(11) +FUNCTION(_savegpr_16) stw 16,-64(11) +FUNCTION(_savegpr_17) stw 17,-60(11) +FUNCTION(_savegpr_18) stw 18,-56(11) +FUNCTION(_savegpr_19) stw 19,-52(11) +FUNCTION(_savegpr_20) stw 20,-48(11) +FUNCTION(_savegpr_21) stw 21,-44(11) +FUNCTION(_savegpr_22) stw 22,-40(11) +FUNCTION(_savegpr_23) stw 23,-36(11) +FUNCTION(_savegpr_24) stw 24,-32(11) +FUNCTION(_savegpr_25) stw 25,-28(11) +FUNCTION(_savegpr_26) stw 26,-24(11) +FUNCTION(_savegpr_27) stw 27,-20(11) +FUNCTION(_savegpr_28) stw 28,-16(11) +FUNCTION(_savegpr_29) stw 29,-12(11) +FUNCTION(_savegpr_30) stw 30,-8(11) +FUNCTION(_savegpr_31) stw 31,-4(11) + blr +CFI_ENDPROC diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c new file mode 100644 index 000000000..7b6418eab --- /dev/null +++ b/grub-core/kern/powerpc/dl.c @@ -0,0 +1,169 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2MSB + || e->e_machine != EM_PPC) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* For low-endian reverse lis and addr_high as well as ori and addr_low. */ +struct trampoline +{ + grub_uint32_t lis; + grub_uint32_t ori; + grub_uint32_t mtctr; + grub_uint32_t bctr; +}; + +static const struct trampoline trampoline_template = + { + 0x3d800000, + 0x618c0000, + 0x7d8903a6, + 0x4e800420, + }; + +#pragma GCC diagnostic ignored "-Wcast-align" + +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) + if (s->sh_type == SHT_RELA) + { + const Elf_Rela *rel, *max; + + for (rel = (const Elf_Rela *) ((const char *) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel++) + if (ELF_R_TYPE (rel->r_info) == GRUB_ELF_R_PPC_REL24 + || ELF_R_TYPE (rel->r_info) == GRUB_ELF_R_PPC_PLTREL24) + (*tramp)++; + + } + + *tramp *= sizeof (struct trampoline); + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rela *rel, *max; + + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + grub_uint32_t value; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + /* On the PPC the value does not have an explicit + addend, add it. */ + value = sym->st_value + rel->r_addend; + switch (ELF_R_TYPE (rel->r_info)) + { + case GRUB_ELF_R_PPC_ADDR16_LO: + *(Elf_Half *) addr = value; + break; + + case GRUB_ELF_R_PPC_PLTREL24: + case GRUB_ELF_R_PPC_REL24: + { + Elf_Sword delta = value - (Elf_Word) addr; + + if (delta << 6 >> 6 != delta) + { + struct trampoline *tptr = mod->trampptr; + grub_memcpy (tptr, &trampoline_template, + sizeof (*tptr)); + delta = (grub_uint8_t *) tptr - (grub_uint8_t *) addr; + tptr->lis |= (((value) >> 16) & 0xffff); + tptr->ori |= ((value) & 0xffff); + mod->trampptr = tptr + 1; + } + + if (delta << 6 >> 6 != delta) + return grub_error (GRUB_ERR_BAD_MODULE, + "relocation overflow"); + *addr = (*addr & 0xfc000003) | (delta & 0x3fffffc); + break; + } + + case GRUB_ELF_R_PPC_ADDR16_HA: + *(Elf_Half *) addr = (value + 0x8000) >> 16; + break; + + case GRUB_ELF_R_PPC_ADDR32: + *addr = value; + break; + + case GRUB_ELF_R_PPC_REL32: + *addr = value - (Elf_Word) addr; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } + + return GRUB_ERR_NONE; +} diff --git a/kern/powerpc/ieee1275/startup.S b/grub-core/kern/powerpc/ieee1275/startup.S similarity index 84% rename from kern/powerpc/ieee1275/startup.S rename to grub-core/kern/powerpc/ieee1275/startup.S index 75e1ed852..21c884b43 100644 --- a/kern/powerpc/ieee1275/startup.S +++ b/grub-core/kern/powerpc/ieee1275/startup.S @@ -18,7 +18,7 @@ */ #include -#include +#include .extern __bss_start .extern _end @@ -28,34 +28,37 @@ .globl start, _start start: _start: - b codestart - - . = _start + GRUB_KERNEL_CPU_PREFIX - -VARIABLE(grub_prefix) - /* to be filled by grub-mkelfimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = _start + GRUB_KERNEL_CPU_DATA_END - -codestart: li 2, 0 li 13, 0 /* Stage1 won't zero BSS for us. In other cases, why not do it again? */ lis 6, (__bss_start - 4)@h ori 6, 6, (__bss_start - 4)@l + +2: stb 2, 4(6) + addi 6, 6, 1 + andi. 7, 6, 3 + cmpi 0, 1, 7, 0 + bne 2b + lis 7, (_end - 4)@h ori 7, 7, (_end - 4)@l subf 7, 6, 7 + subi 8, 7, 1 + andi. 8, 8, 3 + addi 8, 8, 1 + sub 7, 7, 8 + srwi 7, 7, 2 /* We store 4 bytes at a time. */ mtctr 7 2: stwu 2, 4(6) /* We know r2 is already 0 from above. */ bdnz 2b + mtctr 8 +2: stb 2, 4(6) /* We know r2 is already 0 from above. */ + addi 6, 6, 1 + bdnz 2b + /* Store r5 in grub_ieee1275_entry_fn. */ lis 9, grub_ieee1275_entry_fn@ha stw 5, grub_ieee1275_entry_fn@l(9) diff --git a/kern/rescue_parser.c b/grub-core/kern/rescue_parser.c similarity index 71% rename from kern/rescue_parser.c rename to grub-core/kern/rescue_parser.c index d3725e739..799641a03 100644 --- a/kern/rescue_parser.c +++ b/grub-core/kern/rescue_parser.c @@ -23,30 +23,43 @@ #include #include #include +#include -static grub_err_t -grub_rescue_parse_line (char *line, grub_reader_getline_t getline) +grub_err_t +grub_rescue_parse_line (char *line, + grub_reader_getline_t getline, void *getline_data) { char *name; int n; grub_command_t cmd; char **args; - if (grub_parser_split_cmdline (line, getline, &n, &args) || n < 0) - return grub_errno; + if (grub_parser_split_cmdline (line, getline, getline_data, &n, &args) + || n < 0) + { + grub_free(args); + return grub_errno; + } if (n == 0) - return GRUB_ERR_NONE; + { + grub_free(args); + return GRUB_ERR_NONE; + } /* In case of an assignment set the environment accordingly instead of calling a function. */ - if (n == 1 && grub_strchr (line, '=')) + if (n == 1) { char *val = grub_strchr (args[0], '='); - val[0] = 0; - grub_env_set (args[0], val + 1); - val[0] = '='; - goto quit; + + if (val) + { + val[0] = 0; + grub_env_set (args[0], val + 1); + val[0] = '='; + goto quit; + } } /* Get the command name. */ @@ -63,26 +76,15 @@ grub_rescue_parse_line (char *line, grub_reader_getline_t getline) } else { - grub_printf ("Unknown command `%s'\n", name); + grub_printf_ (N_("Unknown command `%s'.\n"), name); if (grub_command_find ("help")) grub_printf ("Try `help' for usage\n"); } quit: + /* Arguments are returned in single memory chunk separated by zeroes */ grub_free (args[0]); grub_free (args); return grub_errno; } - -static struct grub_parser grub_rescue_parser = - { - .name = "rescue", - .parse_line = grub_rescue_parse_line - }; - -void -grub_register_rescue_parser (void) -{ - grub_parser_register ("rescue", &grub_rescue_parser); -} diff --git a/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c similarity index 70% rename from kern/rescue_reader.c rename to grub-core/kern/rescue_reader.c index f573cf41f..a71ada8fb 100644 --- a/kern/rescue_reader.c +++ b/grub-core/kern/rescue_reader.c @@ -30,38 +30,44 @@ 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) +grub_rescue_read_line (char **line, int cont, + void *data __attribute__ ((unused))) { int c; int pos = 0; + char str[4]; grub_printf ((cont) ? "> " : "grub rescue> "); grub_memset (linebuf, 0, GRUB_RESCUE_BUF_SIZE); - while ((c = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && c != '\r') + 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_putchar (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_putchar (c); - grub_putchar (' '); - grub_putchar (c); + grub_xputs (str); } } grub_refresh (); } - grub_putchar ('\n'); + grub_xputs ("\n"); grub_refresh (); *line = grub_strdup (linebuf); @@ -69,9 +75,22 @@ grub_rescue_read_line (char **line, int cont) return 0; } -void +void __attribute__ ((noreturn)) grub_rescue_run (void) { + /* Stall if the CLI has been disabled */ + if (grub_is_cli_disabled () || grub_is_cli_need_auth ()) + { + grub_printf ("Rescue mode has been disabled...\n"); + + do + { + /* Do not optimize out the loop. */ + asm volatile (""); + } + while (1); + } + grub_printf ("Entering rescue mode...\n"); while (1) @@ -82,11 +101,11 @@ grub_rescue_run (void) grub_print_error (); grub_errno = GRUB_ERR_NONE; - grub_rescue_read_line (&line, 0); + grub_rescue_read_line (&line, 0, NULL); if (! line || line[0] == '\0') continue; - grub_parser_get_current ()->parse_line (line, grub_rescue_read_line); + grub_rescue_parse_line (line, grub_rescue_read_line, NULL); grub_free (line); } } diff --git a/grub-core/kern/riscv/cache.c b/grub-core/kern/riscv/cache.c new file mode 100644 index 000000000..47777a033 --- /dev/null +++ b/grub-core/kern/riscv/cache.c @@ -0,0 +1,63 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include + +static grub_int64_t dlinesz; +static grub_int64_t ilinesz; + +/* Prototypes for asm functions. */ +void grub_arch_clean_dcache_range (grub_addr_t beg, grub_addr_t end, + grub_size_t line_size); +void grub_arch_invalidate_icache_range (grub_addr_t beg, grub_addr_t end, + grub_size_t line_size); + +static void +probe_caches (void) +{ + /* TODO */ + dlinesz = 32; + ilinesz = 32; +} + +void +grub_arch_sync_caches (void *address, grub_size_t len) +{ + grub_size_t start, end, max_align; + + if (dlinesz == 0) + probe_caches(); + if (dlinesz == 0) + grub_fatal ("Unknown cache line size!"); + + max_align = dlinesz > ilinesz ? dlinesz : ilinesz; + + start = ALIGN_DOWN ((grub_size_t) address, max_align); + end = ALIGN_UP ((grub_size_t) address + len, max_align); + + grub_arch_clean_dcache_range (start, end, dlinesz); + grub_arch_invalidate_icache_range (start, end, ilinesz); +} + +void +grub_arch_sync_dma_caches (volatile void *address __attribute__((unused)), + grub_size_t len __attribute__((unused))) +{ + /* DMA incoherent devices not supported yet */ +} diff --git a/grub-core/kern/riscv/cache_flush.S b/grub-core/kern/riscv/cache_flush.S new file mode 100644 index 000000000..41de6e411 --- /dev/null +++ b/grub-core/kern/riscv/cache_flush.S @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include + + .file "cache_flush.S" + .text + +/* + * Simple cache maintenance functions + */ + +/* + * a0 - *beg (inclusive) + * a1 - *end (exclusive) + * a2 - line size +*/ +FUNCTION(grub_arch_clean_dcache_range) + /* TODO */ + ret + +/* + * a0 - *beg (inclusive) + * a1 - *end (exclusive) + * a2 - line size + */ +FUNCTION(grub_arch_invalidate_icache_range) + fence.i + ret diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c new file mode 100644 index 000000000..896653bb4 --- /dev/null +++ b/grub-core/kern/riscv/dl.c @@ -0,0 +1,346 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include +#include +#include +#include +#include + +/* + * Instructions and instruction encoding are documented in the RISC-V + * specification. This file is based on version 2.2: + * + * https://github.com/riscv/riscv-isa-manual/blob/master/release/riscv-spec-v2.2.pdf + */ +#define LDR 0x58000050 +#define BR 0xd61f0200 + +/* + * Check if EHDR is a valid ELF header. + */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_RISCV) + return grub_error (GRUB_ERR_BAD_OS, + N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Sym *sym; + void *place; + grub_size_t sym_addr; + + if (rel->r_offset >= seg->size) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + sym_addr = sym->st_value; + if (s->sh_type == SHT_RELA) + sym_addr += ((Elf_Rela *) rel)->r_addend; + + place = (void *) ((grub_addr_t) seg->addr + rel->r_offset); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_RISCV_32: + { + grub_uint32_t *abs_place = place; + + grub_dprintf ("dl", " reloc_abs32 %p => 0x%016llx\n", + place, (unsigned long long) sym_addr); + + *abs_place = (grub_uint32_t) sym_addr; + } + break; + case R_RISCV_64: + { + grub_size_t *abs_place = place; + + grub_dprintf ("dl", " reloc_abs64 %p => 0x%016llx\n", + place, (unsigned long long) sym_addr); + + *abs_place = (grub_size_t) sym_addr; + } + break; + + case R_RISCV_ADD8: + { + grub_uint8_t *abs_place = place; + + *abs_place += (grub_uint8_t) sym_addr; + } + break; + case R_RISCV_ADD16: + { + grub_uint16_t *abs_place = place; + + *abs_place += (grub_uint16_t) sym_addr; + } + break; + case R_RISCV_ADD32: + { + grub_uint32_t *abs_place = place; + + *abs_place += (grub_uint32_t) sym_addr; + } + break; + case R_RISCV_ADD64: + { + grub_size_t *abs_place = place; + + *abs_place += (grub_size_t) sym_addr; + } + break; + + case R_RISCV_SUB8: + { + grub_uint8_t *abs_place = place; + + *abs_place -= (grub_uint8_t) sym_addr; + } + break; + case R_RISCV_SUB16: + { + grub_uint16_t *abs_place = place; + + *abs_place -= (grub_uint16_t) sym_addr; + } + break; + case R_RISCV_SUB32: + { + grub_uint32_t *abs_place = place; + + *abs_place -= (grub_uint32_t) sym_addr; + } + break; + case R_RISCV_SUB64: + { + grub_size_t *abs_place = place; + + *abs_place -= (grub_size_t) sym_addr; + } + break; + + case R_RISCV_BRANCH: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_uint32_t imm12 = (off & 0x1000) << (31 - 12); + grub_uint32_t imm11 = (off & 0x800) >> (11 - 7); + grub_uint32_t imm10_5 = (off & 0x7e0) << (30 - 10); + grub_uint32_t imm4_1 = (off & 0x1e) << (11 - 4); + *abs_place = (*abs_place & 0x1fff07f) + | imm12 | imm11 | imm10_5 | imm4_1; + } + break; + + case R_RISCV_JAL: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_uint32_t imm20 = (off & 0x100000) << (31 - 20); + grub_uint32_t imm19_12 = (off & 0xff000); + grub_uint32_t imm11 = (off & 0x800) << (20 - 11); + grub_uint32_t imm10_1 = (off & 0x7fe) << (30 - 10); + *abs_place = (*abs_place & 0xfff) + | imm20 | imm19_12 | imm11 | imm10_1; + } + break; + + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_uint32_t hi20, lo12; + + if (off != (grub_int32_t) off) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow"); + + hi20 = (off + 0x800) & 0xfffff000; + lo12 = (off - hi20) & 0xfff; + abs_place[0] = (abs_place[0] & 0xfff) | hi20; + abs_place[1] = (abs_place[1] & 0xfffff) | (lo12 << 20); + } + break; + + case R_RISCV_RVC_BRANCH: + { + grub_uint16_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_uint16_t imm8 = (off & 0x100) << (12 - 8); + grub_uint16_t imm7_6 = (off & 0xc0) >> (6 - 5); + grub_uint16_t imm5 = (off & 0x20) >> (5 - 2); + grub_uint16_t imm4_3 = (off & 0x18) << (12 - 5); + grub_uint16_t imm2_1 = (off & 0x6) << (12 - 10); + *abs_place = (*abs_place & 0xe383) + | imm8 | imm7_6 | imm5 | imm4_3 | imm2_1; + } + break; + + case R_RISCV_RVC_JUMP: + { + grub_uint16_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_uint16_t imm11 = (off & 0x800) << (12 - 11); + grub_uint16_t imm10 = (off & 0x400) >> (10 - 8); + grub_uint16_t imm9_8 = (off & 0x300) << (12 - 11); + grub_uint16_t imm7 = (off & 0x80) >> (7 - 6); + grub_uint16_t imm6 = (off & 0x40) << (12 - 11); + grub_uint16_t imm5 = (off & 0x20) >> (5 - 2); + grub_uint16_t imm4 = (off & 0x10) << (12 - 5); + grub_uint16_t imm3_1 = (off & 0xe) << (12 - 10); + *abs_place = ((*abs_place & 0xe003) + | imm11 | imm10 | imm9_8 | imm7 | imm6 + | imm5 | imm4 | imm3_1); + } + break; + + case R_RISCV_PCREL_HI20: + { + grub_uint32_t *abs_place = place; + grub_ssize_t off = sym_addr - (grub_addr_t) place; + grub_int32_t hi20; + + if (off != (grub_int32_t)off) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow"); + + hi20 = (off + 0x800) & 0xfffff000; + *abs_place = (*abs_place & 0xfff) | hi20; + } + break; + + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + { + grub_uint32_t *t32 = place; + Elf_Rela *rel2; + /* Search backwards for matching HI20 reloc. */ + for (rel2 = (Elf_Rela *) ((char *) rel - s->sh_entsize); + (unsigned long)rel2 >= ((unsigned long)ehdr + s->sh_offset); + rel2 = (Elf_Rela *) ((char *) rel2 - s->sh_entsize)) + { + Elf_Addr rel2_info; + Elf_Addr rel2_offset; + Elf_Addr rel2_sym_addr; + Elf_Addr rel2_loc; + grub_ssize_t rel2_off; + grub_ssize_t off; + Elf_Sym *sym2; + + rel2_offset = rel2->r_offset; + rel2_info = rel2->r_info; + rel2_loc = (grub_addr_t) seg->addr + rel2_offset; + + if (ELF_R_TYPE (rel2_info) == R_RISCV_PCREL_HI20 + && rel2_loc == sym_addr) + { + sym2 = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel2->r_info)); + rel2_sym_addr = sym2->st_value; + if (s->sh_type == SHT_RELA) + rel2_sym_addr += ((Elf_Rela *) rel2)->r_addend; + + rel2_off = rel2_sym_addr - rel2_loc; + off = rel2_off - ((rel2_off + 0x800) & 0xfffff000); + + if (ELF_R_TYPE (rel->r_info) == R_RISCV_PCREL_LO12_I) + *t32 = (*t32 & 0xfffff) | (off & 0xfff) << 20; + else + { + grub_uint32_t imm11_5 = (off & 0xfe0) << (31 - 11); + grub_uint32_t imm4_0 = (off & 0x1f) << (11 - 4); + *t32 = (*t32 & 0x1fff07f) | imm11_5 | imm4_0; + } + break; + } + } + if ((unsigned long)rel2 < ((unsigned long)ehdr + s->sh_offset)) + return grub_error (GRUB_ERR_BAD_MODULE, "cannot find matching HI20 relocation"); + } + break; + + case R_RISCV_HI20: + { + grub_uint32_t *abs_place = place; + *abs_place = (*abs_place & 0xfff) | + (((grub_int32_t) sym_addr + 0x800) & 0xfffff000); + } + break; + + case R_RISCV_LO12_I: + { + grub_uint32_t *abs_place = place; + grub_int32_t lo12 = (grub_int32_t) sym_addr - + (((grub_int32_t) sym_addr + 0x800) & 0xfffff000); + *abs_place = (*abs_place & 0xfffff) | ((lo12 & 0xfff) << 20); + } + break; + + case R_RISCV_LO12_S: + { + grub_uint32_t *abs_place = place; + grub_int32_t lo12 = (grub_int32_t) sym_addr - + (((grub_int32_t) sym_addr + 0x800) & 0xfffff000); + grub_uint32_t imm11_5 = (lo12 & 0xfe0) << (31 - 11); + grub_uint32_t imm4_0 = (lo12 & 0x1f) << (11 - 4); + *abs_place = (*abs_place & 0x1fff07f) | imm11_5 | imm4_0; + } + break; + + case R_RISCV_RELAX: + break; + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + (grub_uint64_t) ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/riscv/efi/init.c b/grub-core/kern/riscv/efi/init.c new file mode 100644 index 000000000..0d7de4f54 --- /dev/null +++ b/grub-core/kern/riscv/efi/init.c @@ -0,0 +1,78 @@ +/* init.c - initialize a riscv-based EFI system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +static grub_uint64_t timer_frequency_in_khz; + +static grub_uint64_t +grub_efi_get_time_ms (void) +{ + grub_uint64_t tmr; + +#if __riscv_xlen == 64 + asm volatile ("rdtime %0" : "=r"(tmr)); +#else + grub_uint32_t lo, hi, tmp; + asm volatile ("1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); + tmr = ((grub_uint64_t)hi << 32) | lo; +#endif + + return tmr / timer_frequency_in_khz; +} + +void +grub_machine_init (void) +{ + grub_uint64_t time_before, time_after; + + grub_efi_init (); + + /* Calculate timer frequency */ + timer_frequency_in_khz = 1; + time_before = grub_efi_get_time_ms(); + grub_efi_stall(1000); + time_after = grub_efi_get_time_ms(); + timer_frequency_in_khz = time_after - time_before; + + grub_install_get_time_ms (grub_efi_get_time_ms); +} + +void +grub_machine_fini (int flags) +{ + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/grub-core/kern/riscv/efi/startup.S b/grub-core/kern/riscv/efi/startup.S new file mode 100644 index 000000000..f2a7b2b1e --- /dev/null +++ b/grub-core/kern/riscv/efi/startup.S @@ -0,0 +1,48 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +#include + +#if __riscv_xlen == 64 +#define sl sd +#define ll ld +#else +#define sl sw +#define ll lw +#endif + + + .file "startup.S" + .text +FUNCTION(_start) + /* + * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0. + */ + + ll a2, efi_image_handle_val + sl a0, 0(a2) + ll a2, efi_system_table_val + sl a1, 0(a2) + ll a2, grub_main_val + jr a2 +grub_main_val: + .quad EXT_C(grub_main) +efi_system_table_val: + .quad EXT_C(grub_efi_system_table) +efi_image_handle_val: + .quad EXT_C(grub_efi_image_handle) diff --git a/kern/sparc64/cache.S b/grub-core/kern/sparc64/cache.S similarity index 100% rename from kern/sparc64/cache.S rename to grub-core/kern/sparc64/cache.S diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c new file mode 100644 index 000000000..f3d960186 --- /dev/null +++ b/grub-core/kern/sparc64/dl.c @@ -0,0 +1,191 @@ +/* dl.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2MSB + || e->e_machine != EM_SPARCV9) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +#pragma GCC diagnostic ignored "-Wcast-align" + +struct trampoline +{ + grub_uint8_t code[0x28]; + grub_uint64_t addr; +}; + +static const grub_uint8_t trampoline_code[0x28] = +{ + /* 0: */ 0x82, 0x10, 0x00, 0x0f, /* mov %o7, %g1 */ + /* 4: */ 0x40, 0x00, 0x00, 0x02, /* call 0xc */ + /* 8: */ 0x01, 0x00, 0x00, 0x00, /* nop */ + /* c: */ 0x9e, 0x1b, 0xc0, 0x01, /* xor %o7, %g1, %o7 */ + /* 10: */ 0x82, 0x18, 0x40, 0x0f, /* xor %g1, %o7, %g1 */ + /* 14: */ 0x9e, 0x1b, 0xc0, 0x01, /* xor %o7, %g1, %o7 */ + /* 18: */ 0xc2, 0x58, 0x60, 0x24, /* ldx [ %g1 + 0x24 ], %g1 */ + /* 1c: */ 0x81, 0xc0, 0x40, 0x00, /* jmp %g1 */ + /* 20: */ 0x01, 0x00, 0x00, 0x00, /* nop */ + /* 24: */ 0x01, 0x00, 0x00, 0x00, /* nop */ +}; + +grub_err_t +grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, + grub_size_t *got) +{ + const Elf_Ehdr *e = ehdr; + const Elf_Shdr *s; + unsigned i; + + *tramp = 0; + *got = 0; + + for (i = 0, s = (const Elf_Shdr *) ((grub_addr_t) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *) ((grub_addr_t) s + e->e_shentsize)) + if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA) + { + const Elf_Rel *rel, *max; + + for (rel = (const Elf_Rel *) ((grub_addr_t) e + s->sh_offset), + max = rel + s->sh_size / s->sh_entsize; + rel < max; + rel = (const Elf_Rel *) ((grub_addr_t) rel + s->sh_entsize)) + switch (ELF_R_TYPE (rel->r_info)) + { + case R_SPARC_WDISP30: + { + *tramp += sizeof (struct trampoline); + break; + } + } + } + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rela *rel, *max; + + for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + Elf_Addr value; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + value = sym->st_value + rel->r_addend; + switch (ELF_R_TYPE (rel->r_info) & 0xff) + { + case R_SPARC_32: /* 3 V-word32 */ + if (value & 0xFFFFFFFF00000000) + return grub_error (GRUB_ERR_BAD_MODULE, + "address out of 32 bits range"); + *addr = value; + break; + case R_SPARC_WDISP30: /* 7 V-disp30 */ + if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && + (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) + != 0xFFFFFFFF00000000)) + { + struct trampoline *tp = mod->trampptr; + mod->trampptr = tp + 1; + grub_memcpy (tp->code, trampoline_code, sizeof (tp->code)); + tp->addr = value; + value = (Elf_Addr) tp; + } + + if (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) && + (((value - (Elf_Addr) addr) & 0xFFFFFFFF00000000) + != 0xFFFFFFFF00000000)) + return grub_error (GRUB_ERR_BAD_MODULE, + "displacement out of 30 bits range"); + *addr = (*addr & 0xC0000000) | + (((grub_int32_t) ((value - (Elf_Addr) addr) >> 2)) & + 0x3FFFFFFF); + break; + case R_SPARC_HH22: /* 9 V-imm22 */ + *addr = (*addr & 0xFFC00000) | ((value >> 42) & 0x3FFFFF); + break; + case R_SPARC_HM10: /* 12 T-simm13 */ + *addr = (*addr & 0xFFFFFC00) | ((value >> 32) & 0x3FF); + break; + case R_SPARC_HI22: /* 9 V-imm22 */ + if (value >> 32) + return grub_error (GRUB_ERR_BAD_MODULE, + "address out of 32 bits range"); + /* Fallthrough. */ + case R_SPARC_LM22: + *addr = (*addr & 0xFFC00000) | ((value >> 10) & 0x3FFFFF); + break; + case R_SPARC_LO10: /* 12 T-simm13 */ + *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF); + break; + case R_SPARC_64: /* 32 V-xwords64 */ + *(Elf_Xword *) addr = value; + break; + case R_SPARC_OLO10: + *addr = (*addr & ~0x1fff) + | (((value & 0x3ff) + + (ELF_R_TYPE (rel->r_info) >> 8)) + & 0x1fff); + break; + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + + return GRUB_ERR_NONE; +} diff --git a/kern/sparc64/ieee1275/crt0.S b/grub-core/kern/sparc64/ieee1275/crt0.S similarity index 56% rename from kern/sparc64/ieee1275/crt0.S rename to grub-core/kern/sparc64/ieee1275/crt0.S index 3749f3005..03b916f05 100644 --- a/kern/sparc64/ieee1275/crt0.S +++ b/grub-core/kern/sparc64/ieee1275/crt0.S @@ -18,43 +18,47 @@ */ #include #include +#include .text .align 4 .globl _start _start: ba codestart - nop + mov %o4, %o0 - . = EXT_C(_start) + GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE + .org GRUB_KERNEL_MACHINE_TOTAL_MODULE_SIZE VARIABLE(grub_total_module_size) .word 0 -VARIABLE(grub_kernel_image_size) - .word 0 -VARIABLE(grub_compressed_size) - .word 0 -VARIABLE(grub_prefix) - /* to be filled by grub-mkimage */ - - /* - * Leave some breathing room for the prefix. - */ - - . = EXT_C(_start) + GRUB_KERNEL_MACHINE_DATA_END codestart: /* Copy the modules past the end of the kernel image. * They are currently sitting in the BSS. */ - sethi %hi(__bss_start), %o2 - or %o2, %lo(__bss_start), %o2 - sethi %hi(_end), %o3 - or %o3, %lo(_end), %o3 + sethi %hi(__bss_start + GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN - 1), %o2 + or %o2, %lo(__bss_start + GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN - 1), %o2 + srl %o2, GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN, %o2 + sll %o2, GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN, %o2 + sethi %hi(_end + GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN - 1), %o3 + or %o3, %lo(_end + GRUB_KERNEL_SPARC64_IEEE1275_MOD_ALIGN - 1), %o3 + srl %o3, GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN, %o3 + sll %o3, GRUB_KERNEL_SPARC64_IEEE1275_LOG_MOD_ALIGN, %o3 sethi %hi(grub_total_module_size), %o4 lduw [%o4 + %lo(grub_total_module_size)], %o4 + add %o2, %o4, %o2 add %o3, %o4, %o3 + + /* Save ieee1275 stack for future use by booter. */ + mov %o6, %o1 + /* Our future stack. */ + sethi %hi(GRUB_KERNEL_MACHINE_STACK_SIZE), %o5 + or %o5, %lo(GRUB_KERNEL_MACHINE_STACK_SIZE), %o5 + add %o3, %o5, %o6 + and %o6, ~0xff, %o6 + sub %o6, 2047, %o6 + sub %o2, 4, %o2 sub %o3, 4, %o3 1: lduw [%o2], %o5 @@ -62,20 +66,39 @@ codestart: subcc %o4, 4, %o4 sub %o2, 4, %o2 bne,pt %icc, 1b - sub %o3, 4, %o3 + sub %o3, 4, %o3 /* Now it's safe to clear out the BSS. */ sethi %hi(__bss_start), %o2 or %o2, %lo(__bss_start), %o2 - sethi %hi(_end), %o3 - or %o3, %lo(_end), %o3 +1: stb %g0, [%o2] + add %o2, 1, %o2 + and %o2, 7, %o3 + brnz %o3, 1b + nop + + sethi %hi(_end - 1), %o3 + or %o3, %lo(_end - 1), %o3 + srl %o3, 3, %o3 + sll %o3, 3, %o3 1: stx %g0, [%o2] add %o2, 8, %o2 cmp %o2, %o3 blt,pt %xcc, 1b nop - sethi %hi(grub_ieee1275_entry_fn), %o2 - stx %o0, [%o2 + %lo(grub_ieee1275_entry_fn)] - call grub_main + + sethi %hi(_end), %o3 + or %o3, %lo(_end), %o3 +1: stb %g0, [%o2] + add %o2, 1, %o2 + cmp %o2, %o3 + blt,pt %xcc, 1b nop + + sethi %hi(grub_ieee1275_original_stack), %o2 + stx %o1, [%o2 + %lo(grub_ieee1275_original_stack)] + sethi %hi(grub_ieee1275_entry_fn), %o2 + call grub_main + stx %o0, [%o2 + %lo(grub_ieee1275_entry_fn)] 1: ba,a 1b + nop diff --git a/kern/sparc64/ieee1275/ieee1275.c b/grub-core/kern/sparc64/ieee1275/ieee1275.c similarity index 64% rename from kern/sparc64/ieee1275/ieee1275.c rename to grub-core/kern/sparc64/ieee1275/ieee1275.c index 53be692c3..5a59aaf06 100644 --- a/kern/sparc64/ieee1275/ieee1275.c +++ b/grub-core/kern/sparc64/ieee1275/ieee1275.c @@ -89,3 +89,59 @@ grub_ieee1275_alloc_physmem (grub_addr_t *paddr, grub_size_t size, return args.catch_result; } + +grub_uint64_t +grub_ieee1275_num_blocks (grub_ieee1275_ihandle_t ihandle) +{ + struct nblocks_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t blocks; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "#blocks"; + args.ihandle = ihandle; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result != 0)) + return -1; + + /* + * If the number of blocks exceeds the range of an unsigned number, + * return 0 to alert the caller to try the #blocks64 command. + */ + if (args.blocks >= 0xffffffffULL) + return 0; + + return args.blocks; +} + +grub_uint64_t +grub_ieee1275_num_blocks64 (grub_ieee1275_ihandle_t ihandle) +{ + struct nblocks_args_ieee1275 + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t hi_blocks; + grub_ieee1275_cell_t lo_blocks; + } + args; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 3); + args.method = (grub_ieee1275_cell_t) "#blocks64"; + args.ihandle = ihandle; + args.catch_result = 1; + + if ((IEEE1275_CALL_ENTRY_FN (&args) == -1) || (args.catch_result != 0)) + return -1; + + return ((args.hi_blocks << 32) | (args.lo_blocks)); +} diff --git a/grub-core/kern/term.c b/grub-core/kern/term.c new file mode 100644 index 000000000..14d596498 --- /dev/null +++ b/grub-core/kern/term.c @@ -0,0 +1,169 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +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; + +/* Current color state. */ +grub_uint8_t grub_term_normal_color = GRUB_TERM_DEFAULT_NORMAL_COLOR; +grub_uint8_t grub_term_highlight_color = GRUB_TERM_DEFAULT_HIGHLIGHT_COLOR; + +void (*grub_term_poll_usb) (int wait_for_completion) = NULL; +void (*grub_net_poll_cards_idle) (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, + .estimated_width = 1 + }; + + if (code == '\t' && term->getxy) + { + int n; + + n = GRUB_TERM_TAB_WIDTH - ((term->getxy (term).x) + % GRUB_TERM_TAB_WIDTH); + 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 +grub_getkey_noblock (void) +{ + grub_term_input_t term; + + if (grub_term_poll_usb) + grub_term_poll_usb (0); + + if (grub_net_poll_cards_idle) + grub_net_poll_cards_idle (); + + FOR_ACTIVE_TERM_INPUTS(term) + { + int key = term->getkey (term); + if (key != GRUB_TERM_NO_KEY) + return key; + } + + return GRUB_TERM_NO_KEY; +} + +int +grub_getkey (void) +{ + int ret; + + grub_refresh (); + + while (1) + { + ret = grub_getkey_noblock (); + if (ret != GRUB_TERM_NO_KEY) + return ret; + grub_cpu_idle (); + } +} + +int +grub_getkeystatus (void) +{ + int status = 0; + grub_term_input_t term; + + if (grub_term_poll_usb) + grub_term_poll_usb (0); + + FOR_ACTIVE_TERM_INPUTS(term) + { + if (term->getkeystatus) + status |= term->getkeystatus (term); + } + + return status; +} + +int +grub_key_is_interrupt (int key) +{ + /* + * ESC sometimes is the BIOS setup hotkey and may be hard to discover, also + * check F4, which was chosen because is not used as a hotkey to enter the + * BIOS setup by any vendor. + */ + if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4) + return 1; + + /* + * Pressing keys at the right time during boot is hard to time, also allow + * interrupting sleeps / the menu countdown by keeping shift pressed. + */ + if (grub_getkeystatus() & (GRUB_TERM_STATUS_LSHIFT | GRUB_TERM_STATUS_RSHIFT)) + return 1; + + return 0; +} + +void +grub_refresh (void) +{ + struct grub_term_output *term; + + FOR_ACTIVE_TERM_OUTPUTS(term) + grub_term_refresh (term); +} diff --git a/kern/time.c b/grub-core/kern/time.c similarity index 100% rename from kern/time.c rename to grub-core/kern/time.c diff --git a/grub-core/kern/uboot/hw.c b/grub-core/kern/uboot/hw.c new file mode 100644 index 000000000..272b83bd7 --- /dev/null +++ b/grub-core/kern/uboot/hw.c @@ -0,0 +1,112 @@ +/* hw.c - U-Boot hardware discovery */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_addr_t start_of_ram; + +/* + * grub_uboot_probe_memory(): + * Queries U-Boot for available memory regions. + * + * Sets up heap near the image in memory and sets up "start_of_ram". + */ +void +grub_uboot_mm_init (void) +{ + struct sys_info *si = grub_uboot_get_sys_info (); + + grub_mm_init_region ((void *) grub_modules_get_end (), + GRUB_KERNEL_MACHINE_HEAP_SIZE); + + if (si && (si->mr_no != 0)) + { + int i; + start_of_ram = GRUB_UINT_MAX; + + for (i = 0; i < si->mr_no; i++) + if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM) + if (si->mr[i].start < start_of_ram) + start_of_ram = si->mr[i].start; + } +} + +/* + * grub_uboot_probe_hardware(): + */ +grub_err_t +grub_uboot_probe_hardware (void) +{ + int devcount, i; + + devcount = grub_uboot_dev_enum (); + grub_dprintf ("init", "%d devices found\n", devcount); + + for (i = 0; i < devcount; i++) + { + struct device_info *devinfo = grub_uboot_dev_get (i); + + grub_dprintf ("init", "device handle: %d\n", i); + grub_dprintf ("init", " cookie\t= 0x%08x\n", + (grub_uint32_t) devinfo->cookie); + + if (devinfo->type & DEV_TYP_STOR) + { + grub_dprintf ("init", " type\t\t= DISK\n"); + grub_ubootdisk_register (devinfo); + } + else if (devinfo->type & DEV_TYP_NET) + { + /* Dealt with in ubootnet module. */ + grub_dprintf ("init", " type\t\t= NET (not supported yet)\n"); + } + else + { + grub_dprintf ("init", "%s: unknown device type", __FUNCTION__); + } + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + int i; + struct sys_info *si = grub_uboot_get_sys_info (); + + if (!si || (si->mr_no < 1)) + return GRUB_ERR_BUG; + + /* Iterate and call `hook'. */ + for (i = 0; i < si->mr_no; i++) + if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM) + hook (si->mr[i].start, si->mr[i].size, GRUB_MEMORY_AVAILABLE, + hook_data); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/uboot/init.c b/grub-core/kern/uboot/init.c new file mode 100644 index 000000000..3e338645c --- /dev/null +++ b/grub-core/kern/uboot/init.c @@ -0,0 +1,172 @@ +/* init.c - generic U-Boot initialization and finalization */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char __bss_start[]; +extern char _end[]; +extern grub_size_t grub_total_module_size; +static unsigned long timer_start; + +void +grub_exit (void) +{ + grub_uboot_return (0); +} + +static grub_uint64_t +uboot_timer_ms (void) +{ + static grub_uint32_t last = 0, high = 0; + grub_uint32_t cur = grub_uboot_get_timer (timer_start); + if (cur < last) + high++; + last = cur; + return (((grub_uint64_t) high) << 32) | cur; +} + +#ifdef __arm__ +static grub_uint64_t +rpi_timer_ms (void) +{ + static grub_uint32_t last = 0, high = 0; + grub_uint32_t cur = *(volatile grub_uint32_t *) 0x20003004; + if (cur < last) + high++; + last = cur; + return grub_divmod64 ((((grub_uint64_t) high) << 32) | cur, 1000, 0); +} +#endif + +void +grub_machine_init (void) +{ + int ver; + + /* First of all - establish connection with U-Boot */ + ver = grub_uboot_api_init (); + if (!ver) + { + /* Don't even have a console to log errors to... */ + grub_exit (); + } + else if (ver > API_SIG_VERSION) + { + /* Try to print an error message */ + grub_uboot_puts ("invalid U-Boot API version\n"); + } + + /* Initialize the console so that GRUB can display messages. */ + grub_console_init_early (); + + /* Enumerate memory and initialize the memory management system. */ + grub_uboot_mm_init (); + + /* Should be earlier but it needs memalign. */ +#ifdef __arm__ + grub_arm_enable_caches_mmu (); +#endif + + grub_dprintf ("init", "__bss_start: %p\n", __bss_start); + grub_dprintf ("init", "_end: %p\n", _end); + grub_dprintf ("init", "grub_modbase: %p\n", (void *) grub_modbase); + grub_dprintf ("init", "grub_modules_get_end(): %p\n", + (void *) grub_modules_get_end ()); + + /* Initialise full terminfo support */ + grub_console_init_lately (); + + /* Enumerate uboot devices */ + grub_uboot_probe_hardware (); + + /* Initialise timer */ +#ifdef __arm__ + if (grub_uboot_get_machine_type () == GRUB_ARM_MACHINE_TYPE_RASPBERRY_PI) + { + grub_install_get_time_ms (rpi_timer_ms); + } + else +#endif + { + timer_start = grub_uboot_get_timer (0); + grub_install_get_time_ms (uboot_timer_ms); + } + + /* Initialize */ + grub_ubootdisk_init (); +} + + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ +} + +/* + * grub_machine_get_bootlocation(): + * Called from kern/main.c, which expects a device name (minus parentheses) + * and a filesystem path back, if any are known. + * Any returned values must be pointers to dynamically allocated strings. + */ +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *tmp; + + tmp = grub_uboot_env_get ("grub_bootdev"); + if (tmp) + { + *device = grub_strdup (tmp); + if (*device == NULL) + return; + } + else + *device = NULL; + + tmp = grub_uboot_env_get ("grub_bootpath"); + if (tmp) + { + *path = grub_strdup (tmp); + if (*path == NULL) + return; + } + else + *path = NULL; +} + +void +grub_uboot_fini (void) +{ + grub_ubootdisk_fini (); + grub_console_fini (); +} diff --git a/grub-core/kern/uboot/uboot.c b/grub-core/kern/uboot/uboot.c new file mode 100644 index 000000000..aac8f9ae1 --- /dev/null +++ b/grub-core/kern/uboot/uboot.c @@ -0,0 +1,307 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +/* + * The main syscall entry point is not reentrant, only one call is + * serviced until finished. + * + * int syscall(int call, int *retval, ...) + * e.g. syscall(1, int *, u_int32_t, u_int32_t, u_int32_t, u_int32_t); + * + * call: syscall number + * + * retval: points to the return value placeholder, this is the place the + * syscall puts its return value, if NULL the caller does not + * expect a return value + * + * ... syscall arguments (variable number) + * + * returns: 0 if the call not found, 1 if serviced + */ + +extern int grub_uboot_syscall (int, int *, ...); + +static struct sys_info uboot_sys_info; +static struct mem_region uboot_mem_info[5]; +static struct device_info * devices; +static int num_devices; + + +/* + * All functions below are wrappers around the grub_uboot_syscall() function + */ + +int +grub_uboot_getc (void) +{ + int c; + if (!grub_uboot_syscall (API_GETC, NULL, &c)) + return -1; + + return c; +} + +int +grub_uboot_tstc (void) +{ + int c; + if (!grub_uboot_syscall (API_TSTC, NULL, &c)) + return -1; + + return c; +} + +void +grub_uboot_putc (int c) +{ + grub_uboot_syscall (API_PUTC, NULL, &c); +} + +void +grub_uboot_puts (const char *s) +{ + grub_uboot_syscall (API_PUTS, NULL, s); +} + +void +grub_uboot_reset (void) +{ + grub_uboot_syscall (API_RESET, NULL, 0); +} + +struct sys_info * +grub_uboot_get_sys_info (void) +{ + int retval; + + grub_memset (&uboot_sys_info, 0, sizeof (uboot_sys_info)); + grub_memset (&uboot_mem_info, 0, sizeof (uboot_mem_info)); + uboot_sys_info.mr = uboot_mem_info; + uboot_sys_info.mr_no = sizeof (uboot_mem_info) / sizeof (struct mem_region); + + if (grub_uboot_syscall (API_GET_SYS_INFO, &retval, &uboot_sys_info)) + if (retval == 0) + return &uboot_sys_info; + + return NULL; +} + +void +grub_uboot_udelay (grub_uint32_t usec) +{ + grub_uboot_syscall (API_UDELAY, NULL, &usec); +} + +grub_uint32_t +grub_uboot_get_timer (grub_uint32_t base) +{ + grub_uint32_t current; + + if (!grub_uboot_syscall (API_GET_TIMER, NULL, ¤t, &base)) + return 0; + + return current; +} + +int +grub_uboot_dev_enum (void) +{ + struct device_info * enum_devices; + int num_enum_devices, max_devices; + + if (num_devices) + return num_devices; + + max_devices = 2; + enum_devices = grub_calloc (max_devices, sizeof(struct device_info)); + if (!enum_devices) + return 0; + + /* + * The API_DEV_ENUM call starts a fresh enumeration when passed a + * struct device_info with a NULL cookie, and then depends on having + * the previously enumerated device cookie "seeded" into the target + * structure. + */ + + enum_devices[0].cookie = NULL; + num_enum_devices = 0; + + if (grub_uboot_syscall (API_DEV_ENUM, NULL, + &enum_devices[num_enum_devices]) == 0) + goto error; + + num_enum_devices++; + + while (enum_devices[num_enum_devices - 1].cookie != NULL) + { + if (num_enum_devices == max_devices) + { + struct device_info *tmp; + int new_max; + new_max = max_devices * 2; + tmp = grub_realloc (enum_devices, + sizeof (struct device_info) * new_max); + if (!tmp) + { + /* Failed to realloc, so return what we have */ + break; + } + enum_devices = tmp; + max_devices = new_max; + } + + enum_devices[num_enum_devices].cookie = + enum_devices[num_enum_devices - 1].cookie; + if (grub_uboot_syscall (API_DEV_ENUM, NULL, + &enum_devices[num_enum_devices]) == 0) + goto error; + + if (enum_devices[num_enum_devices].cookie == NULL) + break; + + num_enum_devices++; + } + + devices = enum_devices; + return num_devices = num_enum_devices; + + error: + grub_free (enum_devices); + return 0; +} + +#define VALID_DEV(x) (((x) < num_devices) && ((x) >= 0)) +#define OPEN_DEV(x) ((x->state == DEV_STA_OPEN)) + +struct device_info * +grub_uboot_dev_get (int index) +{ + if (VALID_DEV (index)) + return &devices[index]; + + return NULL; +} + + +int +grub_uboot_dev_open (struct device_info *dev) +{ + int retval; + + if (!grub_uboot_syscall (API_DEV_OPEN, &retval, dev)) + return -1; + + return retval; +} + +int +grub_uboot_dev_close (struct device_info *dev) +{ + int retval; + + if (!grub_uboot_syscall (API_DEV_CLOSE, &retval, dev)) + return -1; + + return retval; +} + + +int +grub_uboot_dev_read (struct device_info *dev, void *buf, grub_size_t blocks, + grub_uint32_t start, grub_size_t * real_blocks) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_READ, &retval, dev, buf, + &blocks, &start, real_blocks)) + return -1; + + return retval; +} + +int +grub_uboot_dev_write (struct device_info *dev, const void *buf, + grub_size_t blocks, grub_uint32_t start) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_WRITE, &retval, dev, buf, + &blocks, &start)) + return -1; + + return retval; +} + +int +grub_uboot_dev_recv (struct device_info *dev, void *buf, + int size, int *real_size) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_READ, &retval, dev, buf, &size, real_size)) + return -1; + + return retval; + +} + +int +grub_uboot_dev_send (struct device_info *dev, void *buf, int size) +{ + int retval; + + if (!OPEN_DEV (dev)) + return -1; + + if (!grub_uboot_syscall (API_DEV_WRITE, &retval, dev, buf, &size)) + return -1; + + return retval; +} + +char * +grub_uboot_env_get (const char *name) +{ + char *value; + + if (!grub_uboot_syscall (API_ENV_GET, NULL, name, &value)) + return NULL; + + return value; +} + +void +grub_uboot_env_set (const char *name, const char *value) +{ + grub_uboot_syscall (API_ENV_SET, NULL, name, value); +} diff --git a/grub-core/kern/verifiers.c b/grub-core/kern/verifiers.c new file mode 100644 index 000000000..75d7994cf --- /dev/null +++ b/grub-core/kern/verifiers.c @@ -0,0 +1,228 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 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 . + * + * Verifiers helper. + */ + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct grub_file_verifier *grub_file_verifiers; + +struct grub_verified +{ + grub_file_t file; + void *buf; +}; +typedef struct grub_verified *grub_verified_t; + +static void +verified_free (grub_verified_t verified) +{ + if (verified) + { + grub_free (verified->buf); + grub_free (verified); + } +} + +static grub_ssize_t +verified_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_verified_t verified = file->data; + + grub_memcpy (buf, (char *) verified->buf + file->offset, len); + return len; +} + +static grub_err_t +verified_close (struct grub_file *file) +{ + grub_verified_t verified = file->data; + + grub_file_close (verified->file); + verified_free (verified); + file->data = 0; + + /* Device and name are freed by parent. */ + file->device = 0; + file->name = 0; + + return grub_errno; +} + +struct grub_fs verified_fs = +{ + .name = "verified_read", + .fs_read = verified_read, + .fs_close = verified_close +}; + +static grub_file_t +grub_verifiers_open (grub_file_t io, enum grub_file_type type) +{ + grub_verified_t verified = NULL; + struct grub_file_verifier *ver; + void *context; + grub_file_t ret = 0; + grub_err_t err; + int defer = 0; + + grub_dprintf ("verify", "file: %s type: %d\n", io->name, type); + + if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE + || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE + || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE)) + return io; + + if (io->device->disk && + (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID + || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID)) + return io; + + FOR_LIST_ELEMENTS(ver, grub_file_verifiers) + { + enum grub_verify_flags flags = 0; + err = ver->init (io, type, &context, &flags); + if (err) + goto fail_noclose; + if (flags & GRUB_VERIFY_FLAGS_DEFER_AUTH) + { + defer = 1; + continue; + } + if (!(flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION)) + break; + } + + if (!ver) + { + if (defer) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + N_("verification requested but nobody cares: %s"), io->name); + goto fail_noclose; + } + + /* No verifiers wanted to verify. Just return underlying file. */ + return io; + } + + ret = grub_malloc (sizeof (*ret)); + if (!ret) + { + goto fail; + } + *ret = *io; + + ret->fs = &verified_fs; + ret->not_easily_seekable = 0; + if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1)) + { + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("big file signature isn't implemented yet")); + goto fail; + } + verified = grub_malloc (sizeof (*verified)); + if (!verified) + { + goto fail; + } + verified->buf = grub_malloc (ret->size); + if (!verified->buf) + { + goto fail; + } + if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + io->name); + goto fail; + } + + err = ver->write (context, verified->buf, ret->size); + if (err) + goto fail; + + err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE; + if (err) + goto fail; + + if (ver->close) + ver->close (context); + + FOR_LIST_ELEMENTS_NEXT(ver, grub_file_verifiers) + { + enum grub_verify_flags flags = 0; + err = ver->init (io, type, &context, &flags); + if (err) + goto fail_noclose; + if (flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION || + /* Verification done earlier. So, we are happy here. */ + flags & GRUB_VERIFY_FLAGS_DEFER_AUTH) + continue; + err = ver->write (context, verified->buf, ret->size); + if (err) + goto fail; + + err = ver->fini ? ver->fini (context) : GRUB_ERR_NONE; + if (err) + goto fail; + + if (ver->close) + ver->close (context); + } + + verified->file = io; + ret->data = verified; + return ret; + + fail: + if (ver->close) + ver->close (context); + fail_noclose: + verified_free (verified); + grub_free (ret); + return NULL; +} + +grub_err_t +grub_verify_string (char *str, enum grub_verify_string_type type) +{ + struct grub_file_verifier *ver; + + grub_dprintf ("verify", "string: %s, type: %d\n", str, type); + + FOR_LIST_ELEMENTS(ver, grub_file_verifiers) + { + grub_err_t err; + err = ver->verify_string ? ver->verify_string (str, type) : GRUB_ERR_NONE; + if (err) + return err; + } + return GRUB_ERR_NONE; +} + +void +grub_verifiers_init (void) +{ + grub_file_filter_register (GRUB_FILE_FILTER_VERIFY, grub_verifiers_open); +} diff --git a/grub-core/kern/vga_init.c b/grub-core/kern/vga_init.c new file mode 100644 index 000000000..3fe2f16de --- /dev/null +++ b/grub-core/kern/vga_init.c @@ -0,0 +1,128 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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 . + */ + +#ifndef __mips__ +#include +#include +#endif +#include +#include +#include + +static struct {grub_uint8_t r, g, b, a; } colors[] = + { + // {R, G, B, A} + {0x00, 0x00, 0x00, 0xFF}, // 0 = black + {0x00, 0x00, 0xA8, 0xFF}, // 1 = blue + {0x00, 0xA8, 0x00, 0xFF}, // 2 = green + {0x00, 0xA8, 0xA8, 0xFF}, // 3 = cyan + {0xA8, 0x00, 0x00, 0xFF}, // 4 = red + {0xA8, 0x00, 0xA8, 0xFF}, // 5 = magenta + {0xA8, 0x54, 0x00, 0xFF}, // 6 = brown + {0xA8, 0xA8, 0xA8, 0xFF}, // 7 = light gray + + {0x54, 0x54, 0x54, 0xFF}, // 8 = dark gray + {0x54, 0x54, 0xFE, 0xFF}, // 9 = bright blue + {0x54, 0xFE, 0x54, 0xFF}, // 10 = bright green + {0x54, 0xFE, 0xFE, 0xFF}, // 11 = bright cyan + {0xFE, 0x54, 0x54, 0xFF}, // 12 = bright red + {0xFE, 0x54, 0xFE, 0xFF}, // 13 = bright magenta + {0xFE, 0xFE, 0x54, 0xFF}, // 14 = yellow + {0xFE, 0xFE, 0xFE, 0xFF} // 15 = white + }; + +#include + +#ifdef __mips__ +#define VGA_ADDR 0xb00a0000 +#else +#define VGA_ADDR 0xa0000 +#endif + +static void +load_font (void) +{ + unsigned i; + + grub_vga_gr_write (0 << 2, GRUB_VGA_GR_GR6); + + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL, GRUB_VGA_SR_MEMORY_MODE); + grub_vga_sr_write (1 << GRUB_VGA_TEXT_FONT_PLANE, + GRUB_VGA_SR_MAP_MASK_REGISTER); + + grub_vga_gr_write (0, GRUB_VGA_GR_DATA_ROTATE); + grub_vga_gr_write (0, GRUB_VGA_GR_MODE); + grub_vga_gr_write (0xff, GRUB_VGA_GR_BITMASK); + + for (i = 0; i < 128; i++) + grub_memcpy ((void *) (VGA_ADDR + 32 * i), ascii_bitmaps + 16 * i, 16); +} + +static void +load_palette (void) +{ + unsigned i; + for (i = 0; i < 16; i++) + grub_vga_write_arx (i, i); + + for (i = 0; i < ARRAY_SIZE (colors); i++) + grub_vga_palette_write (i, colors[i].r, colors[i].g, colors[i].b); +} + +void +grub_qemu_init_cirrus (void) +{ + grub_outb (GRUB_VGA_IO_MISC_COLOR, + GRUB_MACHINE_PCI_IO_BASE + GRUB_VGA_IO_MISC_WRITE); + + load_font (); + + grub_vga_gr_write (GRUB_VGA_GR_GR6_MMAP_CGA, GRUB_VGA_GR_GR6); + grub_vga_gr_write (GRUB_VGA_GR_MODE_ODD_EVEN, GRUB_VGA_GR_MODE); + + grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL, GRUB_VGA_SR_MEMORY_MODE); + + grub_vga_sr_write ((1 << GRUB_VGA_TEXT_TEXT_PLANE) + | (1 << GRUB_VGA_TEXT_ATTR_PLANE), + GRUB_VGA_SR_MAP_MASK_REGISTER); + + grub_vga_cr_write (15, GRUB_VGA_CR_CELL_HEIGHT); + grub_vga_cr_write (79, GRUB_VGA_CR_HORIZ_END); + grub_vga_cr_write (40, GRUB_VGA_CR_PITCH); + + int vert = 25 * 16; + grub_vga_cr_write (vert & 0xff, GRUB_VGA_CR_VDISPLAY_END); + grub_vga_cr_write (((vert >> GRUB_VGA_CR_OVERFLOW_HEIGHT1_SHIFT) + & GRUB_VGA_CR_OVERFLOW_HEIGHT1_MASK) + | ((vert >> GRUB_VGA_CR_OVERFLOW_HEIGHT2_SHIFT) + & GRUB_VGA_CR_OVERFLOW_HEIGHT2_MASK), + GRUB_VGA_CR_OVERFLOW); + + load_palette (); + + grub_vga_write_arx (GRUB_VGA_ARX_MODE_TEXT, GRUB_VGA_ARX_MODE); + grub_vga_write_arx (0, GRUB_VGA_ARX_COLOR_SELECT); + + grub_vga_sr_write (GRUB_VGA_SR_CLOCKING_MODE_8_DOT_CLOCK, + GRUB_VGA_SR_CLOCKING_MODE); + + grub_vga_cr_write (14, GRUB_VGA_CR_CURSOR_START); + grub_vga_cr_write (15, GRUB_VGA_CR_CURSOR_END); + + grub_outb (0x20, GRUB_MACHINE_PCI_IO_BASE + GRUB_VGA_IO_ARX); +} diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c new file mode 100644 index 000000000..e5a8bdcf4 --- /dev/null +++ b/grub-core/kern/x86_64/dl.c @@ -0,0 +1,121 @@ +/* dl-x86_64.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,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 . + */ + +#include +#include +#include +#include +#include + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf64_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS64 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_X86_64) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf64_Rela *rel, *max; + + for (rel = (Elf64_Rela *) ((char *) ehdr + s->sh_offset), + max = (Elf64_Rela *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf64_Rela *) ((char *) rel + s->sh_entsize)) + { + Elf64_Word *addr32; + Elf64_Xword *addr64; + Elf64_Sym *sym; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset); + addr64 = (Elf64_Xword *) addr32; + sym = (Elf64_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_X86_64_64: + *addr64 += rel->r_addend + sym->st_value; + break; + + case R_X86_64_PC32: + case R_X86_64_PLT32: + { + grub_int64_t value; + value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value - + (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset; + if (value != (grub_int32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; + } + break; + + case R_X86_64_PC64: + { + *addr64 += rel->r_addend + sym->st_value - + (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset; + } + break; + + case R_X86_64_32: + { + grub_uint64_t value = *addr32 + rel->r_addend + sym->st_value; + if (value != (grub_uint32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; + } + break; + case R_X86_64_32S: + { + grub_int64_t value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value; + if (value != (grub_int32_t) value) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of range"); + *addr32 = value; + } + break; + + default: + { + char rel_info[17]; /* log16(2^64) = 16, plus NUL. */ + + grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T, + ELF_R_TYPE (rel->r_info)); + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%s is not implemented yet"), rel_info); + } + } + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/x86_64/efi/startup.S b/grub-core/kern/x86_64/efi/startup.S new file mode 100644 index 000000000..9357e5c5d --- /dev/null +++ b/grub-core/kern/x86_64/efi/startup.S @@ -0,0 +1,35 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,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 . + */ + +#include +#include + + .file "startup.S" + .text + .globl start, _start + .code64 + +start: +_start: + movq %rcx, EXT_C(grub_efi_image_handle)(%rip) + movq %rdx, EXT_C(grub_efi_system_table)(%rip) + + andq $~0xf, %rsp + call EXT_C(grub_main) + /* Doesn't return. */ diff --git a/include/grub/i386/pc/vga.h b/grub-core/kern/x86_64/xen/hypercall.S similarity index 51% rename from include/grub/i386/pc/vga.h rename to grub-core/kern/x86_64/xen/hypercall.S index b9822395b..9b04db6a0 100644 --- a/include/grub/i386/pc/vga.h +++ b/grub-core/kern/x86_64/xen/hypercall.S @@ -1,6 +1,7 @@ +/* hypercall.S - wrappers for Xen hypercalls */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2011 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 @@ -16,19 +17,37 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_VGA_MACHINE_HEADER -#define GRUB_VGA_MACHINE_HEADER 1 - #include -#include +#include -/* The VGA (at the beginning of upper memory). */ -#define GRUB_MEMORY_MACHINE_VGA_ADDR GRUB_MEMORY_MACHINE_UPPER +FUNCTION(grub_xen_sched_op) + movq $__HYPERVISOR_sched_op, %rax + syscall + ret -/* Set the video mode to MODE and return the previous mode. */ -unsigned char EXPORT_FUNC(grub_vga_set_mode) (unsigned char mode); +FUNCTION(grub_xen_event_channel_op) + movq $__HYPERVISOR_event_channel_op, %rax + syscall + ret -/* Return a pointer to the ROM font table. */ -unsigned char *EXPORT_FUNC(grub_vga_get_font) (void); +FUNCTION(grub_xen_update_va_mapping) + movq $__HYPERVISOR_update_va_mapping, %rax + syscall + ret -#endif /* ! GRUB_VGA_MACHINE_HEADER */ +FUNCTION(grub_xen_mmuext_op) + movq %rcx, %r10 + movq $__HYPERVISOR_mmuext_op, %rax + syscall + ret + +FUNCTION(grub_xen_grant_table_op) + movq $__HYPERVISOR_grant_table_op, %rax + syscall + ret + +FUNCTION(grub_xen_mmu_update) + movq %rcx, %r10 + movq $__HYPERVISOR_mmu_update, %rax + syscall + ret diff --git a/grub-core/kern/x86_64/xen/startup.S b/grub-core/kern/x86_64/xen/startup.S new file mode 100644 index 000000000..21a139f40 --- /dev/null +++ b/grub-core/kern/x86_64/xen/startup.S @@ -0,0 +1,39 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + + .file "startup.S" + .text + .globl start, _start + .code64 + +start: +_start: + leaq LOCAL(stack_end), %rsp + movq %rsi, EXT_C(grub_xen_start_page_addr)(%rip) + + andq $~0xf, %rsp + call EXT_C(grub_main) + /* Doesn't return. */ + + .bss + .space (1 << 22) +LOCAL(stack_end): diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c new file mode 100644 index 000000000..782ca7295 --- /dev/null +++ b/grub-core/kern/xen/init.c @@ -0,0 +1,601 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +grub_addr_t grub_modbase; +struct start_info *grub_xen_start_page_addr; +volatile struct xencons_interface *grub_xen_xcons; +volatile struct shared_info *grub_xen_shared_info; +volatile struct xenstore_domain_interface *grub_xen_xenstore; +volatile grant_entry_v1_t *grub_xen_grant_table; +static const grub_size_t total_grants = + GRUB_XEN_PAGE_SIZE / sizeof (grub_xen_grant_table[0]); +grub_size_t grub_xen_n_allocated_shared_pages; + +static grub_xen_mfn_t +grub_xen_ptr2mfn (void *ptr) +{ +#ifdef GRUB_MACHINE_XEN + grub_xen_mfn_t *mfn_list = + (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list; + return mfn_list[(grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE]; +#else + return (grub_addr_t) ptr >> GRUB_XEN_LOG_PAGE_SIZE; +#endif +} + +void * +grub_xen_alloc_shared_page (domid_t dom, grub_xen_grant_t * grnum) +{ + void *ret; + grub_xen_mfn_t mfn; + volatile grant_entry_v1_t *entry; + + /* Avoid 0. */ + for (entry = grub_xen_grant_table; + entry < grub_xen_grant_table + total_grants; entry++) + if (!entry->flags) + break; + + if (entry == grub_xen_grant_table + total_grants) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of grant entries"); + return NULL; + } + ret = grub_memalign (GRUB_XEN_PAGE_SIZE, GRUB_XEN_PAGE_SIZE); + if (!ret) + return NULL; + mfn = grub_xen_ptr2mfn (ret); + entry->frame = mfn; + entry->domid = dom; + mb (); + entry->flags = GTF_permit_access; + mb (); + *grnum = entry - grub_xen_grant_table; + grub_xen_n_allocated_shared_pages++; + return ret; +} + +void +grub_xen_free_shared_page (void *ptr) +{ + grub_xen_mfn_t mfn; + volatile grant_entry_v1_t *entry; + + mfn = grub_xen_ptr2mfn (ptr); + for (entry = grub_xen_grant_table + 1; + entry < grub_xen_grant_table + total_grants; entry++) + if (entry->flags && entry->frame == mfn) + { + mb (); + entry->flags = 0; + mb (); + entry->frame = 0; + mb (); + } + grub_xen_n_allocated_shared_pages--; +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_xen_store_send (const void *buf_, grub_size_t len) +{ + const grub_uint8_t *buf = buf_; + struct evtchn_send send; + int event_sent = 0; + while (len) + { + grub_size_t avail, inbuf; + grub_size_t prod, cons; + mb (); + prod = grub_xen_xenstore->req_prod; + cons = grub_xen_xenstore->req_cons; + if (prod >= cons + sizeof (grub_xen_xenstore->req)) + { + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + continue; + } + event_sent = 0; + avail = cons + sizeof (grub_xen_xenstore->req) - prod; + inbuf = (~prod & (sizeof (grub_xen_xenstore->req) - 1)) + 1; + if (avail > inbuf) + avail = inbuf; + if (avail > len) + avail = len; + grub_memcpy ((void *) &grub_xen_xenstore->req[prod & (sizeof (grub_xen_xenstore->req) - 1)], + buf, avail); + buf += avail; + len -= avail; + mb (); + grub_xen_xenstore->req_prod += avail; + mb (); + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + } +} + +void +grub_xen_store_recv (void *buf_, grub_size_t len) +{ + grub_uint8_t *buf = buf_; + struct evtchn_send send; + int event_sent = 0; + while (len) + { + grub_size_t avail, inbuf; + grub_size_t prod, cons; + mb (); + prod = grub_xen_xenstore->rsp_prod; + cons = grub_xen_xenstore->rsp_cons; + if (prod <= cons) + { + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op (EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op (SCHEDOP_yield, 0); + continue; + } + event_sent = 0; + avail = prod - cons; + inbuf = (~cons & (sizeof (grub_xen_xenstore->req) - 1)) + 1; + if (avail > inbuf) + avail = inbuf; + if (avail > len) + avail = len; + grub_memcpy (buf, + (void *) &grub_xen_xenstore->rsp[cons & (sizeof (grub_xen_xenstore->rsp) - 1)], + avail); + buf += avail; + len -= avail; + mb (); + grub_xen_xenstore->rsp_cons += avail; + mb (); + if (!event_sent) + { + send.port = grub_xen_start_page_addr->store_evtchn; + grub_xen_event_channel_op(EVTCHNOP_send, &send); + event_sent = 1; + } + grub_xen_sched_op(SCHEDOP_yield, 0); + } +} + +void * +grub_xenstore_get_file (const char *dir, grub_size_t *len) +{ + struct xsd_sockmsg msg; + char *buf; + grub_size_t dirlen = grub_strlen (dir) + 1; + + if (len) + *len = 0; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_READ; + msg.len = dirlen; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_recv (&msg, sizeof (msg)); + buf = grub_malloc (msg.len + 1); + if (!buf) + return NULL; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (buf, msg.len); + buf[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", dir, buf); + grub_free (buf); + return NULL; + } + if (len) + *len = msg.len; + return buf; +} + +grub_err_t +grub_xenstore_write_file (const char *dir, const void *buf, grub_size_t len) +{ + struct xsd_sockmsg msg; + grub_size_t dirlen = grub_strlen (dir) + 1; + char *resp; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_WRITE; + msg.len = dirlen + len; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_send (buf, len); + grub_xen_store_recv (&msg, sizeof (msg)); + resp = grub_malloc (msg.len + 1); + if (!resp) + return grub_errno; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (resp, msg.len); + resp[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_dprintf ("xen", "error = %s\n", resp); + grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", + dir, resp); + grub_free (resp); + return grub_errno; + } + grub_free (resp); + return GRUB_ERR_NONE; +} + +/* FIXME: error handling. */ +grub_err_t +grub_xenstore_dir (const char *dir, + int (*hook) (const char *dir, void *hook_data), + void *hook_data) +{ + struct xsd_sockmsg msg; + char *buf; + char *ptr; + grub_size_t dirlen = grub_strlen (dir) + 1; + + grub_memset (&msg, 0, sizeof (msg)); + msg.type = XS_DIRECTORY; + msg.len = dirlen; + grub_xen_store_send (&msg, sizeof (msg)); + grub_xen_store_send (dir, dirlen); + grub_xen_store_recv (&msg, sizeof (msg)); + buf = grub_malloc (msg.len + 1); + if (!buf) + return grub_errno; + grub_dprintf ("xen", "msg type = %d, len = %d\n", msg.type, msg.len); + grub_xen_store_recv (buf, msg.len); + buf[msg.len] = '\0'; + if (msg.type == XS_ERROR) + { + grub_err_t err; + err = grub_error (GRUB_ERR_IO, "couldn't read xenstorage `%s': %s", + dir, buf); + grub_free (buf); + return err; + } + for (ptr = buf; ptr < buf + msg.len; ptr += grub_strlen (ptr) + 1) + if (hook (ptr, hook_data)) + break; + grub_free (buf); + return grub_errno; +} + +unsigned long gntframe = 0; + +static void +grub_xen_setup_gnttab (void) +{ + struct gnttab_set_version gnttab_setver; + struct gnttab_setup_table gnttab_setup; + + grub_memset (&gnttab_setver, 0, sizeof (gnttab_setver)); + + gnttab_setver.version = 1; + grub_xen_grant_table_op (GNTTABOP_set_version, &gnttab_setver, 1); + + grub_memset (&gnttab_setup, 0, sizeof (gnttab_setup)); + gnttab_setup.dom = DOMID_SELF; + gnttab_setup.nr_frames = 1; + gnttab_setup.frame_list.p = &gntframe; + + grub_xen_grant_table_op (GNTTABOP_setup_table, &gnttab_setup, 1); +} + +#ifdef GRUB_MACHINE_XEN +static grub_uint8_t window[GRUB_XEN_PAGE_SIZE] + __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE))); + +#ifdef __x86_64__ +#define NUMBER_OF_LEVELS 4 +#else +#define NUMBER_OF_LEVELS 3 +#endif + +#define LOG_POINTERS_PER_PAGE 9 +#define POINTERS_PER_PAGE (1 << LOG_POINTERS_PER_PAGE) + +#define MAX_N_UNUSABLE_PAGES 4 + +static int +grub_xen_is_page_usable (grub_xen_mfn_t mfn) +{ + if (mfn == grub_xen_start_page_addr->console.domU.mfn) + return 0; + if (mfn == grub_xen_start_page_addr->shared_info) + return 0; + if (mfn == grub_xen_start_page_addr->store_mfn) + return 0; + if (mfn == gntframe) + return 0; + return 1; +} + +static grub_uint64_t +page2offset (grub_uint64_t page) +{ + return page << 12; +} + +#if defined (__x86_64__) && defined (__code_model_large__) +#define MAX_TOTAL_PAGES (1LL << (64 - 12)) +#elif defined (__x86_64__) +#define MAX_TOTAL_PAGES (1LL << (31 - 12)) +#else +#define MAX_TOTAL_PAGES (1LL << (32 - 12)) +#endif + +static void +map_all_pages (void) +{ + grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages; + grub_uint64_t i, j; + grub_xen_mfn_t *mfn_list = + (grub_xen_mfn_t *) grub_xen_start_page_addr->mfn_list; + grub_uint64_t *pg = (grub_uint64_t *) window; + grub_uint64_t oldpgstart, oldpgend; + grub_size_t n_unusable_pages = 0; + struct mmu_update m2p_updates[2 * MAX_N_UNUSABLE_PAGES]; + + if (total_pages > MAX_TOTAL_PAGES - 4) + total_pages = MAX_TOTAL_PAGES - 4; + + for (j = 0; j < total_pages - n_unusable_pages; j++) + while (!grub_xen_is_page_usable (mfn_list[j])) + { + grub_xen_mfn_t t; + if (n_unusable_pages >= MAX_N_UNUSABLE_PAGES) + { + struct sched_shutdown arg; + arg.reason = SHUTDOWN_crash; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + while (1); + } + t = mfn_list[j]; + mfn_list[j] = mfn_list[total_pages - n_unusable_pages - 1]; + mfn_list[total_pages - n_unusable_pages - 1] = t; + + m2p_updates[2 * n_unusable_pages].ptr + = page2offset (mfn_list[j]) | MMU_MACHPHYS_UPDATE; + m2p_updates[2 * n_unusable_pages].val = j; + m2p_updates[2 * n_unusable_pages + 1].ptr + = page2offset (mfn_list[total_pages - n_unusable_pages - 1]) + | MMU_MACHPHYS_UPDATE; + m2p_updates[2 * n_unusable_pages + 1].val = total_pages + - n_unusable_pages - 1; + + n_unusable_pages++; + } + + grub_xen_mmu_update (m2p_updates, 2 * n_unusable_pages, NULL, DOMID_SELF); + + total_pages += 4; + + grub_uint64_t lx[NUMBER_OF_LEVELS], nlx; + grub_uint64_t paging_start = total_pages - 4 - n_unusable_pages, curpage; + + for (nlx = total_pages, i = 0; i < (unsigned) NUMBER_OF_LEVELS; i++) + { + nlx = (nlx + POINTERS_PER_PAGE - 1) >> LOG_POINTERS_PER_PAGE; + /* PAE wants all 4 root directories present. */ +#ifdef __i386__ + if (i == 1) + nlx = 4; +#endif + lx[i] = nlx; + paging_start -= nlx; + } + + oldpgstart = grub_xen_start_page_addr->pt_base >> 12; + oldpgend = oldpgstart + grub_xen_start_page_addr->nr_pt_frames; + + curpage = paging_start; + + int l; + + for (l = NUMBER_OF_LEVELS - 1; l >= 1; l--) + { + for (i = 0; i < lx[l]; i++) + { + grub_xen_update_va_mapping (&window, + page2offset (mfn_list[curpage + i]) | 7, + UVMF_INVLPG); + grub_memset (&window, 0, sizeof (window)); + + for (j = i * POINTERS_PER_PAGE; + j < (i + 1) * POINTERS_PER_PAGE && j < lx[l - 1]; j++) + pg[j - i * POINTERS_PER_PAGE] = + page2offset (mfn_list[curpage + lx[l] + j]) +#ifdef __x86_64__ + | 4 +#endif + | 3; + } + curpage += lx[l]; + } + + for (i = 0; i < lx[0]; i++) + { + grub_xen_update_va_mapping (&window, + page2offset (mfn_list[curpage + i]) | 7, + UVMF_INVLPG); + grub_memset (&window, 0, sizeof (window)); + + for (j = i * POINTERS_PER_PAGE; + j < (i + 1) * POINTERS_PER_PAGE && j < total_pages; j++) + if (j < paging_start && !(j >= oldpgstart && j < oldpgend)) + pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 0x7; + else if (j < grub_xen_start_page_addr->nr_pages) + pg[j - i * POINTERS_PER_PAGE] = page2offset (mfn_list[j]) | 5; + else if (j == grub_xen_start_page_addr->nr_pages) + { + pg[j - i * POINTERS_PER_PAGE] = + page2offset (grub_xen_start_page_addr->console.domU.mfn) | 7; + grub_xen_xcons = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 1) + { + pg[j - i * POINTERS_PER_PAGE] = + grub_xen_start_page_addr->shared_info | 7; + grub_xen_shared_info = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 2) + { + pg[j - i * POINTERS_PER_PAGE] = + page2offset (grub_xen_start_page_addr->store_mfn) | 7; + grub_xen_xenstore = (void *) (grub_addr_t) page2offset (j); + } + else if (j == grub_xen_start_page_addr->nr_pages + 3) + { + pg[j - i * POINTERS_PER_PAGE] = page2offset (gntframe) | 7; + grub_xen_grant_table = (void *) (grub_addr_t) page2offset (j); + } + } + + grub_xen_update_va_mapping (&window, 0, UVMF_INVLPG); + + mmuext_op_t op[3]; + + op[0].cmd = MMUEXT_PIN_L1_TABLE + (NUMBER_OF_LEVELS - 1); + op[0].arg1.mfn = mfn_list[paging_start]; + op[1].cmd = MMUEXT_NEW_BASEPTR; + op[1].arg1.mfn = mfn_list[paging_start]; + op[2].cmd = MMUEXT_UNPIN_TABLE; + op[2].arg1.mfn = mfn_list[oldpgstart]; + + grub_xen_mmuext_op (op, 3, NULL, DOMID_SELF); + + for (i = oldpgstart; i < oldpgend; i++) + grub_xen_update_va_mapping ((void *) (grub_addr_t) page2offset (i), + page2offset (mfn_list[i]) | 7, UVMF_INVLPG); + void *new_start_page, *new_mfn_list; + new_start_page = (void *) (grub_addr_t) page2offset (paging_start - 1); + grub_memcpy (new_start_page, grub_xen_start_page_addr, 4096); + grub_xen_start_page_addr = new_start_page; + new_mfn_list = (void *) (grub_addr_t) + page2offset (paging_start - 1 + - ((grub_xen_start_page_addr->nr_pages + * sizeof (grub_uint64_t) + 4095) / 4096)); + grub_memcpy (new_mfn_list, mfn_list, grub_xen_start_page_addr->nr_pages + * sizeof (grub_uint64_t)); + grub_xen_start_page_addr->pt_base = page2offset (paging_start); + grub_xen_start_page_addr->mfn_list = (grub_addr_t) new_mfn_list; + + grub_addr_t heap_start = grub_modules_get_end (); + grub_addr_t heap_end = (grub_addr_t) new_mfn_list; + + grub_mm_init_region ((void *) heap_start, heap_end - heap_start); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + grub_uint64_t total_pages = grub_xen_start_page_addr->nr_pages; + grub_uint64_t usable_pages = grub_xen_start_page_addr->pt_base >> 12; + if (hook (0, page2offset (usable_pages), GRUB_MEMORY_AVAILABLE, hook_data)) + return GRUB_ERR_NONE; + + hook (page2offset (usable_pages), page2offset (total_pages - usable_pages), + GRUB_MEMORY_RESERVED, hook_data); + + return GRUB_ERR_NONE; +} +#endif + +extern char _end[]; + +void +grub_machine_init (void) +{ +#ifdef GRUB_MACHINE_XEN +#ifdef __i386__ + grub_xen_vm_assist (VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3); +#endif +#endif + + grub_modbase = ALIGN_UP ((grub_addr_t) _end + + GRUB_KERNEL_MACHINE_MOD_GAP, + GRUB_KERNEL_MACHINE_MOD_ALIGN); + +#ifdef GRUB_MACHINE_XEN_PVH + grub_xen_setup_pvh (); +#endif + + grub_xen_setup_gnttab (); + +#ifdef GRUB_MACHINE_XEN + map_all_pages (); +#endif + + grub_console_init (); + + grub_tsc_init (); + + grub_xendisk_init (); + + grub_boot_init (); +} + +void +grub_exit (void) +{ + struct sched_shutdown arg; + + arg.reason = SHUTDOWN_poweroff; + grub_xen_sched_op (SCHEDOP_shutdown, &arg); + while (1); +} + +void +grub_machine_fini (int flags __attribute__ ((unused))) +{ + grub_xendisk_fini (); + grub_boot_fini (); +} diff --git a/lib/LzFind.c b/grub-core/lib/LzFind.c similarity index 98% rename from lib/LzFind.c rename to grub-core/lib/LzFind.c index cd7a1cbab..dcb20d921 100644 --- a/lib/LzFind.c +++ b/grub-core/lib/LzFind.c @@ -24,6 +24,9 @@ * See , for more information about LZMA. */ + +#include + #include #include @@ -66,9 +69,9 @@ static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *a } Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } -Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; } +static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 curindex) { return p->buffer[curindex]; } -UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } +static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue) { diff --git a/lib/LzmaDec.c b/grub-core/lib/LzmaDec.c similarity index 99% rename from lib/LzmaDec.c rename to grub-core/lib/LzmaDec.c index 62ebee686..952edb346 100644 --- a/lib/LzmaDec.c +++ b/grub-core/lib/LzmaDec.c @@ -26,7 +26,9 @@ #include -#include +#pragma GCC diagnostic ignored "-Wshadow" +#include +#define memcpy grub_memcpy #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) @@ -718,7 +720,7 @@ static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data) p->needFlush = 0; } -void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { p->needFlush = 1; p->remainLen = 0; diff --git a/lib/LzmaEnc.c b/grub-core/lib/LzmaEnc.c similarity index 93% rename from lib/LzmaEnc.c rename to grub-core/lib/LzmaEnc.c index 842d43ac1..52b331558 100644 --- a/lib/LzmaEnc.c +++ b/grub-core/lib/LzmaEnc.c @@ -24,6 +24,8 @@ * See , for more information about LZMA. */ +#include + #include #include @@ -118,7 +120,7 @@ UInt32 GetPosSlot1(UInt32 pos) #define kNumLogBits (9 + (int)sizeof(size_t) / 2) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) -void LzmaEnc_FastPosInit(Byte *g_FastPos) +static void LzmaEnc_FastPosInit(Byte *g_FastPos) { int c = 2, slotFast; g_FastPos[0] = 0; @@ -372,58 +374,6 @@ typedef struct _CLzmaEnc CSaveState saveState; } CLzmaEnc; -void LzmaEnc_SaveState(CLzmaEncHandle pp) -{ - CLzmaEnc *p = (CLzmaEnc *)pp; - CSaveState *dest = &p->saveState; - int i; - dest->lenEnc = p->lenEnc; - dest->repLenEnc = p->repLenEnc; - dest->state = p->state; - - for (i = 0; i < kNumStates; i++) - { - memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); - memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); - } - for (i = 0; i < kNumLenToPosStates; i++) - memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); - memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); - memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); - memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); - memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); - memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); - memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); - memcpy(dest->reps, p->reps, sizeof(p->reps)); - memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); -} - -void LzmaEnc_RestoreState(CLzmaEncHandle pp) -{ - CLzmaEnc *dest = (CLzmaEnc *)pp; - const CSaveState *p = &dest->saveState; - int i; - dest->lenEnc = p->lenEnc; - dest->repLenEnc = p->repLenEnc; - dest->state = p->state; - - for (i = 0; i < kNumStates; i++) - { - memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); - memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); - } - for (i = 0; i < kNumLenToPosStates; i++) - memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); - memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); - memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); - memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); - memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); - memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); - memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); - memcpy(dest->reps, p->reps, sizeof(p->reps)); - memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); -} - SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) { CLzmaEnc *p = (CLzmaEnc *)pp; @@ -641,7 +591,7 @@ static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, while (symbol < 0x10000); } -void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) +static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) { UInt32 i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) @@ -973,6 +923,8 @@ static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) #define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) +#pragma GCC diagnostic ignored "-Wshadow" + static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 numAvailableBytes, lenMain, numDistancePairs; @@ -1259,7 +1211,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) { UInt32 i; reps[0] = prevOpt->backs[pos]; - for (i = 1; i <= pos; i++) + for (i = 1; i < pos + 1; i++) reps[i] = prevOpt->backs[i - 1]; for (; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i]; @@ -1407,7 +1359,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) startLen = lenTest + 1; /* if (_maxMode) */ - { + { UInt32 lenTest2 = lenTest + 1; UInt32 limit = lenTest2 + p->numFastBytes; UInt32 nextRepMatchPrice; @@ -1451,7 +1403,7 @@ static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) } } } - } + } } } /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ @@ -1767,7 +1719,7 @@ static void FillDistancesPrices(CLzmaEnc *p) p->matchPriceCount = 0; } -void LzmaEnc_Construct(CLzmaEnc *p) +static void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); MatchFinder_Construct(&p->matchFinderBase); @@ -1800,7 +1752,7 @@ CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) return p; } -void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) { alloc->Free(alloc, p->litProbs); alloc->Free(alloc, p->saveState.litProbs); @@ -1808,7 +1760,7 @@ void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) p->saveState.litProbs = 0; } -void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) { #ifdef COMPRESS_MF_MT MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); @@ -1925,13 +1877,19 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize } else { - UInt32 posSlot; + UInt32 posSlot, lenToPosState; RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); pos -= LZMA_NUM_REPS; GetPosSlot(pos, posSlot); - RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + lenToPosState = GetLenToPosState(len); + if (lenToPosState >= kNumLenToPosStates) + { + p->result = SZ_ERROR_DATA; + return CheckErrors(p); + } + RcTree_Encode(&p->rc, p->posSlotEncoder[lenToPosState], kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { @@ -1992,13 +1950,15 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) { UInt32 beforeSize = kNumOpts; +#ifdef COMPRESS_MF_MT Bool btMode; +#endif if (!RangeEnc_Alloc(&p->rc, alloc)) return SZ_ERROR_MEM; +#ifdef COMPRESS_MF_MT btMode = (p->matchFinderBase.btMode != 0); - #ifdef COMPRESS_MF_MT p->mtMode = (p->multiThread && !p->fastMode && btMode); - #endif +#endif { unsigned lclp = p->lc + p->lp; @@ -2039,7 +1999,7 @@ static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, I return SZ_OK; } -void LzmaEnc_Init(CLzmaEnc *p) +static void LzmaEnc_Init(CLzmaEnc *p) { UInt32 i; p->state = 0; @@ -2098,7 +2058,7 @@ void LzmaEnc_Init(CLzmaEnc *p) p->lpMask = (1 << p->lp) - 1; } -void LzmaEnc_InitPrices(CLzmaEnc *p) +static void LzmaEnc_InitPrices(CLzmaEnc *p) { if (!p->fastMode) { @@ -2139,15 +2099,6 @@ static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutSt return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig); } -SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, - ISeqInStream *inStream, UInt32 keepWindowSize, - ISzAlloc *alloc, ISzAlloc *allocBig) -{ - CLzmaEnc *p = (CLzmaEnc *)pp; - p->inStream = inStream; - return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); -} - static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) { p->seqBufInStream.funcTable.Read = MyRead; @@ -2155,16 +2106,7 @@ static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen) p->seqBufInStream.rem = srcLen; } -SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, - UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) -{ - CLzmaEnc *p = (CLzmaEnc *)pp; - LzmaEnc_SetInputBuf(p, src, srcLen); - p->inStream = &p->seqBufInStream.funcTable; - return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); -} - -void LzmaEnc_Finish(CLzmaEncHandle pp) +static void LzmaEnc_Finish(CLzmaEncHandle pp) { #ifdef COMPRESS_MF_MT CLzmaEnc *p = (CLzmaEnc *)pp; @@ -2197,53 +2139,6 @@ static size_t MyWrite(void *pp, const void *data, size_t size) return size; } - -UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) -{ - const CLzmaEnc *p = (CLzmaEnc *)pp; - return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); -} - -const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) -{ - const CLzmaEnc *p = (CLzmaEnc *)pp; - return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; -} - -SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, - Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize) -{ - CLzmaEnc *p = (CLzmaEnc *)pp; - UInt64 nowPos64; - SRes res; - CSeqOutStreamBuf outStream; - - outStream.funcTable.Write = MyWrite; - outStream.data = dest; - outStream.rem = *destLen; - outStream.overflow = False; - - p->writeEndMark = False; - p->finished = False; - p->result = SZ_OK; - - if (reInit) - LzmaEnc_Init(p); - LzmaEnc_InitPrices(p); - nowPos64 = p->nowPos64; - RangeEnc_Init(&p->rc); - p->rc.outStream = &outStream.funcTable; - - res = LzmaEnc_CodeOneBlock(pp, True, desiredPackSize, *unpackSize); - - *unpackSize = (UInt32)(p->nowPos64 - nowPos64); - *destLen -= outStream.rem; - if (outStream.overflow) - return SZ_ERROR_OUTPUT_EOF; - - return res; -} - SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) { diff --git a/grub-core/lib/adler32.c b/grub-core/lib/adler32.c new file mode 100644 index 000000000..43b68af62 --- /dev/null +++ b/grub-core/lib/adler32.c @@ -0,0 +1,102 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +struct adler32_context +{ + grub_uint16_t a, b; + grub_uint32_t c; +}; + +static void +adler32_init (void *context) +{ + struct adler32_context *ctx = context; + + ctx->a = 1; + ctx->b = 0; +} + +#define MOD 65521 + +static grub_uint16_t +mod_add (grub_uint16_t a, grub_uint16_t b) +{ + if ((grub_uint32_t) a + (grub_uint32_t) b >= MOD) + return a + b - MOD; + return a + b; +} + +static void +adler32_write (void *context, const void *inbuf, grub_size_t inlen) +{ + struct adler32_context *ctx = context; + const grub_uint8_t *ptr = inbuf; + + while (inlen) + { + ctx->a = mod_add (ctx->a, *ptr); + ctx->b = mod_add (ctx->a, ctx->b); + inlen--; + ptr++; + } +} + +static void +adler32_final (void *context __attribute__ ((unused))) +{ +} + +static grub_uint8_t * +adler32_read (void *context) +{ + struct adler32_context *ctx = context; + if (ctx->a > MOD) + ctx->a -= MOD; + if (ctx->b > MOD) + ctx->b -= MOD; + ctx->c = grub_cpu_to_be32 (ctx->a | (ctx->b << 16)); + return (grub_uint8_t *) &ctx->c; +} + +static gcry_md_spec_t spec_adler32 = + { + "ADLER32", 0, 0, 0, 4, + adler32_init, adler32_write, adler32_final, adler32_read, + sizeof (struct adler32_context), +#ifdef GRUB_UTIL + .modname = "adler32", +#endif + .blocksize = 64 + }; + + +GRUB_MOD_INIT(adler32) +{ + grub_md_register (&spec_adler32); +} + +GRUB_MOD_FINI(adler32) +{ + grub_md_unregister (&spec_adler32); +} diff --git a/grub-core/lib/arc/datetime.c b/grub-core/lib/arc/datetime.c new file mode 100644 index 000000000..b8d910e5f --- /dev/null +++ b/grub-core/lib/arc/datetime.c @@ -0,0 +1,48 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + struct grub_arc_timeinfo *dt; + grub_memset (datetime, 0, sizeof (*datetime)); + + dt = GRUB_ARC_FIRMWARE_VECTOR->gettime (); + + datetime->year = dt->y; + datetime->month = dt->m; + datetime->day = dt->d; + datetime->hour = dt->h; + datetime->minute = dt->min; + datetime->second = dt->s; + + return 0; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_IO, "setting time isn't supported"); +} diff --git a/grub-core/lib/arg.c b/grub-core/lib/arg.c new file mode 100644 index 000000000..c2bd6a452 --- /dev/null +++ b/grub-core/lib/arg.c @@ -0,0 +1,492 @@ +/* arg.c - argument parser */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2004,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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Built-in parser for default options. */ +static const struct grub_arg_option help_options[] = + { + {"help", 0, 0, + N_("Display this help and exit."), 0, ARG_TYPE_NONE}, + {"usage", 0, 0, + N_("Display the usage of this command and exit."), 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} + }; + +/* Helper for find_short. */ +static const struct grub_arg_option * +fnd_short (const struct grub_arg_option *opt, char c) +{ + while (opt->doc) + { + if (opt->shortarg == c) + return opt; + opt++; + } + return 0; +} + +static const struct grub_arg_option * +find_short (const struct grub_arg_option *options, char c) +{ + const struct grub_arg_option *found = 0; + + if (options) + found = fnd_short (options, c); + + if (! found) + { + switch (c) + { + case 'h': + found = help_options; + break; + + case 'u': + found = (help_options + 1); + break; + + default: + break; + } + } + + return found; +} + +/* Helper for find_long. */ +static const struct grub_arg_option * +fnd_long (const struct grub_arg_option *opt, const char *s, int len) +{ + while (opt->doc) + { + if (opt->longarg && ! grub_strncmp (opt->longarg, s, len) && + opt->longarg[len] == '\0') + return opt; + opt++; + } + return 0; +} + +static const struct grub_arg_option * +find_long (const struct grub_arg_option *options, const char *s, int len) +{ + const struct grub_arg_option *found = 0; + + if (options) + found = fnd_long (options, s, len); + + if (! found) + found = fnd_long (help_options, s, len); + + return found; +} + +static void +show_usage (grub_extcmd_t cmd) +{ + grub_printf ("%s %s %s\n", _("Usage:"), cmd->cmd->name, _(cmd->cmd->summary)); +} + +static void +showargs (const struct grub_arg_option *opt, + int h_is_used, int u_is_used) +{ + for (; opt->doc; opt++) + { + int spacing = 20; + + if (opt->shortarg && grub_isgraph (opt->shortarg)) + grub_printf ("-%c%c ", opt->shortarg, opt->longarg ? ',':' '); + else if (opt == help_options && ! h_is_used) + grub_printf ("-h, "); + else if (opt == help_options + 1 && ! u_is_used) + grub_printf ("-u, "); + else + grub_printf (" "); + + if (opt->longarg) + { + grub_printf ("--%s", opt->longarg); + spacing -= grub_strlen (opt->longarg) + 2; + + if (opt->arg) + { + grub_printf ("=%s", opt->arg); + spacing -= grub_strlen (opt->arg) + 1; + } + } + + if (spacing <= 0) + spacing = 3; + + while (spacing--) + grub_xputs (" "); + + grub_printf ("%s\n", _(opt->doc)); + } +} + +void +grub_arg_show_help (grub_extcmd_t cmd) +{ + int h_is_used = 0; + int u_is_used = 0; + const struct grub_arg_option *opt; + + show_usage (cmd); + grub_printf ("%s\n\n", _(cmd->cmd->description)); + + for (opt = cmd->options; opt && opt->doc; opt++) + switch (opt->shortarg) + { + case 'h': + h_is_used = 1; + break; + + case 'u': + u_is_used = 1; + break; + } + + if (cmd->options) + showargs (cmd->options, h_is_used, u_is_used); + showargs (help_options, h_is_used, u_is_used); +#if 0 + grub_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT); +#endif +} + + +static int +parse_option (grub_extcmd_t cmd, const struct grub_arg_option *opt, + char *arg, struct grub_arg_list *usr) +{ + if (opt == help_options) + { + grub_arg_show_help (cmd); + return -1; + } + + if (opt == help_options + 1) + { + show_usage (cmd); + return -1; + } + { + int found = opt - cmd->options; + + if (opt->flags & GRUB_ARG_OPTION_REPEATABLE) + { + usr[found].args[usr[found].set++] = arg; + usr[found].args[usr[found].set] = NULL; + } + else + { + usr[found].set = 1; + usr[found].arg = arg; + } + } + + return 0; +} + +static inline grub_err_t +add_arg (char ***argl, int *num, char *s) +{ + char **p = *argl; + grub_size_t sz; + + if (grub_add (++(*num), 1, &sz) || + grub_mul (sz, sizeof (char *), &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + + *argl = grub_realloc (*argl, sz); + if (! *argl) + { + grub_free (p); + return grub_errno; + } + (*argl)[(*num) - 1] = s; + (*argl)[(*num)] = NULL; + return 0; +} + + +int +grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv, + struct grub_arg_list *usr, char ***args, int *argnum) +{ + int curarg; + int arglen; + char **argl = 0; + int num = 0; + + for (curarg = 0; curarg < argc; curarg++) + { + char *arg = argv[curarg]; + const struct grub_arg_option *opt; + char *option = 0; + + /* No option is used. */ + if ((num && (cmd->cmd->flags & GRUB_COMMAND_OPTIONS_AT_START)) + || arg[0] != '-' || grub_strlen (arg) == 1) + { + if (add_arg (&argl, &num, arg) != 0) + goto fail; + + continue; + } + + /* One or more short options. */ + if (arg[1] != '-') + { + char *curshort; + + if (cmd->cmd->flags & GRUB_COMMAND_ACCEPT_DASH) + { + for (curshort = arg + 1; *curshort; curshort++) + if (!find_short (cmd->options, *curshort)) + break; + + if (*curshort) + { + if (add_arg (&argl, &num, arg) != 0) + goto fail; + continue; + } + } + + curshort = arg + 1; + + while (1) + { + opt = find_short (cmd->options, *curshort); + + if (! opt) + { + char tmp[3] = { '-', *curshort, 0 }; + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unknown argument `%s'"), tmp); + goto fail; + } + + curshort++; + + /* Parse all arguments here except the last one because + it can have an argument value. */ + if (*curshort) + { + /* + * Only permit further short opts if this one doesn't + * require a value. + */ + if (opt->type != ARG_TYPE_NONE && + !(opt->flags & GRUB_ARG_OPTION_OPTIONAL)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("missing mandatory option for `%s'"), + opt->longarg); + goto fail; + } + + if (parse_option (cmd, opt, 0, usr) || grub_errno) + goto fail; + } + else + { + if (opt->type != ARG_TYPE_NONE) + { + if (curarg + 1 < argc) + { + char *nextarg = argv[curarg + 1]; + if (!(opt->flags & GRUB_ARG_OPTION_OPTIONAL) + || (grub_strlen (nextarg) < 2 || nextarg[0] != '-')) + option = argv[++curarg]; + } + } + break; + } + } + + } + else /* The argument starts with "--". */ + { + /* If the argument "--" is used just pass the other + arguments. */ + if (grub_strlen (arg) == 2) + { + for (curarg++; curarg < argc; curarg++) + if (add_arg (&argl, &num, argv[curarg]) != 0) + goto fail; + break; + } + + option = grub_strchr (arg, '='); + if (option) + { + arglen = option - arg - 2; + option++; + } + else + arglen = grub_strlen (arg) - 2; + + opt = find_long (cmd->options, arg + 2, arglen); + + if (!option && argv[curarg + 1] && argv[curarg + 1][0] != '-' + && opt && opt->type != ARG_TYPE_NONE) + option = argv[++curarg]; + + if (!opt && (cmd->cmd->flags & GRUB_COMMAND_ACCEPT_DASH)) + { + if (add_arg (&argl, &num, arg) != 0) + goto fail; + continue; + } + + if (! opt) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unknown argument `%s'"), arg); + goto fail; + } + } + + if (! (opt->type == ARG_TYPE_NONE + || (! option && (opt->flags & GRUB_ARG_OPTION_OPTIONAL)))) + { + if (! option) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("missing mandatory option for `%s'"), opt->longarg); + goto fail; + } + + switch (opt->type) + { + case ARG_TYPE_NONE: + /* This will never happen. */ + break; + + case ARG_TYPE_STRING: + /* No need to do anything. */ + break; + + case ARG_TYPE_INT: + { + const char * tail; + + grub_strtoull (option, &tail, 0); + if (tail == 0 || tail == option || *tail != '\0' || grub_errno) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("the argument `%s' requires an integer"), + arg); + + goto fail; + } + break; + } + + case ARG_TYPE_DEVICE: + case ARG_TYPE_DIR: + case ARG_TYPE_FILE: + case ARG_TYPE_PATHNAME: + /* XXX: Not implemented. */ + break; + } + if (parse_option (cmd, opt, option, usr) || grub_errno) + goto fail; + } + else + { + if (option) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("a value was assigned to the argument `%s' while it " + "doesn't require an argument"), arg); + goto fail; + } + + if (parse_option (cmd, opt, 0, usr) || grub_errno) + goto fail; + } + } + + *args = argl; + *argnum = num; + return 1; + + fail: + return 0; +} + +struct grub_arg_list* +grub_arg_list_alloc(grub_extcmd_t extcmd, int argc, + char **argv __attribute__((unused))) +{ + int i; + char **args; + grub_size_t argcnt; + struct grub_arg_list *list; + const struct grub_arg_option *options; + grub_size_t sz0, sz1; + + options = extcmd->options; + if (! options) + return 0; + + argcnt = 0; + for (i = 0; options[i].doc; i++) + { + if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE) + argcnt += ((grub_size_t) argc + 1) / 2 + 1; /* max possible for any option */ + } + + if (grub_mul (sizeof (*list), i, &sz0) || + grub_mul (sizeof (char *), argcnt, &sz1) || + grub_add (sz0, sz1, &sz0)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return 0; + } + + list = grub_zalloc (sz0); + if (! list) + return 0; + + args = (char**) (list + i); + for (i = 0; options[i].doc; i++) + { + list[i].set = 0; + list[i].arg = 0; + + if (options[i].flags & GRUB_ARG_OPTION_REPEATABLE) + { + list[i].args = args; + args += (grub_size_t) argc / 2 + 1; + } + } + return list; +} diff --git a/grub-core/lib/arm/setjmp.S b/grub-core/lib/arm/setjmp.S new file mode 100644 index 000000000..a5373d3a9 --- /dev/null +++ b/grub-core/lib/arm/setjmp.S @@ -0,0 +1,53 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + + .file "setjmp.S" + +GRUB_MOD_LICENSE "GPLv3+" + + .syntax unified +#if !defined (__thumb2__) + .arm +#else + .thumb +#endif + + .text + +/* + * int grub_setjmp (grub_jmp_buf env) + */ +FUNCTION(grub_setjmp) + mov r12, sp + stm r0, { r4-r12, lr } + mov r0, #0 + bx lr + +/* + * int grub_longjmp (grub_jmp_buf env, int val) + */ +FUNCTION(grub_longjmp) + ldm r0, { r4-r12, lr } + mov sp, r12 + movs r0, r1 + it eq + moveq r0, #1 + bx lr diff --git a/lib/mips/setjmp.S b/grub-core/lib/arm64/setjmp.S similarity index 59% rename from lib/mips/setjmp.S rename to grub-core/lib/arm64/setjmp.S index 8ab6222c4..ffcabf6e4 100644 --- a/lib/mips/setjmp.S +++ b/grub-core/lib/arm64/setjmp.S @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2013 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 @@ -17,49 +17,40 @@ */ #include +#include .file "setjmp.S" - +GRUB_MOD_LICENSE "GPLv3+" .text /* * int grub_setjmp (grub_jmp_buf env) */ FUNCTION(grub_setjmp) - sw $s0, 0($a0) - sw $s1, 4($a0) - sw $s2, 8($a0) - sw $s3, 12($a0) - sw $s4, 16($a0) - sw $s5, 20($a0) - sw $s6, 24($a0) - sw $s7, 28($a0) - sw $s8, 32($a0) - sw $gp, 36($a0) - sw $sp, 40($a0) - sw $ra, 44($a0) - move $v0, $zero - move $v1, $zero - jr $ra + stp x19, x20, [x0], #16 + stp x21, x22, [x0], #16 + stp x23, x24, [x0], #16 + stp x25, x26, [x0], #16 + stp x27, x28, [x0], #16 + stp x29, x30, [x0], #16 + mov x1, sp + str x1, [x0] + mov x0, #0 + ret + /* * int grub_longjmp (grub_jmp_buf env, int val) */ FUNCTION(grub_longjmp) - lw $s0, 0($a0) - lw $s1, 4($a0) - lw $s2, 8($a0) - lw $s3, 12($a0) - lw $s4, 16($a0) - lw $s5, 20($a0) - lw $s6, 24($a0) - lw $s7, 28($a0) - lw $s8, 32($a0) - lw $gp, 36($a0) - lw $sp, 40($a0) - lw $ra, 44($a0) - move $v0, $a1 - bne $v0, $zero, 1f - addiu $v0, $v0, 1 -1: - move $v1, $zero - jr $ra + ldp x19, x20, [x0], #16 + ldp x21, x22, [x0], #16 + ldp x23, x24, [x0], #16 + ldp x25, x26, [x0], #16 + ldp x27, x28, [x0], #16 + ldp x29, x30, [x0], #16 + ldr x2, [x0] + mov sp, x2 + mov x0, #1 + cmp x1, #0 + csel x0, x1, x0, ne + ret diff --git a/grub-core/lib/backtrace.c b/grub-core/lib/backtrace.c new file mode 100644 index 000000000..825a8800e --- /dev/null +++ b/grub-core/lib/backtrace.c @@ -0,0 +1,70 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +void +grub_backtrace_print_address (void *addr) +{ + grub_dl_t mod; + + FOR_DL_MODULES (mod) + { + grub_dl_segment_t segment; + for (segment = mod->segment; segment; segment = segment->next) + if (segment->addr <= addr && (grub_uint8_t *) segment->addr + + segment->size > (grub_uint8_t *) addr) + { + grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name, segment->section, + (grub_size_t) ((grub_uint8_t *) addr - (grub_uint8_t *) segment->addr)); + return; + } + } + + grub_printf ("%p", addr); +} + +static grub_err_t +grub_cmd_backtrace (grub_command_t cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_backtrace (); + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(backtrace) +{ + cmd = grub_register_command ("backtrace", grub_cmd_backtrace, + 0, N_("Print backtrace.")); +} + +GRUB_MOD_FINI(backtrace) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c new file mode 100644 index 000000000..ed0b149dc --- /dev/null +++ b/grub-core/lib/cmdline.c @@ -0,0 +1,109 @@ +/* cmdline.c - linux command line handling */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include + +static unsigned int check_arg (char *c, int *has_space) +{ + int space = 0; + unsigned int size = 0; + + while (*c) + { + if (*c == '\\' || *c == '\'' || *c == '"') + size++; + else if (*c == ' ') + space = 1; + + size++; + c++; + } + + if (space) + size += 2; + + if (has_space) + *has_space = space; + + return size; +} + +unsigned int grub_loader_cmdline_size (int argc, char *argv[]) +{ + int i; + unsigned int size = 0; + + for (i = 0; i < argc; i++) + { + size += check_arg (argv[i], 0); + size++; /* Separator space or NULL. */ + } + + if (size == 0) + size = 1; + + return size; +} + +grub_err_t +grub_create_loader_cmdline (int argc, char *argv[], char *buf, + grub_size_t size, enum grub_verify_string_type type) +{ + int i, space; + unsigned int arg_size; + char *c, *orig_buf = buf; + + for (i = 0; i < argc; i++) + { + c = argv[i]; + arg_size = check_arg(argv[i], &space); + arg_size++; /* Separator space or NULL. */ + + if (size < arg_size) + break; + + size -= arg_size; + + if (space) + *buf++ = '"'; + + while (*c) + { + if (*c == '\\' || *c == '\'' || *c == '"') + *buf++ = '\\'; + + *buf++ = *c; + c++; + } + + if (space) + *buf++ = '"'; + + *buf++ = ' '; + } + + /* Replace last space with null. */ + if (i) + buf--; + + *buf = 0; + + return grub_verify_string (orig_buf, type); +} diff --git a/lib/cmos_datetime.c b/grub-core/lib/cmos_datetime.c similarity index 62% rename from lib/cmos_datetime.c rename to grub-core/lib/cmos_datetime.c index 8db60b48c..86cd91180 100644 --- a/lib/cmos_datetime.c +++ b/grub-core/lib/cmos_datetime.c @@ -19,31 +19,48 @@ #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#if !defined (__powerpc__) && !defined (__sparc__) +#define grub_get_datetime_cmos grub_get_datetime +#define grub_set_datetime_cmos grub_set_datetime +#endif grub_err_t -grub_get_datetime (struct grub_datetime *datetime) +grub_get_datetime_cmos (struct grub_datetime *datetime) { int is_bcd, is_12hour; grub_uint8_t value, flag; + grub_err_t err; - flag = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B); + err = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B, &flag); + if (err) + return err; is_bcd = ! (flag & GRUB_CMOS_STATUS_B_BINARY); - value = grub_cmos_read (GRUB_CMOS_INDEX_YEAR); + err = grub_cmos_read (GRUB_CMOS_INDEX_YEAR, &value); + if (err) + return err; if (is_bcd) value = grub_bcd_to_num (value); datetime->year = value; datetime->year += (value < 80) ? 2000 : 1900; - value = grub_cmos_read (GRUB_CMOS_INDEX_MONTH); + err = grub_cmos_read (GRUB_CMOS_INDEX_MONTH, &value); + if (err) + return err; if (is_bcd) value = grub_bcd_to_num (value); datetime->month = value; - value = grub_cmos_read (GRUB_CMOS_INDEX_DAY_OF_MONTH); + err = grub_cmos_read (GRUB_CMOS_INDEX_DAY_OF_MONTH, &value); + if (err) + return err; if (is_bcd) value = grub_bcd_to_num (value); @@ -51,7 +68,9 @@ grub_get_datetime (struct grub_datetime *datetime) is_12hour = ! (flag & GRUB_CMOS_STATUS_B_24HOUR); - value = grub_cmos_read (GRUB_CMOS_INDEX_HOUR); + err = grub_cmos_read (GRUB_CMOS_INDEX_HOUR, &value); + if (err) + return err; if (is_12hour) { is_12hour = (value & 0x80); @@ -68,13 +87,18 @@ grub_get_datetime (struct grub_datetime *datetime) datetime->hour = value; - value = grub_cmos_read (GRUB_CMOS_INDEX_MINUTE); + err = grub_cmos_read (GRUB_CMOS_INDEX_MINUTE, &value); + if (err) + return err; + if (is_bcd) value = grub_bcd_to_num (value); datetime->minute = value; - value = grub_cmos_read (GRUB_CMOS_INDEX_SECOND); + err = grub_cmos_read (GRUB_CMOS_INDEX_SECOND, &value); + if (err) + return err; if (is_bcd) value = grub_bcd_to_num (value); @@ -84,12 +108,15 @@ grub_get_datetime (struct grub_datetime *datetime) } grub_err_t -grub_set_datetime (struct grub_datetime *datetime) +grub_set_datetime_cmos (struct grub_datetime *datetime) { int is_bcd, is_12hour; grub_uint8_t value, flag; + grub_err_t err; - flag = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B); + err = grub_cmos_read (GRUB_CMOS_INDEX_STATUS_B, &flag); + if (err) + return err; is_bcd = ! (flag & GRUB_CMOS_STATUS_B_BINARY); @@ -99,21 +126,27 @@ grub_set_datetime (struct grub_datetime *datetime) if (is_bcd) value = grub_num_to_bcd (value); - grub_cmos_write (GRUB_CMOS_INDEX_YEAR, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_YEAR, value); + if (err) + return err; value = datetime->month; if (is_bcd) value = grub_num_to_bcd (value); - grub_cmos_write (GRUB_CMOS_INDEX_MONTH, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_MONTH, value); + if (err) + return err; value = datetime->day; if (is_bcd) value = grub_num_to_bcd (value); - grub_cmos_write (GRUB_CMOS_INDEX_DAY_OF_MONTH, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_DAY_OF_MONTH, value); + if (err) + return err; value = datetime->hour; @@ -135,21 +168,27 @@ grub_set_datetime (struct grub_datetime *datetime) if (is_12hour) value |= 0x80; - grub_cmos_write (GRUB_CMOS_INDEX_HOUR, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_HOUR, value); + if (err) + return err; value = datetime->minute; if (is_bcd) value = grub_num_to_bcd (value); - grub_cmos_write (GRUB_CMOS_INDEX_MINUTE, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_MINUTE, value); + if (err) + return err; value = datetime->second; if (is_bcd) value = grub_num_to_bcd (value); - grub_cmos_write (GRUB_CMOS_INDEX_SECOND, value); + err = grub_cmos_write (GRUB_CMOS_INDEX_SECOND, value); + if (err) + return err; return 0; } diff --git a/lib/crc.c b/grub-core/lib/crc.c similarity index 56% rename from lib/crc.c rename to grub-core/lib/crc.c index bc0d8aa8d..bf97cc63a 100644 --- a/lib/crc.c +++ b/grub-core/lib/crc.c @@ -20,54 +20,55 @@ #include #include -static grub_uint32_t crc32_table [256]; +static grub_uint32_t crc32c_table [256]; -static void -init_crc32_table (void) +/* Helper for init_crc32c_table. */ +static grub_uint32_t +reflect (grub_uint32_t ref, int len) { - auto grub_uint32_t reflect (grub_uint32_t ref, int len); - grub_uint32_t reflect (grub_uint32_t ref, int len) + grub_uint32_t result = 0; + int i; + + for (i = 1; i <= len; i++) { - grub_uint32_t result = 0; - int i; - - for (i = 1; i <= len; i++) - { - if (ref & 1) - result |= 1 << (len - i); - ref >>= 1; - } - - return result; + if (ref & 1) + result |= 1 << (len - i); + ref >>= 1; } - grub_uint32_t polynomial = 0x04c11db7; + return result; +} + +static void +init_crc32c_table (void) +{ + grub_uint32_t polynomial = 0x1edc6f41; int i, j; for(i = 0; i < 256; i++) { - crc32_table[i] = reflect(i, 8) << 24; + crc32c_table[i] = reflect(i, 8) << 24; for (j = 0; j < 8; j++) - crc32_table[i] = (crc32_table[i] << 1) ^ - (crc32_table[i] & (1 << 31) ? polynomial : 0); - crc32_table[i] = reflect(crc32_table[i], 32); + crc32c_table[i] = (crc32c_table[i] << 1) ^ + (crc32c_table[i] & (1 << 31) ? polynomial : 0); + crc32c_table[i] = reflect(crc32c_table[i], 32); } } grub_uint32_t -grub_getcrc32 (grub_uint32_t crc, void *buf, int size) +grub_getcrc32c (grub_uint32_t crc, const void *buf, int size) { int i; - grub_uint8_t *data = buf; + const grub_uint8_t *data = buf; - if (! crc32_table[1]) - init_crc32_table (); + if (! crc32c_table[1]) + init_crc32c_table (); crc^= 0xffffffff; for (i = 0; i < size; i++) { - crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data]; + crc = (crc >> 8) ^ crc32c_table[(crc & 0xFF) ^ *data]; data++; } diff --git a/grub-core/lib/crc64.c b/grub-core/lib/crc64.c new file mode 100644 index 000000000..4960f3f89 --- /dev/null +++ b/grub-core/lib/crc64.c @@ -0,0 +1,114 @@ +/* crc64.c - crc64 function */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2011 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 . + */ + +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint64_t crc64_table [256]; + +/* Helper for init_crc64_table. */ +static grub_uint64_t +reflect (grub_uint64_t ref, int len) +{ + grub_uint64_t result = 0; + int i; + + for (i = 1; i <= len; i++) + { + if (ref & 1) + result |= 1ULL << (len - i); + ref >>= 1; + } + + return result; +} + +static void +init_crc64_table (void) +{ + grub_uint64_t polynomial = 0x42f0e1eba9ea3693ULL; + int i, j; + + for(i = 0; i < 256; i++) + { + crc64_table[i] = reflect(i, 8) << 56; + for (j = 0; j < 8; j++) + { + crc64_table[i] = (crc64_table[i] << 1) ^ + (crc64_table[i] & (1ULL << 63) ? polynomial : 0); + } + crc64_table[i] = reflect(crc64_table[i], 64); + } +} + +static void +crc64_init (void *context) +{ + if (! crc64_table[1]) + init_crc64_table (); + *(grub_uint64_t *) context = 0; +} + +static void +crc64_write (void *context, const void *buf, grub_size_t size) +{ + unsigned i; + const grub_uint8_t *data = buf; + grub_uint64_t crc = ~grub_le_to_cpu64 (*(grub_uint64_t *) context); + + for (i = 0; i < size; i++) + { + crc = (crc >> 8) ^ crc64_table[(crc & 0xFF) ^ *data]; + data++; + } + + *(grub_uint64_t *) context = grub_cpu_to_le64 (~crc); +} + +static grub_uint8_t * +crc64_read (void *context) +{ + return context; +} + +static void +crc64_final (void *context __attribute__ ((unused))) +{ +} + +gcry_md_spec_t _gcry_digest_spec_crc64 = + { + "CRC64", 0, 0, 0, 8, + crc64_init, crc64_write, crc64_final, crc64_read, + sizeof (grub_uint64_t), + .blocksize = 64 + }; + +GRUB_MOD_INIT(crc64) +{ + grub_md_register (&_gcry_digest_spec_crc64); +} + +GRUB_MOD_FINI(crc64) +{ + grub_md_unregister (&_gcry_digest_spec_crc64); +} diff --git a/lib/crypto.c b/grub-core/lib/crypto.c similarity index 73% rename from lib/crypto.c rename to grub-core/lib/crypto.c index d11f0994f..396f76410 100644 --- a/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -21,6 +21,11 @@ #include #include #include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); struct grub_crypto_hmac_handle { @@ -45,8 +50,40 @@ grub_burn_stack (grub_size_t size) grub_burn_stack (size - sizeof (buf)); } +void +_gcry_burn_stack (int size) +{ + grub_burn_stack (size); +} -void +void __attribute__ ((noreturn)) +_gcry_assert_failed (const char *expr, const char *file, int line, + const char *func) + +{ + grub_fatal ("assertion %s at %s:%d (%s) failed\n", expr, file, line, func); +} + + +void _gcry_log_error (const char *fmt, ...) +{ + va_list args; + const char *debug = grub_env_get ("debug"); + + if (! debug) + return; + + if (grub_strword (debug, "all") || grub_strword (debug, "gcrypt")) + { + grub_printf ("gcrypt error: "); + va_start (args, fmt); + grub_vprintf (fmt, args); + va_end (args); + grub_refresh (); + } +} + +void grub_cipher_register (gcry_cipher_spec_t *cipher) { cipher->next = grub_ciphers; @@ -65,14 +102,14 @@ grub_cipher_unregister (gcry_cipher_spec_t *cipher) } } -void +void grub_md_register (gcry_md_spec_t *digest) { digest->next = grub_digests; grub_digests = digest; } -void +void grub_md_unregister (gcry_md_spec_t *cipher) { gcry_md_spec_t **ciph; @@ -88,7 +125,10 @@ 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]; + GRUB_PROPERLY_ALIGNED_ARRAY (ctx, GRUB_CRYPTO_MAX_MD_CONTEXT_SIZE); + + if (hash->contextsize > sizeof (ctx)) + grub_fatal ("Too large md context"); hash->init (&ctx); hash->write (&ctx, in, inlen); hash->final (&ctx); @@ -159,103 +199,99 @@ grub_crypto_cipher_set_key (grub_crypto_cipher_handle_t cipher, 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) + void *out, const void *in, grub_size_t size) { - grub_uint8_t *inptr, *outptr, *end; + const grub_uint8_t *inptr, *end; + grub_uint8_t *outptr; + grub_size_t blocksize; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; - if (size % cipher->cipher->blocksize != 0) + blocksize = cipher->cipher->blocksize; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; - end = (grub_uint8_t *) in + size; + end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; - inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + inptr += blocksize, outptr += 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) + void *out, const void *in, grub_size_t size) { - grub_uint8_t *inptr, *outptr, *end; + const grub_uint8_t *inptr, *end; + grub_uint8_t *outptr; + grub_size_t blocksize; if (!cipher->cipher->encrypt) return GPG_ERR_NOT_SUPPORTED; - if (size % cipher->cipher->blocksize != 0) + blocksize = cipher->cipher->blocksize; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; - end = (grub_uint8_t *) in + size; + end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; - inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + inptr += blocksize, outptr += 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 *out, const void *in, grub_size_t size, void *iv_in) { - grub_uint8_t *inptr, *outptr, *end; + grub_uint8_t *outptr; + const grub_uint8_t *inptr, *end; void *iv; - if (!cipher->cipher->decrypt) + grub_size_t blocksize; + if (!cipher->cipher->encrypt) return GPG_ERR_NOT_SUPPORTED; - if (size % cipher->cipher->blocksize != 0) + blocksize = cipher->cipher->blocksize; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; - end = (grub_uint8_t *) in + size; + end = (const grub_uint8_t *) in + size; iv = iv_in; for (inptr = in, outptr = out; inptr < end; - inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + inptr += blocksize, outptr += blocksize) { - grub_crypto_xor (outptr, inptr, iv, cipher->cipher->blocksize); + grub_crypto_xor (outptr, inptr, iv, blocksize); cipher->cipher->encrypt (cipher->ctx, outptr, outptr); iv = outptr; } - grub_memcpy (iv_in, iv, cipher->cipher->blocksize); + grub_memcpy (iv_in, iv, 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 *out, const void *in, grub_size_t size, void *iv) { - grub_uint8_t *inptr, *outptr, *end; - grub_uint8_t ivt[cipher->cipher->blocksize]; + const grub_uint8_t *inptr, *end; + grub_uint8_t *outptr; + grub_uint8_t ivt[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; + grub_size_t blocksize; if (!cipher->cipher->decrypt) return GPG_ERR_NOT_SUPPORTED; - if (size % cipher->cipher->blocksize != 0) + blocksize = cipher->cipher->blocksize; + if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) + || ((size & (blocksize - 1)) != 0)) return GPG_ERR_INV_ARG; - end = (grub_uint8_t *) in + size; + if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) + return GPG_ERR_INV_ARG; + end = (const grub_uint8_t *) in + size; for (inptr = in, outptr = out; inptr < end; - inptr += cipher->cipher->blocksize, outptr += cipher->cipher->blocksize) + inptr += blocksize, outptr += blocksize) { - grub_memcpy (ivt, inptr, cipher->cipher->blocksize); + grub_memcpy (ivt, inptr, blocksize); cipher->cipher->decrypt (cipher->ctx, outptr, inptr); - grub_crypto_xor (outptr, outptr, iv, cipher->cipher->blocksize); - grub_memcpy (iv, ivt, cipher->cipher->blocksize); + grub_crypto_xor (outptr, outptr, iv, blocksize); + grub_memcpy (iv, ivt, blocksize); } return GPG_ERR_NO_ERROR; } @@ -278,7 +314,7 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, if (!ctx) goto err; - if ( keylen > md->blocksize ) + if ( keylen > md->blocksize ) { helpkey = grub_malloc (md->mdlen); if (!helpkey) @@ -299,7 +335,7 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, grub_memcpy ( ipad, key, keylen ); grub_memcpy ( opad, key, keylen ); - for (i=0; i < md->blocksize; i++ ) + for (i=0; i < md->blocksize; i++ ) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; @@ -333,7 +369,8 @@ grub_crypto_hmac_init (const struct gcry_md_spec *md, } void -grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, void *data, +grub_crypto_hmac_write (struct grub_crypto_hmac_handle *hnd, + const void *data, grub_size_t datalen) { hnd->md->write (hnd->ctx, data, datalen); @@ -375,7 +412,7 @@ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out) 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) + const void *data, grub_size_t datalen, void *out) { struct grub_crypto_hmac_handle *hnd; @@ -411,7 +448,8 @@ grub_crypto_memcmp (const void *a, const void *b, grub_size_t n) return !!counter; } -#ifndef GRUB_MKPASSWD +#ifndef GRUB_UTIL + int grub_password_get (char buf[], unsigned buf_size) { @@ -420,11 +458,11 @@ grub_password_get (char buf[], unsigned buf_size) while (1) { - key = GRUB_TERM_ASCII_CHAR (grub_getkey ()); + key = grub_getkey (); if (key == '\n' || key == '\r') break; - if (key == '\e') + if (key == GRUB_TERM_ESC) { cur_len = 0; break; @@ -432,7 +470,8 @@ grub_password_get (char buf[], unsigned buf_size) if (key == '\b') { - cur_len--; + if (cur_len) + cur_len--; continue; } @@ -445,9 +484,10 @@ grub_password_get (char buf[], unsigned buf_size) grub_memset (buf + cur_len, 0, buf_size - cur_len); - grub_putchar ('\n'); + grub_xputs ("\n"); grub_refresh (); - return (key != '\e'); + return (key != GRUB_TERM_ESC); } #endif + diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c new file mode 100644 index 000000000..8f0922fb0 --- /dev/null +++ b/grub-core/lib/datetime.c @@ -0,0 +1,122 @@ +/* datetime.c - Module for common datetime function. */ +/* + * 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 . + */ + +#include +#include +#include +#include +#ifdef GRUB_MACHINE_EMU +#include + +GRUB_MOD_LICENSE ("GPLv3+"); +#endif + +static const char *const grub_weekday_names[] = +{ + N_("Sunday"), + N_("Monday"), + N_("Tuesday"), + N_("Wednesday"), + N_("Thursday"), + N_("Friday"), + N_("Saturday"), +}; + +int +grub_get_weekday (struct grub_datetime *datetime) +{ + unsigned a, y, m; + + if (datetime->month <= 2) + a = 1; + else + a = 0; + y = datetime->year - a; + m = datetime->month + 12 * a - 2; + + return (datetime->day + y + y / 4 - y / 100 + y / 400 + (31 * m / 12)) % 7; +} + +const char * +grub_get_weekday_name (struct grub_datetime *datetime) +{ + return _ (grub_weekday_names[grub_get_weekday (datetime)]); +} + +#define SECPERMIN 60 +#define SECPERHOUR (60*SECPERMIN) +#define SECPERDAY (24*SECPERHOUR) +#define DAYSPERYEAR 365 +#define DAYSPER4YEARS (4*DAYSPERYEAR+1) + + +void +grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime) +{ + int i; + grub_uint8_t months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + /* In the period of validity of unixtime all years divisible by 4 + are bissextile*/ + /* Convenience: let's have 3 consecutive non-bissextile years + at the beginning of the counting date. So count from 1901. */ + int days_epoch; + /* Number of days since 1st Januar, 1901. */ + unsigned days; + /* Seconds into current day. */ + unsigned secs_in_day; + + /* Transform C divisions and modulos to mathematical ones */ + if (nix < 0) + /* + * The result of division here shouldn't be larger than GRUB_INT_MAX. + * So, it's safe to store the result back in an int. + */ + days_epoch = -(grub_divmod64 (((grub_int64_t) (SECPERDAY) - nix - 1), SECPERDAY, NULL)); + else + days_epoch = grub_divmod64 (nix, SECPERDAY, NULL); + + secs_in_day = nix - days_epoch * SECPERDAY; + days = days_epoch + 69 * DAYSPERYEAR + 17; + + datetime->year = 1901 + 4 * (days / DAYSPER4YEARS); + days %= DAYSPER4YEARS; + /* On 31st December of bissextile years 365 days from the beginning + of the year elapsed but year isn't finished yet */ + if (days / DAYSPERYEAR == 4) + { + datetime->year += 3; + days -= 3*DAYSPERYEAR; + } + else + { + datetime->year += days / DAYSPERYEAR; + days %= DAYSPERYEAR; + } + for (i = 0; i < 12 + && days >= (i==1 && datetime->year % 4 == 0 + ? 29 : months[i]); i++) + days -= (i==1 && datetime->year % 4 == 0 + ? 29 : months[i]); + datetime->month = i + 1; + datetime->day = 1 + days; + datetime->hour = (secs_in_day / SECPERHOUR); + secs_in_day %= SECPERHOUR; + datetime->minute = secs_in_day / SECPERMIN; + datetime->second = secs_in_day % SECPERMIN; +} diff --git a/grub-core/lib/disk.c b/grub-core/lib/disk.c new file mode 100644 index 000000000..123993dd4 --- /dev/null +++ b/grub-core/lib/disk.c @@ -0,0 +1,161 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "../kern/disk_common.c" + +static void +grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id, + grub_disk_addr_t sector) +{ + unsigned cache_index; + struct grub_disk_cache *cache; + + sector &= ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1); + cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector); + cache = grub_disk_cache_table + cache_index; + + if (cache->dev_id == dev_id && cache->disk_id == disk_id + && cache->sector == sector && cache->data) + { + cache->lock = 1; + grub_free (cache->data); + cache->data = 0; + cache->lock = 0; + } +} + +grub_err_t +grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector, + grub_off_t offset, grub_size_t size, const void *buf) +{ + unsigned real_offset; + grub_disk_addr_t aligned_sector; + + grub_dprintf ("disk", "Writing `%s'...\n", disk->name); + + if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) + return -1; + + aligned_sector = (sector & ~((1ULL << (disk->log_sector_size + - GRUB_DISK_SECTOR_BITS)) - 1)); + real_offset = offset + ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS); + sector = aligned_sector; + + while (size) + { + if (real_offset != 0 || (size < (1U << disk->log_sector_size) + && size != 0)) + { + char *tmp_buf; + grub_size_t len; + grub_partition_t part; + + tmp_buf = grub_malloc (1U << disk->log_sector_size); + if (!tmp_buf) + return grub_errno; + + part = disk->partition; + disk->partition = 0; + if (grub_disk_read (disk, sector, + 0, (1U << disk->log_sector_size), tmp_buf) + != GRUB_ERR_NONE) + { + disk->partition = part; + grub_free (tmp_buf); + goto finish; + } + disk->partition = part; + + len = (1U << disk->log_sector_size) - real_offset; + if (len > size) + len = size; + + grub_memcpy (tmp_buf + real_offset, buf, len); + + grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); + + if ((disk->dev->disk_write) (disk, grub_disk_to_native_sector (disk, sector), + 1, tmp_buf) != GRUB_ERR_NONE) + { + grub_free (tmp_buf); + goto finish; + } + + grub_free (tmp_buf); + + sector += (1U << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); + buf = (const char *) buf + len; + size -= len; + real_offset = 0; + } + else + { + grub_size_t len; + grub_size_t n; + + len = size & ~((1ULL << disk->log_sector_size) - 1); + n = size >> disk->log_sector_size; + + if (n > (disk->max_agglomerate + << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size))) + n = (disk->max_agglomerate + << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS + - disk->log_sector_size)); + + if ((disk->dev->disk_write) (disk, grub_disk_to_native_sector (disk, sector), + n, buf) != GRUB_ERR_NONE) + goto finish; + + while (n--) + { + grub_disk_cache_invalidate (disk->dev->id, disk->id, sector); + sector += (1U << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)); + } + + buf = (const char *) buf + len; + size -= len; + } + } + + finish: + + return grub_errno; +} + +GRUB_MOD_INIT(disk) +{ + grub_disk_write_weak = grub_disk_write; +} + +GRUB_MOD_FINI(disk) +{ + grub_disk_write_weak = NULL; +} diff --git a/grub-core/lib/division.c b/grub-core/lib/division.c new file mode 100644 index 000000000..35606fea7 --- /dev/null +++ b/grub-core/lib/division.c @@ -0,0 +1,74 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2015 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 . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_uint64_t +abs64(grub_int64_t a) +{ + return a > 0 ? a : -a; +} + +grub_int64_t +grub_divmod64s (grub_int64_t n, + grub_int64_t d, + grub_int64_t *ro) +{ + grub_uint64_t ru; + grub_int64_t q, r; + q = grub_divmod64 (abs64(n), abs64(d), &ru); + r = ru; + /* Now: |n| = |d| * q + r */ + if (n < 0) + { + /* -|n| = |d| * (-q) + (-r) */ + q = -q; + r = -r; + } + /* Now: n = |d| * q + r */ + if (d < 0) + { + /* n = (-|d|) * (-q) + r */ + q = -q; + } + /* Now: n = d * q + r */ + if (ro) + *ro = r; + return q; +} + +grub_uint32_t +grub_divmod32 (grub_uint32_t n, grub_uint32_t d, grub_uint32_t *ro) +{ + grub_uint64_t q, r; + q = grub_divmod64 (n, d, &r); + *ro = r; + return q; +} + +grub_int32_t +grub_divmod32s (grub_int32_t n, grub_int32_t d, grub_int32_t *ro) +{ + grub_int64_t q, r; + q = grub_divmod64s (n, d, &r); + *ro = r; + return q; +} diff --git a/grub-core/lib/dummy/datetime.c b/grub-core/lib/dummy/datetime.c new file mode 100644 index 000000000..cf693fc6b --- /dev/null +++ b/grub-core/lib/dummy/datetime.c @@ -0,0 +1,40 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* No simple platform-independent RTC access exists in U-Boot. */ + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_INVALID_COMMAND, + "can\'t get datetime on this machine"); +} + +grub_err_t +grub_set_datetime (struct grub_datetime * datetime __attribute__ ((unused))) +{ + return grub_error (GRUB_ERR_INVALID_COMMAND, + "can\'t set datetime on this machine"); +} diff --git a/grub-core/lib/dummy/halt.c b/grub-core/lib/dummy/halt.c new file mode 100644 index 000000000..378d50f3f --- /dev/null +++ b/grub-core/lib/dummy/halt.c @@ -0,0 +1,32 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +void +grub_halt (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); + + /* Just stop here */ + + while (1); +} diff --git a/grub-core/lib/dummy/reboot.c b/grub-core/lib/dummy/reboot.c new file mode 100644 index 000000000..b8cbed8f8 --- /dev/null +++ b/grub-core/lib/dummy/reboot.c @@ -0,0 +1,32 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +void +grub_reboot (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); + + /* Just stop here */ + + while (1); +} diff --git a/lib/efi/datetime.c b/grub-core/lib/efi/datetime.c similarity index 85% rename from lib/efi/datetime.c rename to grub-core/lib/efi/datetime.c index 0a91c345a..b03e4df5e 100644 --- a/lib/efi/datetime.c +++ b/grub-core/lib/efi/datetime.c @@ -22,6 +22,9 @@ #include #include #include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); grub_err_t grub_get_datetime (struct grub_datetime *datetime) @@ -29,8 +32,7 @@ grub_get_datetime (struct grub_datetime *datetime) grub_efi_status_t status; struct grub_efi_time efi_time; - status = efi_call_2 (grub_efi_system_table->runtime_services->get_time, - &efi_time, 0); + status = grub_efi_system_table->runtime_services->get_time (&efi_time, 0); if (status) return grub_error (GRUB_ERR_INVALID_COMMAND, @@ -54,8 +56,7 @@ grub_set_datetime (struct grub_datetime *datetime) grub_efi_status_t status; struct grub_efi_time efi_time; - status = efi_call_2 (grub_efi_system_table->runtime_services->get_time, - &efi_time, 0); + status = grub_efi_system_table->runtime_services->get_time (&efi_time, 0); if (status) return grub_error (GRUB_ERR_INVALID_COMMAND, @@ -68,8 +69,7 @@ grub_set_datetime (struct grub_datetime *datetime) efi_time.minute = datetime->minute; efi_time.second = datetime->second; - status = efi_call_1 (grub_efi_system_table->runtime_services->set_time, - &efi_time); + status = grub_efi_system_table->runtime_services->set_time (&efi_time); if (status) return grub_error (GRUB_ERR_INVALID_COMMAND, diff --git a/grub-core/lib/efi/halt.c b/grub-core/lib/efi/halt.c new file mode 100644 index 000000000..a58a747e5 --- /dev/null +++ b/grub-core/lib/efi/halt.c @@ -0,0 +1,41 @@ +/* efi.c - generic EFI support */ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +void +grub_halt (void) +{ + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN | + GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY); +#if !defined(__ia64__) && !defined(__arm__) && !defined(__aarch64__) && \ + !defined(__loongarch__) && !defined(__riscv) + grub_acpi_halt (); +#endif + grub_efi_system_table->runtime_services->reset_system (GRUB_EFI_RESET_SHUTDOWN, + GRUB_EFI_SUCCESS, 0, NULL); + + while (1); +} diff --git a/grub-core/lib/efi/relocator.c b/grub-core/lib/efi/relocator.c new file mode 100644 index 000000000..b4518d000 --- /dev/null +++ b/grub-core/lib/efi/relocator.c @@ -0,0 +1,119 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include +#include +#include + +#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ + ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) + +unsigned +grub_relocator_firmware_get_max_events (void) +{ + grub_efi_uintn_t mmapsize = 0, descriptor_size = 0; + grub_efi_uint32_t descriptor_version = 0; + grub_efi_uintn_t key; + grub_efi_get_memory_map (&mmapsize, NULL, &key, &descriptor_size, + &descriptor_version); + /* Since grub_relocator_firmware_fill_events uses malloc + we need some reserve. Hence +10. */ + return 2 * (mmapsize / descriptor_size + 10); +} + +unsigned +grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events) +{ + grub_efi_uintn_t mmapsize = 0, desc_size = 0; + grub_efi_uint32_t descriptor_version = 0; + grub_efi_memory_descriptor_t *descs = NULL; + grub_efi_uintn_t key; + int counter = 0; + grub_efi_memory_descriptor_t *desc; + + grub_efi_get_memory_map (&mmapsize, NULL, &key, &desc_size, + &descriptor_version); + descs = grub_malloc (mmapsize); + if (!descs) + return 0; + + grub_efi_get_memory_map (&mmapsize, descs, &key, &desc_size, + &descriptor_version); + + for (desc = descs; + (char *) desc < ((char *) descs + mmapsize); + desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) + { + grub_uint64_t start = desc->physical_start; + grub_uint64_t end = desc->physical_start + (desc->num_pages << 12); + + /* post-4G addresses are never supported on 32-bit EFI. + Moreover it has been reported that some 64-bit EFI contrary to the + spec don't map post-4G pages. So if you enable post-4G allocations, + map pages manually or check that they are mapped. + */ + if (end >= 0x100000000ULL) + end = 0x100000000ULL; + if (end <= start) + continue; + if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY) + continue; + events[counter].type = REG_FIRMWARE_START; + events[counter].pos = start; + counter++; + events[counter].type = REG_FIRMWARE_END; + events[counter].pos = end; + counter++; + } + + return counter; +} + +int +grub_relocator_firmware_alloc_region (grub_addr_t start, grub_size_t size) +{ + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address = start; + grub_efi_status_t status; + + if (grub_efi_is_finished) + return 1; +#ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF + grub_dprintf ("relocator", "EFI alloc: %llx, %llx\n", + (unsigned long long) start, (unsigned long long) size); +#endif + b = grub_efi_system_table->boot_services; + status = b->allocate_pages (GRUB_EFI_ALLOCATE_ADDRESS, + GRUB_EFI_LOADER_DATA, size >> 12, &address); + return (status == GRUB_EFI_SUCCESS); +} + +void +grub_relocator_firmware_free_region (grub_addr_t start, grub_size_t size) +{ + grub_efi_boot_services_t *b; + + if (grub_efi_is_finished) + return; + + b = grub_efi_system_table->boot_services; + b->free_pages (start, size >> 12); +} diff --git a/grub-core/lib/efi/tcg2.c b/grub-core/lib/efi/tcg2.c new file mode 100644 index 000000000..841bf50bb --- /dev/null +++ b/grub-core/lib/efi/tcg2.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 Microsoft Corporation + * Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include + +#include + +static grub_err_t +tcg2_get_caps (grub_efi_tpm2_protocol_t *protocol, int *tpm2, grub_size_t *max_output_size) +{ + grub_efi_status_t status; + static bool has_caps = 0; + static EFI_TCG2_BOOT_SERVICE_CAPABILITY caps = + { + .Size = (grub_uint8_t) sizeof (caps) + }; + + if (has_caps) + goto exit; + + status = protocol->get_capability (protocol, &caps); + if (status != GRUB_EFI_SUCCESS || !caps.TPMPresentFlag) + return GRUB_ERR_FILE_NOT_FOUND; + + has_caps = 1; + + exit: + if (tpm2 != NULL) + *tpm2 = caps.TPMPresentFlag; + if (max_output_size != NULL) + *max_output_size = caps.MaxResponseSize; + + return GRUB_ERR_NONE; +} + +static grub_err_t +tcg2_get_protocol (grub_efi_tpm2_protocol_t **protocol) +{ + static grub_guid_t tpm2_guid = EFI_TPM2_GUID; + static grub_efi_tpm2_protocol_t *tpm2_protocol = NULL; + int tpm2; + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + grub_efi_handle_t tpm2_handle; + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + + if (tpm2_protocol != NULL) + { + *protocol = tpm2_protocol; + return GRUB_ERR_NONE; + } + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &tpm2_guid, NULL, + &num_handles); + if (handles == NULL || num_handles == 0) + return err; + + tpm2_handle = handles[0]; + + tpm2_protocol = grub_efi_open_protocol (tpm2_handle, &tpm2_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (tpm2_protocol == NULL) + goto exit; + + err = tcg2_get_caps (tpm2_protocol, &tpm2, NULL); + if (err != GRUB_ERR_NONE || tpm2 == 0) + goto exit; + + *protocol = tpm2_protocol; + err = GRUB_ERR_NONE; + + exit: + grub_free (handles); + return err; +} + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + grub_err_t err; + grub_size_t max; + grub_efi_tpm2_protocol_t *protocol; + + if (size == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = tcg2_get_protocol (&protocol); + if (err != GRUB_ERR_NONE) + return err; + + err = tcg2_get_caps (protocol, NULL, &max); + if (err != GRUB_ERR_NONE) + return err; + + *size = max; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, + grub_uint8_t *input, + grub_size_t output_size, + grub_uint8_t *output) +{ + grub_err_t err; + grub_efi_status_t status; + grub_efi_tpm2_protocol_t *protocol; + + if (input_size == 0 || input == NULL || + output_size == 0 || output == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = tcg2_get_protocol (&protocol); + if (err != GRUB_ERR_NONE) + return err; + + status = protocol->submit_command (protocol, input_size, input, + output_size, output); + if (status != GRUB_EFI_SUCCESS) + return GRUB_ERR_INVALID_COMMAND; + + return GRUB_ERR_NONE; +} diff --git a/include/grub/i386/efi/loader.h b/grub-core/lib/emu/halt.c similarity index 78% rename from include/grub/i386/efi/loader.h rename to grub-core/lib/emu/halt.c index 222dae82d..620935b5a 100644 --- a/include/grub/i386/efi/loader.h +++ b/grub-core/lib/emu/halt.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2002,2003,2004,2006,2007 Free Software Foundation, Inc. + * 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 @@ -16,7 +16,10 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_LOADER_MACHINE_HEADER -#define GRUB_LOADER_MACHINE_HEADER 1 +#include -#endif /* ! GRUB_LOADER_MACHINE_HEADER */ +void +grub_halt (void) +{ + grub_reboot (); +} diff --git a/lib/envblk.c b/grub-core/lib/envblk.c similarity index 97% rename from lib/envblk.c rename to grub-core/lib/envblk.c index 311927bd8..2e4e78b13 100644 --- a/lib/envblk.c +++ b/grub-core/lib/envblk.c @@ -143,7 +143,7 @@ grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value) /* Move the following characters backward, and fill the new space with harmless characters. */ grub_memmove (p + vl, p + len, pend - (p + len)); - grub_memset (space + len - vl, '#', len - vl); + grub_memset (space - (len - vl), '#', len - vl); } else /* Move the following characters forward. */ @@ -225,7 +225,8 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name) void grub_envblk_iterate (grub_envblk_t envblk, - int hook (const char *name, const char *value)) + void *hook_data, + int hook (const char *name, const char *value, void *hook_data)) { char *p, *pend; @@ -285,7 +286,7 @@ grub_envblk_iterate (grub_envblk_t envblk, } *q = '\0'; - ret = hook (name, value); + ret = hook (name, value, hook_data); grub_free (name); if (ret) return; diff --git a/grub-core/lib/fake_module.c b/grub-core/lib/fake_module.c new file mode 100644 index 000000000..3646cedbc --- /dev/null +++ b/grub-core/lib/fake_module.c @@ -0,0 +1,4 @@ +/* This file is intentionally empty: it's used to generate modules with no code or data. (purely dependency modules) */ +#include + +GRUB_MOD_LICENSE ("GPLv3+"); diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c new file mode 100644 index 000000000..73cfa94a2 --- /dev/null +++ b/grub-core/lib/fdt.c @@ -0,0 +1,531 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define FDT_SUPPORTED_VERSION 17 + +#define FDT_BEGIN_NODE 0x00000001 +#define FDT_END_NODE 0x00000002 +#define FDT_PROP 0x00000003 +#define FDT_NOP 0x00000004 +#define FDT_END 0x00000009 + +#define struct_end(fdt) \ + ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) \ + + grub_fdt_get_size_dt_struct(fdt)) + +/* Size needed by a node entry: 2 tokens (FDT_BEGIN_NODE and FDT_END_NODE), plus + the NULL-terminated string containing the name, plus padding if needed. */ +#define node_entry_size(node_name) \ + (2 * sizeof(grub_uint32_t) \ + + ALIGN_UP (grub_strlen (name) + 1, sizeof(grub_uint32_t))) + +#define SKIP_NODE_NAME(name, token, end) \ + name = (char *) ((token) + 1); \ + while (name < (char *) end) \ + { \ + if (!*name++) \ + break; \ + } \ + token = (grub_uint32_t *) ALIGN_UP((grub_addr_t) (name), sizeof(*token)) + + +static grub_uint32_t *get_next_node (const void *fdt, char *node_name) +{ + grub_uint32_t *end = (void *) struct_end (fdt); + grub_uint32_t *token; + + if (node_name >= (char *) end) + return NULL; + while (*node_name++) + { + if (node_name >= (char *) end) + return NULL; + } + token = (grub_uint32_t *) ALIGN_UP ((grub_addr_t) node_name, 4); + while (token < end) + { + switch (grub_be_to_cpu32(*token)) + { + case FDT_BEGIN_NODE: + token = get_next_node (fdt, (char *) (token + 1)); + if (!token) + return NULL; + break; + case FDT_END_NODE: + token++; + if (token >= end) + return NULL; + return token; + case FDT_PROP: + /* Skip property token and following data (len, nameoff and property + value). */ + token += grub_fdt_prop_entry_size(grub_be_to_cpu32(*(token + 1))) + / sizeof(*token); + break; + case FDT_NOP: + token++; + break; + default: + return NULL; + } + } + return NULL; +} + +static int get_mem_rsvmap_size (const void *fdt) +{ + int size = 0; + grub_unaligned_uint64_t *ptr = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_mem_rsvmap (fdt)); + + do + { + size += 2 * sizeof(*ptr); + if (!ptr[0].val && !ptr[1].val) + return size; + ptr += 2; + } while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt) + - 2 * sizeof(grub_uint64_t)); + return -1; +} + +static grub_uint32_t get_free_space (void *fdt) +{ + int mem_rsvmap_size = get_mem_rsvmap_size (fdt); + + if (mem_rsvmap_size < 0) + /* invalid memory reservation block */ + return 0; + return (grub_fdt_get_totalsize (fdt) - sizeof(grub_fdt_header_t) + - mem_rsvmap_size - grub_fdt_get_size_dt_strings (fdt) + - grub_fdt_get_size_dt_struct (fdt)); +} + +static int add_subnode (void *fdt, int parentoffset, const char *name) +{ + grub_uint32_t *token = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct(fdt) + + parentoffset); + grub_uint32_t *end = (void *) struct_end (fdt); + unsigned int entry_size = node_entry_size (name); + unsigned int struct_size = grub_fdt_get_size_dt_struct(fdt); + char *node_name; + + SKIP_NODE_NAME(node_name, token, end); + + /* Insert the new subnode just after the properties of the parent node (if + any).*/ + while (1) + { + if (token >= end) + return -1; + switch (grub_be_to_cpu32(*token)) + { + case FDT_PROP: + /* Skip len, nameoff and property value. */ + token += grub_fdt_prop_entry_size(grub_be_to_cpu32(*(token + 1))) + / sizeof(*token); + break; + case FDT_BEGIN_NODE: + case FDT_END_NODE: + goto insert; + case FDT_NOP: + token++; + break; + default: + /* invalid token */ + return -1; + } + } +insert: + grub_memmove (token + entry_size / sizeof(*token), token, + (grub_addr_t) end - (grub_addr_t) token); + *token = grub_cpu_to_be32_compile_time(FDT_BEGIN_NODE); + token[entry_size / sizeof(*token) - 2] = 0; /* padding bytes */ + grub_strcpy((char *) (token + 1), name); + token[entry_size / sizeof(*token) - 1] = grub_cpu_to_be32_compile_time(FDT_END_NODE); + grub_fdt_set_size_dt_struct (fdt, struct_size + entry_size); + return ((grub_addr_t) token - (grub_addr_t) fdt + - grub_fdt_get_off_dt_struct(fdt)); +} + +/* Rearrange FDT blocks in the canonical order: first the memory reservation + block (just after the FDT header), then the structure block and finally the + strings block. No free space is left between the first and the second block, + while the space between the second and the third block is given by the + clearance argument. */ +static int rearrange_blocks (void *fdt, unsigned int clearance) +{ + grub_uint32_t off_mem_rsvmap = ALIGN_UP(sizeof(grub_fdt_header_t), 8); + grub_uint32_t off_dt_struct = off_mem_rsvmap + get_mem_rsvmap_size (fdt); + grub_uint32_t off_dt_strings = off_dt_struct + + grub_fdt_get_size_dt_struct (fdt) + + clearance; + grub_uint8_t *fdt_ptr = fdt; + grub_uint8_t *tmp_fdt; + + if ((grub_fdt_get_off_mem_rsvmap (fdt) == off_mem_rsvmap) + && (grub_fdt_get_off_dt_struct (fdt) == off_dt_struct)) + { + /* No need to allocate memory for a temporary FDT, just move the strings + block if needed. */ + if (grub_fdt_get_off_dt_strings (fdt) != off_dt_strings) + { + grub_memmove(fdt_ptr + off_dt_strings, + fdt_ptr + grub_fdt_get_off_dt_strings (fdt), + grub_fdt_get_size_dt_strings (fdt)); + grub_fdt_set_off_dt_strings (fdt, off_dt_strings); + } + return 0; + } + tmp_fdt = grub_malloc (grub_fdt_get_totalsize (fdt)); + if (!tmp_fdt) + return -1; + grub_memcpy (tmp_fdt + off_mem_rsvmap, + fdt_ptr + grub_fdt_get_off_mem_rsvmap (fdt), + get_mem_rsvmap_size (fdt)); + grub_fdt_set_off_mem_rsvmap (fdt, off_mem_rsvmap); + grub_memcpy (tmp_fdt + off_dt_struct, + fdt_ptr + grub_fdt_get_off_dt_struct (fdt), + grub_fdt_get_size_dt_struct (fdt)); + grub_fdt_set_off_dt_struct (fdt, off_dt_struct); + grub_memcpy (tmp_fdt + off_dt_strings, + fdt_ptr + grub_fdt_get_off_dt_strings (fdt), + grub_fdt_get_size_dt_strings (fdt)); + grub_fdt_set_off_dt_strings (fdt, off_dt_strings); + + /* Copy reordered blocks back to fdt. */ + grub_memcpy (fdt_ptr + off_mem_rsvmap, tmp_fdt + off_mem_rsvmap, + grub_fdt_get_totalsize (fdt) - off_mem_rsvmap); + + grub_free(tmp_fdt); + return 0; +} + +static grub_uint32_t *find_prop (const void *fdt, unsigned int nodeoffset, + const char *name) +{ + grub_uint32_t *prop = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + + nodeoffset); + grub_uint32_t *end = (void *) struct_end(fdt); + grub_uint32_t nameoff; + char *node_name; + + SKIP_NODE_NAME(node_name, prop, end); + while (prop < end - 2) + { + if (grub_be_to_cpu32(*prop) == FDT_PROP) + { + nameoff = grub_be_to_cpu32(*(prop + 2)); + if ((nameoff + grub_strlen (name) < grub_fdt_get_size_dt_strings (fdt)) + && !grub_strcmp (name, (char *) fdt + + grub_fdt_get_off_dt_strings (fdt) + nameoff)) + { + if (prop + grub_fdt_prop_entry_size(grub_be_to_cpu32(*(prop + 1))) + / sizeof (*prop) >= end) + return NULL; + return prop; + } + prop += grub_fdt_prop_entry_size(grub_be_to_cpu32(*(prop + 1))) / sizeof (*prop); + } + else if (grub_be_to_cpu32(*prop) == FDT_NOP) + prop++; + else + return NULL; + } + return NULL; +} + +/* Check the FDT header for consistency and adjust the totalsize field to match + the size allocated for the FDT; if this function is called before the other + functions in this file and returns success, the other functions are + guaranteed not to access memory locations outside the allocated memory. */ +int grub_fdt_check_header_nosize (const void *fdt) +{ + if (((grub_addr_t) fdt & 0x3) || (grub_fdt_get_magic (fdt) != FDT_MAGIC) + || (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION) + || (grub_fdt_get_last_comp_version (fdt) > FDT_SUPPORTED_VERSION) + || (grub_fdt_get_off_dt_struct (fdt) & 0x00000003) + || (grub_fdt_get_size_dt_struct (fdt) & 0x00000003) + || (grub_fdt_get_off_dt_struct (fdt) + grub_fdt_get_size_dt_struct (fdt) + > grub_fdt_get_totalsize (fdt)) + || (grub_fdt_get_off_dt_strings (fdt) + grub_fdt_get_size_dt_strings (fdt) + > grub_fdt_get_totalsize (fdt)) + || (grub_fdt_get_off_mem_rsvmap (fdt) & 0x00000007) + || (grub_fdt_get_off_mem_rsvmap (fdt) + > grub_fdt_get_totalsize (fdt) - 2 * sizeof(grub_uint64_t))) + return -1; + return 0; +} + +int grub_fdt_check_header (const void *fdt, unsigned int size) +{ + if (size < sizeof (grub_fdt_header_t) + || (grub_fdt_get_totalsize (fdt) > size) + || grub_fdt_check_header_nosize (fdt) == -1) + return -1; + return 0; +} + +static const grub_uint32_t * +advance_token (const void *fdt, const grub_uint32_t *token, const grub_uint32_t *end, int skip_current) +{ + for (; token < end; skip_current = 0) + { + switch (grub_be_to_cpu32 (*token)) + { + case FDT_BEGIN_NODE: + if (skip_current) + { + token = get_next_node (fdt, (char *) (token + 1)); + continue; + } + char *ptr; + for (ptr = (char *) (token + 1); *ptr && ptr < (char *) end; ptr++) + ; + if (ptr >= (char *) end) + return 0; + return token; + case FDT_PROP: + /* Skip property token and following data (len, nameoff and property + value). */ + if (token >= end - 1) + return 0; + token += grub_fdt_prop_entry_size(grub_be_to_cpu32(*(token + 1))) + / sizeof(*token); + break; + case FDT_NOP: + token++; + break; + default: + return 0; + } + } + return 0; +} + +int grub_fdt_next_node (const void *fdt, unsigned int currentoffset) +{ + const grub_uint32_t *token = (const grub_uint32_t *) fdt + (currentoffset + grub_fdt_get_off_dt_struct (fdt)) / 4; + token = advance_token (fdt, token, (const void *) struct_end (fdt), 1); + if (!token) + return -1; + return (int) ((grub_addr_t) token - (grub_addr_t) fdt + - grub_fdt_get_off_dt_struct (fdt)); +} + +int grub_fdt_first_node (const void *fdt, unsigned int parentoffset) +{ + const grub_uint32_t *token, *end; + char *node_name; + + if (parentoffset & 0x3) + return -1; + token = (const void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) + + parentoffset); + end = (const void *) struct_end (fdt); + if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE)) + return -1; + SKIP_NODE_NAME(node_name, token, end); + token = advance_token (fdt, token, end, 0); + if (!token) + return -1; + return (int) ((grub_addr_t) token - (grub_addr_t) fdt + - grub_fdt_get_off_dt_struct (fdt)); +} + +/* Find a direct sub-node of a given parent node. */ +int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, + const char *name) +{ + const grub_uint32_t *token, *end; + const char *node_name; + int skip_current = 0; + + if (parentoffset & 0x3) + return -1; + token = (const void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt) + + parentoffset); + end = (const void *) struct_end (fdt); + if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE)) + return -1; + SKIP_NODE_NAME(node_name, token, end); + while (1) { + token = advance_token (fdt, token, end, skip_current); + if (!token) + return -1; + skip_current = 1; + node_name = (const char *) token + 4; + if (grub_strcmp (node_name, name) == 0) + return (int) ((grub_addr_t) token - (grub_addr_t) fdt + - grub_fdt_get_off_dt_struct (fdt)); + } +} + +const char * +grub_fdt_get_nodename (const void *fdt, unsigned int nodeoffset) +{ + return (const char *) fdt + grub_fdt_get_off_dt_struct(fdt) + nodeoffset + 4; +} + +int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, + const char *name) +{ + unsigned int entry_size = node_entry_size(name); + + if ((parentoffset & 0x3) || (get_free_space (fdt) < entry_size)) + return -1; + + /* The new node entry will increase the size of the structure block: rearrange + blocks such that there is sufficient free space between the structure and + the strings block, then add the new node entry. */ + if (rearrange_blocks (fdt, entry_size) < 0) + return -1; + return add_subnode (fdt, parentoffset, name); +} + +const void * +grub_fdt_get_prop (const void *fdt, unsigned int nodeoffset, const char *name, + grub_uint32_t *len) +{ + grub_uint32_t *prop; + if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3) + || (grub_be_to_cpu32(*(grub_uint32_t *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + nodeoffset)) + != FDT_BEGIN_NODE)) + return 0; + prop = find_prop (fdt, nodeoffset, name); + if (!prop) + return 0; + if (len) + *len = grub_be_to_cpu32 (*(prop + 1)); + return prop + 3; +} + +int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name, + const void *val, grub_uint32_t len) +{ + grub_uint32_t *prop; + int prop_name_present = 0; + grub_uint32_t nameoff = 0; + + if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3) + || (grub_be_to_cpu32(*(grub_uint32_t *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + nodeoffset)) + != FDT_BEGIN_NODE)) + return -1; + prop = find_prop (fdt, nodeoffset, name); + if (prop) + { + grub_uint32_t prop_len = ALIGN_UP(grub_be_to_cpu32 (*(prop + 1)), + sizeof(grub_uint32_t)); + grub_uint32_t i; + + prop_name_present = 1; + for (i = 0; i < prop_len / sizeof(grub_uint32_t); i++) + *(prop + 3 + i) = grub_cpu_to_be32_compile_time (FDT_NOP); + if (len > ALIGN_UP(prop_len, sizeof(grub_uint32_t))) + { + /* Length of new property value is greater than the space allocated + for the current value: a new entry needs to be created, so save the + nameoff field of the current entry and replace the current entry + with NOP tokens. */ + nameoff = grub_be_to_cpu32 (*(prop + 2)); + *prop = *(prop + 1) = *(prop + 2) = grub_cpu_to_be32_compile_time (FDT_NOP); + prop = NULL; + } + } + if (!prop || !prop_name_present) { + unsigned int needed_space = 0; + + if (!prop) + needed_space = grub_fdt_prop_entry_size(len); + if (!prop_name_present) + needed_space += grub_strlen (name) + 1; + if (needed_space > get_free_space (fdt)) + return -1; + if (rearrange_blocks (fdt, !prop ? grub_fdt_prop_entry_size(len) : 0) < 0) + return -1; + } + if (!prop_name_present) { + /* Append the property name at the end of the strings block. */ + nameoff = grub_fdt_get_size_dt_strings (fdt); + grub_strcpy ((char *) fdt + grub_fdt_get_off_dt_strings (fdt) + nameoff, + name); + grub_fdt_set_size_dt_strings (fdt, grub_fdt_get_size_dt_strings (fdt) + + grub_strlen (name) + 1); + } + if (!prop) { + char *node_name = (char *) ((grub_addr_t) fdt + + grub_fdt_get_off_dt_struct (fdt) + nodeoffset + + sizeof(grub_uint32_t)); + + prop = (void *) (node_name + ALIGN_UP(grub_strlen(node_name) + 1, 4)); + grub_memmove (prop + grub_fdt_prop_entry_size(len) / sizeof(*prop), prop, + struct_end(fdt) - (grub_addr_t) prop); + grub_fdt_set_size_dt_struct (fdt, grub_fdt_get_size_dt_struct (fdt) + + grub_fdt_prop_entry_size(len)); + *prop = grub_cpu_to_be32_compile_time (FDT_PROP); + *(prop + 2) = grub_cpu_to_be32 (nameoff); + } + *(prop + 1) = grub_cpu_to_be32 (len); + + /* Insert padding bytes at the end of the value; if they are not needed, they + will be overwritten by the following memcpy. */ + *(prop + grub_fdt_prop_entry_size(len) / sizeof(grub_uint32_t) - 1) = 0; + + grub_memcpy (prop + 3, val, len); + return 0; +} + +int +grub_fdt_create_empty_tree (void *fdt, unsigned int size) +{ + struct grub_fdt_empty_tree *et; + + if (size < GRUB_FDT_EMPTY_TREE_SZ) + return -1; + + grub_memset (fdt, 0, size); + et = fdt; + + et->empty_node.tree_end = grub_cpu_to_be32_compile_time (FDT_END); + et->empty_node.node_end = grub_cpu_to_be32_compile_time (FDT_END_NODE); + et->empty_node.node_start = grub_cpu_to_be32_compile_time (FDT_BEGIN_NODE); + ((struct grub_fdt_empty_tree *) fdt)->header.off_mem_rsvmap = + grub_cpu_to_be32_compile_time (ALIGN_UP (sizeof (grub_fdt_header_t), 8)); + + grub_fdt_set_off_dt_strings (fdt, sizeof (*et)); + grub_fdt_set_off_dt_struct (fdt, + sizeof (et->header) + sizeof (et->empty_rsvmap)); + grub_fdt_set_version (fdt, FDT_SUPPORTED_VERSION); + grub_fdt_set_last_comp_version (fdt, FDT_SUPPORTED_VERSION); + grub_fdt_set_size_dt_struct (fdt, sizeof (et->empty_node)); + grub_fdt_set_totalsize (fdt, size); + grub_fdt_set_magic (fdt, FDT_MAGIC); + + return 0; +} diff --git a/grub-core/lib/getline.c b/grub-core/lib/getline.c new file mode 100644 index 000000000..edb8e9ffe --- /dev/null +++ b/grub-core/lib/getline.c @@ -0,0 +1,92 @@ +/* main.c - the normal mode main routine */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000,2001,2002,2003,2005,2006,2007,2008,2009,2013 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Read a line from the file FILE. */ +char * +grub_file_getline (grub_file_t file) +{ + char c; + grub_size_t pos = 0; + char *cmdline; + int have_newline = 0; + grub_size_t 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; + + + if (pos + 1 >= 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') + { + have_newline = 1; + break; + } + + cmdline[pos++] = c; + } + + cmdline[pos] = '\0'; + + /* If the buffer is empty, don't return anything at all. */ + if (pos == 0 && !have_newline) + { + grub_free (cmdline); + cmdline = 0; + } + + return cmdline; +} diff --git a/grub-core/lib/gnulib-patches/fix-width.patch b/grub-core/lib/gnulib-patches/fix-width.patch new file mode 100644 index 000000000..15f091c0d --- /dev/null +++ b/grub-core/lib/gnulib-patches/fix-width.patch @@ -0,0 +1,217 @@ +diff --git a/lib/argp-fmtstream.c b/lib/argp-fmtstream.c +index ba6a407f7..d0685b3d4 100644 +--- a/lib/argp-fmtstream.c ++++ b/lib/argp-fmtstream.c +@@ -28,9 +28,11 @@ + #include + #include + #include ++#include + + #include "argp-fmtstream.h" + #include "argp-namefrob.h" ++#include "mbswidth.h" + + #ifndef ARGP_FMTSTREAM_USE_LINEWRAP + +@@ -115,6 +117,51 @@ weak_alias (__argp_fmtstream_free, argp_fmtstream_free) + #endif + #endif + ++ ++/* Return the pointer to the first character that doesn't fit in l columns. */ ++static inline const ptrdiff_t ++add_width (const char *ptr, const char *end, size_t l) ++{ ++ mbstate_t ps; ++ const char *ptr0 = ptr; ++ ++ memset (&ps, 0, sizeof (ps)); ++ ++ while (ptr < end) ++ { ++ wchar_t wc; ++ size_t s, k; ++ ++ s = mbrtowc (&wc, ptr, end - ptr, &ps); ++ if (s == (size_t) -1) ++ break; ++ if (s == (size_t) -2) ++ { ++ if (1 >= l) ++ break; ++ l--; ++ ptr++; ++ continue; ++ } ++ ++ if (wc == '\e' && ptr + 3 < end ++ && ptr[1] == '[' && (ptr[2] == '0' || ptr[2] == '1') ++ && ptr[3] == 'm') ++ { ++ ptr += 4; ++ continue; ++ } ++ ++ k = wcwidth (wc); ++ ++ if (k >= l) ++ break; ++ l -= k; ++ ptr += s; ++ } ++ return ptr - ptr0; ++} ++ + /* Process FS's buffer so that line wrapping is done from POINT_OFFS to the + end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ + void +@@ -168,13 +215,15 @@ __argp_fmtstream_update (argp_fmtstream_t fs) + if (!nl) + { + /* The buffer ends in a partial line. */ ++ size_t display_width = mbsnwidth (buf, fs->p - buf, ++ MBSW_STOP_AT_NUL); + +- if (fs->point_col + len < fs->rmargin) ++ if (fs->point_col + display_width < fs->rmargin) + { + /* The remaining buffer text is a partial line and fits + within the maximum line width. Advance point for the + characters to be written and stop scanning. */ +- fs->point_col += len; ++ fs->point_col += display_width; + break; + } + else +@@ -182,14 +231,18 @@ __argp_fmtstream_update (argp_fmtstream_t fs) + the end of the buffer. */ + nl = fs->p; + } +- else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) +- { +- /* The buffer contains a full line that fits within the maximum +- line width. Reset point and scan the next line. */ +- fs->point_col = 0; +- buf = nl + 1; +- continue; +- } ++ else ++ { ++ size_t display_width = mbsnwidth (buf, nl - buf, MBSW_STOP_AT_NUL); ++ if (display_width < (ssize_t) fs->rmargin) ++ { ++ /* The buffer contains a full line that fits within the maximum ++ line width. Reset point and scan the next line. */ ++ fs->point_col = 0; ++ buf = nl + 1; ++ continue; ++ } ++ } + + /* This line is too long. */ + r = fs->rmargin - 1; +@@ -225,7 +278,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) + char *p, *nextline; + int i; + +- p = buf + (r + 1 - fs->point_col); ++ p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col)); + while (p >= buf && !isblank ((unsigned char) *p)) + --p; + nextline = p + 1; /* This will begin the next line. */ +@@ -243,7 +296,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) + { + /* A single word that is greater than the maximum line width. + Oh well. Put it on an overlong line by itself. */ +- p = buf + (r + 1 - fs->point_col); ++ p = buf + add_width (buf, fs->p, (r + 1 - fs->point_col)); + /* Find the end of the long word. */ + if (p < nl) + do +@@ -277,7 +330,8 @@ __argp_fmtstream_update (argp_fmtstream_t fs) + && fs->p > nextline) + { + /* The margin needs more blanks than we removed. */ +- if (fs->end - fs->p > fs->wmargin + 1) ++ if (mbsnwidth (fs->p, fs->end - fs->p, MBSW_STOP_AT_NUL) ++ > fs->wmargin + 1) + /* Make some space for them. */ + { + size_t mv = fs->p - nextline; +diff --git a/lib/argp-help.c b/lib/argp-help.c +index e5375a0f0..5d8f451ec 100644 +--- a/lib/argp-help.c ++++ b/lib/argp-help.c +@@ -52,6 +52,7 @@ + #include "argp.h" + #include "argp-fmtstream.h" + #include "argp-namefrob.h" ++#include "mbswidth.h" + + #ifndef SIZE_MAX + # define SIZE_MAX ((size_t) -1) +@@ -1547,7 +1548,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state, + + /* Manually do line wrapping so that it (probably) won't get wrapped at + any embedded spaces. */ +- space (stream, 1 + nl - cp); ++ space (stream, 1 + mbsnwidth (cp, nl - cp, MBSW_STOP_AT_NUL)); + + __argp_fmtstream_write (stream, cp, nl - cp); + } +diff --git a/lib/mbswidth.c b/lib/mbswidth.c +index 408a15e34..b3fb7f83a 100644 +--- a/lib/mbswidth.c ++++ b/lib/mbswidth.c +@@ -38,6 +38,14 @@ + /* Get INT_MAX. */ + #include + ++#ifndef FALLTHROUGH ++# if __GNUC__ < 7 ++# define FALLTHROUGH ((void) 0) ++# else ++# define FALLTHROUGH __attribute__ ((__fallthrough__)) ++# endif ++#endif ++ + /* Returns the number of columns needed to represent the multibyte + character string pointed to by STRING. If a non-printable character + occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned. +@@ -90,6 +98,10 @@ mbsnwidth (const char *string, size_t nbytes, int flags) + p++; + width++; + break; ++ case '\0': ++ if (flags & MBSW_STOP_AT_NUL) ++ return width; ++ FALLTHROUGH; + default: + /* If we have a multibyte sequence, scan it up to its end. */ + { +@@ -168,6 +180,9 @@ mbsnwidth (const char *string, size_t nbytes, int flags) + { + unsigned char c = (unsigned char) *p++; + ++ if (c == 0 && (flags & MBSW_STOP_AT_NUL)) ++ return width; ++ + if (isprint (c)) + { + if (width == INT_MAX) +diff --git a/lib/mbswidth.h b/lib/mbswidth.h +index 2b5c53c37..45a123e63 100644 +--- a/lib/mbswidth.h ++++ b/lib/mbswidth.h +@@ -40,6 +40,10 @@ extern "C" { + control characters and 1 otherwise. */ + #define MBSW_REJECT_UNPRINTABLE 2 + ++/* If this bit is set \0 is treated as the end of string. ++ Otherwise it's treated as a normal one column width character. */ ++#define MBSW_STOP_AT_NUL 4 ++ + + /* Returns the number of screen columns needed for STRING. */ + #define mbswidth gnu_mbswidth /* avoid clash with UnixWare 7.1.1 function */ diff --git a/lib/hexdump.c b/grub-core/lib/hexdump.c similarity index 100% rename from lib/hexdump.c rename to grub-core/lib/hexdump.c diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c new file mode 100644 index 000000000..c3e03c727 --- /dev/null +++ b/grub-core/lib/i386/backtrace.c @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +void +grub_backtrace_pointer (void *ebp) +{ + void *ptr, *nptr; + unsigned i; + + ptr = ebp; + while (1) + { + grub_printf ("%p: ", ptr); + grub_backtrace_print_address (((void **) ptr)[1]); + grub_printf (" ("); + for (i = 0; i < 2; i++) + grub_printf ("%p,", ((void **)ptr) [i + 2]); + grub_printf ("%p)\n", ((void **)ptr) [i + 2]); + nptr = *(void **)ptr; + if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME + || nptr == ptr) + { + grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); + break; + } + ptr = nptr; + } +} + +void +grub_backtrace (void) +{ +#ifdef __x86_64__ + asm volatile ("movq %%rbp, %%rdi\n" + "callq *%%rax": :"a"(grub_backtrace_pointer)); +#else + asm volatile ("movl %%ebp, %%eax\n" + "calll *%%ecx": :"c"(grub_backtrace_pointer)); +#endif +} + diff --git a/grub-core/lib/i386/halt.c b/grub-core/lib/i386/halt.c new file mode 100644 index 000000000..2364fe4d7 --- /dev/null +++ b/grub-core/lib/i386/halt.c @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +const char bochs_shutdown[] = "Shutdown"; + +/* + * This call is special... it never returns... in fact it should simply + * hang at this point! + */ +static inline void __attribute__ ((noreturn)) +stop (void) +{ + asm volatile ("cli"); + while (1) + { + asm volatile ("hlt"); + } +} + +static int +grub_shutdown_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid, + void *data __attribute__ ((unused))) +{ + /* QEMU. */ + if (pciid == 0x71138086) + { + grub_pci_address_t addr; + addr = grub_pci_make_address (dev, 0x40); + grub_pci_write (addr, 0x7001); + addr = grub_pci_make_address (dev, 0x80); + grub_pci_write (addr, grub_pci_read (addr) | 1); + grub_outw (0x2000, 0x7004); + } + return 0; +} + +void +grub_halt (void) +{ + unsigned int i; + +#if defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_MULTIBOOT) + grub_acpi_halt (); +#endif + + /* Disable interrupts. */ + asm volatile ("cli"); + + /* Bochs, QEMU, etc. Removed in newer QEMU releases. */ + for (i = 0; i < sizeof (bochs_shutdown) - 1; i++) + grub_outb (bochs_shutdown[i], 0x8900); + + grub_pci_iterate (grub_shutdown_pci_iter, NULL); + + grub_puts_ (N_("GRUB doesn't know how to halt this machine yet!")); + + /* In order to return we'd have to check what the previous status of IF + flag was. But user most likely doesn't want to return anyway ... */ + stop (); +} diff --git a/lib/i386/pc/biosnum.c b/grub-core/lib/i386/pc/biosnum.c similarity index 95% rename from lib/i386/pc/biosnum.c rename to grub-core/lib/i386/pc/biosnum.c index 058c9d331..0f0e743c4 100644 --- a/lib/i386/pc/biosnum.c +++ b/grub-core/lib/i386/pc/biosnum.c @@ -19,11 +19,12 @@ #include #include #include +#include static int grub_get_root_biosnumber_default (void) { - char *biosnum; + const char *biosnum; int ret = -1; grub_device_t dev; diff --git a/grub-core/lib/i386/pc/vesa_modes_table.c b/grub-core/lib/i386/pc/vesa_modes_table.c new file mode 100644 index 000000000..6dc4b7d8d --- /dev/null +++ b/grub-core/lib/i386/pc/vesa_modes_table.c @@ -0,0 +1,127 @@ + +#include + +/* This is the reverse of the table in [linux]/Documentation/fb/vesafb.txt + plus a few more modes based on the table in + http://en.wikipedia.org/wiki/VESA_BIOS_Extensions */ +struct grub_vesa_mode_table_entry +grub_vesa_mode_table[GRUB_VESA_MODE_TABLE_END + - GRUB_VESA_MODE_TABLE_START + 1] = + { + { 640, 400, 8 }, /* 0x300 */ + { 640, 480, 8 }, /* 0x301 */ + { 800, 600, 4 }, /* 0x302 */ + { 800, 600, 8 }, /* 0x303 */ + { 1024, 768, 4 }, /* 0x304 */ + { 1024, 768, 8 }, /* 0x305 */ + { 1280, 1024, 4 }, /* 0x306 */ + { 1280, 1024, 8 }, /* 0x307 */ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 320, 200, 15 }, /* 0x30d */ + { 320, 200, 16 }, /* 0x30e */ + { 320, 200, 24 }, /* 0x30f */ + { 640, 480, 15 }, /* 0x310 */ + { 640, 480, 16 }, /* 0x311 */ + { 640, 480, 24 }, /* 0x312 */ + { 800, 600, 15 }, /* 0x313 */ + { 800, 600, 16 }, /* 0x314 */ + { 800, 600, 24 }, /* 0x315 */ + { 1024, 768, 15 }, /* 0x316 */ + { 1024, 768, 16 }, /* 0x317 */ + { 1024, 768, 24 }, /* 0x318 */ + { 1280, 1024, 15 }, /* 0x319 */ + { 1280, 1024, 16 }, /* 0x31a */ + { 1280, 1024, 24 }, /* 0x31b */ + { 1600, 1200, 8 }, /* 0x31c */ + { 1600, 1200, 15 }, /* 0x31d */ + { 1600, 1200, 16 }, /* 0x31e */ + { 1600, 1200, 24 }, /* 0x31f */ + { 0, 0, 0 }, + { 640, 400, 15 }, /* 0x321 */ + { 640, 400, 16 }, /* 0x322 */ + { 640, 400, 24 }, /* 0x323 */ + { 640, 400, 32 }, /* 0x324 */ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 640, 480, 32 }, /* 0x329 */ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 896, 672, 8 }, /* 0x32f */ + { 896, 672, 15 }, /* 0x330 */ + { 896, 672, 16 }, /* 0x331 */ + { 896, 672, 24 }, /* 0x332 */ + { 896, 672, 32 }, /* 0x333 */ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 1600, 1200, 32 }, /* 0x342 */ + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 1440, 900, 8 }, /* 0x360 */ + { 1440, 900, 15 }, /* 0x361 */ + { 1440, 900, 16 }, /* 0x362 */ + { 1440, 900, 24 }, /* 0x363 */ + { 1440, 900, 32 }, /* 0x364 */ + { 1152, 720, 8 }, /* 0x365 */ + { 1152, 720, 15 }, /* 0x366 */ + { 1152, 720, 16 }, /* 0x367 */ + { 1152, 720, 24 }, /* 0x368 */ + { 1152, 720, 32 }, /* 0x369 */ + { 1024, 640, 8 }, /* 0x36a */ + { 1024, 640, 15 }, /* 0x36b */ + { 1024, 640, 16 }, /* 0x36c */ + { 1024, 640, 24 }, /* 0x36d */ + { 1024, 640, 32 }, /* 0x36e */ + { 800, 500, 8 }, /* 0x36f */ + { 800, 500, 15 }, /* 0x370 */ + { 800, 500, 16 }, /* 0x371 */ + { 800, 500, 24 }, /* 0x372 */ + { 800, 500, 32 }, /* 0x373 */ + }; diff --git a/grub-core/lib/i386/random.c b/grub-core/lib/i386/random.c new file mode 100644 index 000000000..cd83d2f8f --- /dev/null +++ b/grub-core/lib/i386/random.c @@ -0,0 +1,103 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2016 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 . + */ + +#include +#include +#include +#include +#include + +static int have_tsc = -1, have_pmtimer = -1; +static grub_port_t pmtimer_port; + +static int +detect_pmtimer (void) +{ + struct grub_acpi_fadt *fadt; + fadt = grub_acpi_find_fadt (); + if (!fadt) + return 0; + pmtimer_port = fadt->pmtimer; + if (!pmtimer_port) + return 0; + return 1; +} + +static int +pmtimer_tsc_get_random_bit (void) +{ + /* It's hard to come up with figures about pmtimer and tsc jitter but + 50 ppm seems to be typical. So we need 10^6/50 tsc cycles to get drift + of one tsc cycle. With TSC at least of 800 MHz it means 1/(50*800) + = 1/40000 s or about 3579545 / 40000 = 90 pmtimer ticks. + This gives us rate of 40000 bit/s or 5 kB/s. + */ + grub_uint64_t tsc_diff; + tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer_port, 90); + if (tsc_diff == 0) + { + have_pmtimer = 0; + return -1; + } + return tsc_diff & 1; +} + +static int +pmtimer_tsc_get_random_byte (void) +{ + grub_uint8_t ret = 0; + int i, c; + for (i = 0; i < 8; i++) + { + c = pmtimer_tsc_get_random_bit (); + if (c < 0) + return -1; + ret |= c << i; + } + return ret; +} + +static int +pmtimer_fill_buffer (void *buffer, grub_size_t sz) +{ + grub_uint8_t *p = buffer; + int c; + while (sz) + { + c = pmtimer_tsc_get_random_byte (); + if (c < 0) + return 0; + *p++ = c; + sz--; + } + return 1; +} + +int +grub_crypto_arch_get_random (void *buffer, grub_size_t sz) +{ + if (have_tsc == -1) + have_tsc = grub_cpu_is_tsc_supported (); + if (!have_tsc) + return 0; + if (have_pmtimer == -1) + have_pmtimer = detect_pmtimer (); + if (!have_pmtimer) + return 0; + return pmtimer_fill_buffer (buffer, sz); +} diff --git a/grub-core/lib/i386/reboot.c b/grub-core/lib/i386/reboot.c new file mode 100644 index 000000000..d0fd6a50e --- /dev/null +++ b/grub-core/lib/i386/reboot.c @@ -0,0 +1,64 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#ifndef GRUB_MACHINE_EFI + +#include +#include +#include +#include +#include +#include + +void +grub_reboot (void) +{ + struct grub_relocator *relocator = NULL; + grub_relocator_chunk_t ch; + grub_err_t err; + void *buf; + struct grub_relocator16_state state; + grub_uint16_t segment; + + relocator = grub_relocator_new (); + if (!relocator) + while (1); + err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000, 0x1000, + grub_reboot_end - grub_reboot_start, + 16, GRUB_RELOCATOR_PREFERENCE_NONE, + 0); + if (err) + while (1); + buf = get_virtual_current_address (ch); + grub_memcpy (buf, grub_reboot_start, grub_reboot_end - grub_reboot_start); + + segment = ((grub_addr_t) get_physical_target_address (ch)) >> 4; + state.gs = state.fs = state.es = state.ds = state.ss = segment; + state.sp = 0; + state.cs = segment; + state.ip = 0; + state.a20 = 0; + + grub_stop_floppy (); + + err = grub_relocator16_boot (relocator, state); + + while (1); +} + +#endif /* GRUB_MACHINE_EFI */ diff --git a/grub-core/lib/i386/reboot_trampoline.S b/grub-core/lib/i386/reboot_trampoline.S new file mode 100644 index 000000000..c088cd081 --- /dev/null +++ b/grub-core/lib/i386/reboot_trampoline.S @@ -0,0 +1,34 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include + + .p2align 4 + +VARIABLE(grub_reboot_start) + .code16 + + /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */ + movw $0x0472, %di + xorw %ax, %ax + movw %ax, (%di) + ljmp $0xf000, $0xfff0 + + .code32 +VARIABLE(grub_reboot_end) diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c new file mode 100644 index 000000000..54a1dcd8b --- /dev/null +++ b/grub-core/lib/i386/relocator.c @@ -0,0 +1,210 @@ +/* + * 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 . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +extern grub_uint8_t grub_relocator16_start; +extern grub_uint8_t grub_relocator16_end; +extern grub_uint16_t grub_relocator16_cs; +extern grub_uint16_t grub_relocator16_ip; +extern grub_uint16_t grub_relocator16_ds; +extern grub_uint16_t grub_relocator16_es; +extern grub_uint16_t grub_relocator16_fs; +extern grub_uint16_t grub_relocator16_gs; +extern grub_uint16_t grub_relocator16_ss; +extern grub_uint16_t grub_relocator16_sp; +extern grub_uint32_t grub_relocator16_edx; +extern grub_uint32_t grub_relocator16_ebx; +extern grub_uint32_t grub_relocator16_esi; +extern grub_uint32_t grub_relocator16_ebp; + +extern grub_uint16_t grub_relocator16_keep_a20_enabled; + +extern grub_uint8_t grub_relocator32_start; +extern grub_uint8_t grub_relocator32_end; +extern grub_uint32_t grub_relocator32_eax; +extern grub_uint32_t grub_relocator32_ebx; +extern grub_uint32_t grub_relocator32_ecx; +extern grub_uint32_t grub_relocator32_edx; +extern grub_uint32_t grub_relocator32_eip; +extern grub_uint32_t grub_relocator32_esp; +extern grub_uint32_t grub_relocator32_ebp; +extern grub_uint32_t grub_relocator32_esi; +extern grub_uint32_t grub_relocator32_edi; + +extern grub_uint8_t grub_relocator64_start; +extern grub_uint8_t grub_relocator64_end; +extern grub_uint64_t grub_relocator64_rax; +extern grub_uint64_t grub_relocator64_rbx; +extern grub_uint64_t grub_relocator64_rcx; +extern grub_uint64_t grub_relocator64_rdx; +extern grub_uint64_t grub_relocator64_rip; +extern grub_uint64_t grub_relocator64_rsp; +extern grub_uint64_t grub_relocator64_rsi; +extern grub_addr_t grub_relocator64_cr3; +extern struct grub_i386_idt grub_relocator16_idt; + +#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) + +grub_err_t +grub_relocator32_boot (struct grub_relocator *rel, + struct grub_relocator32_state state, + int avoid_efi_bootservices) +{ + grub_err_t err; + void *relst; + grub_relocator_chunk_t ch; + + /* Specific memory range due to Global Descriptor Table for use by payload + that we will store in returned chunk. The address range and preference + are based on "THE LINUX/x86 BOOT PROTOCOL" specification. */ + err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000, + RELOCATOR_SIZEOF (32), 16, + GRUB_RELOCATOR_PREFERENCE_LOW, + avoid_efi_bootservices); + if (err) + return err; + + grub_relocator32_eax = state.eax; + grub_relocator32_ebx = state.ebx; + grub_relocator32_ecx = state.ecx; + grub_relocator32_edx = state.edx; + grub_relocator32_eip = state.eip; + grub_relocator32_esp = state.esp; + grub_relocator32_ebp = state.ebp; + grub_relocator32_esi = state.esi; + grub_relocator32_edi = state.edi; + + grub_memmove (get_virtual_current_address (ch), &grub_relocator32_start, + RELOCATOR_SIZEOF (32)); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), + &relst, NULL); + if (err) + return err; + + asm volatile ("cli"); + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} + +grub_err_t +grub_relocator16_boot (struct grub_relocator *rel, + struct grub_relocator16_state state) +{ + grub_err_t err; + void *relst; + grub_relocator_chunk_t ch; + + /* Put it higher than the byte it checks for A20 check. */ + err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x8010, 0xa0000, + RELOCATOR_SIZEOF (16) + + GRUB_RELOCATOR16_STACK_SIZE, 16, + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + if (err) + return err; + + grub_relocator16_cs = state.cs; + grub_relocator16_ip = state.ip; + + grub_relocator16_ds = state.ds; + grub_relocator16_es = state.es; + grub_relocator16_fs = state.fs; + grub_relocator16_gs = state.gs; + + grub_relocator16_ss = state.ss; + grub_relocator16_sp = state.sp; + + grub_relocator16_ebp = state.ebp; + grub_relocator16_ebx = state.ebx; + grub_relocator16_edx = state.edx; + grub_relocator16_esi = state.esi; +#ifdef GRUB_MACHINE_PCBIOS + grub_relocator16_idt = *grub_realidt; +#else + grub_relocator16_idt.base = 0; + grub_relocator16_idt.limit = 0; +#endif + + grub_relocator16_keep_a20_enabled = state.a20; + + grub_memmove (get_virtual_current_address (ch), &grub_relocator16_start, + RELOCATOR_SIZEOF (16)); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), + &relst, NULL); + if (err) + return err; + + asm volatile ("cli"); + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} + +grub_err_t +grub_relocator64_boot (struct grub_relocator *rel, + struct grub_relocator64_state state, + grub_addr_t min_addr, grub_addr_t max_addr) +{ + grub_err_t err; + void *relst; + grub_relocator_chunk_t ch; + + err = grub_relocator_alloc_chunk_align_safe (rel, &ch, min_addr, max_addr, + RELOCATOR_SIZEOF (64), 16, + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + if (err) + return err; + + grub_relocator64_rax = state.rax; + grub_relocator64_rbx = state.rbx; + grub_relocator64_rcx = state.rcx; + grub_relocator64_rdx = state.rdx; + grub_relocator64_rip = state.rip; + grub_relocator64_rsp = state.rsp; + grub_relocator64_rsi = state.rsi; + grub_relocator64_cr3 = state.cr3; + + grub_memmove (get_virtual_current_address (ch), &grub_relocator64_start, + RELOCATOR_SIZEOF (64)); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), + &relst, NULL); + if (err) + return err; + + asm volatile ("cli"); + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/i386/relocator16.S b/grub-core/lib/i386/relocator16.S new file mode 100644 index 000000000..e9238119b --- /dev/null +++ b/grub-core/lib/i386/relocator16.S @@ -0,0 +1,341 @@ +/* + * 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 . + */ + +/* The code segment of the protected mode. */ +#define CODE_SEGMENT 0x08 + +/* The data segment of the protected mode. */ +#define DATA_SEGMENT 0x10 + +#define PSEUDO_REAL_CSEG 0x18 + +#define PSEUDO_REAL_DSEG 0x20 + +#include + +#include "relocator_common.S" + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator16_start) + PREAMBLE + +#ifdef __APPLE__ + LOCAL(cs_base_bytes12_offset) = LOCAL (cs_base_bytes12) - LOCAL (base) + LOCAL(cs_base_byte3_offset) = LOCAL (cs_base_byte3) - LOCAL (base) + LOCAL(ds_base_bytes12_offset) = LOCAL (ds_base_bytes12) - LOCAL (base) + LOCAL(ds_base_byte3_offset) = LOCAL (ds_base_byte3) - LOCAL (base) + movl %esi, %eax + movw %ax, (LOCAL(cs_base_bytes12_offset)) (RSI, 1) + movw %ax, (LOCAL(ds_base_bytes12_offset)) (RSI, 1) + shrl $16, %eax + movb %al, (LOCAL (cs_base_byte3_offset)) (RSI, 1) + movb %al, (LOCAL (ds_base_byte3_offset)) (RSI, 1) +#else + movl %esi, %eax + movw %ax, (LOCAL (cs_base_bytes12) - LOCAL (base)) (RSI, 1) + movw %ax, (LOCAL (ds_base_bytes12) - LOCAL (base)) (RSI, 1) + shrl $16, %eax + movb %al, (LOCAL (cs_base_byte3) - LOCAL (base)) (RSI, 1) + movb %al, (LOCAL (ds_base_byte3) - LOCAL (base)) (RSI, 1) +#endif + + RELOAD_GDT + .code32 + /* Update other registers. */ + movl $DATA_SEGMENT, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + DISABLE_PAGING + +#ifdef __x86_64__ + /* Disable amd64. */ + movl $GRUB_MEMORY_CPU_AMD64_MSR, %ecx + rdmsr + andl $(~GRUB_MEMORY_CPU_AMD64_MSR_ON), %eax + wrmsr +#endif + + /* Turn off PAE. */ + movl %cr4, %eax + andl $(~GRUB_MEMORY_CPU_CR4_PAE_ON), %eax + movl %eax, %cr4 + + /* Update other registers. */ + movl $PSEUDO_REAL_DSEG, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + movl %esi, %eax + shrl $4, %eax +#ifdef __APPLE__ + LOCAL(segment_offset) = LOCAL (segment) - LOCAL (base) + LOCAL(idt_offset) = LOCAL(relocator16_idt) - LOCAL (base) + LOCAL(cont2_offset) = LOCAL (cont2) - LOCAL(base) + movw %ax, (LOCAL(segment_offset)) + lidt (LOCAL(idt_offset)) + + /* jump to a 16 bit segment */ + ljmp $PSEUDO_REAL_CSEG, $(LOCAL(cont2_offset)) +#else + movw %ax, (LOCAL (segment) - LOCAL (base)) + + lidt (EXT_C(grub_relocator16_idt) - LOCAL (base)) + + /* jump to a 16 bit segment */ + ljmp $PSEUDO_REAL_CSEG, $(LOCAL (cont2) - LOCAL(base)) +#endif +LOCAL(cont2): + .code16 + + /* clear the PE bit of CR0 */ + movl %cr0, %eax + andl $(~GRUB_MEMORY_CPU_CR0_PE_ON), %eax + movl %eax, %cr0 + + /* flush prefetch queue, reload %cs */ + /* ljmp */ + .byte 0xea +#ifdef __APPLE__ + LOCAL(cont3_offset) = LOCAL(cont3) - LOCAL(base) + .word LOCAL(cont3_offset) +#else + .word LOCAL(cont3)-LOCAL(base) +#endif +LOCAL(segment): + .word 0 + +LOCAL(cont3): + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_keep_a20_enabled) + .word 0 + + test %ax, %ax + jnz LOCAL(gate_a20_done) + + movw %cs, %ax + movw %ax, %ss +#ifdef __APPLE__ + LOCAL(relocator16_end_offset) = LOCAL(relocator16_end) - LOCAL(base) + leaw LOCAL(relocator16_end_offset), %sp +#else + leaw LOCAL(relocator16_end) - LOCAL(base), %sp +#endif + addw $GRUB_RELOCATOR16_STACK_SIZE, %sp + + /* second, try a BIOS call */ + movw $0x2400, %ax + int $0x15 + + call LOCAL(gate_a20_check_state) + testb %al, %al + jz LOCAL(gate_a20_done) + + /* + * 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 + 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. */ + jmp LOCAL(gate_a20_done) + +LOCAL(gate_a20_check_state): + /* iterate the checking for a while */ + movw $100, %cx +1: + xorw %ax, %ax + movw %ax, %ds + decw %ax + movw %ax, %es + xorw %ax, %ax + + movw $0x8000, %ax + /* compare the byte at ADDR with that at 0x100000 + ADDR */ + movw %ax, %si + addw $0x10, %ax + movw %ax, %di + + /* save the original byte in DL */ + movb %ds:(%si), %dl + movb %es:(%di), %al + /* try to set one less value at ADDR */ + movb %al, %dh + decb %dh + movb %dh, %ds:(%si) + /* serialize */ + outb %al, $0x80 + outb %al, $0x80 + /* obtain the value at 0x100000 + ADDR in CH */ + movb %es:(%di), %dh + /* this result is 1 if A20 is on or 0 if it is off */ + subb %dh, %al + xorb $1, %al + /* restore the original */ + movb %dl, %ds:(%si) + + testb %al, %al + jz LOCAL(gate_a20_done) + loop 1b +2: + ret + +LOCAL(gate_a20_done): + /* + * We are in real mode now. Set up the real mode segment registers and + * all the other general purpose registers. cs is updated with ljmp. + */ + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_ds) + .word 0 + movw %ax, %ds + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_es) + .word 0 + movw %ax, %es + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_fs) + .word 0 + movw %ax, %fs + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_gs) + .word 0 + movw %ax, %gs + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_ss) + .word 0 + movw %ax, %ss + + /* movw imm16, %ax. */ + .byte 0xb8 +VARIABLE(grub_relocator16_sp) + .word 0 + movzwl %ax, %esp + + /* movw imm32, %eax. */ + .byte 0x66, 0xb8 +VARIABLE(grub_relocator16_esi) + .long 0 + movl %eax, %esi + + /* movw imm32, %edx. */ + .byte 0x66, 0xba +VARIABLE(grub_relocator16_edx) + .long 0 + + /* movw imm32, %ebx. */ + .byte 0x66, 0xbb +VARIABLE(grub_relocator16_ebx) + .long 0 + + /* movl imm32, %ebp. */ + .byte 0x66, 0xbd +VARIABLE(grub_relocator16_ebp) + .long 0 + + /* Cleared direction flag is of no problem with any current + payload and makes this implementation easier. */ + cld + + /* ljmp */ + .byte 0xea +VARIABLE(grub_relocator16_ip) + .word 0 +VARIABLE(grub_relocator16_cs) + .word 0 + + .code32 + + /* GDT. Copied from loader/i386/linux.c. */ + .p2align 4 +LOCAL(gdt): + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + + /* -- 16 bit real mode CS -- + * base = filled by code, limit 0x0FFFF (1 B Granularity), present + * type = 16 bit code execute/read only/conforming, DPL = 0 + */ + .word 0xFFFF +LOCAL(cs_base_bytes12): + .word 0 +LOCAL(cs_base_byte3): + .byte 0 + + .byte 0x9E, 0, 0 + + /* -- 16 bit real mode DS -- + * base = filled by code, limit 0x0FFFF (1 B Granularity), present + * type = 16 bit data read/write, DPL = 0 + */ + .word 0xFFFF +LOCAL(ds_base_bytes12): + .word 0 +LOCAL(ds_base_byte3): + .byte 0 + + .byte 0x92, 0, 0 + +LOCAL(gdt_end): + +#ifdef __APPLE__ +LOCAL(relocator16_idt): +#endif +VARIABLE(grub_relocator16_idt) + .word 0 + .long 0 +LOCAL(relocator16_end): +VARIABLE(grub_relocator16_end) + .byte 0 diff --git a/grub-core/lib/i386/relocator32.S b/grub-core/lib/i386/relocator32.S new file mode 100644 index 000000000..09ce56ad0 --- /dev/null +++ b/grub-core/lib/i386/relocator32.S @@ -0,0 +1,134 @@ +/* + * 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 . + */ + +/* The code segment of the protected mode. */ +#define CODE_SEGMENT 0x10 + +/* The data segment of the protected mode. */ +#define DATA_SEGMENT 0x18 + +#include "relocator_common.S" + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator32_start) + PREAMBLE + + RELOAD_GDT + .code32 + /* Update other registers. */ + movl $DATA_SEGMENT, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + movl %eax, %ss + + DISABLE_PAGING + +#ifdef __x86_64__ + /* Disable amd64. */ + movl $GRUB_MEMORY_CPU_AMD64_MSR, %ecx + rdmsr + andl $(~GRUB_MEMORY_CPU_AMD64_MSR_ON), %eax + wrmsr +#endif + + /* Turn off PAE. */ + movl %cr4, %eax + andl $(~GRUB_MEMORY_CPU_CR4_PAE_ON), %eax + movl %eax, %cr4 + + jmp LOCAL(cont2) +LOCAL(cont2): + .code32 + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator32_esp) + .long 0 + + movl %eax, %esp + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator32_ebp) + .long 0 + + movl %eax, %ebp + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator32_esi) + .long 0 + + movl %eax, %esi + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator32_edi) + .long 0 + + movl %eax, %edi + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator32_eax) + .long 0 + + /* mov imm32, %ebx */ + .byte 0xbb +VARIABLE(grub_relocator32_ebx) + .long 0 + + /* mov imm32, %ecx */ + .byte 0xb9 +VARIABLE(grub_relocator32_ecx) + .long 0 + + /* mov imm32, %edx */ + .byte 0xba +VARIABLE(grub_relocator32_edx) + .long 0 + + /* Cleared direction flag is of no problem with any current + payload and makes this implementation easier. */ + cld + + .byte 0xea +VARIABLE(grub_relocator32_eip) + .long 0 + .word CODE_SEGMENT + + /* GDT. Copied from loader/i386/linux.c. */ + .p2align 4 +LOCAL(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 +LOCAL(gdt_end): + +VARIABLE(grub_relocator32_end) diff --git a/grub-core/lib/i386/relocator64.S b/grub-core/lib/i386/relocator64.S new file mode 100644 index 000000000..c80538e7e --- /dev/null +++ b/grub-core/lib/i386/relocator64.S @@ -0,0 +1,210 @@ +/* + * 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 . + */ + +#define CODE32_SEGMENT 0x18 +#define CODE_SEGMENT 0x08 + +/* The data segment of the protected mode. */ +#define DATA_SEGMENT 0x10 + +#include "relocator_common.S" + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator64_start) + PREAMBLE +#ifndef __x86_64__ + DISABLE_PAGING + + /* Turn on PAE. */ + movl %cr4, %eax + orl $(GRUB_MEMORY_CPU_CR4_PAE_ON | GRUB_MEMORY_CPU_CR4_PSE_ON), %eax + movl %eax, %cr4 + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator64_cr3) + .long 0 + movl %eax, %cr3 + + /* Turn on amd64. */ + movl $GRUB_MEMORY_CPU_AMD64_MSR, %ecx + rdmsr + orl $GRUB_MEMORY_CPU_AMD64_MSR_ON, %eax + wrmsr + + /* Enable paging. */ + movl %cr0, %eax + orl $GRUB_MEMORY_CPU_CR0_PAGING_ON, %eax + movl %eax, %cr0 + + RELOAD_GDT +#else + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_cr3) + .quad 0 + movq %rax, %cr3 +#endif + +#ifdef __x86_64__ + .code64 +#endif + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rsp) + .quad 0 + +#ifdef __x86_64__ + movq %rax, %rsp +#else + /* movq %rax, %rsp */ + .byte 0x48 + .byte 0x89 + .byte 0xc4 +#endif + +#ifdef GRUB_MACHINE_EFI + jmp LOCAL(skip_efi_stack_align) + + /* + * Here is grub_relocator64_efi_start() entry point. Most of the + * code below is shared between grub_relocator64_efi_start() + * and grub_relocator64_start(). + * + * Think twice before changing anything there!!! + */ +VARIABLE(grub_relocator64_efi_start) + /* Align the stack as UEFI spec requires. */ +#ifdef __x86_64__ + andq $~15, %rsp +#else + /* andq $~15, %rsp */ + .byte 0x48 + .byte 0x83 + .byte 0xe4 + .byte 0xf0 +#endif + +LOCAL(skip_efi_stack_align): +#endif + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rsi) + .quad 0 + +#ifdef __x86_64__ + movq %rax, %rsi +#else + /* movq %rax, %rsi */ + .byte 0x48 + .byte 0x89 + .byte 0xc6 +#endif + + /* mov imm64, %rax */ + .byte 0x48 + .byte 0xb8 +VARIABLE(grub_relocator64_rax) + .quad 0 + + /* mov imm64, %rbx */ + .byte 0x48 + .byte 0xbb +VARIABLE(grub_relocator64_rbx) + .quad 0 + + /* mov imm64, %rcx */ + .byte 0x48 + .byte 0xb9 +VARIABLE(grub_relocator64_rcx) + .quad 0 + + /* mov imm64, %rdx */ + .byte 0x48 + .byte 0xba +VARIABLE(grub_relocator64_rdx) + .quad 0 + + /* Cleared direction flag is of no problem with any current + payload and makes this implementation easier. */ + cld + +#if defined (__APPLE__) || !defined (__x86_64__) + .byte 0xff, 0x25 + .long 0 +#else + jmp *LOCAL(jump_addr) (%rip) +#endif + +LOCAL(jump_addr): +VARIABLE(grub_relocator64_rip) + .quad 0 + +#ifdef GRUB_MACHINE_EFI + /* Here grub_relocator64_efi_start() ends. Ufff... */ +VARIABLE(grub_relocator64_efi_end) +#endif + +#ifndef __x86_64__ + .p2align 4 +LOCAL(gdt): + /* NULL. */ + .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* 64-bit segment. */ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x8 /* Type 8. */ | (1 << 4) /* Code. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (1 << 5) /* 64-bit. */ | (0 << 6) \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + + /* Data segment*/ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x0 /* Type 0. */ | (0 << 4) /* Data. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (0 << 5) /* Data. */ | (0 << 6) \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + + /* Compatibility segment. */ + .word 0xffff /* Limit xffff. */ + .word 0x0000 /* Base xxxx0000. */ + .byte 0x00 /* Base xx00xxxx. */ + .byte (0x8 /* Type 8. */ | (1 << 4) /* Code. */ \ + | (0 << 5) /* Ring 0. */ | (1 << 7) /* Present. */) + .byte (0xf /* Limit fxxxx. */ | (0 << 4) /* AVL flag. */ \ + | (0 << 5) /* 32-bit. */ | (1 << 6) /* 32-bit. */ \ + | (1 << 7) /* 4K granular. */) + .byte 0x00 /* Base 00xxxxxx. */ + +LOCAL(gdt_end): +#endif + +VARIABLE(grub_relocator64_end) diff --git a/grub-core/lib/i386/relocator_asm.S b/grub-core/lib/i386/relocator_asm.S new file mode 100644 index 000000000..f273586fe --- /dev/null +++ b/grub-core/lib/i386/relocator_asm.S @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +#include +#include + + .p2align 2 + +VARIABLE(grub_relocator_backward_start) + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_backward_dest) + .long 0 + movl %eax, %edi + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_backward_src) + .long 0 + movl %eax, %esi + + /* mov imm32, %ecx */ + .byte 0xb9 +VARIABLE(grub_relocator_backward_chunk_size) + .long 0 + + add %ecx, %esi + add %ecx, %edi + + + /* Backward movsb is implicitly off-by-one. compensate that. */ + sub $1, %esi + sub $1, %edi + + /* Backward copy. */ + std + + rep + movsb +VARIABLE(grub_relocator_backward_end) + + +VARIABLE(grub_relocator_forward_start) + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_forward_dest) + .long 0 + movl %eax, %edi + + /* mov imm32, %rax */ + .byte 0xb8 +VARIABLE(grub_relocator_forward_src) + .long 0 + movl %eax, %esi + + /* mov imm32, %ecx */ + .byte 0xb9 +VARIABLE(grub_relocator_forward_chunk_size) + .long 0 + + /* Forward copy. */ + cld + rep + movsb +VARIABLE(grub_relocator_forward_end) diff --git a/grub-core/lib/i386/relocator_common.S b/grub-core/lib/i386/relocator_common.S new file mode 100644 index 000000000..1b5210dd3 --- /dev/null +++ b/grub-core/lib/i386/relocator_common.S @@ -0,0 +1,111 @@ +/* + * 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 . + */ + + +#include +#include + +#ifdef __x86_64__ +#define RAX %rax +#define RSI %rsi +#else +#define RAX %eax +#define RSI %esi +#endif + + .macro DISABLE_PAGING + + movl %cr0, %eax + andl $(~GRUB_MEMORY_CPU_CR0_PAGING_ON), %eax + movl %eax, %cr0 + .endm + + .macro PREAMBLE +LOCAL(base): + /* %rax contains now our new 'base'. */ + mov RAX, RSI + +#if defined (__APPLE__) && defined (__x86_64__) + leaq LOCAL(cont0) (%rip), RAX +#elif defined (__APPLE__) + LOCAL(cont0_offset) = LOCAL(cont0) - LOCAL(base) + add $LOCAL(cont0_offset), RAX +#else + add $(LOCAL(cont0) - LOCAL(base)), RAX +#endif + jmp *RAX +LOCAL(cont0): + .endm + + .macro RELOAD_GDT +#ifdef __APPLE__ + LOCAL(cont1_offset) = LOCAL(cont1) - LOCAL(base) + LOCAL(jump_vector_offset) = LOCAL(jump_vector) - LOCAL(base) + LOCAL(gdt_offset) = LOCAL(gdt) - LOCAL(base) + LOCAL(gdt_addr_offset) = LOCAL(gdt_addr) - LOCAL(base) + LOCAL(gdtdesc_offset) = LOCAL(gdtdesc) - LOCAL(base) + + lea LOCAL(cont1_offset) (RSI, 1), RAX + movl %eax, LOCAL(jump_vector_offset) (RSI, 1) + + lea LOCAL(gdt_offset) (RSI, 1), RAX + mov RAX, (LOCAL(gdt_addr_offset)) (RSI, 1) + + /* Switch to compatibility mode. */ + lgdt (LOCAL(gdtdesc_offset)) (RSI, 1) + + /* Update %cs. */ + ljmp *(LOCAL(jump_vector_offset)) (RSI, 1) + .p2align 4 +LOCAL(gdtdesc): + LOCAL(gdtsize) = LOCAL(gdt_end) - LOCAL(gdt) + .word LOCAL(gdtsize) +#else + lea (LOCAL(cont1) - LOCAL(base)) (RSI, 1), RAX + movl %eax, (LOCAL(jump_vector) - LOCAL(base)) (RSI, 1) + + lea (LOCAL(gdt) - LOCAL(base)) (RSI, 1), RAX + mov RAX, (LOCAL(gdt_addr) - LOCAL(base)) (RSI, 1) + + /* Switch to compatibility mode. */ + lgdt (LOCAL(gdtdesc) - LOCAL(base)) (RSI, 1) + + /* Update %cs. */ + ljmp *(LOCAL(jump_vector) - LOCAL(base)) (RSI, 1) + + .p2align 4 +LOCAL(gdtdesc): + .word LOCAL(gdt_end) - LOCAL(gdt) +#endif +LOCAL(gdt_addr): +#ifdef __x86_64__ + /* Filled by the code. */ + .quad 0 +#else + /* Filled by the code. */ + .long 0 +#endif + + .p2align 4 +LOCAL(jump_vector): + /* Jump location. Is filled by the code */ + .long 0 + .long CODE_SEGMENT + +LOCAL(cont1): + .endm diff --git a/grub-core/lib/i386/relocator_common_c.c b/grub-core/lib/i386/relocator_common_c.c new file mode 100644 index 000000000..7be609b73 --- /dev/null +++ b/grub-core/lib/i386/relocator_common_c.c @@ -0,0 +1,109 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009-2013 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 . + */ + +#include +#include + +#include +#include +#include + +#include +#include + +extern grub_uint8_t grub_relocator_forward_start; +extern grub_uint8_t grub_relocator_forward_end; +extern grub_uint8_t grub_relocator_backward_start; +extern grub_uint8_t grub_relocator_backward_end; + +extern void *grub_relocator_backward_dest; +extern void *grub_relocator_backward_src; +extern grub_size_t grub_relocator_backward_chunk_size; + +extern void *grub_relocator_forward_dest; +extern void *grub_relocator_forward_src; +extern grub_size_t grub_relocator_forward_chunk_size; + +#define RELOCATOR_SIZEOF(x) (&grub_relocator##x##_end - &grub_relocator##x##_start) + +grub_size_t grub_relocator_align = 1; +grub_size_t grub_relocator_forward_size; +grub_size_t grub_relocator_backward_size; +#ifdef __x86_64__ +grub_size_t grub_relocator_jumper_size = 12; +#else +grub_size_t grub_relocator_jumper_size = 7; +#endif + +void +grub_cpu_relocator_init (void) +{ + grub_relocator_forward_size = RELOCATOR_SIZEOF (_forward); + grub_relocator_backward_size = RELOCATOR_SIZEOF (_backward); +} + +void +grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) +{ + grub_uint8_t *ptr; + ptr = rels; +#ifdef __x86_64__ + /* movq imm64, %rax (for relocator) */ + *(grub_uint8_t *) ptr = 0x48; + ptr++; + *(grub_uint8_t *) ptr = 0xb8; + ptr++; + *(grub_uint64_t *) ptr = addr; + ptr += sizeof (grub_uint64_t); +#else + /* movl imm32, %eax (for relocator) */ + *(grub_uint8_t *) ptr = 0xb8; + ptr++; + *(grub_uint32_t *) ptr = addr; + ptr += sizeof (grub_uint32_t); +#endif + /* jmp $eax/$rax */ + *(grub_uint8_t *) ptr = 0xff; + ptr++; + *(grub_uint8_t *) ptr = 0xe0; + ptr++; +} + +void +grub_cpu_relocator_backward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + grub_relocator_backward_dest = dest; + grub_relocator_backward_src = src; + grub_relocator_backward_chunk_size = size; + + grub_memmove (ptr, + &grub_relocator_backward_start, RELOCATOR_SIZEOF (_backward)); +} + +void +grub_cpu_relocator_forward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + grub_relocator_forward_dest = dest; + grub_relocator_forward_src = src; + grub_relocator_forward_chunk_size = size; + + grub_memmove (ptr, + &grub_relocator_forward_start, RELOCATOR_SIZEOF (_forward)); +} diff --git a/lib/i386/setjmp.S b/grub-core/lib/i386/setjmp.S similarity index 96% rename from lib/i386/setjmp.S rename to grub-core/lib/i386/setjmp.S index a2002ae3d..0b0740f11 100644 --- a/lib/i386/setjmp.S +++ b/grub-core/lib/i386/setjmp.S @@ -17,9 +17,12 @@ */ #include +#include .file "setjmp.S" +GRUB_MOD_LICENSE "GPLv3+" + .text /* diff --git a/grub-core/lib/i386/xen/relocator.S b/grub-core/lib/i386/xen/relocator.S new file mode 100644 index 000000000..dab4d8ace --- /dev/null +++ b/grub-core/lib/i386/xen/relocator.S @@ -0,0 +1,165 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include +#include +#include + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE(grub_relocator_xen_remap_start) +LOCAL(base): + /* Remap the remapper to it's new address. */ + /* mov imm32, %ebx - %ebx: new virtual address of remapper */ + .byte 0xbb +VARIABLE(grub_relocator_xen_remapper_virt) + .long 0 + + /* mov imm32, %ecx - %ecx: low part of page table entry */ + .byte 0xb9 +VARIABLE(grub_relocator_xen_remapper_map) + .long 0 + + /* mov imm32, %edx - %edx: high part of page table entry */ + .byte 0xba +VARIABLE(grub_relocator_xen_remapper_map_high) + .long 0 + + movl %ebx, %ebp /* %ebx is clobbered by hypercall */ + + movl $UVMF_INVLPG, %esi /* esi: flags (inv. single entry) */ + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + + movl %ebp, %ebx + addl $(LOCAL(cont) - LOCAL(base)), %ebx + + jmp *%ebx /* Continue with new virtual address */ + +LOCAL(cont): + /* Modify mappings of new page tables to be read-only. */ + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_paging_areas_addr) + .long 0 + movl %eax, %ebx +1: + movl 0(%ebx), %ebp /* Get start pfn of the current area */ + movl GRUB_TARGET_SIZEOF_LONG(%ebx), %ecx /* Get # of pg tables */ + testl %ecx, %ecx /* 0 -> last area reached */ + jz 3f + addl $(2 * GRUB_TARGET_SIZEOF_LONG), %ebx + movl %ebx, %esp /* Save current area pointer */ + +2: + movl %ecx, %edi + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_mfn_list) + .long 0 + movl 0(%eax, %ebp, 4), %ecx /* mfn */ + movl %ebp, %ebx + shll $GRUB_PAGE_SHIFT, %ebx /* virtual address (1:1 mapping) */ + movl %ecx, %edx + shll $GRUB_PAGE_SHIFT, %ecx /* prepare pte low part */ + shrl $(32 - GRUB_PAGE_SHIFT), %edx /* pte high part */ + orl $(GRUB_PAGE_PRESENT | GRUB_PAGE_USER), %ecx /* pte low */ + movl $UVMF_INVLPG, %esi + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 /* parameters: eax, ebx, ecx, edx, esi */ + + incl %ebp /* next pfn */ + movl %edi, %ecx + + loop 2b + + mov %esp, %ebx /* restore area poniter */ + jmp 1b + +3: + /* Switch page tables: pin new L3 pt, load cr3, unpin old L3. */ + /* mov imm32, %ebx */ + .byte 0xbb +VARIABLE(grub_relocator_xen_mmu_op_addr) + .long 0 + movl $3, %ecx /* 3 mmu ops */ + movl $0, %edx /* pdone (not used) */ + movl $DOMID_SELF, %esi + movl $__HYPERVISOR_mmuext_op, %eax + int $0x82 + + /* Continue in virtual kernel mapping. */ + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_remap_continue) + .long 0 + + jmp *%eax + +VARIABLE(grub_relocator_xen_paging_areas) + .long 0, 0, 0, 0, 0, 0, 0, 0 + +VARIABLE(grub_relocator_xen_mmu_op) + .space 256 + +VARIABLE(grub_relocator_xen_remap_end) + + +VARIABLE(grub_relocator_xen_start) + /* Unmap old remapper area. */ + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_remapper_virt2) + .long 0 + + movl %eax, %ebx + + xorl %ecx, %ecx /* Invalid pte */ + xorl %edx, %edx + + movl $UVMF_INVLPG, %esi + movl $__HYPERVISOR_update_va_mapping, %eax + int $0x82 + + /* Prepare registers for starting kernel. */ + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_stack) + .long 0 + + movl %eax, %esp + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_start_info) + .long 0 + + movl %eax, %esi + + cld + + /* mov imm32, %eax */ + .byte 0xb8 +VARIABLE(grub_relocator_xen_entry_point) + .long 0 + + /* Now start the new kernel. */ + jmp *%eax + +VARIABLE(grub_relocator_xen_end) diff --git a/grub-core/lib/ia64/longjmp.S b/grub-core/lib/ia64/longjmp.S new file mode 100644 index 000000000..38afb2243 --- /dev/null +++ b/grub-core/lib/ia64/longjmp.S @@ -0,0 +1,162 @@ +/* Copyright (C) 1999, 2000, 2001, 2002, 2008 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + Note that __sigsetjmp() did NOT flush the register stack. Instead, + we do it here since __longjmp() is usually much less frequently + invoked than __sigsetjmp(). The only difficulty is that __sigsetjmp() + didn't (and wouldn't be able to) save ar.rnat either. This is a problem + because if we're not careful, we could end up loading random NaT bits. + There are two cases: + + (i) ar.bsp < ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ar.rnat contains the desired bits---preserve ar.rnat + across loadrs and write to ar.bspstore + + (ii) ar.bsp >= ia64_rse_rnat_addr(jmpbuf.ar_bsp) + The desired ar.rnat is stored in + ia64_rse_rnat_addr(jmpbuf.ar_bsp). Load those + bits into ar.rnat after setting ar.bspstore. */ + + + +# define pPos p6 /* is rotate count positive? */ +# define pNeg p7 /* is rotate count negative? */ + + + /* __longjmp(__jmp_buf buf, int val) */ + + .text + + .proc EXT_C(grub_longjmp) +FUNCTION(grub_longjmp) + alloc r8=ar.pfs,2,1,0,0 + mov r27=ar.rsc + add r2=0x98,in0 // r2 <- &jmpbuf.orig_jmp_buf_addr + ;; + ld8 r8=[r2],-16 // r8 <- orig_jmp_buf_addr + mov r10=ar.bsp + and r11=~0x3,r27 // clear ar.rsc.mode + ;; + flushrs // flush dirty regs to backing store (must be first in insn grp) + ld8 r23=[r2],8 // r23 <- jmpbuf.ar_bsp + sub r8=r8,in0 // r8 <- &orig_jmpbuf - &jmpbuf + ;; + ld8 r25=[r2] // r25 <- jmpbuf.ar_unat + extr.u r8=r8,3,6 // r8 <- (&orig_jmpbuf - &jmpbuf)/8 & 0x3f + ;; + cmp.lt pNeg,pPos=r8,r0 + mov r2=in0 + ;; +(pPos) mov r16=r8 +(pNeg) add r16=64,r8 +(pPos) sub r17=64,r8 +(pNeg) sub r17=r0,r8 + ;; + mov ar.rsc=r11 // put RSE in enforced lazy mode + shr.u r8=r25,r16 + add r3=8,in0 // r3 <- &jmpbuf.r1 + shl r9=r25,r17 + ;; + or r25=r8,r9 + ;; + mov r26=ar.rnat + mov ar.unat=r25 // setup ar.unat (NaT bits for r1, r4-r7, and r12) + ;; + ld8.fill.nta sp=[r2],16 // r12 (sp) + ld8.fill.nta gp=[r3],16 // r1 (gp) + dep r11=-1,r23,3,6 // r11 <- ia64_rse_rnat_addr(jmpbuf.ar_bsp) + ;; + ld8.nta r16=[r2],16 // caller's unat + ld8.nta r17=[r3],16 // fpsr + ;; + ld8.fill.nta r4=[r2],16 // r4 + ld8.fill.nta r5=[r3],16 // r5 (gp) + cmp.geu p8,p0=r10,r11 // p8 <- (ar.bsp >= jmpbuf.ar_bsp) + ;; + ld8.fill.nta r6=[r2],16 // r6 + ld8.fill.nta r7=[r3],16 // r7 + ;; + mov ar.unat=r16 // restore caller's unat + mov ar.fpsr=r17 // restore fpsr + ;; + ld8.nta r16=[r2],16 // b0 + ld8.nta r17=[r3],16 // b1 + ;; +(p8) ld8 r26=[r11] // r26 <- *ia64_rse_rnat_addr(jmpbuf.ar_bsp) + mov ar.bspstore=r23 // restore ar.bspstore + ;; + ld8.nta r18=[r2],16 // b2 + ld8.nta r19=[r3],16 // b3 + ;; + ld8.nta r20=[r2],16 // b4 + ld8.nta r21=[r3],16 // b5 + ;; + ld8.nta r11=[r2],16 // ar.pfs + ld8.nta r22=[r3],56 // ar.lc + ;; + ld8.nta r24=[r2],32 // pr + mov b0=r16 + ;; + ldf.fill.nta f2=[r2],32 + ldf.fill.nta f3=[r3],32 + mov b1=r17 + ;; + ldf.fill.nta f4=[r2],32 + ldf.fill.nta f5=[r3],32 + mov b2=r18 + ;; + ldf.fill.nta f16=[r2],32 + ldf.fill.nta f17=[r3],32 + mov b3=r19 + ;; + ldf.fill.nta f18=[r2],32 + ldf.fill.nta f19=[r3],32 + mov b4=r20 + ;; + ldf.fill.nta f20=[r2],32 + ldf.fill.nta f21=[r3],32 + mov b5=r21 + ;; + ldf.fill.nta f22=[r2],32 + ldf.fill.nta f23=[r3],32 + mov ar.lc=r22 + ;; + ldf.fill.nta f24=[r2],32 + ldf.fill.nta f25=[r3],32 + cmp.eq p8,p9=0,in1 + ;; + ldf.fill.nta f26=[r2],32 + ldf.fill.nta f27=[r3],32 + mov ar.pfs=r11 + ;; + ldf.fill.nta f28=[r2],32 + ldf.fill.nta f29=[r3],32 + ;; + ldf.fill.nta f30=[r2] + ldf.fill.nta f31=[r3] +(p8) mov r8=1 + + mov ar.rnat=r26 // restore ar.rnat + ;; + mov ar.rsc=r27 // restore ar.rsc +(p9) mov r8=in1 + + invala // virt. -> phys. regnum mapping may change + mov pr=r24,-1 + br.ret.dptk.few rp + .endp EXT_C(grub_longjmp) diff --git a/grub-core/lib/ia64/setjmp.S b/grub-core/lib/ia64/setjmp.S new file mode 100644 index 000000000..a0382d83d --- /dev/null +++ b/grub-core/lib/ia64/setjmp.S @@ -0,0 +1,177 @@ +/* Copyright (C) 1999, 2000, 2001, 2002, 2008 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. + + The layout of the jmp_buf is as follows. This is subject to change + and user-code should never depend on the particular layout of + jmp_buf! + + + offset: description: + ------- ------------ + 0x000 stack pointer (r12) ; unchangeable (see _JMPBUF_UNWINDS) + 0x008 r1 (gp) + 0x010 caller's unat + 0x018 fpsr + 0x020 r4 + 0x028 r5 + 0x030 r6 + 0x038 r7 + 0x040 rp (b0) + 0x048 b1 + 0x050 b2 + 0x058 b3 + 0x060 b4 + 0x068 b5 + 0x070 ar.pfs + 0x078 ar.lc + 0x080 pr + 0x088 ar.bsp ; unchangeable (see __longjmp.S) + 0x090 ar.unat + 0x098 &__jmp_buf ; address of the jmpbuf (needed to locate NaT bits in unat) + 0x0a0 f2 + 0x0b0 f3 + 0x0c0 f4 + 0x0d0 f5 + 0x0e0 f16 + 0x0f0 f17 + 0x100 f18 + 0x110 f19 + 0x120 f20 + 0x130 f21 + 0x130 f22 + 0x140 f23 + 0x150 f24 + 0x160 f25 + 0x170 f26 + 0x180 f27 + 0x190 f28 + 0x1a0 f29 + 0x1b0 f30 + 0x1c0 f31 */ + +#include +#include + + .file "setjmp.S" + +GRUB_MOD_LICENSE "GPLv2+" + + /* The following two entry points are the traditional entry points: */ + + .text + + .proc EXT_C(grub_setjmp) +FUNCTION(grub_setjmp) + alloc r8=ar.pfs,2,0,0,0 + mov in1=1 + br.cond.sptk.many __sigsetjmp + .endp EXT_C(grub_setjmp) + + /* __sigsetjmp(__jmp_buf buf, int savemask) */ + + .proc __sigsetjmp +__sigsetjmp: + //.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) + alloc loc1=ar.pfs,2,2,2,0 + mov r16=ar.unat + ;; + mov r17=ar.fpsr + mov r2=in0 + add r3=8,in0 + ;; + st8.spill.nta [r2]=sp,16 // r12 (sp) + st8.spill.nta [r3]=gp,16 // r1 (gp) + ;; + st8.nta [r2]=r16,16 // save caller's unat + st8.nta [r3]=r17,16 // save fpsr + add r8=0xa0,in0 + ;; + st8.spill.nta [r2]=r4,16 // r4 + st8.spill.nta [r3]=r5,16 // r5 + add r9=0xb0,in0 + ;; + stf.spill.nta [r8]=f2,32 + stf.spill.nta [r9]=f3,32 + mov loc0=rp + .body + ;; + stf.spill.nta [r8]=f4,32 + stf.spill.nta [r9]=f5,32 + mov r17=b1 + ;; + stf.spill.nta [r8]=f16,32 + stf.spill.nta [r9]=f17,32 + mov r18=b2 + ;; + stf.spill.nta [r8]=f18,32 + stf.spill.nta [r9]=f19,32 + mov r19=b3 + ;; + stf.spill.nta [r8]=f20,32 + stf.spill.nta [r9]=f21,32 + mov r20=b4 + ;; + stf.spill.nta [r8]=f22,32 + stf.spill.nta [r9]=f23,32 + mov r21=b5 + ;; + stf.spill.nta [r8]=f24,32 + stf.spill.nta [r9]=f25,32 + mov r22=ar.lc + ;; + stf.spill.nta [r8]=f26,32 + stf.spill.nta [r9]=f27,32 + mov r24=pr + ;; + stf.spill.nta [r8]=f28,32 + stf.spill.nta [r9]=f29,32 + ;; + stf.spill.nta [r8]=f30 + stf.spill.nta [r9]=f31 + + st8.spill.nta [r2]=r6,16 // r6 + st8.spill.nta [r3]=r7,16 // r7 + ;; + mov r23=ar.bsp + mov r25=ar.unat + mov out0=in0 + + st8.nta [r2]=loc0,16 // b0 + st8.nta [r3]=r17,16 // b1 + mov out1=in1 + ;; + st8.nta [r2]=r18,16 // b2 + st8.nta [r3]=r19,16 // b3 + ;; + st8.nta [r2]=r20,16 // b4 + st8.nta [r3]=r21,16 // b5 + ;; + st8.nta [r2]=loc1,16 // ar.pfs + st8.nta [r3]=r22,16 // ar.lc + ;; + st8.nta [r2]=r24,16 // pr + st8.nta [r3]=r23,16 // ar.bsp + ;; + st8.nta [r2]=r25 // ar.unat + st8.nta [r3]=in0 // &__jmp_buf + mov r8=0 + mov rp=loc0 + mov ar.pfs=loc1 + br.ret.sptk.many rp + + .endp __sigsetjmp diff --git a/grub-core/lib/ieee1275/cmos.c b/grub-core/lib/ieee1275/cmos.c new file mode 100644 index 000000000..1400cfb67 --- /dev/null +++ b/grub-core/lib/ieee1275/cmos.c @@ -0,0 +1,77 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2011 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 . + */ + +#include +#include +#include +#include +#include + +volatile grub_uint8_t *grub_cmos_port = 0; + +/* Helper for grub_cmos_find_port. */ +static int +grub_cmos_find_port_iter (struct grub_ieee1275_devalias *alias) +{ + grub_ieee1275_phandle_t dev; + grub_uint32_t addr[2]; + grub_ssize_t actual; + /* Enough to check if it's "m5819" */ + char compat[100]; + if (grub_ieee1275_finddevice (alias->path, &dev)) + return 0; + if (grub_ieee1275_get_property (dev, "compatible", compat, sizeof (compat), + 0)) + return 0; + if (grub_strcmp (compat, "m5819") != 0) + return 0; + if (grub_ieee1275_get_integer_property (dev, "address", + addr, sizeof (addr), &actual)) + return 0; + if (actual == 4) + { + grub_cmos_port = (volatile grub_uint8_t *) (grub_addr_t) addr[0]; + return 1; + } + +#if GRUB_CPU_SIZEOF_VOID_P == 8 + if (actual == 8) + { + grub_cmos_port = (volatile grub_uint8_t *) + ((((grub_addr_t) addr[0]) << 32) | addr[1]); + return 1; + } +#else + if (actual == 8 && addr[0] == 0) + { + grub_cmos_port = (volatile grub_uint8_t *) addr[1]; + return 1; + } +#endif + return 0; +} + +grub_err_t +grub_cmos_find_port (void) +{ + grub_ieee1275_devices_iterate (grub_cmos_find_port_iter); + if (!grub_cmos_port) + return grub_error (GRUB_ERR_IO, "no cmos found"); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/ieee1275/datetime.c b/grub-core/lib/ieee1275/datetime.c new file mode 100644 index 000000000..74578f15a --- /dev/null +++ b/grub-core/lib/ieee1275/datetime.c @@ -0,0 +1,156 @@ +/* kern/cmos_datetime.c - CMOS datetime function. + * + * 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 . + */ + +#include +#include +#include +#include +#if defined (__powerpc__) || defined (__sparc__) +#include +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +static char *rtc = 0; +static int no_ieee1275_rtc = 0; + +/* Helper for find_rtc. */ +static int +find_rtc_iter (struct grub_ieee1275_devalias *alias) +{ + if (grub_strcmp (alias->type, "rtc") == 0) + { + grub_dprintf ("datetime", "Found RTC %s\n", alias->path); + rtc = grub_strdup (alias->path); + return 1; + } + return 0; +} + +static void +find_rtc (void) +{ + grub_ieee1275_devices_iterate (find_rtc_iter); + if (!rtc) + no_ieee1275_rtc = 1; +} + +grub_err_t +grub_get_datetime (struct grub_datetime *datetime) +{ + struct get_time_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t device; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t year; + grub_ieee1275_cell_t month; + grub_ieee1275_cell_t day; + grub_ieee1275_cell_t hour; + grub_ieee1275_cell_t minute; + grub_ieee1275_cell_t second; + } + args; + int status; + grub_ieee1275_ihandle_t ihandle; + + if (no_ieee1275_rtc) + return grub_get_datetime_cmos (datetime); + if (!rtc) + find_rtc (); + if (!rtc) + return grub_get_datetime_cmos (datetime); + + status = grub_ieee1275_open (rtc, &ihandle); + if (status == -1) + return grub_error (GRUB_ERR_IO, "couldn't open RTC"); + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 7); + args.device = (grub_ieee1275_cell_t) ihandle; + args.method = (grub_ieee1275_cell_t) "get-time"; + + status = IEEE1275_CALL_ENTRY_FN (&args); + + grub_ieee1275_close (ihandle); + + if (status == -1 || args.catch_result) + return grub_error (GRUB_ERR_IO, "get-time failed"); + + datetime->year = args.year; + datetime->month = args.month; + datetime->day = args.day; + datetime->hour = args.hour; + datetime->minute = args.minute; + datetime->second = args.second; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_set_datetime (struct grub_datetime *datetime) +{ + struct set_time_args + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t device; + grub_ieee1275_cell_t year; + grub_ieee1275_cell_t month; + grub_ieee1275_cell_t day; + grub_ieee1275_cell_t hour; + grub_ieee1275_cell_t minute; + grub_ieee1275_cell_t second; + grub_ieee1275_cell_t catch_result; + } + args; + int status; + grub_ieee1275_ihandle_t ihandle; + + if (no_ieee1275_rtc) + return grub_set_datetime_cmos (datetime); + if (!rtc) + find_rtc (); + if (!rtc) + return grub_set_datetime_cmos (datetime); + + status = grub_ieee1275_open (rtc, &ihandle); + if (status == -1) + return grub_error (GRUB_ERR_IO, "couldn't open RTC"); + + INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 1); + args.device = (grub_ieee1275_cell_t) ihandle; + args.method = (grub_ieee1275_cell_t) "set-time"; + + args.year = datetime->year; + args.month = datetime->month; + args.day = datetime->day; + args.hour = datetime->hour; + args.minute = datetime->minute; + args.second = datetime->second; + + status = IEEE1275_CALL_ENTRY_FN (&args); + + grub_ieee1275_close (ihandle); + + if (status == -1 || args.catch_result) + return grub_error (GRUB_ERR_IO, "set-time failed"); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/ieee1275/halt.c b/grub-core/lib/ieee1275/halt.c new file mode 100644 index 000000000..8fc16d243 --- /dev/null +++ b/grub-core/lib/ieee1275/halt.c @@ -0,0 +1,33 @@ +/* openfw.c -- Open firmware support functions. */ +/* + * 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 . + */ + +#include +#include + +void +grub_halt (void) +{ + /* Not standardized. We try three known commands. */ + + grub_ieee1275_interpret ("power-off", 0); + grub_ieee1275_interpret ("shut-down", 0); + grub_ieee1275_interpret ("poweroff", 0); + + while (1); +} diff --git a/include/grub/powerpc/ieee1275/memory.h b/grub-core/lib/ieee1275/reboot.c similarity index 80% rename from include/grub/powerpc/ieee1275/memory.h rename to grub-core/lib/ieee1275/reboot.c index f8f2ff08d..91c8779f2 100644 --- a/include/grub/powerpc/ieee1275/memory.h +++ b/grub-core/lib/ieee1275/reboot.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2011 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 @@ -16,11 +16,12 @@ * along with GRUB. If not, see . */ -#ifndef GRUB_MEMORY_MACHINE_HEADER -#define GRUB_MEMORY_MACHINE_HEADER 1 - #include +#include -#define GRUB_MACHINE_MEMORY_AVAILABLE 1 - -#endif +void +grub_reboot (void) +{ + grub_ieee1275_interpret ("reset-all", 0); + for (;;) ; +} diff --git a/grub-core/lib/ieee1275/relocator.c b/grub-core/lib/ieee1275/relocator.c new file mode 100644 index 000000000..918392f1e --- /dev/null +++ b/grub-core/lib/ieee1275/relocator.c @@ -0,0 +1,110 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 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 . + */ + +#include +#include +#include +#include + +/* Helper for grub_relocator_firmware_get_max_events. */ +static int +count (grub_uint64_t addr __attribute__ ((unused)), + grub_uint64_t len __attribute__ ((unused)), + grub_memory_type_t type __attribute__ ((unused)), void *data) +{ + int *counter = data; + + (*counter)++; + return 0; +} + +unsigned +grub_relocator_firmware_get_max_events (void) +{ + int counter = 0; + + grub_machine_mmap_iterate (count, &counter); + return 2 * counter; +} + +/* Context for grub_relocator_firmware_fill_events. */ +struct grub_relocator_firmware_fill_events_ctx +{ + struct grub_relocator_mmap_event *events; + int counter; +}; + +/* Helper for grub_relocator_firmware_fill_events. */ +static int +grub_relocator_firmware_fill_events_iter (grub_uint64_t addr, + grub_uint64_t len, + grub_memory_type_t type, void *data) +{ + struct grub_relocator_firmware_fill_events_ctx *ctx = data; + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM)) + { + if (addr + len <= 0x180000) + return 0; + + if (addr < 0x180000) + { + len = addr + len - 0x180000; + addr = 0x180000; + } + } + + ctx->events[ctx->counter].type = REG_FIRMWARE_START; + ctx->events[ctx->counter].pos = addr; + ctx->counter++; + ctx->events[ctx->counter].type = REG_FIRMWARE_END; + ctx->events[ctx->counter].pos = addr + len; + ctx->counter++; + + return 0; +} + +unsigned +grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events) +{ + struct grub_relocator_firmware_fill_events_ctx ctx = { + .events = events, + .counter = 0 + }; + + grub_machine_mmap_iterate (grub_relocator_firmware_fill_events_iter, &ctx); + return ctx.counter; +} + +int +grub_relocator_firmware_alloc_region (grub_addr_t start, grub_size_t size) +{ + grub_err_t err; + err = grub_claimmap (start, size); + grub_errno = 0; + return (err == 0); +} + +void +grub_relocator_firmware_free_region (grub_addr_t start, grub_size_t size) +{ + grub_ieee1275_release (start, size); +} diff --git a/grub-core/lib/ieee1275/tcg2.c b/grub-core/lib/ieee1275/tcg2.c new file mode 100644 index 000000000..40161c2f9 --- /dev/null +++ b/grub-core/lib/ieee1275/tcg2.c @@ -0,0 +1,157 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2024 IBM Corporation + * Copyright (C) 2024 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 . + */ + +#include +#include +#include +#include +#include + +#include + +grub_ieee1275_ihandle_t grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; + +grub_err_t +grub_ieee1275_tpm_init (void) +{ + static bool init_tried = false; + grub_ieee1275_phandle_t vtpm; + char buffer[20]; + + if (init_tried == false) + { + init_tried = true; + + if (grub_ieee1275_open ("/vdevice/vtpm", &grub_ieee1275_tpm_ihandle) < 0 || + grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) || + grub_ieee1275_get_property (vtpm, "compatible", buffer, sizeof (buffer), NULL) || + grub_strcmp (buffer, "IBM,vtpm20")) + { + if (grub_ieee1275_tpm_ihandle != GRUB_IEEE1275_IHANDLE_INVALID) + grub_ieee1275_close (grub_ieee1275_tpm_ihandle); + + grub_ieee1275_tpm_ihandle = GRUB_IEEE1275_IHANDLE_INVALID; + } + } + + if (grub_ieee1275_tpm_ihandle == GRUB_IEEE1275_IHANDLE_INVALID) + return GRUB_ERR_UNKNOWN_DEVICE; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_get_max_output_size (grub_size_t *size) +{ + struct tpm_get_maximum_cmd_size + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t size; + }; + struct tpm_get_maximum_cmd_size args; + static bool error_displayed = false; + grub_err_t err; + + err = grub_ieee1275_tpm_init (); + if (err != GRUB_ERR_NONE) + return err; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 2); + args.method = (grub_ieee1275_cell_t) "get-maximum-cmd-size"; + args.ihandle = grub_ieee1275_tpm_ihandle; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return GRUB_ERR_INVALID_COMMAND; + + /* + * args.catch_result is set if firmware does not support get-maximum-cmd-size. + * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure. + */ + if (args.catch_result) + { + if (error_displayed == false) + { + error_displayed = true; + return grub_error (GRUB_ERR_BAD_DEVICE, + "get-maximum-cmd-size failed: Firmware is likely too old"); + } + return GRUB_ERR_INVALID_COMMAND; + } + + *size = args.size; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_tcg2_submit_command (grub_size_t input_size, + grub_uint8_t *input, + grub_size_t output_size, + grub_uint8_t *output) +{ + struct tpm_pass_through_to_tpm + { + struct grub_ieee1275_common_hdr common; + grub_ieee1275_cell_t method; + grub_ieee1275_cell_t ihandle; + grub_ieee1275_cell_t buf_size; + grub_ieee1275_cell_t buf_addr; + grub_ieee1275_cell_t catch_result; + grub_ieee1275_cell_t resp_size; + }; + struct tpm_pass_through_to_tpm args; + static bool error_displayed = false; + grub_err_t err; + + if (input_size == 0 || input == NULL || + output_size == 0 || output == NULL) + return GRUB_ERR_BAD_ARGUMENT; + + err = grub_ieee1275_tpm_init (); + if (err != GRUB_ERR_NONE) + return err; + + INIT_IEEE1275_COMMON (&args.common, "call-method", 4, 2); + args.method = (grub_ieee1275_cell_t) "pass-through-to-tpm"; + args.ihandle = grub_ieee1275_tpm_ihandle; + args.buf_size = (grub_ieee1275_cell_t) input_size; + args.buf_addr = (grub_ieee1275_cell_t) input; + + if (IEEE1275_CALL_ENTRY_FN (&args) == -1) + return GRUB_ERR_INVALID_COMMAND; + + /* args.catch_result is set if firmware does not support pass-through-to-tpm. */ + if (args.catch_result) + { + if (error_displayed == false) + { + error_displayed = true; + return grub_error (GRUB_ERR_BAD_DEVICE, + "pass-through-to-tpm failed: Firmware is likely too old"); + } + return GRUB_ERR_INVALID_COMMAND; + } + + grub_memcpy (output, input, args.resp_size); + + return GRUB_ERR_NONE; +} diff --git a/grub-core/lib/json/jsmn.h b/grub-core/lib/json/jsmn.h new file mode 100644 index 000000000..3178dcc97 --- /dev/null +++ b/grub-core/lib/json/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c new file mode 100644 index 000000000..1eadd1ce9 --- /dev/null +++ b/grub-core/lib/json/json.c @@ -0,0 +1,382 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#include +#include + +#define JSMN_STATIC +#include "jsmn.h" +#include "json.h" + +GRUB_MOD_LICENSE ("GPLv3"); + +grub_err_t +grub_json_parse (grub_json_t **out, char *string, grub_size_t string_len) +{ + grub_json_t *json = NULL; + jsmn_parser parser; + grub_err_t ret = GRUB_ERR_NONE; + int jsmn_ret; + + if (!string) + return GRUB_ERR_BAD_ARGUMENT; + + json = grub_zalloc (sizeof (*json)); + if (!json) + return GRUB_ERR_OUT_OF_MEMORY; + json->string = string; + + /* + * Parse the string twice: first to determine how many tokens + * we need to allocate, second to fill allocated tokens. + */ + jsmn_init (&parser); + jsmn_ret = jsmn_parse (&parser, string, string_len, NULL, 0); + if (jsmn_ret <= 0) + { + ret = GRUB_ERR_BAD_ARGUMENT; + goto err; + } + + json->tokens = grub_calloc (jsmn_ret, sizeof (jsmntok_t)); + if (!json->tokens) + { + ret = GRUB_ERR_OUT_OF_MEMORY; + goto err; + } + + jsmn_init (&parser); + jsmn_ret = jsmn_parse (&parser, string, string_len, json->tokens, jsmn_ret); + if (jsmn_ret <= 0) + { + ret = GRUB_ERR_BAD_ARGUMENT; + goto err; + } + + *out = json; + + err: + if (ret) + grub_json_free (json); + + return ret; +} + +void +grub_json_free (grub_json_t *json) +{ + if (json) + { + grub_free (json->tokens); + grub_free (json); + } +} + +grub_err_t +grub_json_getsize (grub_size_t *out, const grub_json_t *json) +{ + int size; + + size = json->tokens[json->idx].size; + if (size < 0) + return GRUB_ERR_OUT_OF_RANGE; + + *out = (grub_size_t) size; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_gettype (grub_json_type_t *out, const grub_json_t *json) +{ + switch (json->tokens[json->idx].type) + { + case JSMN_OBJECT: + *out = GRUB_JSON_OBJECT; + break; + case JSMN_ARRAY: + *out = GRUB_JSON_ARRAY; + break; + case JSMN_STRING: + *out = GRUB_JSON_STRING; + break; + case JSMN_PRIMITIVE: + *out = GRUB_JSON_PRIMITIVE; + break; + default: + return GRUB_ERR_BAD_ARGUMENT; + } + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_getchild (grub_json_t *out, const grub_json_t *parent, grub_size_t n) +{ + grub_size_t offset = 1, size; + jsmntok_t *p; + + if (grub_json_getsize (&size, parent) || n >= size) + return GRUB_ERR_OUT_OF_RANGE; + + /* + * Skip the first n children. For each of the children, we need + * to skip their own potential children (e.g. if it's an + * array), as well. We thus add the children's size to n on + * each iteration. + */ + p = &parent->tokens[parent->idx]; + while (n--) + n += p[offset++].size; + + out->string = parent->string; + out->tokens = parent->tokens; + out->idx = parent->idx + offset; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_getvalue (grub_json_t *out, const grub_json_t *parent, const char *key) +{ + grub_json_type_t type; + grub_size_t i, size; + + if (grub_json_gettype (&type, parent) || type != GRUB_JSON_OBJECT) + return GRUB_ERR_BAD_ARGUMENT; + + if (grub_json_getsize (&size, parent)) + return GRUB_ERR_BAD_ARGUMENT; + + for (i = 0; i < size; i++) + { + grub_json_t child; + const char *s; + + if (grub_json_getchild (&child, parent, i) || + grub_json_getstring (&s, &child, NULL) || + grub_strcmp (s, key) != 0) + continue; + + return grub_json_getchild (out, &child, 0); + } + + return GRUB_ERR_FILE_NOT_FOUND; +} + +static grub_err_t +get_value (grub_json_type_t *out_type, const char **out_string, const grub_json_t *parent, const char *key) +{ + const grub_json_t *p = parent; + grub_json_t child; + grub_err_t ret; + jsmntok_t *tok; + + if (key) + { + ret = grub_json_getvalue (&child, parent, key); + if (ret) + return ret; + p = &child; + } + + tok = &p->tokens[p->idx]; + p->string[tok->end] = '\0'; + + *out_string = p->string + tok->start; + + return grub_json_gettype (out_type, p); +} + +grub_err_t +grub_json_getstring (const char **out, const grub_json_t *parent, const char *key) +{ + grub_json_type_t type; + const char *value; + grub_err_t ret; + + ret = get_value (&type, &value, parent, key); + if (ret) + return ret; + if (type != GRUB_JSON_STRING) + return GRUB_ERR_BAD_ARGUMENT; + + *out = value; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_getuint64 (grub_uint64_t *out, const grub_json_t *parent, const char *key) +{ + grub_json_type_t type; + const char *value; + const char *end; + grub_err_t ret; + + ret = get_value (&type, &value, parent, key); + if (ret) + return ret; + if (type != GRUB_JSON_STRING && type != GRUB_JSON_PRIMITIVE) + return GRUB_ERR_BAD_ARGUMENT; + + grub_errno = GRUB_ERR_NONE; + *out = grub_strtoul (value, &end, 10); + if (grub_errno != GRUB_ERR_NONE || *end) + return GRUB_ERR_BAD_NUMBER; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_getint64 (grub_int64_t *out, const grub_json_t *parent, const char *key) +{ + grub_json_type_t type; + const char *value; + const char *end; + grub_err_t ret; + + ret = get_value (&type, &value, parent, key); + if (ret) + return ret; + if (type != GRUB_JSON_STRING && type != GRUB_JSON_PRIMITIVE) + return GRUB_ERR_BAD_ARGUMENT; + + grub_errno = GRUB_ERR_NONE; + *out = grub_strtol (value, &end, 10); + if (grub_errno != GRUB_ERR_NONE || *end) + return GRUB_ERR_BAD_NUMBER; + + return GRUB_ERR_NONE; +} + +grub_err_t +grub_json_unescape (char **out, grub_size_t *outlen, const char *in, grub_size_t inlen) +{ + grub_err_t ret = GRUB_ERR_NONE; + grub_size_t inpos, resultpos; + char *result; + + if (out == NULL || outlen == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("output parameters are not set")); + + result = grub_calloc (1, inlen + 1); + if (result == NULL) + return GRUB_ERR_OUT_OF_MEMORY; + + for (inpos = resultpos = 0; inpos < inlen; inpos++) + { + if (in[inpos] == '\\') + { + inpos++; + if (inpos >= inlen) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("expected escaped character")); + goto err; + } + + switch (in[inpos]) + { + case '"': + result[resultpos++] = '"'; + break; + + case '/': + result[resultpos++] = '/'; + break; + + case '\\': + result[resultpos++] = '\\'; + break; + + case 'b': + result[resultpos++] = '\b'; + break; + + case 'f': + result[resultpos++] = '\f'; + break; + + case 'r': + result[resultpos++] = '\r'; + break; + + case 'n': + result[resultpos++] = '\n'; + break; + + case 't': + result[resultpos++] = '\t'; + break; + + case 'u': + { + char values[4] = {0}; + unsigned i; + + inpos++; + if (inpos + ARRAY_SIZE(values) > inlen) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unicode sequence too short")); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(values); i++) + { + char c = in[inpos++]; + + if (c >= '0' && c <= '9') + values[i] = c - '0'; + else if (c >= 'A' && c <= 'F') + values[i] = c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + values[i] = c - 'a' + 10; + else + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unicode sequence with invalid character '%c'"), c); + goto err; + } + } + + if (values[0] != 0 || values[1] != 0) + result[resultpos++] = values[0] << 4 | values[1]; + result[resultpos++] = values[2] << 4 | values[3]; + + /* Offset the increment that's coming in via the loop increment. */ + inpos--; + + break; + } + + default: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized escaped character '%c'"), in[inpos]); + goto err; + } + } + else + result[resultpos++] = in[inpos]; + } + + *out = result; + *outlen = resultpos; + + err: + if (ret != GRUB_ERR_NONE) + grub_free (result); + + return ret; +} diff --git a/grub-core/lib/json/json.h b/grub-core/lib/json/json.h new file mode 100644 index 000000000..626074c35 --- /dev/null +++ b/grub-core/lib/json/json.h @@ -0,0 +1,140 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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 . + */ + +#ifndef GRUB_JSON_JSON_H +#define GRUB_JSON_JSON_H 1 + +#include + +enum grub_json_type +{ + /* Unordered collection of key-value pairs. */ + GRUB_JSON_OBJECT, + /* Ordered list of zero or more values. */ + GRUB_JSON_ARRAY, + /* Zero or more Unicode characters. */ + GRUB_JSON_STRING, + /* Number, boolean or empty value. */ + GRUB_JSON_PRIMITIVE, + /* Invalid token. */ + GRUB_JSON_UNDEFINED, +}; +typedef enum grub_json_type grub_json_type_t; + +/* Forward-declaration to avoid including jsmn.h. */ +struct jsmntok; + +struct grub_json +{ + struct jsmntok *tokens; + char *string; + grub_size_t idx; +}; +typedef struct grub_json grub_json_t; + +/* + * Parse a JSON-encoded string. Note that the string passed to + * this function will get modified on subsequent calls to + * grub_json_get*(). Returns the root object of the parsed JSON + * object, which needs to be free'd via grub_json_free(). Callers + * must ensure that the string outlives the returned root object, + * and that child objects must not be used after the root object + * has been free'd. + */ +extern grub_err_t EXPORT_FUNC(grub_json_parse) (grub_json_t **out, + char *string, + grub_size_t string_len); + +/* + * Free the structure and its contents. The string passed to + * grub_json_parse() will not be free'd. + */ +extern void EXPORT_FUNC(grub_json_free) (grub_json_t *json); + +/* + * Get the child count of a valid grub_json_t instance. Children + * are present for arrays, objects (dicts) and keys of a dict. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getsize) (grub_size_t *out, + const grub_json_t *json); + +/* Get the type of a valid grub_json_t instance. */ +extern grub_err_t EXPORT_FUNC(grub_json_gettype) (grub_json_type_t *out, + const grub_json_t *json); + +/* + * Get n'th child of a valid object, array or key. Will return an + * error if no such child exists. The result does not need to be + * free'd. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getchild) (grub_json_t *out, + const grub_json_t *parent, + grub_size_t n); + +/* + * Get value of key from a valid grub_json_t instance. The result + * does not need to be free'd. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getvalue) (grub_json_t *out, + const grub_json_t *parent, + const char *key); + +/* + * Get the string representation of a valid grub_json_t instance. + * If a key is given and parent is a JSON object, this function + * will return the string value of a child mapping to the key. + * If no key is given, it will return the string value of the + * parent itself. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getstring) (const char **out, + const grub_json_t *parent, + const char *key); + +/* + * Get the uint64 representation of a valid grub_json_t instance. + * Returns an error if the value pointed to by `parent` cannot be + * converted to an uint64. See grub_json_getstring() for details + * on the key parameter. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getuint64) (grub_uint64_t *out, + const grub_json_t *parent, + const char *key); + +/* + * Get the int64 representation of a valid grub_json_t instance. + * Returns an error if the value pointed to by `parent` cannot be + * converted to an int64. See grub_json_getstring() for + * details on the key parameter. + */ +extern grub_err_t EXPORT_FUNC(grub_json_getint64) (grub_int64_t *out, + const grub_json_t *parent, + const char *key); + +/* + * Unescape escaped characters and Unicode sequences in the + * given JSON-encoded string. Returns a newly allocated string + * passed back via the `out` parameter that has a length of + * `*outlen`. + * + * See https://datatracker.ietf.org/doc/html/rfc8259#section-7 for more + * information on escaping in JSON. + */ +extern grub_err_t EXPORT_FUNC(grub_json_unescape) (char **out, grub_size_t *outlen, + const char *in, grub_size_t inlen); + +#endif diff --git a/grub-core/lib/legacy_parse.c b/grub-core/lib/legacy_parse.c new file mode 100644 index 000000000..fa0131a1e --- /dev/null +++ b/grub-core/lib/legacy_parse.c @@ -0,0 +1,875 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2010,2012 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + +struct legacy_command +{ + const char *name; + const char *map; + const char *suffix; + unsigned suffixarg; + unsigned argc; + enum arg_type { + TYPE_VERBATIM, + TYPE_FORCE_OPTION, + TYPE_NOAPM_OPTION, + TYPE_TYPE_OR_NOMEM_OPTION, + TYPE_OPTION, + TYPE_FILE, + TYPE_FILE_NO_CONSUME, + TYPE_PARTITION, + TYPE_BOOL, + TYPE_INT, + TYPE_REST_VERBATIM, + TYPE_VBE_MODE, + TYPE_WITH_CONFIGFILE_OPTION + } argt[4]; + enum { + FLAG_IGNORE_REST = 0x001, + FLAG_FALLBACK_AVAILABLE = 0x004, + FLAG_FALLBACK = 0x008, + FLAG_COLOR_INVERT = 0x010, + FLAG_NO_MENUENTRY = 0x020, + FLAG_MENUENTRY_ONLY = 0x040, + FLAG_TERMINAL = 0x080, + FLAG_TITLE = 0x100, + } flags; + const char *shortdesc; + const char *longdesc; +}; + +/* Help texts are kept here mostly for reference. They are never shown. So + no need to gettextize. + */ +static struct legacy_command legacy_commands[] = + { + /* FIXME: background unsupported. */ + {"blocklist", "blocklist '%s'\n", NULL, 0, 1, {TYPE_FILE}, 0, "FILE", + "Print the blocklist notation of the file FILE."}, + {"boot", "boot\n", NULL, 0, 0, {}, 0, 0, + "Boot the OS/chain-loader which has been loaded."}, + {"bootp", "net_bootp; net_ls_addr; echo $\"" N_("Default server is ${net_default_server}") "\"; if [ x%s = x--with-configfile ]; then " + "if net_get_dhcp_option configfile_name pxe 150 string; then " + "configfile $configfile_name; fi; fi\n", NULL, 0, 1, + {TYPE_WITH_CONFIGFILE_OPTION}, FLAG_IGNORE_REST, "[--with-configfile]", + "Initialize a network device via BOOTP. If the option `--with-configfile'" + " is given, try to load a configuration file specified by the 150 vendor" + " tag."}, + /* FIXME: border unsupported. */ + {"cat", "cat '%s'\n", NULL, 0, 1, {TYPE_FILE}, 0, "FILE", + "Print the contents of the file FILE."}, + {"chainloader", "chainloader %s '%s'\n", NULL, 0, + 2, {TYPE_FORCE_OPTION, TYPE_FILE}, 0, "[--force] FILE", + "Load the chain-loader FILE. If --force is specified, then load it" + " forcibly, whether the boot loader signature is present or not."}, + {"clear", "clear\n", NULL, 0, 0, {}, 0, 0, + "Clear the screen."}, + {"cmp", "cmp '%s' '%s'\n", NULL, 0, + 2, {TYPE_FILE, TYPE_FILE}, FLAG_IGNORE_REST, "FILE1 FILE2", + "Compare the file FILE1 with the FILE2 and inform the different values" + " if any."}, + {"color", "set color_normal='%s'; set color_highlight='%s'\n", NULL, 0, + 2, {TYPE_VERBATIM, TYPE_VERBATIM}, + FLAG_IGNORE_REST | FLAG_FALLBACK_AVAILABLE, "NORMAL [HIGHLIGHT]", + "Change the menu colors. The color NORMAL is used for most" + " lines in the menu, and the color HIGHLIGHT is used to highlight the" + " line where the cursor points. If you omit HIGHLIGHT, then the" + " inverted color of NORMAL is used for the highlighted line." + " The format of a color is \"FG/BG\". FG and BG are symbolic color names." + " A symbolic color name must be one of these: black, blue, green," + " cyan, red, magenta, brown, light-gray, dark-gray, light-blue," + " light-green, light-cyan, light-red, light-magenta, yellow and white." + " But only the first eight names can be used for BG. You can prefix" + " \"blink-\" to FG if you want a blinking foreground color."}, + {"color", "set color_normal='%s'; set color_highlight='%s'\n", NULL, 0, + 1, {TYPE_VERBATIM}, + FLAG_IGNORE_REST | FLAG_FALLBACK | FLAG_COLOR_INVERT, NULL, NULL}, + {"configfile", "legacy_configfile '%s'\n", NULL, 0, 1, {TYPE_FILE}, + 0, "FILE", "Load FILE as the configuration file."}, + {"debug", + "if [ -z \"$debug\" ]; then set debug=all; else set debug=; fi\n", NULL, 0, + 0, {}, 0, 0, "Turn on/off the debug mode."}, + {"default", + "set default='%s'; if [ x\"$default\" = xsaved ]; then load_env; " + "set default=\"$saved_entry\"; fi\n", NULL, 0, 1, {TYPE_VERBATIM}, 0, + "[NUM | `saved']", + "Set the default entry to entry number NUM (if not specified, it is" + " 0, the first entry) or the entry number saved by savedefault."}, + {"dhcp", "net_bootp; net_ls_addr; if [ x%s = x--with-configfile ]; then " + "if net_get_dhcp_option configfile_name pxe 150 string; then " + "configfile $configfile_name; fi; fi\n", NULL, 0, 1, + {TYPE_WITH_CONFIGFILE_OPTION}, FLAG_IGNORE_REST, "[--with-configfile]", + "Initialize a network device via BOOTP. If the option `--with-configfile'" + " is given, try to load a configuration file specified by the 150 vendor" + " tag."}, + {"displayapm", "lsapm\n", NULL, 0, 0, {}, 0, 0, + "Display APM BIOS information."}, + {"displaymem", "lsmmap\n", NULL, 0, 0, {}, 0, 0, + "Display what GRUB thinks the system address space map of the" + " machine is, including all regions of physical RAM installed."}, + /* FIXME: device and efimap unsupported. */ + /* NOTE: embed unsupported. */ + {"fallback", "set fallback='%s'\n", NULL, 0, + 1, {TYPE_VERBATIM}, 0, "NUM...", + "Go into unattended boot mode: if the default boot entry has any" + " errors, instead of waiting for the user to do anything, it" + " immediately starts over using the NUM entry (same numbering as the" + " `default' command). This obviously won't help if the machine" + " was rebooted by a kernel that GRUB loaded."}, + {"find", "search -f '%s'\n", NULL, 0, 1, {TYPE_FILE}, 0, "FILENAME", + "Search for the filename FILENAME in all of partitions and print the list of" + " the devices which contain the file."}, + /* FIXME: findiso unsupported. */ + /* FIXME: foreground unsupported. */ + /* FIXME: fstest unsupported. */ + /* NOTE: The obsolete C/H/S geometry isn't shown anymore. */ + {"geometry", "insmod regexp; ls -l (%s*)\n", NULL, 0, 1, {TYPE_VERBATIM}, 0, "DRIVE", + "Print the information for a drive DRIVE. "}, + {"halt", "halt %s\n", NULL, 0, 1, {TYPE_NOAPM_OPTION}, 0, "[--no-apm]", + "Halt your system. If APM is available on it, turn off the power using" + " the APM BIOS, unless you specify the option `--no-apm'."}, + /* FIXME: help unsupported. */ /* NUL_TERMINATE */ + {"hiddenmenu", NULL, + "if sleep -i $timeout; then timeout=0; else timeout=-1; fi\n", 0, + 0, {}, 0, "", "Hide the menu."}, + {"hide", "parttool '%s' hidden+\n", NULL, 0, 1, {TYPE_PARTITION}, + 0, "PARTITION", + "Hide PARTITION by setting the \"hidden\" bit in" + " its partition type code."}, + /* FIXME: ifconfig unsupported. */ + /* FIXME: impsprobe unsupported. */ + {"initrd", "legacy_initrd '%s' %s\n", NULL, 0, 2, {TYPE_FILE_NO_CONSUME, + TYPE_REST_VERBATIM}, 0, + "FILE [ARG ...]", + "Load an initial ramdisk FILE for a Linux format boot image and set the" + " appropriate parameters in the Linux setup area in memory."}, + /* NOTE: install unsupported. */ + /* FIXME: ioprobe unsupported. */ + /* FIXME: really support --no-mem-option. */ + {"kernel", "legacy_kernel %s %s '%s' %s\n", NULL, 0, + 4, {TYPE_TYPE_OR_NOMEM_OPTION, TYPE_TYPE_OR_NOMEM_OPTION, + TYPE_FILE_NO_CONSUME, TYPE_REST_VERBATIM}, 0, + "[--no-mem-option] [--type=TYPE] FILE [ARG ...]", + "Attempt to load the primary boot image from FILE. The rest of the" + " line is passed verbatim as the \"kernel command line\". Any modules" + " must be reloaded after using this command. The option --type is used" + " to suggest what type of kernel to be loaded. TYPE must be either of" + " \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and" + " \"multiboot\". The option --no-mem-option tells GRUB not to pass a" + " Linux's mem option automatically."}, + {"lock", "if ! authenticate legacy; then return; fi", NULL, 0, 0, {}, 0, + 0, "Break a command execution unless the user is authenticated."}, + {"makeactive", "parttool \"$root\" boot+\n", NULL, 0, 0, {}, 0, 0, + "Set the active partition on the root disk to GRUB's root device." + " This command is limited to _primary_ PC partitions on a hard disk."}, + {"map", "drivemap '%s' '%s'\n", NULL, 0, + 2, {TYPE_PARTITION, TYPE_PARTITION}, + FLAG_IGNORE_REST, "TO_DRIVE FROM_DRIVE", + "Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary" + " when you chain-load some operating systems, such as DOS, if such an" + " OS resides at a non-first drive."}, + /* NOTE: md5crypt unsupported since GRUB has not enough entropy and this + hash shouldn't be used anymore. */ + {"module", "legacy_initrd '%s' %s\n", NULL, 0, 2, {TYPE_FILE_NO_CONSUME, + TYPE_REST_VERBATIM}, 0, + "FILE [ARG ...]", + "Load a boot module FILE for a Multiboot format boot image (no" + " interpretation of the file contents is made, so users of this" + " command must know what the kernel in question expects). The" + " rest of the line is passed as the \"module command line\", like" + " the `kernel' command."}, + {"modulenounzip", "legacy_initrd_nounzip '%s' %s\n", NULL, 0, 2, + {TYPE_FILE_NO_CONSUME, TYPE_REST_VERBATIM}, 0, + "FILE [ARG ...]", + "The same as `module', except that automatic decompression is" + " disabled."}, + {"pager", "set pager=%s; if [ \"$pager\" = 0 ]; then " + " echo Internal pager is now off; else " + "echo Internal pager is now on; fi\n", NULL, 0, + 1, {TYPE_BOOL}, FLAG_FALLBACK_AVAILABLE, "[FLAG]", + "Toggle pager mode with no argument. If FLAG is given and its value" + " is `on', turn on the mode. If FLAG is `off', turn off the mode."}, + {"pager", + "if [ \"$pager\" = 1 ]; then pager=0; echo Internal pager is now off;" + "else pager=1; echo Internal pager is now on; fi\n", NULL, 0, 0, {}, + FLAG_FALLBACK, NULL, NULL}, + /* FIXME: partnew unsupported. */ + {"parttype", "parttool '%s' type=%s\n", NULL, 0, + 2, {TYPE_PARTITION, TYPE_INT}, 0, + "PART TYPE", "Change the type of the partition PART to TYPE."}, + {"password", "if [ \"$superusers\" = "" ]; then superusers=legacy; fi;\n" + "legacy_password %s '%s'\n", + "menuentry \"Superuser menu\" --users \"legacy\" { configfile '%s'; }\n", + 2, 3, {TYPE_OPTION, TYPE_VERBATIM, TYPE_FILE}, + FLAG_IGNORE_REST | FLAG_FALLBACK_AVAILABLE | FLAG_NO_MENUENTRY, + "[--md5] PASSWD [FILE]", + "If used in the first section of a menu file, disable all" + " interactive editing control (menu entry editor and" + " command line). If the password PASSWD is entered, it loads the" + " FILE as a new config file and restarts the GRUB Stage 2. If you" + " omit the argument FILE, then GRUB just unlocks privileged" + " instructions. You can also use it in the script section, in" + " which case it will ask for the password, before continuing." + " The option --md5 tells GRUB that PASSWD is encrypted with" + " md5crypt."}, + {"password", "if [ \"$superusers\" = "" ]; then superusers=legacy; fi;\n" + "legacy_password %s '%s'\n", NULL, 0, 2, {TYPE_OPTION, TYPE_VERBATIM}, + FLAG_IGNORE_REST | FLAG_FALLBACK | FLAG_NO_MENUENTRY, NULL, NULL}, + {"password", "if legacy_check_password %s '%s'; then configfile '%s'; " + "else return; fi\n", NULL, 2, 3, {TYPE_OPTION, TYPE_VERBATIM, TYPE_FILE}, + FLAG_IGNORE_REST | FLAG_FALLBACK_AVAILABLE | FLAG_MENUENTRY_ONLY, + NULL, NULL}, + {"password", "if ! legacy_check_password %s '%s'; then return fi;\n", + NULL, 0, 2, {TYPE_OPTION, TYPE_VERBATIM}, + FLAG_IGNORE_REST | FLAG_FALLBACK | FLAG_MENUENTRY_ONLY, NULL, NULL}, + /* NOTE: GRUB2 has a design principle of not eternally waiting for user + input. 60 seconds should be enough. + */ + {"pause", "echo %s; if ! sleep -i 60; then return; fi\n", NULL, 0, 1, + {TYPE_REST_VERBATIM}, 0, + "[MESSAGE ...]", "Print MESSAGE, then wait until a key is pressed."}, + {"print", "echo %s\n", NULL, 0, 1, + {TYPE_REST_VERBATIM}, 0, + "[MESSAGE ...]", "Print MESSAGE."}, + /* FIXME: quit unsupported. */ + /* FIXME: rarp unsupported. */ + {"read", "read_dword %s\n", NULL, 0, 1, {TYPE_INT}, 0, "ADDR", + "Read a 32-bit value from memory at address ADDR and" + " display it in hex format."}, + {"reboot", "reboot\n", NULL, 0, 0, {}, 0, 0, "Reboot your system."}, + {"root", "set root='%s'; set legacy_hdbias='%s'\n", NULL, 0, + 2, {TYPE_PARTITION, TYPE_INT}, FLAG_FALLBACK_AVAILABLE, + "[DEVICE [HDBIAS]]", + "Set the current \"root device\" to the device DEVICE, then" + " attempt to mount it to get the partition size (for passing the" + " partition descriptor in `ES:ESI', used by some chain-loaded" + " bootloaders), the BSD drive-type (for booting BSD kernels using" + " their native boot format), and correctly determine " + " the PC partition where a BSD sub-partition is located. The" + " optional HDBIAS parameter is a number to tell a BSD kernel" + " how many BIOS drive numbers are on controllers before the current" + " one. For example, if there is an IDE disk and a SCSI disk, and your" + " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."}, + {"root", "echo \"$root\"\n", NULL, 0, 0, {}, FLAG_FALLBACK, NULL, NULL}, + {"rootnoverify", "set root='%s'; set legacy_hdbias='%s'\n", NULL, 0, + 2, {TYPE_PARTITION, TYPE_INT}, 0, + "[DEVICE [HDBIAS]]", + "Similar to `root', but don't attempt to mount the partition. This" + " is useful for when an OS is outside of the area of the disk that" + " GRUB can read, but setting the correct root device is still" + " desired. Note that the items mentioned in `root' which" + " derived from attempting the mount will NOT work correctly."}, + {"rootnoverify", "echo \"$root\"\n", NULL, 0, + 0, {}, FLAG_FALLBACK, NULL, NULL}, + /* FIXME: support saving NUM and fallback. */ + {"savedefault", "saved_entry=${chosen}; save_env saved_entry\n", NULL, 0, + 0, {}, 0, "[NUM | `fallback']", + "Save the current entry as the default boot entry if no argument is" + " specified. If a number is specified, this number is saved. If" + " `fallback' is used, next fallback entry is saved."}, + {"serial", "serial %s\n", NULL, 0, 1, {TYPE_REST_VERBATIM}, 0, + "[--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] " + "[--parity=PARITY] [--stop=STOP] [--device=DEV]", + "Initialize a serial device. UNIT is a digit that specifies which serial" + " device is used (e.g. 0 == COM1). If you need to specify the port number," + " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length," + " PARITY is the type of parity, which is one of `no', `odd' and `even'." + " STOP is the length of stop bit(s). The option --device can be used only" + " in the grub shell, which specifies the file name of a tty device. The" + " default values are COM1, 9600, 8N1."}, + /* FIXME: shade unsupported. */ + /* FIXME: silent unsupported. */ + /* FIXME: splashimage unsupported. */ + /* FIXME: setkey unsupported. */ /* NUL_TERMINATE */ + /* NOTE: setup unsupported. */ + /* FIXME: --no-echo, --no-edit unsupported. */ + /* NOTE: both terminals are activated so --silent and --timeout + are useless. */ + /* FIXME: graphics unsupported. */ + {"terminal", NULL, NULL, 0, 0, {}, FLAG_TERMINAL | FLAG_IGNORE_REST, + "[--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] " + "[--silent] [console] [serial] [hercules] [graphics]", + "Select a terminal. When multiple terminals are specified, wait until" + " you push any key to continue. If both console and serial are specified," + " the terminal to which you input a key first will be selected. If no" + " argument is specified, print current setting. The option --dumb" + " specifies that your terminal is dumb, otherwise, vt100-compatibility" + " is assumed. If you specify --no-echo, input characters won't be echoed." + " If you specify --no-edit, the BASH-like editing feature will be disabled." + " If --timeout is present, this command will wait at most for SECS" + " seconds. The option --lines specifies the maximum number of lines." + " The option --silent is used to suppress messages."}, + /* FIXME: terminfo unsupported. */ /* NUL_TERMINATE */ + {"testload", "testload '%s'\n", NULL, 0, 1, {TYPE_FILE}, 0, "FILE", + "Read the entire contents of FILE in several different ways and" + " compares them, to test the filesystem code. " + " If this test succeeds, then a good next" + " step is to try loading a kernel."}, + {"testvbe", "insmod vbe; videotest '%s'\n", NULL, 0, 1, {TYPE_VBE_MODE}, 0, + "MODE", "Test the VBE mode MODE. Hit any key to return."}, + /* FIXME: tftpserver unsupported. */ + {"timeout", "set timeout=%s\n", NULL, 0, 1, {TYPE_INT}, 0, "SEC", + "Set a timeout, in SEC seconds, before automatically booting the" + " default entry (normally the first entry defined)."}, + {"title", NULL, NULL, 0, 0, {}, FLAG_TITLE, "NAME ...", + "Start a new boot entry, and set its name to the contents of the" + " rest of the line, starting with the first non-space character."}, + {"unhide", "parttool '%s' hidden-\n", NULL, 0, + 1, {TYPE_PARTITION}, 0, "PARTITION", + "Unhide PARTITION by clearing the \"hidden\" bit in its" + " partition type code."}, + /* FIXME: uppermem unsupported. */ + {"uuid", "search --set=root --fs-uuid '%s'\n", NULL, 0, 1, {TYPE_VERBATIM}, + 0, "UUID", "Find root by UUID"}, + {"vbeprobe", "insmod vbe; videoinfo '%s'\n", NULL, 0, 1, {TYPE_VBE_MODE}, + FLAG_FALLBACK_AVAILABLE, "[MODE]", + "Probe VBE information. If the mode number MODE is specified, show only" + " the information about only the mode."}, + {"vbeprobe", "insmod vbe; videoinfo\n", NULL, 0, 0, {}, + FLAG_FALLBACK, NULL, NULL} + /* FIXME: verbose unsupported. */ + /* FIXME: version unsupported. */ + /* FIXME: viewport unsupported. */ + }; + +char * +grub_legacy_escape (const char *in, grub_size_t len) +{ + char *ptr; + char *ret; + char saved; + int overhead = 0; + + for (ptr = (char*)in; ptr < in + len && *ptr; ptr++) + if (*ptr == '\'') + overhead += 3; + ret = grub_malloc (ptr - in + overhead + 1); + if (!ret) + return NULL; + + ptr = (char*)in; + saved = ptr[len]; + ptr[len] = '\0'; + grub_strchrsub (ret, ptr, '\'', "'\\''"); + ptr[len] = saved; + return ret; +} + +static char * +adjust_file (const char *in, grub_size_t len) +{ + const char *comma, *ptr, *rest; + char *ret, *outptr; + int overhead = 0; + int part = -1, subpart = -1; + if (in[0] != '(') + return grub_legacy_escape (in, len); + for (ptr = in + 1; ptr < in + len && *ptr && *ptr != ')' + && *ptr != ','; ptr++) + if (*ptr == '\'' || *ptr == '\\') + overhead++; + comma = ptr; + if (*comma == ')' && comma - in == 3 + && in[1] == 'n' && in[2] == 'd') + { + rest = comma + 1; + for (ptr = rest; ptr < in + len && *ptr; ptr++) + if (*ptr == '\'' || *ptr == '\\') + overhead++; + + ret = grub_malloc (ptr - in + overhead + 15); + if (!ret) + return NULL; + + outptr = grub_stpcpy (ret, "(tftp)");; + for (ptr = rest; ptr < in + len; ptr++) + { + if (*ptr == '\'' || *ptr == '\\') + *outptr++ = '\\'; + + *outptr++ = *ptr; + } + *outptr = 0; + return ret; + } + if (*comma != ',') + return grub_legacy_escape (in, len); + part = grub_strtoull (comma + 1, &rest, 0); + if (rest[0] == ',' && rest[1] >= 'a' && rest[1] <= 'z') + { + subpart = rest[1] - 'a'; + rest += 2; + } + for (ptr = rest; ptr < in + len && *ptr; ptr++) + if (*ptr == '\'' || *ptr == '\\') + overhead++; + + /* 35 is enough for any 2 numbers. */ + ret = grub_malloc (ptr - in + overhead + 35 + 5); + if (!ret) + return NULL; + + outptr = ret; + for (ptr = in; ptr < in + len && ptr <= comma; ptr++) + { + if (*ptr == '\'' || *ptr == '\\') + *outptr++ = '\\'; + + *outptr++ = *ptr; + } + if (subpart != -1) + grub_snprintf (outptr, 35, "%d,%d", part + 1, subpart + 1); + else + grub_snprintf (outptr, 35, "%d", part + 1); + while (*outptr) + outptr++; + for (ptr = rest; ptr < in + len; ptr++) + { + if (*ptr == '\'' || *ptr == '\\') + *outptr++ = '\\'; + + *outptr++ = *ptr; + } + *outptr = 0; + return ret; +} + +static int +check_option (const char *a, const char *b, grub_size_t len) +{ + if (grub_strlen (b) != len) + return 0; + return grub_strncmp (a, b, len) == 0; +} + +static int +is_option (enum arg_type opt, const char *curarg, grub_size_t len) +{ + switch (opt) + { + case TYPE_WITH_CONFIGFILE_OPTION: + return check_option (curarg, "--with-configfile", len); + case TYPE_NOAPM_OPTION: + return check_option (curarg, "--no-apm", len); + case TYPE_FORCE_OPTION: + return check_option (curarg, "--force", len); + case TYPE_TYPE_OR_NOMEM_OPTION: + return check_option (curarg, "--type=netbsd", len) + || check_option (curarg, "--type=freebsd", len) + || check_option (curarg, "--type=openbsd", len) + || check_option (curarg, "--type=linux", len) + || check_option (curarg, "--type=biglinux", len) + || check_option (curarg, "--type=multiboot", len) + || check_option (curarg, "--no-mem-option", len); + case TYPE_OPTION: + return (len >= 2 && curarg[0] == '-' && curarg[1] == '-'); + default: + return 0; + } +} + +char * +grub_legacy_parse (const char *buf, char **entryname, char **suffix) +{ + const char *ptr; + const char *cmdname; + unsigned i, cmdnum; + char *args[ARRAY_SIZE (legacy_commands[0].argt)]; + + *suffix = NULL; + + for (ptr = buf; *ptr && grub_isspace (*ptr); ptr++); + if (!*ptr || *ptr == '#') + { + char *ret; + int len = grub_strlen (buf); + ret = grub_malloc (len + 2); + grub_memcpy (ret, buf, len); + if (len && ret[len - 1] == '\n') + ret[len] = 0; + else + { + ret[len] = '\n'; + ret[len + 1] = 0; + } + return ret; + } + + cmdname = ptr; + for (ptr = buf; *ptr && !grub_isspace (*ptr) && *ptr != '='; ptr++); + + for (cmdnum = 0; cmdnum < ARRAY_SIZE (legacy_commands); cmdnum++) + if (grub_strncmp (legacy_commands[cmdnum].name, cmdname, ptr - cmdname) == 0 + && legacy_commands[cmdnum].name[ptr - cmdname] == 0 + && (!(*entryname != NULL && (legacy_commands[cmdnum].flags + & FLAG_NO_MENUENTRY))) + && (!(*entryname == NULL && (legacy_commands[cmdnum].flags + & FLAG_MENUENTRY_ONLY)))) + break; + if (cmdnum == ARRAY_SIZE (legacy_commands)) + return grub_xasprintf ("# Unsupported legacy command: %s\n", buf); + + for (; grub_isspace (*ptr) || *ptr == '='; ptr++); + + if (legacy_commands[cmdnum].flags & FLAG_TITLE) + { + const char *ptr2; + ptr2 = ptr + grub_strlen (ptr); + while (ptr2 > ptr && grub_isspace (*(ptr2 - 1))) + ptr2--; + *entryname = grub_strndup (ptr, ptr2 - ptr); + return NULL; + } + + if (legacy_commands[cmdnum].flags & FLAG_TERMINAL) + { + int dumb = 0, lines = 24; +#ifdef TODO + int no_echo = 0, no_edit = 0; +#endif + int hercules = 0; + int console = 0, serial = 0, graphics = 0; + /* Big enough for any possible resulting command. */ + char outbuf[512] = ""; + char *outptr; + while (*ptr) + { + /* "[--timeout=SECS] [--silent]" + " [console] [serial] [hercules]"*/ + if (grub_memcmp (ptr, "--dumb", sizeof ("--dumb") - 1) == 0) + dumb = 1; +#ifdef TODO + if (grub_memcmp (ptr, "--no-echo", sizeof ("--no-echo") - 1) == 0) + no_echo = 1; + + if (grub_memcmp (ptr, "--no-edit", sizeof ("--no-edit") - 1) == 0) + no_edit = 1; +#endif + if (grub_memcmp (ptr, "--lines=", sizeof ("--lines=") - 1) == 0) + { + lines = grub_strtoul (ptr + sizeof ("--lines=") - 1, 0, 0); + if (grub_errno) + { + lines = 24; + grub_errno = GRUB_ERR_NONE; + } + } + + if (grub_memcmp (ptr, "console", sizeof ("console") - 1) == 0) + console = 1; + + if (grub_memcmp (ptr, "serial", sizeof ("serial") - 1) == 0) + serial = 1; + if (grub_memcmp (ptr, "hercules", sizeof ("hercules") - 1) == 0) + hercules = 1; + if (grub_memcmp (ptr, "graphics", sizeof ("graphics") - 1) == 0) + graphics = 1; + while (*ptr && !grub_isspace (*ptr)) + ptr++; + while (*ptr && grub_isspace (*ptr)) + ptr++; + } + + if (!console && !serial && !hercules && !graphics) + return grub_strdup ("terminal_input; terminal_output; terminfo\n"); + + outptr = outbuf; + + if (graphics) + outptr = grub_stpcpy (outptr, "insmod all_video; "); + + outptr = grub_stpcpy (outptr, "terminal_input "); + if (serial) + outptr = grub_stpcpy (outptr, "serial "); + if (console || hercules || graphics) + outptr = grub_stpcpy (outptr, "console "); + outptr = grub_stpcpy (outptr, "; terminal_output "); + if (serial) + outptr = grub_stpcpy (outptr, "serial "); + if (console) + outptr = grub_stpcpy (outptr, "console "); + if (hercules) + outptr = grub_stpcpy (outptr, "mda_text "); + if (graphics) + outptr = grub_stpcpy (outptr, "gfxterm "); + outptr = grub_stpcpy (outptr, "; "); + *outptr = '\0'; + if (serial) + { + grub_snprintf (outptr, outbuf + sizeof (outbuf) - outptr, + "terminfo serial -g 80x%d %s; ", + lines, dumb ? "dumb" : "vt100"); + outptr += grub_strlen (outptr); + } + + grub_strcpy (outptr, "\n"); + + return grub_strdup (outbuf); + } + + grub_memset (args, 0, sizeof (args)); + + { + int hold_arg = 0; + const char *curarg = NULL; + for (i = 0; i < legacy_commands[cmdnum].argc; i++) + { + grub_size_t curarglen; + if (hold_arg) + { + ptr = curarg; + hold_arg = 0; + } + for (; grub_isspace (*ptr); ptr++); + curarg = ptr; + if (!*curarg) + break; + for (; *ptr && !grub_isspace (*ptr); ptr++); + if (i != legacy_commands[cmdnum].argc - 1 + || (legacy_commands[cmdnum].flags & FLAG_IGNORE_REST)) + curarglen = ptr - curarg; + else + { + curarglen = grub_strlen (curarg); + while (curarglen > 0 && grub_isspace (curarg[curarglen - 1])) + curarglen--; + } + if (*ptr) + ptr++; + switch (legacy_commands[cmdnum].argt[i]) + { + case TYPE_FILE_NO_CONSUME: + hold_arg = 1; + /* Fallthrough. */ + case TYPE_PARTITION: + case TYPE_FILE: + args[i] = adjust_file (curarg, curarglen); + break; + + case TYPE_REST_VERBATIM: + { + char *outptr, *outptr0; + int overhead = 3; + ptr = curarg; + while (*ptr) + { + for (; *ptr && grub_isspace (*ptr); ptr++); + for (; *ptr && !grub_isspace (*ptr); ptr++) + if (*ptr == '\'') + overhead += 3; + if (*ptr) + ptr++; + overhead += 3; + } + + outptr0 = args[i] = grub_malloc (overhead + (ptr - curarg)); + if (!outptr0) + return NULL; + ptr = curarg; + outptr = outptr0; + while (*ptr) + { + for (; *ptr && grub_isspace (*ptr); ptr++); + if (outptr != outptr0) + *outptr++ = ' '; + *outptr++ = '\''; + for (; *ptr && !grub_isspace (*ptr); ptr++) + { + if (*ptr == '\'') + { + *outptr++ = '\''; + *outptr++ = '\\'; + *outptr++ = '\''; + *outptr++ = '\''; + } + else + *outptr++ = *ptr; + } + *outptr++ = '\''; + if (*ptr) + ptr++; + } + *outptr++ = 0; + } + break; + + case TYPE_VERBATIM: + args[i] = grub_legacy_escape (curarg, curarglen); + break; + case TYPE_WITH_CONFIGFILE_OPTION: + case TYPE_FORCE_OPTION: + case TYPE_NOAPM_OPTION: + case TYPE_TYPE_OR_NOMEM_OPTION: + case TYPE_OPTION: + if (is_option (legacy_commands[cmdnum].argt[i], curarg, curarglen)) + { + args[i] = grub_strndup (curarg, curarglen); + break; + } + args[i] = grub_strdup (""); + hold_arg = 1; + break; + case TYPE_INT: + { + const char *brk; + int base = 10; + brk = curarg; + if (brk[0] == '0' && brk[1] == 'x') + { + base = 16; + brk += 2; + } + else if (brk[0] == '0') + base = 8; + for (; *brk && brk < curarg + curarglen; brk++) + { + if (base == 8 && (*brk == '8' || *brk == '9')) + break; + if (grub_isdigit (*brk)) + continue; + if (base != 16) + break; + if (!(*brk >= 'a' && *brk <= 'f') + && !(*brk >= 'A' && *brk <= 'F')) + break; + } + if (brk == curarg) + args[i] = grub_strdup ("0"); + else + args[i] = grub_strndup (curarg, brk - curarg); + } + break; + case TYPE_VBE_MODE: + { + unsigned mod; + struct grub_vesa_mode_table_entry *modedesc; + + mod = grub_strtoul (curarg, 0, 0); + if (grub_errno) + { + mod = 0; + grub_errno = GRUB_ERR_NONE; + } + if (mod < GRUB_VESA_MODE_TABLE_START + || mod > GRUB_VESA_MODE_TABLE_END) + { + args[i] = grub_strdup ("auto"); + break; + } + modedesc = &grub_vesa_mode_table[mod - GRUB_VESA_MODE_TABLE_START]; + if (!modedesc->width) + { + args[i] = grub_strdup ("auto"); + break; + } + args[i] = grub_xasprintf ("%ux%ux%u", + modedesc->width, modedesc->height, + modedesc->depth); + break; + } + case TYPE_BOOL: + if (curarglen == 2 && curarg[0] == 'o' && curarg[1] == 'n') + args[i] = grub_strdup ("1"); + else + args[i] = grub_strdup ("0"); + break; + } + } + } + + while (legacy_commands[cmdnum].argc > 0 + && args[legacy_commands[cmdnum].argc - 1] == NULL + && (legacy_commands[cmdnum].flags & FLAG_FALLBACK_AVAILABLE) + && args[legacy_commands[cmdnum + 1].argc] == NULL) + cmdnum++; + + for (; i < legacy_commands[cmdnum].argc; i++) + switch (legacy_commands[cmdnum].argt[i]) + { + case TYPE_FILE_NO_CONSUME: + case TYPE_PARTITION: + case TYPE_FILE: + case TYPE_REST_VERBATIM: + case TYPE_VERBATIM: + case TYPE_WITH_CONFIGFILE_OPTION: + case TYPE_FORCE_OPTION: + case TYPE_NOAPM_OPTION: + case TYPE_TYPE_OR_NOMEM_OPTION: + case TYPE_OPTION: + args[i] = grub_strdup (""); + break; + case TYPE_BOOL: + case TYPE_INT: + args[i] = grub_strdup ("0"); + break; + case TYPE_VBE_MODE: + args[i] = grub_strdup ("auto"); + break; + } + + if (legacy_commands[cmdnum].flags & FLAG_COLOR_INVERT) + { + char *corig = args[legacy_commands[cmdnum].argc - 1]; + char *slash = grub_strchr (corig, '/'); + char *invert; + grub_size_t len; + + len = grub_strlen (corig); + if (!slash) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid color specification `%s'"), + args[0]); + return NULL; + } + invert = grub_malloc (len + 1); + if (!invert) + return NULL; + grub_memcpy (invert, slash + 1, len - (slash - corig) - 1); + invert[len - (slash - args[0]) - 1] = '/'; + grub_memcpy (invert + len - (slash - corig), corig, slash - corig); + invert[len] = 0; + args[legacy_commands[cmdnum].argc] = invert; + } + + if (legacy_commands[cmdnum].suffix) + { + *suffix = grub_xasprintf (legacy_commands[cmdnum].suffix, + args[legacy_commands[cmdnum].suffixarg]); + if (*suffix) + return NULL; + } + + { + char *ret = grub_xasprintf (legacy_commands[cmdnum].map, args[0], args[1], + args[2], args[3]); + grub_free (args[0]); + grub_free (args[1]); + grub_free (args[2]); + grub_free (args[3]); + return ret; + } +} diff --git a/lib/libgcrypt/cipher/ChangeLog b/grub-core/lib/libgcrypt/cipher/ChangeLog similarity index 97% rename from lib/libgcrypt/cipher/ChangeLog rename to grub-core/lib/libgcrypt/cipher/ChangeLog index 8924f17e1..1b3694f58 100644 --- a/lib/libgcrypt/cipher/ChangeLog +++ b/grub-core/lib/libgcrypt/cipher/ChangeLog @@ -1,3 +1,93 @@ +2010-08-19 Werner Koch + + * cipher.c (gcry_cipher_open): Remove double release of the module. + Fixes bug#1263. + +2010-06-10 Jeff Johnson (wk) + + * ecc.c (ecc_generate_ext): Parse transient-key flag. + (generate_key): Add arg TRANSIENT_KEY and use it to set the random + level. + +2010-04-12 Brad Hards (wk) + + Spelling fixes. + +2010-03-26 Werner Koch + + * tiger.c (asn): Unfetter the old TIGER from an OID. + (TIGER_CONTEXT): Add field VARIANT. + (tiger_init): Factor code out to ... + (do_init): New. + (tiger1_init, tiger2_init): New. + (_gcry_digest_spec_tiger1, _gcry_digest_spec_tiger2): New. + * md.c (digest_table): Add TIGER1 and TIGER2 variants. + +2009-12-11 Werner Koch + + * sha256.c (Cho, Maj, Sum0, Sum1): Turn macros into inline + functions. + (transform): Partly unroll to interweave the chain variables + + * sha512.c (ROTR, Ch, Maj, Sum0, Sum1): Turn macros into inline + functions. + (transform): Partly unroll to interweave the chain variables. + Suggested by Christian Grothoff. + +2009-12-10 Werner Koch + + * Makefile.am (o_flag_munging): New. + (tiger.o, tiger.lo): Use it. + + * cipher.c (do_ctr_encrypt): Add arg OUTBUFLEN. Check for + suitable value. Add check for valid inputlen. Wipe temporary + memory. + (do_ctr_decrypt): Likewise. + (do_cbc_encrypt, do_cbc_decrypt): Add arg OUTBUFLEN. Check for + suitable value. Move check for valid inputlen to here; change + returned error from INV_ARG to INV_LENGTH. + (do_ecb_encrypt, do_ecb_decrypt): Ditto. + (do_cfb_encrypt, do_cfb_decrypt): Ditto. + (do_ofb_encrypt, do_ofb_decrypt): Ditto. + (cipher_encrypt, cipher_encrypt): Adjust for above changes. + (gcry_cipher_encrypt, gcry_cipher_decrypt): Simplify. + +2009-12-09 Werner Koch + + * cipher.c (gcry_cipher_open): Allow for GCRY_CIPHER_MODE_AESWRAP. + (cipher_encrypt, cipher_decrypt): Ditto. + (do_aeswrap_encrypt, do_aeswrap_decrypt): New. + (struct gcry_cipher_handle): Add field marks. + (cipher_setkey, cipher_setiv): Update marks flags. + (cipher_reset): Reset marks. + (cipher_encrypt, cipher_decrypt): Add new arg OUTBUFLEN. + (gcry_cipher_encrypt, gcry_cipher_decrypt): Pass outbuflen to + cipher_encrypt. Replace GPG_ERR_TOO_SHORT by + GPG_ERR_BUFFER_TOO_SHORT. + +2009-08-21 Werner Koch + + * dsa.c (dsa_generate_ext): Release retfactors array before + setting it to NULL. Reported by Daiko Ueno. + +2009-07-02 Werner Koch + + * md.c (md_read): Fix incomplete check for NULL. + Reported by Fabian Kail. + +2009-03-31 Werner Koch + + * rsa.c (rsa_check_secret_key): Return GPG_ERR_BAD_SECKEY and not + GPG_ERR_PUBKEY_ALGO. + +2009-02-16 Werner Koch + + * rsa.c (generate_x931): Do not initialize TBL with automatic + variables. + * whirlpool.c, tiger.c, sha256.c, sha1.c, rmd160.c, md5.c + * md4.c, crc.c: Remove memory.h. This is garbage from gnupg. + Reported by Dan Fandrich. + 2009-01-22 Werner Koch * ecc.c (compute_keygrip): Remove superfluous const. @@ -3888,8 +3978,8 @@ Mon Feb 16 10:08:47 1998 Werner Koch (wk@isil.d.shuttle.de) (digest_algo_to_string): New. - Copyright 1998,1999,2000,2001,2002,2003,2004,2005,2006 - 2007, 2008, 2009 Free Software Foundation, Inc. + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 2007, 2008, 2009, 2010 Free Software Foundation, Inc. This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/grub-core/lib/libgcrypt/cipher/ChangeLog-2011 b/grub-core/lib/libgcrypt/cipher/ChangeLog-2011 new file mode 100644 index 000000000..05516c99e --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/ChangeLog-2011 @@ -0,0 +1,4247 @@ +2011-12-01 Werner Koch + + NB: ChangeLog files are no longer manually maintained. Starting + on December 1st, 2011 we put change information only in the GIT + commit log, and generate a top-level ChangeLog file from logs at + "make dist". See doc/HACKING for details. + +2011-06-29 Werner Koch + + * cipher.c (cipher_get_keylen): Return zero for an invalid algorithm. + (cipher_get_blocksize): Ditto. + +2011-06-13 Werner Koch + + * dsa.c (selftest_sign_1024): Use the raw and not the pkcs1 flag. + + * pubkey.c (gcry_pk_sign): Special case output generation for PKCS1. + (sexp_data_to_mpi): Parse "random-override" for pkcs1 encryption. + (pkcs1_encode_for_encryption): Add args RANDOM_OVERRIDE and + RANDOM_OVERRIDE_LEN. + (gcry_pk_encrypt): Special case output generation for PKCS1. + (sexp_data_to_mpi): Use GCRYMPI_FMT_USG for raw encoding. + +2011-06-10 Werner Koch + + * pubkey.c (gcry_pk_sign): Use format specifier '%M' to avoid + leading zeroes. Special case output generation for PSS. + (gcry_pk_encrypt): Special case output generation for OAEP. + (sexp_data_to_mpi): Use GCRYMPI_FMT_USG for PSS verify. + +2011-06-09 Werner Koch + + * pubkey.c (oaep_decode): Make use of octet_string_from_mpi. + (sexp_to_enc): Skip "random-override". + + * pubkey.c (oaep_encode, pss_encode): Add args RANDOM_OVERRIDE and + RANDOM_OVERRIDE_LEN. + (sexp_data_to_mpi): Extract new random-override parameter. + + * pubkey.c (pss_encode, pss_verify): Use VALUE verbatim for MHASH. + (octet_string_from_mpi): Add arg SPACE. + +2011-06-08 Werner Koch + + * pubkey.c (pss_encode, pss_verify): Restructure and comment code + to match rfc-3447. Replace secure allocs by plain allocs and + wipememory. Use gcry_md_hash_buffer. + (octet_string_from_mpi): New. + +2011-06-03 Werner Koch + + * pubkey.c (oaep_decode): Add more comments and restructure to + match the description in RFC-3447. + (oaep_encode): Check for mgf1 error. s/dlen/hlen/. + +2011-05-31 Werner Koch + + * pubkey.c (mgf1): Optimize by using gcry_md_reset. Re-implement + for easier readability. + (oaep_encode): Add more comments and restructure to match the + description in RFC-3447. + + * pubkey.c (pkcs1_encode_for_signature, oaep_decode): Change + return value from one MPI to a buffer. + (gcry_pk_decrypt): Adjust for this change. + +2011-05-30 Werner Koch + + * pubkey.c (pkcs1_decode_for_encryption): Change handling of + leading zero byte. + +2011-05-27 Daiki Ueno + + * pubkey.c (gcry_pk_decrypt): Fix double-free when un-padding + invalid data. Thanks to Tom Ritter. + +2011-05-24 Daiki Ueno + + * rsa.c (rsa_verify): Use CMP if given, to check the decrypted + sig. + + * pubkey.c (sexp_to_enc, sexp_data_to_mpi): Factor out + CTX initialization to ... + (init_encoding_ctx): .. new. + (gcry_pk_verify): Pass verify func and the arg to pubkey_verify. + (pss_encode, pss_verify, pss_verify_cmp): New. + +2011-05-23 Daiki Ueno + + * pubkey.c (pkcs1_decode_for_encryption, oaep_decode): Fix memleak + when gcry_mpi_print fails. + +2011-05-18 Daiki Ueno + + * pubkey.c (sexp_data_to_mpi): Factor some code out to ... + (pkcs1_encode_for_encryption): .. new, + (pkcs1_encode_for_signature): .. new. + (pkcs1_decode_for_encryption): New. + (gcry_pk_decrypt): Do un-padding for PKCS#1 as well as OAEP. + (sexp_to_enc): Abolish "unpad" flag, which is not necessary since + we can do un-padding implicitly when "pkcs1" or "oaep" is given. + +2011-05-11 Werner Koch + + * pubkey.c (sexp_to_enc, sexp_data_to_mpi): Set LABEL to NULL + after free. + (sexp_to_enc, sexp_data_to_mpi): Do not allow multiple encoding + flags. + (oaep_encode, oaep_decode, sexp_to_key, sexp_to_sig) + (sexp_to_enc, sexp_data_to_mpi, gcry_pk_encrypt, gcry_pk_sign) + (gcry_pk_genkey, _gcry_pk_get_elements): Replace access to ERRNO + by gpg_err_code_from_syserror. + +2011-05-11 Daiki Ueno + + * pubkey.c (sexp_data_to_mpi): Factor some code out to ... + (get_hash_algo): .. new. + (mgf1, oaep_encode, oaep_decode): New. + (sexp_to_enc): Add arg CTX. Remove arg RET_WANT_PKCS1. Support + OAEP. + (sexp_data_to_mpi): Add arg CTX. Support OAEP. + (gcry_pk_encrypt): Pass a CTX to sexp_data_to_mpi. + (gcry_pk_decrypt): Pass a CTX tp sexp_to_enc and replace + WANT_PKCS1. Implement unpadding for OAEP. + (gcry_pk_sign): Pass NULL for CTX arg of sexp_data_to_mpi. + (gcry_pk_verify): Ditto. + +2011-04-19 Werner Koch + + * cipher.c (gcry_cipher_open): Replace gpg_err_code_from_errno by + gpg_err_code_from_syserror. + +2011-04-11 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): Avoid double free of L2. + + * cipher.c (_gcry_cipher_setctr): Clear unused lastiv info. + (gcry_cipher_ctl) : Implement by calling + _gcry_cipher_setctr. + (do_ctr_encrypt): Save last counter and reuse it. + + * cipher.c (do_ctr_encrypt): Allow arbitrary length inputs to + match the 1.4 behaviour. + +2011-04-04 Werner Koch + + * ecc.c (compute_keygrip): Release L1 while parsing "curve". + + * pubkey.c (gcry_pk_get_keygrip): Always release NAME and L2. + Reported by Ben Kibbey. + +2011-03-28 Werner Koch + + * primegen.c (_gcry_generate_elg_prime): Make sure that PRIME is + NULL if the called func ever returns an error. + + * pubkey.c (gcry_pk_decrypt): Remove unused var PUBKEY. + +2011-03-09 Werner Koch + + * kdf.c: New. + +2011-02-22 Werner Koch + + * rijndael.c (aesni_cleanup_2_4): New. + (aesenc_xmm1_xmm0, do_aesni_ctr_4): New. + (_gcry_aes_ctr_enc): New. + * cipher.c (struct gcry_cipher_handle): Add CTR_ENC. Move field + CTR into an u_ctr union and adjust all users. + (gcry_cipher_open): Use _gcry_aes_ctr_enc. + (do_ctr_encrypt): Use bulk mode. + +2011-02-18 Werner Koch + + * rijndael.c (u32_a_t): New. + (do_encrypt_aligned, do_encrypt_aligned): Use the new type to + avoid problems with strict aliasing rules. + +2011-02-16 Werner Koch + + * rijndael.c (do_aesni_cfb) [USE_AESNI]: New. + (_gcry_aes_cfb_enc, _gcry_aes_cfb_dec) [USE_AESNI]: Use new fucntion. + +2011-02-15 Werner Koch + + * rijndael.c (do_aesni_enc_aligned, do_aesni_dec_aligned): Use + movdqa for the key but keep using movdqu for the data. + (do_aesni): Remove alignment detection. Don't burn the stack. + (aesni_prepare, aesni_cleanup): New macros. + (rijndael_encrypt, _gcry_aes_cfb_enc, _gcry_aes_cbc_enc) + (rijndael_decrypt, _gcry_aes_cfb_dec, _gcry_aes_cbc_dec): Use + these macros. Don't burn the stack in the USE_AESNI case. + (do_setkey): Add disabled code to use aeskeygenassist. + +2011-02-14 Werner Koch + + * rijndael.c (ATTR_ALIGNED_16): New + (do_aesni): Do not copy if already aligned. + (do_encrypt, do_decrypt): Ditto. + (rijndael_decrypt, rijndael_encrypt): Increase stack burning amount. + + * rijndael.c (RIJNDAEL_context): Reorder fields. Change fieldname + ROUNDS to rounds. Move padlock_key into u1. + (keySched, keySched2): Rename macros to keyscherr and keyschdec + and change all users. + (padlockkey): New macro. Change all users of padlock_key. + * cipher.c (NEED_16BYTE_ALIGNED_CONTEXT): Always define if using gcc. + (struct gcry_cipher_handle): Align U_IV to at least 16 byte. + +2011-02-13 Werner Koch + + * rijndael.c (USE_AESNI): New. Define for ia32 and gcc >= 4. + (m128i_t) [USE_AESNI]: New. + (RIJNDAEL_context) [USE_AESNI]: Add field use_aesni. + (do_setkey): Set USE_AESNI for all key lengths. + (prepare_decryption) [USE_AESNI]: Use aesimc instn if requested. + (do_aesni_enc_aligned, do_aesni_dec_aligned) + (do_aesni) [USE_AESNI]: New. + (rijndael_encrypt, _gcry_aes_cfb_enc, _gcry_aes_cbc_enc) + (rijndael_decrypt, _gcry_aes_cfb_dec) + (_gcry_aes_cbc_dec) [USE_AESNI]: Use do_aesni. + +2011-02-01 Werner Koch + + * pubkey.c (gcry_pk_get_curve): New. + (sexp_to_key): Add arg OVERRIDE_ELEMS. + (sexp_elements_extract_ecc): Allow for params only. + (gcry_pk_get_param): New. + * ecc.c (ecc_get_curve): New. + (ecc_get_param_sexp): New. + +2011-01-28 Werner Koch + + * pubkey.c (gcry_pk_genkey): Hack to insert the used curve name. + +2011-01-27 Werner Koch + + * ecc.c (fill_in_curve): Remove. + (generate_curve): Rename to .. + (fill_in_curve): this. Remove setting of NAME_OID. + (ecc_encrypt_raw): Change name of arg DATA to K for better + readability. Use ECC_public_key instead of ECC_secret_key. + Require a caller to pass a complete pkey array. + (ecc_decrypt_raw): Require a caller to pass a complete skey array. + (elliptic_curve_t): Add field NAME. + (fill_in_curve): Set field. + (generate_key): Add arg R_USED_CURVE. + (ecc_generate_ext): Return used curve name. + +2011-01-13 Andrey Jivsov (wk) + + * ecc.c (ec2os): Do not free passed parameters X and Y. Adjust + callers. + (ecc_encrypt_raw, ecc_decrypt_raw): New. + (ecdh_names, _gcry_pubkey_spec_ecdh): New. + * pubkey.c (pubkey_table): Support ECDH. + +2010-08-19 Werner Koch + + * cipher.c (gcry_cipher_open): Remove double release of the module. + Fixes bug#1263. + +2010-06-10 Jeff Johnson (wk) + + * ecc.c (ecc_generate_ext): Parse transient-key flag. + (generate_key): Add arg TRANSIENT_KEY and use it to set the random + level. + +2010-04-12 Brad Hards (wk) + + Spelling fixes. + +2010-03-26 Werner Koch + + * tiger.c (asn): Unfetter the old TIGER from an OID. + (TIGER_CONTEXT): Add field VARIANT. + (tiger_init): Factor code out to ... + (do_init): New. + (tiger1_init, tiger2_init): New. + (_gcry_digest_spec_tiger1, _gcry_digest_spec_tiger2): New. + * md.c (digest_table): Add TIGER1 and TIGER2 variants. + +2009-12-11 Werner Koch + + * sha256.c (Cho, Maj, Sum0, Sum1): Turn macros into inline + functions. + (transform): Partly unroll to interweave the chain variables + + * sha512.c (ROTR, Ch, Maj, Sum0, Sum1): Turn macros into inline + functions. + (transform): Partly unroll to interweave the chain variables. + Suggested by Christian Grothoff. + +2009-12-10 Werner Koch + + * Makefile.am (o_flag_munging): New. + (tiger.o, tiger.lo): Use it. + + * cipher.c (do_ctr_encrypt): Add arg OUTBUFLEN. Check for + suitable value. Add check for valid inputlen. Wipe temporary + memory. + (do_ctr_decrypt): Likewise. + (do_cbc_encrypt, do_cbc_decrypt): Add arg OUTBUFLEN. Check for + suitable value. Move check for valid inputlen to here; change + returned error from INV_ARG to INV_LENGTH. + (do_ecb_encrypt, do_ecb_decrypt): Ditto. + (do_cfb_encrypt, do_cfb_decrypt): Ditto. + (do_ofb_encrypt, do_ofb_decrypt): Ditto. + (cipher_encrypt, cipher_encrypt): Adjust for above changes. + (gcry_cipher_encrypt, gcry_cipher_decrypt): Simplify. + +2009-12-09 Werner Koch + + * cipher.c (gcry_cipher_open): Allow for GCRY_CIPHER_MODE_AESWRAP. + (cipher_encrypt, cipher_decrypt): Ditto. + (do_aeswrap_encrypt, do_aeswrap_decrypt): New. + (struct gcry_cipher_handle): Add field marks. + (cipher_setkey, cipher_setiv): Update marks flags. + (cipher_reset): Reset marks. + (cipher_encrypt, cipher_decrypt): Add new arg OUTBUFLEN. + (gcry_cipher_encrypt, gcry_cipher_decrypt): Pass outbuflen to + cipher_encrypt. Replace GPG_ERR_TOO_SHORT by + GPG_ERR_BUFFER_TOO_SHORT. + +2009-08-21 Werner Koch + + * dsa.c (dsa_generate_ext): Release retfactors array before + setting it to NULL. Reported by Daiko Ueno. + +2009-07-02 Werner Koch + + * md.c (md_read): Fix incomplete check for NULL. + Reported by Fabian Kail. + +2009-03-31 Werner Koch + + * rsa.c (rsa_check_secret_key): Return GPG_ERR_BAD_SECKEY and not + GPG_ERR_PUBKEY_ALGO. + +2009-02-16 Werner Koch + + * rsa.c (generate_x931): Do not initialize TBL with automatic + variables. + * whirlpool.c, tiger.c, sha256.c, sha1.c, rmd160.c, md5.c + * md4.c, crc.c: Remove memory.h. This is garbage from gnupg. + Reported by Dan Fandrich. + +2009-01-22 Werner Koch + + * ecc.c (compute_keygrip): Remove superfluous const. + +2009-01-06 Werner Koch + + * rmd160.c (oid_spec_rmd160): Add TeleTrust identifier. + +2008-12-10 Werner Koch + + * dsa.c (generate): Add arg DOMAIN and use it if specified. + (generate_fips186): Ditto. + (dsa_generate_ext): Parse and check the optional "domain" + parameter and pass them to the generate functions. + + * rijndael.c (rijndael_names): Add "AES128" and "AES-128". + (rijndael192_names): Add "AES-192". + (rijndael256_names): Add "AES-256". + +2008-12-05 Werner Koch + + * dsa.c (generate): Add arg TRANSIENT_KEY and use it to detrmine + the RNG quality needed. + (dsa_generate_ext): Parse the transient-key flag und pass it to + generate. + +2008-11-28 Werner Koch + + * dsa.c (generate_fips186): Add arg DERIVEPARMS and use the seed + value if available. + + * primegen.c (_gcry_generate_fips186_2_prime): Fix inner p loop. + +2008-11-26 Werner Koch + + * primegen.c (_gcry_generate_fips186_3_prime): New. + * dsa.c (generate_fips186): Add arg USE_FIPS186_2. + (dsa_generate_ext): Parse new flag use-fips183-2. + +2008-11-25 Werner Koch + + * dsa.c (generate_fips186): New. + (dsa_generate_ext): Use new function if derive-parms are given or + if in FIPS mode. + * primegen.c (_gcry_generate_fips186_2_prime): New. + +2008-11-24 Werner Koch + + * pubkey.c (gcry_pk_genkey): Insert code to output extrainfo. + (pubkey_generate): Add arg R_EXTRAINFO and pass it to the extended + key generation function. + * rsa.c (gen_x931_parm_xp, gen_x931_parm_xi): New. + (generate_x931): Generate params if not given. + (rsa_generate_ext): Parse use-x931 flag. Return p-q-swapped + indicator. + * dsa.c (dsa_generate_ext): Put RETFACTORS into R_EXTRAINFO if + possible. + + * pubkey.c (gcry_pk_genkey): Remove parsing of almost all + parameters and pass the parameter S-expression to pubkey_generate. + (pubkey_generate): Simplify by requitring modules to parse the + parameters. Remove the special cases for Elgamal and ECC. + (sexp_elements_extract_ecc): Add arg EXTRASPEC and use it. Fix + small memory leak. + (sexp_to_key): Pass EXTRASPEC to sexp_elements_extract_ecc. + (pubkey_table) [USE_ELGAMAL]: Add real extraspec. + * rsa.c (rsa_generate_ext): Adjust for new calling convention. + * dsa.c (dsa_generate_ext): Ditto. + * elgamal.c (_gcry_elg_generate): Ditto. Rename to elg_generate_ext. + (elg_generate): New. + (_gcry_elg_generate_using_x): Remove after merging code with + elg_generate_ext. + (_gcry_pubkey_extraspec_elg): New. + (_gcry_elg_check_secret_key, _gcry_elg_encrypt, _gcry_elg_sign) + (_gcry_elg_verify, _gcry_elg_get_nbits): Make static and remove + _gcry_ prefix. + * ecc.c (_gcry_ecc_generate): Rename to ecc_generate_ext and + adjust for new calling convention. + (_gcry_ecc_get_param): Rename to ecc_get_param and make static. + (_gcry_pubkey_extraspec_ecdsa): Add ecc_generate_ext and + ecc_get_param. + +2008-11-20 Werner Koch + + * pubkey.c (pubkey_generate): Add arg DERIVEPARMS. + (gcry_pk_genkey): Parse derive-parms and pass it to above. + * rsa.c (generate_x931): New. + (rsa_generate_ext): Add arg DERIVEPARMS and call new function in + fips mode or if DERIVEPARMS is given. + * primegen.c (_gcry_derive_x931_prime, find_x931_prime): New. + +2008-11-19 Werner Koch + + * rsa.c (rsa_decrypt): Use gcry_create_nonce for blinding. + (generate): Rename to generate_std. + +2008-11-05 Werner Koch + + * md.c (md_open): Use a switch to set the Bsize. + (prepare_macpads): Fix long key case for SHA384 and SHA512. + + * cipher.c (gcry_cipher_handle): Add field EXTRASPEC. + (gcry_cipher_open): Set it. + (gcry_cipher_ctl): Add private control code to disable weak key + detection and to return the current input block. + * des.c (_tripledes_ctx): Add field FLAGS. + (do_tripledes_set_extra_info): New. + (_gcry_cipher_extraspec_tripledes): Add new function. + (do_tripledes_setkey): Disable weak key detection. + +2008-10-24 Werner Koch + + * md.c (digest_table): Allow MD5 in fips mode. + (md_register_default): Take special action for MD5. + (md_enable, gcry_md_hash_buffer): Ditto. + +2008-09-30 Werner Koch + + * rijndael.c (do_setkey): Properly align "t" and "tk". + (prepare_decryption): Properly align "w". Fixes bug #936. + +2008-09-18 Werner Koch + + * pubkey.c (gcry_pk_genkey): Parse domain parameter. + (pubkey_generate): Add new arg DOMAIN and remove special case for + DSA with qbits. + * rsa.c (rsa_generate): Add dummy args QBITS, NAME and DOMAIN and + rename to rsa_generate_ext. Change caller. + (_gcry_rsa_generate, _gcry_rsa_check_secret_key) + (_gcry_rsa_encrypt, _gcry_rsa_decrypt, _gcry_rsa_sign) + (_gcry_rsa_verify, _gcry_rsa_get_nbits): Make static and remove + _gcry_ prefix. + (_gcry_pubkey_spec_rsa, _gcry_pubkey_extraspec_rsa): Adjust names. + * dsa.c (dsa_generate_ext): New. + (_gcry_dsa_generate): Replace code by a call to dsa_generate. + (_gcry_dsa_check_secret_key, _gcry_dsa_sign, _gcry_dsa_verify) + (_gcry_dsa_get_nbits): Make static and remove _gcry prefix. + (_gcry_dsa_generate2): Remove. + (_gcry_pubkey_spec_dsa): Adjust to name changes. + (_gcry_pubkey_extraspec_rsa): Add dsa_generate_ext. + +2008-09-16 Werner Koch + + * ecc.c (run_selftests): Add arg EXTENDED. + +2008-09-12 Werner Koch + + * rsa.c (test_keys): Do a bad case signature check. + * dsa.c (test_keys): Do a bad case check. + + * cipher.c (_gcry_cipher_selftest): Add arg EXTENDED and pass it + to the called tests. + * md.c (_gcry_md_selftest): Ditto. + * pubkey.c (_gcry_pk_selftest): Ditto. + * rijndael.c (run_selftests): Add arg EXTENDED and pass it to the + called tests. + (selftest_fips_128): Add arg EXTENDED and run only one test + non-extended mode. + (selftest_fips_192): Add dummy arg EXTENDED. + (selftest_fips_256): Ditto. + * hmac-tests.c (_gcry_hmac_selftest): Ditto. + (run_selftests): Ditto. + (selftests_sha1): Add arg EXTENDED and run only one test + non-extended mode. + (selftests_sha224, selftests_sha256): Ditto. + (selftests_sha384, selftests_sha512): Ditto. + * sha1.c (run_selftests): Add arg EXTENDED and pass it to the + called test. + (selftests_sha1): Add arg EXTENDED and run only one test + non-extended mode. + * sha256.c (run_selftests): Add arg EXTENDED and pass it to the + called tests. + (selftests_sha224): Add arg EXTENDED and run only one test + non-extended mode. + (selftests_sha256): Ditto. + * sha512.c (run_selftests): Add arg EXTENDED and pass it to the + called tests. + (selftests_sha384): Add arg EXTENDED and run only one test + non-extended mode. + (selftests_sha512): Ditto. + * des.c (run_selftests): Add arg EXTENDED and pass it to the + called test. + (selftest_fips): Add dummy arg EXTENDED. + * rsa.c (run_selftests): Add dummy arg EXTENDED. + + * dsa.c (run_selftests): Add dummy arg EXTENDED. + + * rsa.c (extract_a_from_sexp): New. + (selftest_encr_1024): Check that the ciphertext does not match the + plaintext. + (test_keys): Improve tests and return an error status. + (generate): Return an error if test_keys fails. + * dsa.c (test_keys): Add comments and return an error status. + (generate): Return an error if test_keys failed. + +2008-09-11 Werner Koch + + * rsa.c (_gcry_rsa_decrypt): Return an error instead of calling + BUG in case of a practically impossible condition. + (sample_secret_key, sample_public_key): New. + (selftest_sign_1024, selftest_encr_1024): New. + (selftests_rsa): Implement tests. + * dsa.c (sample_secret_key, sample_public_key): New. + (selftest_sign_1024): New. + (selftests_dsa): Implement tests. + +2008-09-09 Werner Koch + + * hmac-tests.c (selftests_sha1): Add tests. + (selftests_sha224, selftests_sha384, selftests_sha512): Make up tests. + + * hash-common.c, hash-common.h: New. + * sha1.c (selftests_sha1): Add 3 tests. + * sha256.c (selftests_sha256, selftests_sha224): Ditto. + * sha512.c (selftests_sha512, selftests_sha384): Ditto. + +2008-08-29 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): Remove the special case for RSA + and check whether a custom computation function has been setup. + * rsa.c (compute_keygrip): New. + (_gcry_pubkey_extraspec_rsa): Setup this function. + * ecc.c (compute_keygrip): New. + (_gcry_pubkey_extraspec_ecdsa): Setup this function. + +2008-08-28 Werner Koch + + * cipher.c (cipher_decrypt, cipher_encrypt): Return an error if + mode NONE is used. + (gcry_cipher_open): Allow mode NONE only with a debug flag set and + if not in FIPS mode. + +2008-08-26 Werner Koch + + * pubkey.c (pubkey_generate): Add arg KEYGEN_FLAGS. + (gcry_pk_genkey): Implement new parameter "transient-key" and + pass it as flags to pubkey_generate. + (pubkey_generate): Make use of an ext_generate function. + * rsa.c (generate): Add new arg transient_key and pass appropriate + args to the prime generator. + (_gcry_rsa_generate): Factor all code out to ... + (rsa_generate): .. new func with extra arg KEYGEN_FLAGS. + (_gcry_pubkey_extraspec_ecdsa): Setup rsa_generate. + * primegen.c (_gcry_generate_secret_prime) + (_gcry_generate_public_prime): Add new arg RANDOM_LEVEL. + +2008-08-21 Werner Koch + + * primegen.c (_gcry_generate_secret_prime) + (_gcry_generate_public_prime): Use a constant macro for the random + level. + +2008-08-19 Werner Koch + + * pubkey.c (sexp_elements_extract_ecc) [!USE_ECC]: Do not allow + allow "curve" parameter. + +2008-08-15 Werner Koch + + * pubkey.c (_gcry_pk_selftest): New. + * dsa.c (selftests_dsa, run_selftests): New. + * rsa.c (selftests_rsa, run_selftests): New. + * ecc.c (selftests_ecdsa, run_selftests): New. + + * md.c (_gcry_md_selftest): New. + * sha1.c (run_selftests, selftests_sha1): New. + * sha256.c (selftests_sha224, selftests_sha256, run_selftests): New. + * sha512.c (selftests_sha384, selftests_sha512, run_selftests): New. + + * des.c (selftest): Remove static variable form selftest. + (des_setkey): No on-the-fly self test in fips mode. + (tripledes_set3keys): Ditto. + + * cipher.c (_gcry_cipher_setkey, _gcry_cipher_setiv): + + * dsa.c (generate): Bail out in fips mode if NBITS is less than 1024. + * rsa.c (generate): Return an error code if the the requested size + is less than 1024 and we are in fpis mode. + (_gcry_rsa_generate): Take care of that error code. + + * ecc.c (generate_curve): In fips mode enable only NIST curves. + + * cipher.c (_gcry_cipher_selftest): New. + + * sha512.c (_gcry_digest_extraspec_sha384) + (_gcry_digest_extraspec_sha512): New. + * sha256.c (_gcry_digest_extraspec_sha224) + (_gcry_digest_extraspec_sha256): New. + * sha1.c (_gcry_digest_extraspec_sha1): New. + * ecc.c (_gcry_pubkey_extraspec_ecdsa): New. + * dsa.c (_gcry_pubkey_extraspec_dsa): New. + * rsa.c (_gcry_pubkey_extraspec_rsa): New. + * rijndael.c (_gcry_cipher_extraspec_aes) + (_gcry_cipher_extraspec_aes192, _gcry_cipher_extraspec_aes256): New. + * des.c (_gcry_cipher_extraspec_tripledes): New. + + * cipher.c (gcry_cipher_register): Rename to _gcry_cipher_register. + Add arg EXTRASPEC. + (dummy_extra_spec): New. + (cipher_table_entry): Add extraspec field. + * md.c (_gcry_md_register): Rename to _gcry_md_register. Add + arg EXTRASPEC. + (dummy_extra_spec): New. + (digest_table_entry): Add extraspec field. + * pubkey.c (gcry_pk_register): Rename to _gcry_pk_register. Add + arg EXTRASPEC. + (dummy_extra_spec): New. + (pubkey_table_entry): Add extraspec field. + + * ac.c: Let most public functions return GPG_ERR_UNSUPPORTED in + fips mode. + + * pubkey.c (pubkey_table_entry): Add field FIPS_ALLOWED and mark + appropriate algorithms. + (dummy_generate, dummy_check_secret_key, dummy_encrypt) + (dummy_decrypt, dummy_sign, dummy_verify, dummy_get_nbits): Signal + a fips error when used. + (gcry_pk_register): In fips mode do not allow to register new + algorithms. + + * md.c (digest_table): Add field FIPS_ALLOWED and mark appropriate + algorithms. + (md_register_default): In fips mode register only fips algorithms. + (gcry_md_register): In fips mode do not allow to register new + algorithms. + (gcry_md_get): Signal a fips error if called. + (gcry_md_hash_buffer): Do not allow rmd160 when not in fips mode. + (md_start_debug): Disable in fips_mode. + + * md.c (gcry_md_register_default): Rename to .. + (md_register_default): .. this. + (md_digest): Remove this commented fucntion. + * pubkey.c (gcry_pk_register_default): Rename to .. + (pk_register_default): .. this. + + * cipher.c (cipher_table_entry): Add field FIPS_ALLOWED. + (gcry_cipher_register_default): Register only fips approved + algorithms. + (gcry_cipher_register): Do not allow to register new ciphers. + (cipher_setiv): Signal fips error. + + * cipher (gcry_cipher_register_default): Rename to .. + (cipher_register_default): .. this. + (REGISTER_DEFAULT_CIPHERS): Adjust for that change. + +2008-07-05 Werner Koch + + * random-daemon.c, random.h, rndhw.c, rndunix.c, rand-internal.h + * random.c, rndegd.c, rndlinux.c, rndw32.c: Move to ../cipher. + * Makefile.am: Remove random stuff. + +2008-06-24 Szakats Istvan (wk) + + * ac.c (_gcry_ac_key_destroy, _gcry_ac_key_pair_generate): Relase + some more memory. + +2008-04-22 Werner Koch + + * rijndael.c (_gcry_aes_cfb_enc, _gcry_aes_cbc_enc) + (_gcry_aes_cfb_dec, _gcry_aes_cbc_dec): Use Padlock if possible. + +2008-04-18 Werner Koch + + * sha1.c (transform_aligned): Remove. That is will obviosuly not + work because we need a scratch working area and our internal API + does not allow to modify the buffers. + + * rijndael.c: Factor tables out to .. + * rijndael-tables.h: .. new. + + * ac.c (ac_data_extract): Make static. + + * camellia.h [HAVE_CONFIG_H]: Include config.h. + + * rndw32.c (registry_poll): Only print the performance data + problem warning once. Suggested by Simon Josefsson. + +2008-03-19 Werner Koch + + * cipher.c (gcry_cipher_open) [USE_AES]: Init bulk encryption only + if requested. Suggested by Dirk Stoecker. + +2008-03-18 Werner Koch + + * sha1.c: Include stdint.h. + (transform): Add arg NBLOCKS so that we can work on more than one + block and avoid updates of the chaining variables. Changed all + callers to use 1. + (sha1_write): Replace loop around transform. + (transform_aligned) [WORDS_BIGENDIAN]: New. + (TRANSFORM): New macro to replace all direct calls of transform. + +2008-03-17 Werner Koch + + * rijndael.c (_gcry_aes_cfb_dec): New. + (do_encrypt): Factor code out to .. + (do_encrypt_aligned): .. New. + (_gcry_aes_cfb_enc, _gcry_aes_cfb_dec): Use new function. + (do_decrypt): Factor code out to .. + (do_decrypt_aligned): .. new. + (_gcry_aes_cbc_enc, _gcry_aes_cbc_dec): New. + * cipher.c (struct gcry_cipher_handle): Put field IV into new + union U_IV to enforce proper alignment. Change all users. + (do_cfb_decrypt): Optimize. + (do_cbc_encrypt, do_cbc_decrypt): Optimize. + +2008-03-15 Werner Koch + + * rijndael.c (_gcry_aes_cfb_enc): New. + * cipher.c (struct gcry_cipher_handle): Add field ALGO and BULK. + (gcry_cipher_open): Set ALGO and BULK. + (do_cfb_encrypt): Optimize. + +2008-02-18 Werner Koch + + * rsa.c (_gcry_rsa_verify) [IS_DEVELOPMENT_VERSION]: Print + intermediate results. + +2008-01-08 Werner Koch + + * random.c (add_randomness): Do not just increment + POOL_FILLED_COUNTER but update it by the actual amount of data. + +2007-12-13 Werner Koch + + * pubkey.c (sexp_data_to_mpi): Support SHA-224. + +2007-12-05 Werner Koch + + * rijndael.c (USE_PADLOCK): Depend on ENABLE_PADLOCK_SUPPORT. + * rndhw.c (USE_PADLOCK): Ditto + + * rsa.c (secret): Fixed condition test for using CRT. Reported by + Dean Scarff. Fixes bug#864. + (_gcry_rsa_check_secret_key): Return an erro if the optional + parameters are missing. + * pubkey.c (sexp_elements_extract): Add arg ALGO_NAME. Changed all + callers to pass NULL. Add hack to allow for optional RSA + parameters. + (sexp_to_key): Pass algo name to sexp_elements_extract. + +2007-12-03 Werner Koch + + * random.c (gcry_random_add_bytes): Implement it. + * rand-internal.h (RANDOM_ORIGIN_EXTERNAL): New. + +2007-11-30 Werner Koch + + * rndhw.c: New. + * rndlinux.c (_gcry_rndlinux_gather_random): Try to read 50% + directly from the hwrng. + * random.c (do_fast_random_poll): Also run the hw rng fast poll. + (_gcry_random_dump_stats): Tell whether the hw rng failed. + +2007-11-29 Werner Koch + + * rijndael.c (USE_PADLOCK): Define new macro used for ia32. + (RIJNDAEL_context) [USE_PADLOCK]: Add fields USE_PADLOCK and + PADLOCK_KEY. + (do_setkey) [USE_PADLOCK]: Enable padlock if available for 128 bit + AES. + (do_padlock) [USE_PADLOCK]: New. + (rijndael_encrypt, rijndael_decrypt) [USE_PADLOCK]: Divert to + do_padlock. + * cipher.c (cipher_context_alignment_t): New. Use it in this + module in place of PROPERLY_ALIGNED_TYPE. + (NEED_16BYTE_ALIGNED_CONTEXT): Define macro for ia32. + (struct gcry_cipher_handle): Add field HANDLE_OFFSET. + (gcry_cipher_open): Take care of increased alignment requirements. + (gcry_cipher_close): Ditto. + +2007-11-28 Werner Koch + + * sha256.c (asn224): Fixed wrong template. It happened due to a + bug in RFC4880. SHA-224 is not in the stable version of libgcrypt + so the consequences are limited to users of this devel version. + +2007-10-31 Werner Koch + + * ac.c (gcry_ac_data_new): Remove due to the visibility wrapper. + (gcry_ac_data_destroy, gcry_ac_data_copy, gcry_ac_data_length) + (gcry_ac_data_set, gcry_ac_data_get_name, gcry_ac_data_get_index) + (gcry_ac_data_to_sexp, gcry_ac_data_from_sexp) + (gcry_ac_data_clear, gcry_ac_io_init, gcry_ac_open) + (gcry_ac_close, gcry_ac_key_init, gcry_ac_key_pair_generate) + (gcry_ac_key_pair_extract, gcry_ac_key_destroy) + (gcry_ac_key_pair_destroy, gcry_ac_key_data_get) + (gcry_ac_key_test, gcry_ac_key_get_nbits, gcry_ac_key_get_grip) + (gcry_ac_data_encrypt, gcry_ac_data_decrypt, gcry_ac_data_sign) + (gcry_ac_data_verify, gcry_ac_data_encode, gcry_ac_data_decode) + (gcry_ac_mpi_to_os, gcry_ac_mpi_to_os_alloc, gcry_ac_os_to_mpi) + (gcry_ac_data_encrypt_scheme, gcry_ac_data_decrypt_scheme) + (gcry_ac_data_sign_scheme, gcry_ac_data_verify_scheme) + (gcry_ac_io_init_va): Ditto. + (gcry_ac_id_to_name, gcry_ac_name_to_id): Remove as these + deprecated functions are now implemented by visibility.c. + +2007-10-26 Werner Koch + + * rndw32.c: Disable debug flag. + +2007-10-25 Werner Koch + + * rndw32.c: Updated from current cryptlib snapshot and modified + for our use. Removed support from pre NT systems. + (slow_gatherer_windows95): Remove. + (_gcry_rndw32_gather_random): Require an NT platform. + (init_system_rng, read_system_rng, read_mbm_data): New. + (slow_gatherer_windowsNT): Rename to ... + (slow_gatherer): .. this. Read system RNG and MBM. + (registry_poll): New with code factored out from slow_gatherer. + +2007-08-23 Werner Koch + + * random.c (pool_filled_counter): New. + (add_randomness): Use it. + +2007-08-22 Werner Koch + + * rndw32.c, rndunix.c: Switched to LGPL. + +2007-05-30 Werner Koch + + * camellia.h, camellia.c: Replace by new LGPL version and adjusted + camellia.h. + +2007-05-09 Marcus Brinkmann + + * ac.c (_gcry_ac_io_init_va, _gcry_ac_io_write, _gcry_ac_io_read): + Adjust users of gcry_ac_io_t because union is not anonymous + anymore. + +2007-05-02 Werner Koch + + * camellia-glue.c (camellia_setkey, camellia_encrypt) + (camellia_decrypt): Recalculated used stack size in called + functions. + * camellia.h: Redefine external symbols. + +2007-05-02 David Shaw + + * Makefile.am, cipher.c: Add Camellia. + + * camellia-glue.c: New. The necessary glue to interface libgcrypt + to the stock NTT Camellia distribution. + + * camellia.h, camellia.c: The stock NTT Camellia distribution + (GPL). + +2007-04-30 David Shaw + + * cipher.c: Use #if instead of #ifdef as configure defines the + USE_cipher defines as 0 for disabled. + +2007-04-30 Werner Koch + + * rndegd.c (_gcry_rndegd_set_socket_name): New. + +2007-04-30 Marcus Brinkmann + + * ecc.c (ec2os): Fix relocation of short numbers. + + * ecc.c (generate_key): Do not allocate D, which will be allocated + by GEN_K. Remove G. Fix test if g_x, g_y resp. q_x, q_y are + requested. + (_gcry_ecc_generate): Release unneeded members of SK. + * pubkey.c (sexp_to_key): Release NAME. + +2007-04-28 Marcus Brinkmann + + * ac.c (gcry_ac_mpi): Remove member NAME_PROVIDED. + (ac_data_mpi_copy, _gcry_ac_data_set, _gcry_ac_data_get_name) + (_gcry_ac_data_get_index, ac_data_construct): Adjust handling of + NAME accordingly. + +2007-04-20 Werner Koch + + * ecc.c (domain_parms): Add standard brainpool curves. + +2007-04-18 Werner Koch + + * ecc.c (generate_curve): Implement alias mechanism. + + * pubkey.c (sexp_elements_extract_ecc): New. + (sexp_to_key): Add special case for ecc. + (sexp_to_key, sexp_to_sig, sexp_to_enc, gcry_pk_genkey): Replace + name_terminated stuff by a call to _gcry_sexp_nth_string. + (gcry_pk_get_keygrip): Ditto. + +2007-04-16 Werner Koch + + * ecc.c (_gcry_ecc_generate): Renamed DUMMY to CURVE and use it. + +2007-04-13 Marcus Brinkmann + + * ac.c (ac_data_construct): Cast const away to suppress compiler + warning. + + * ecc.c (ecc_generate): Avoid compiler warning for unused argument + DUMMY. + (ecc_verify): Avoid compiler warning for unused arguments CMP and + OPAQUEV. + +2007-04-06 Werner Koch + + * sha1.c (oid_spec_sha1): Add another oid from X9.62. + +2007-03-28 Werner Koch + + * pubkey.c (gcry_pk_genkey): Do not issue misc-key-info if it is + empty. + (gcry_pk_genkey): New parameter "curve". + + * ecc.c: Entirely rewritten with only a few traces of the old + code left. + (_gcry_ecc_generate): New. + (generate_key) New arg NAME. + (generate_curve): Ditto. Return actual number of NBITS. + +2007-03-26 Werner Koch + + * pubkey.c (gcry_pk_genkey): Increase size of SKEY array and add a + runtime bounds check. + +2007-03-23 Werner Koch + + * ecc.c (ecc_ctx_init, ecc_ctx_free, ecc_mod, ecc_mulm): New. + (duplicate_point, sum_points, escalar_mult): Don't use a + copy of base->p. Replaced all mpi_mulm by ecc_mulm so that we can + experiment with different algorithms. + (generate_key, check_secret_key, sign, verify): Initialize a + computation context for use by ecc_mulm. + +2007-03-22 Werner Koch + + * pubkey.c (pubkey_table): Initialize ECC. + * Makefile.am (EXTRA_libcipher_la_SOURCES): Add ecc.c. + * ecc.c: New. Heavily reformatted and changed for use in libgcrypt. + (point_init): New. + (escalar_mult): Make arg R the first arg to be similar to the mpi + functions. + (duplicate_point): Ditto + (sum_points): Ditto + (sign, verify): Remove unneeded copy operations. + (sum_points): Removed memory leaks and optimized some compares. + (verify): Simplified input check. + +2007-03-14 Werner Koch + + * random.c (MASK_LEVEL): Removed macro as it was used only at one + place. Open coded it there. + (gcry_randomize, _gcry_update_random_seed_file) + (_gcry_fast_random_poll): Factor lock code out to .. + (lock_pool, unlock_pool): .. new. + (initialize): Look the pool while allocating. + (read_random_source, do_fast_random_poll): Moved intialization to ... + (initialize): .. here. + (_gcry_enable_quick_random_gen): No more need for initialization. + (is_initialized): Moved this global flag to .. + (initialize): .. here and changed all users to unconditionally call + initialize. + (add_randomness): Remove initalization here. It simply can't + happen. + + * random.c (enum random_origins): Moved to .. + * rand-internal.h: .. here. + * rndunix.c (_gcry_rndunix_gather_random): Use enum in prototype + for ORIGIN and renamed REQUESTOR to ORIGIN. + * rndegd.c (_gcry_rndegd_gather_random): Ditto. + * rndlinux.c (_gcry_rndlinux_gather_random): Ditto. + * rndw32.c (_gcry_rndw32_gather_random): Ditto. + (_gcry_rndw32_gather_random_fast): Ditto. + +2007-03-13 Werner Koch + + * random.c (enum random_origins): New. + (add_randomness): Renamed arg SOURCE to ORIGIN. + (read_random_source): Renamed arg REQUESTOR to ORIGIN. + (getfnc_gather_random): Removed static variable because this + function is only called one and thus we don't need this + optimization. + (_gcry_quick_random_gen): Removed and replaced by.. + (_gcry_enable_quick_random_gen): .. this. It is onlyu used to + enable it and it does not make sense to disable it later. Changed + the only one caller too. + (get_random_bytes): Removed. + (gcry_random_bytes, gcry_random_bytes_secure): Implement in terms + of gcry_randomize. + * random-daemon.c (_gcry_daemon_get_random_bytes): Removed. + +2007-02-23 Werner Koch + + * elgamal.c (generate): Removed unused variable TEMP. + (test_keys): New arg NODIE. + (generate_using_x, _gcry_elg_generate_using_x): New. + * pubkey.c (pubkey_generate): New arg XVALUE and direct call to + the new elgamal generate fucntion. + (gcry_pk_genkey): Parse the new "xvalue" tag. + +2007-02-22 Werner Koch + + * pubkey.c (sexp_data_to_mpi): Handle dynamically allocated + algorithms. Suggested by Neil Dunbar. Fixes bug#596. + + * rndw32.c (_gcry_rndw32_gather_random_fast): Make it return void. + + * cipher.c (gcry_cipher_algo_name): Simplified. + + * random.c: Use the daemon only if compiled with USE_RANDOM_DAEMON. + + * Makefile.am (libcipher_la_SOURCES): Build random-daemon support + only if requested. + +2007-02-21 Werner Koch + + * random.c (rndpool, keypool): Make unsigned. + (mix_pool): Change char* variables to unsigned char*. + (gcry_randomize): Make arg BUFFER a void*. + (gcry_create_nonce): Ditto. + + * rmd160.c (gcry_rmd160_mixblock): Make BUFFER a void*. + (_gcry_rmd160_hash_buffer): Make OUTBUF and BUFFER void*. + * sha1.c (_gcry_sha1_hash_buffer): Ditto. + + * cipher.c (gcry_cipher_encrypt, cry_cipher_decrypt): Change + buffer args to void*. + (gcry_cipher_register): Make ALGORITHM_ID a int *. + + * md.c (md_start_debug): Make SUFFIX a const char*. Use snprintf. + (gcry_md_debug): New. + (gcry_md_ctl): Changed arg BUFFER from unsigned char*. + + * md.c (md_write): Make INBUF a const void*. + (gcry_md_write): Remove needless cast. + * crc.c (crc32_write): Make INBUF a const void* + (update_crc32, crc24rfc2440_write): Ditto. + * sha512.c (sha512_write, transform): Ditto. + * sha256.c (sha256_write, transform): Ditto. + * rmd160.c (rmd160_write, transform): Ditto. + * md5.c (md5_write, transform): Ditto. + * md4.c (md4_write, transform): Ditto. + * sha1.c (sha1_write, transform): Ditto. + + * tiger.c (tiger_write, transform): Ditto. + * whirlpool.c (whirlpool_write, whirlpool_add, transform): Ditto. + + * elgamal.c (elg_names): Change to a const*. + * dsa.c (dsa_names): Ditto. + * rsa.c (rsa_names): Ditto. + * pubkey.c (gcry_pk_lookup_func_name): Make ALIASES a const. + +2007-02-20 Werner Koch + + * rndlinux.c (open_device): Remove unsused arg MINOR. + +2007-01-30 Werner Koch + + * sha256.c (oid_spec_sha256): Add alias from pkcs#1. + * sha512.c (oid_spec_sha512): Ditto. + (oid_spec_sha384): Ditto. + +2006-12-18 Werner Koch + + * rndlinux.c (set_cloexec_flag): New. + (open_device): Set close-on-exit flags. Suggested by Max + Kellermann. Fixes Debian#403613. + + * Makefile.am (AM_CPPFLAGS, AM_CFLAGS): Splitted and merged + Moritz' changes. + (INCLUDES): Removed. + +2006-11-30 Werner Koch + + * serpent.c (byte_swap_32): Remove trailing semicolon. + +2006-11-15 Werner Koch + + * Makefile.am (INCLUDES): Include ../src/ + +2006-11-03 Werner Koch + + * random.c [HAVE_GETTIMEOFDAY]: Included sys/time.h and not + sys/times.h. Reported by RafaĂ«l CarrĂ©. + +2006-11-05 Moritz Schulte + + * Makefile.am (AM_CFLAGS): Added -I$(top_builddir)/src so that the + new gcrypt.h is used, not the one installed in the system. + +2006-10-25 Werner Koch + + * primegen.c (prime_generate_internal): Tweaked use of secure + memory and entropy use. Safe unused primes from the pool. Allocate + at least a pool of 30. + (save_pool_prime, get_pool_prime): New. + +2006-10-23 Werner Koch + + * ac.c (_gcry_ac_data_from_sexp): Reset sexp_tmp for failsafe + means. Release sexp_cur if needed. Reported by Dirk Stoecker. + + * pubkey.c (pubkeys_registered_lock): Intialized it. It is not + realy needed because this is a mere initialization to 0 anyway. + Noted by Victor Stinner. + +2006-10-17 Werner Koch + + * dsa.c (_gcry_dsa_generate2): New. + (generate): New arg QBITS. Add sanity checks for reasonable qbits + and nbits. + * pubkey.c (gcry_pk_genkey): Parse an qbits element. + (pubkey_generate): New arg QBITS. Pass it to the DSA generation. + +2006-10-05 Werner Koch + + * md.c (gcry_md_algo_info) : Check that the algo is + available. + +2006-10-04 David Shaw (wk) + + * tiger.c (round): Rename to tiger_round as gcc 4 has a built-in + round function that this conflicts with. + +2006-09-11 Werner Koch + + * rndw32.c (slow_gatherer_windowsNT): While adding data use the + size of the diskPerformance and not its address. Has been fixed in + GnuPG more than a year ago. Noted by Lee Fisher. + +2006-08-30 Werner Koch + + * pubkey.c (sexp_data_to_mpi): Need to allow "ripemd160" here as + this is the canonical name. + +2006-08-29 Hye-Shik Chang (wk) + + * seed.c: New. + +2006-08-03 Werner Koch + + * random-daemon.c (_gcry_daemon_initialize_basics): Don't + initialize the socket. Remove arg SOCKETNAME. + (connect_to_socket): Make sure that daemon is set to -1 on error. + (call_daemon): Initialize the socket on the first call. + (_gcry_daemon_randomize, _gcry_daemon_get_random_bytes) + (_gcry_daemon_create_nonce): New arg SOCKETNAME. + * random.c (initialize): Call new daemon initializator. + (get_random_bytes, gcry_randomize, gcry_create_nonce): Pass socket + name to daemon call and reset allow_daemon on failure. + +2006-07-26 Werner Koch + + * rmd160.c (_gcry_rmd160_mixblock): Add cast to transform call. + + * blowfish.c (selftest): Cast string to usnigned char*. + + * primegen.c (prime_generate_internal): Cast unsigned/char* + mismatch in calling m_out_of_n. + (is_prime): Changed COUNT to unsigned int *. + + * ac.c (_gcry_ac_data_copy): Initialize DATA_MPIS. + + * random.c (gcry_create_nonce): Update the pid after a fork. + Reported by Uoti Urpala. + +2006-07-04 Marcus Brinkmann + + * sha512.c: Fix typo in copyright notice. + +2006-06-21 Werner Koch + + * rsa.c (_gcry_rsa_generate): Replace xcalloc by calloc. + * pubkey.c (gcry_pk_encrypt, gcry_pk_sign): Ditto. + (sexp_to_key, sexp_to_sig, sexp_to_enc, gcry_pk_encrypt) + (gcry_pk_sign, gcry_pk_genkey, gcry_pk_get_keygrip): Ditto. + * md.c (md_copy): Ditto. + +2006-04-22 Moritz Schulte + + * random-daemon.c (_gcry_daemon_initialize_basics): New argument: + SOCKETNAME. Passing on to connect_to_socket() if non-NULL. + (connect_to_socket, writen, readn, call_daemon): New functions. + (_gcry_daemon_randomize, _gcry_daemon_get_random_bytes) + (_gcry_daemon_create_nonce): Call call_daemon(). + (RANDOM_DAEMON_SOCKET): New symbol. + (daemon_socket): New static variable. + + * random.h (_gcry_daemon_initialize_basics): New parameter: + SOCKETNAME. + (_gcry_set_random_daemon_socket): New declaration. + + * random.c (initialize_basics): Pass DAEMON_SOCKET_NAME to + _gcry_daemon_initialize_basics. + (_gcry_set_random_daemon_socket): New function, setting + DAEMON_SOCKET_NAME. + +2006-04-01 Moritz Schulte + + * ac.c (eme_pkcs_v1_5_encode): Use KEY_SIZE directly, no need to + call gcry_ac_key_get_nbits. + (eme_pkcs_v1_5_decode): Likewise. + (ac_es_dencode_prepare_pkcs_v1_5): Fill options_em structure with + key_size. + (_gcry_ac_data_dump, gcry_ac_data_dump): New functions. + (_gcry_ac_data_to_sexp, _gcry_ac_data_from_sexp): More or less + rewritten; changed S-Expression format so that it matches the one + used in pubkey.c. + +2006-03-15 Werner Koch + + * random-daemon.c: New. + * random.c (_gcry_use_random_daemon): New. + (get_random_bytes, gcry_randomize, gcry_create_nonce): Try + diverting to the daemon functions. + +2006-03-14 Werner Koch + + * random.c (lock_seed_file): New. + (read_seed_file, _gcry_update_random_seed_file): Use it. + + * random.c (gcry_create_nonce): Detect a fork and re-seed. + (read_pool): Fixed the fork detection; it used to work only for + multi-threaded processes. + +2006-03-12 Brad Hards (wk) + + * md.c (md_open): Use new variable macpads_Bsize instead of + hardwiring the block size. Changed at all places. + +2006-03-10 Brad Hards (wk, patch 2005-04-22) + + * md.c, sha256.c: Add support for SHA-224. + (sha224_init): New. + +2006-01-18 Brad Hards (wk 2006-03-07) + + * cipher.c (cipher_encrypt, cipher_decrypt, do_ofb_encrypt) + (do_ofb_decrypt, gcry_cipher_open): Implement Output Feedback Mode. + +2005-11-02 Moritz Schulte + + * pubkey.c (gcry_pk_algo_name): Return "?" instead of NULL for + unknown algorithm IDs. + * cipher.c (cipher_algo_to_string): Likewise. + +2005-11-01 Moritz Schulte + + * pubkey.c (gcry_pk_algo_info): Don't forget to break after switch + case. + +2005-09-19 Werner Koch + + * dsa.c (generate): Add preliminary support for 2 and 4 keys. + Return an error code if the key size is not supported. + (_gcry_dsa_generate): Return an error. + +2005-08-22 Werner Koch + + * primegen.c (check_prime): New arg RM_ROUNDS. + (prime_generate_internal): Call it here with 5 rounds as used + before. + (gcry_prime_check): But here with 64 rounds. + (is_prime): Make sure never to use less than 5 rounds. + +2005-04-16 Moritz Schulte + + * ac.c (_gcry_ac_init): New function. + +2005-04-12 Moritz Schulte + + * ac.c (_gcry_ac_io_write, _gcry_ac_io_read): Initialize err to + make the compiler happy. + Always use errno, now that gcry_malloc() is guaranteed to set + errno on failure. + (_gcry_ac_data_to_sexp): Don't forget to goto out after error in + loop. + (_gcry_ac_data_to_sexp): Remove unused variable: mpi_list; + (_gcry_ac_data_to_sexp): Always deallocate sexp_buffer. + (_gcry_ac_data_from_sexp): Don't forget to initialize data_set_new. + (_gcry_ac_data_from_sexp): Handle special case, which is + necessary, since gcry_sexp_nth() does not distinguish between + "element does not exist" and "element is the empty list". + (_gcry_ac_io_init_va): Use assert to make sure that mode and type + are correct. + Use gcry_error_t types where gcry_err_code_t types have been used + before. + +2005-04-11 Moritz Schulte + + * ac.c (_gcry_ac_data_sign_scheme): Don't forget to initialize + buffer. + + * whirlpool.c: New file. + * md.c (digest_table): Add whirlpool. + * Makefile.am (EXTRA_libcipher_la_SOURCES): Added: whirlpool.c. + +2005-03-30 Moritz Schulte + + * ac.c (_gcry_ac_data_from_sexp): Use length of SEXP_CUR, not + length of SEXP; do not forget to set SEXP_TMP to NULL after it has + been released. + + (struct gcry_ac_mpi): New member: name_provided. + (_gcry_ac_data_set): Rename variable `name_final' to `name_cp'; + remove const qualifier; change code to not cast away const + qualifiers; use name_provided member as well. + (_gcry_ac_data_set, _gcry_ac_data_get_name): Use name_provided + member of named mpi structure. + + (gcry_ac_name_to_id): Do not forget to initialize err. + (_gcry_ac_data_get_index): Do not forget to initialize mpi_return; + use gcry_free() instead of free(); remove unnecessary cast; rename + mpi_return and name_return to mpi_cp and name_cp; adjust code. + (ac_data_mpi_copy): Do not cast away const qualifier. + (ac_data_values_destroy): Likewise. + (ac_data_construct): Likewise. + + (ac_data_mpi_copy): Initialize flags to GCRY_AC_FLAG_DEALLOC. + (ac_data_extract): Use GCRY_AC_FLAG_DEALLOC instead of + GCRY_AC_FLAG_COPY. + + (_gcry_ac_io_init_va, _gcry_ac_io_init, gcry_ac_io_init) + (gcry_ac_io_init_va, _gcry_ac_io_write, _gcry_ac_io_read) + (_gcry_ac_io_read_all, _gcry_ac_io_process): New functions. + (gry_ac_em_dencode_t): Use gcry_ac_io_t in prototype instead of + memroy strings directly; adjust encode/decode functions to use io + objects. + (emsa_pkcs_v1_5_encode_data_cb): New function ... + (emsa_pkcs_v1_5_encode): ... use it here. + (ac_data_dencode): Use io objects. + (_gcry_ac_data_encode, _gcry_ac_data_decode, gcry_ac_data_encode) + (gcry_ac_data_decode): Likewise. + (_gcry_ac_data_encrypt_scheme, gcry_ac_data_encrypt_scheme) + (_gcry_ac_data_decrypt_scheme, gcry_ac_data_decrypt_scheme) + (_gcry_ac_data_sign_scheme, gcry_ac_data_sign_scheme) + (_gcry_ac_data_verify_scheme, gcry_ac_data_verify_scheme): + Likewise. + +2005-03-23 Werner Koch + + * rndw32.c (_gcry_rndw32_gather_random_fast): While adding data + use the size of the object and not the one of its address. Bug + reported by Sascha Kiefer. + +2005-03-19 Moritz Schulte + + * cipher.c (do_cbc_encrypt): Be careful to not overwrite data, + which is to be used later on. This happend, in case CTS is + enabled and OUTBUF is equal to INBUF. + +2005-02-25 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): Allow for shadowed-private-key. + +2005-02-13 Moritz Schulte + + * serpent.c: Updated from 1.2 branch: + + s/u32_t/u32/ and s/byte_t/byte/. Too match what we have always + used and are using in all other files too + (serpent_test): Moved prototype out of a fucntion. + +2005-02-07 Moritz Schulte + + * ac.c: Major parts rewritten. + * pubkey.c (_gcry_pk_get_elements): New function. + +2004-12-09 Werner Koch + + * serpent.c (serpent_setkey): Moved prototype of serpent_test to + outer scope. + +2004-09-11 Moritz Schulte + + * pubkey.c (pubkey_table): Added an alias entry for GCRY_PK_ELG_E. + +2004-08-23 Moritz Schulte + + * ac.c: Do not include . + * rndegd.c: Likewise. + * sha1.c: Likewise. + * rndunix.c: Likewise. + * rndlinux.c: Likewise. + * rmd160.c: Likewise. + * md5.c: Likewise. + * md4.c: Likewise. + * cipher.c: Likewise. + * crc.c: Likewise. + * blowfish.c: Likewise. + + * pubkey.c (dummy_generate, dummy_check_secret_key) + (dummy_encrypt, dummy_decrypt, dummy_sign, dummy_verify): Return + err code GPG_ERR_NOT_IMPLEMENTED instead of aborting through + log_bug(). + (dummy_get_nbits): Return 0 instead of aborting though log_bug(). + +2004-08-19 Werner Koch + + * pubkey.c (sexp_data_to_mpi): Changed the zero random byte + substituting code to actually do clever things. Thanks to + Matthias Urlichs for noting the implementation problem. + +2004-08-09 Moritz Schulte + + * pubkey.c (gcry_pk_sign): Fixed memory leak; fix provided by + Modestas Vainius. + +2004-07-16 Werner Koch + + * rijndael.c (do_encrypt): Fix alignment problem. Bugs found by + Matthias Urlichs. + (do_decrypt): Ditto. + (keySched, keySched2): Use 2 macros along with unions in the key + schedule context. + +2004-07-14 Moritz Schulte + + * rsa.c (_gcry_rsa_decrypt): Don't forget to free "a". Thanks to + Nikos Mavroyanopoulos. + +2004-05-09 Werner Koch + + * random.c (read_pool): Mix the PID in to better protect after a + fork. + +2004-07-04 Moritz Schulte + + * serpent.c: Use "u32_t" instead of "unsigned long", do not + declare S-Box variables as "register". Fixes failure on + OpenBSD/sparc64, reported by Nikolay Sturm. + +2004-05-07 Werner Koch + + * random.c (initialize): Factored out some code to .. + (initialize_basics): .. new function. + (_gcry_random_initialize): Just call initialize_basics unless the + new arg FULL is set to TRUE. + (_gcry_fast_random_poll): Don't do anything unless the random + system has been really initialized. + +2004-05-07 Moritz Schulte + + * ac.c (gcry_ac_open): Do not dereference NULL pointer. Reported + by Umberto Salsi. + +2004-02-20 Werner Koch + + * primegen.c (check_prime): New args CB_FUNC and CB_ARG; call them + at different stages. Pass these arguments through all callers. + +2004-02-06 Werner Koch + + * des.c: Add a new OID as used by pkcs#12. + + * rfc2268.c: New. Taken from libgcrypt. + * cipher.c: Setup the rfc2268 algorithm. + +2004-01-25 Moritz Schulte + + * primegen.c (prime_generate_internal): Do not forget to free + `q_factor'; fixed by Brieuc Jeunhomme. + (prime_generate_internal): Do not forget to free `prime'. + +2004-01-14 Moritz Schulte + + * ac.c (gcry_ac_data_set): New argument: flags; slightly + rewritten. + (gcry_ac_data_get_name, gcry_ac_data_get_index): Likewise. + (gcry_ac_key_pair_generate): New argument: misc_data; modified + order of arguments. + (gcry_ac_key_test): New argument: handle. + (gcry_ac_key_get_nbits, gcry_ac_key_get_grip): Likewise. + Use GCRY_AC_FLAG_NO_BLINDING instead of + GCRY_AC_DATA_FLAG_NO_BLINDING. + (gcry_ac_mpi): New member: flags. + (gcry_ac_data_search, gcry_ac_data_add): Removed functions. + +2003-12-22 Werner Koch + + * primegen.c (is_prime): Release A2. + +2003-12-19 Werner Koch + + * md.c: Moved a couple of functions down below the data structure + definitions. + (struct gcry_md_context): New field ACTUAL_HANDLE_SIZE. + (md_open): Set it here. + (strcut gcry_md_list): New field ACTUAL_STRUCT_SIZE. + (md_enable): Set it here. + (md_close): Wipe the context memory. + secure memory. + * cipher.c (struct gcry_cipher_handle): New field ACTUAL_HANDLE_SIZE. + (gcry_cipher_open): Set it here. + (gcry_cipher_close): Use it to always wipe out the handle data. + + * ac.c (gcry_ac_open): Make sure HANDLE gets initialized even when + the function is not successful. + (gcry_ac_close): Allow a NULL handle. + (gcry_ac_key_destroy, gcry_ac_key_pair_destroy): Ditto. + (gcry_ac_key_get_grip): Return INV_OBJ on error. + + * primegen.c (prime_generate_internal): Fixed error code for + failed malloc. Replaced the !err if chain by gotos. + (gcry_prime_group_generator): Remove the extra sanity check. + + * md.c: Minor code and comment cleanups. + +2003-12-16 Werner Koch + + * primegen.c (gen_prime): Doc fix. Thanks to Newton Hammet. + +2003-12-11 Werner Koch + + * rndunix.c (slow_poll): Don't use #warning but #error. + + * rndegd.c: Changed indentation. + (my_make_filename): Removd the var_arg cruft becuase we + don't need it here. Changed caller. + + * rndlinux.c: Changed indentation. + (open_device): Remove the superfluous stat call and clarify + comment. + + * rsa.c: Changed indentation. + (secret): Use the standard algorithm if p, q and u are not + available. + (rsa_blind, rsa_unblind): Renamed from _gcry_rsa_blind, + _gcry_rsa_unblind and moved more to the top. + + * md4.c: Changed indentation. Removed unnecessary casts. + * md5.c, rmd160.c, sha1.c, tiger.c: Ditto. + * rijndael.c, twofish.c: Ditto. + * serpent.c: Removed unnecessary casts. + * sha256.c, sha512.c: Ditto. + +2003-12-09 Werner Koch + + * dsa.c: Unified indentation style. + * elgamal.c: Ditto. + * des.c (des_key_schedule): Code beautifications. + * blowfish.c: Changed indentation style. + * cast5.c (do_cast_setkey): Ditto. + + * pubkey.c (gcry_pk_encrypt): Replaced the chain of if(!err) tests + by straightforward gotos. Other cleanups. + (gcry_pk_decrypt): Ditto. + (gcry_pk_sign): Ditto. + (gcry_pk_verify): Ditto. + (gcry_pk_genkey): Ditto. Use strtoul instead of strtol. + (gcry_pk_ctl): Use GPG_ERR_INV_ARG to indicate bad arguments. + +2003-12-07 Werner Koch + + * pubkey.c (gcry_pk_register_default): Undef the helper macro. + (gcry_pk_map_name): Allow NULL for string. + (sexp_to_key): Use memcpy and not strncpy. Use gcry_free and not + free. + (sexp_to_sig): Ditto. + (sexp_to_enc): Ditto. Replaced the chain of if(!err) tests by + straightforward gotos. + +2003-12-05 Werner Koch + + * cipher.c: Documentation cleanups. + (gcry_cipher_mode_from_oid): Allow NULL for STRING. + +2003-12-03 Werner Koch + + * elgamal.c (sign, do_encrypt, gen_k): Make sure that a small K is + only used for encryption. + +2003-11-18 Werner Koch + + * random.h (rndw32_set_dll_name): Removed unused prototype. + + * Makefile.am (EXTRA_DIST): Added Manifest. + +2003-11-11 Werner Koch + + * Manifest: New. + +2003-11-04 Werner Koch + + * md.c (gcry_md_hash_buffer): Use shortcut for SHA1 + * sha1.c (_gcry_sha1_hash_buffer): New. + + * random.c: Reformatted most functions. + (mix_pool): Moved the failsafe_digest from global + scope to here. + (do_fast_random_poll): Use the generic fucntions even if a fast + gathering function has been used. + (read_pool): Detect a fork and retry. + (gcry_randomize, get_random_bytes): Don't distinguish anymore + between weak and strong random. + (gcry_create_nonce): New. + +2003-10-31 Werner Koch + + * rndw32.c (slow_gatherer_windowsNT): Use a plain buffer for the + disk performance values and not the W32 API structure. + + * dsa.c (verify): s/exp/ex/ due to shadowing of a builtin. + * elgamal.c (verify): Ditto. + + * ac.c (gcry_ac_data_get_index): s/index/idx/ + (gcry_ac_data_copy_internal): Remove the cast in _gcry_malloc. + (gcry_ac_data_add): Must use gcry_realloc instead of realloc. + * pubkey.c (sexp_elements_extract): s/index/idx/ as tribute to the + forehackers. + (gcry_pk_encrypt): Removed shadowed definition of I. Reordered + arguments to malloc for clarity. + (gcry_pk_sign, gcry_pk_genkey): Ditto. + * primegen.c (prime_generate_internal): s/random/randomlevel/. + +2003-10-27 Moritz Schulte + + * pubkey.c (gcry_pk_encrypt): Don't forget to deallocate pkey. + +2003-10-27 Werner Koch + + * random.c (gcry_random_add_bytes): Return if buflen is zero to + avoid gcc warning about unsed parameter. + (MASK_LEVEL): Simplified; does now work for signed and unsigned + w/o warnings. + + * md.c (md_start_debug): Removed the const from SUFFIX, because + this function is called from the control fucntion which does not + require const. + + Prefixed all (pubkey,digest,cipher}_spec_* globale variables with + _gcry_. + + * ac.c (ac_key_identifiers): Made static. + + * random.c (getfnc_gather_random,getfnc_fast_random_poll): Move + prototypes to .. + * rand-internal.h: .. here + * random.c (getfnc_gather_random): Include rndw32 gatherer. + * rndunix.c, rndw32.c, rndegd.c: Include them here. + * rndlinux.c (_gcry_rndlinux_gather_random): Prepend the _gcry_ + prefix. Changed all callers. + * rndegd.c (_gcry_rndegd_gather_random): Likewise. + (_gcry_rndegd_connect_socket): Likewise. + * rndunix.c (_gcry_rndunix_gather_random): Likewise. + (waitpid): Made static. + * rndw32.c: Removed the old and unused winseed.dll cruft. + (_gcry_rndw32_gather_random_fast): Renamed from + gather_random_fast. + (_gcry_rndw32_gather_random): Renamed from gather_random. Note, + that the changes 2003-04-08 somehow got lost. + + * sha512.c (sha512_init, sha384_init): Made static. + + * cipher.c (do_ctr_decrypt): Removed "return" from this void + function. + +2003-10-24 Moritz Schulte + + * serpent.c: Fix an issue on big-endian systems. + + * rndw32.c: Removed IS_MODULE -cruft. + * rndlinux.c (rndlinux_gather_random): Likewise. + +2003-10-10 Werner Koch + + * primegen.c (gen_prime): Bail out if NBITS is less than 16. + (prime_generate_internal): Initialize prime variable to suppress + compiler warning. Check pbits, initialize qbits when passed as + zero. + + * primegen.c (prime_generate_internal): New arg + ALL_FACTORS. Changed all callers. + (gcry_prime_generate): Make the factors arg optional. Request + all_factors. Make sure PRIME is set to NULL even on error. + (gcry_prime_group_generator): New. + (gcry_prime_release_factors): New. + +2003-10-06 Werner Koch + + * primegen.c (gen_prime): Assert that NBITS is never zero, it + would cause a segv. + +2003-09-28 Moritz Schulte + + * ac.c: Include "cipher.h". + +2003-09-27 Moritz Schulte + + * rndegd.c (do_read): Return nread instead of nbytes; thanks to + Michael Caerwyn. + +2003-09-04 Werner Koch + + * pubkey.c (_gcry_pk_aliased_algo_name): New. + * ac.c (gcry_ac_open): Use it here. + + * Makefile.am (EXTRA_libcipher_la_SOURCES): Add serpent.c + +2003-09-02 Moritz Schulte + + * primegen.c (gcry_prime_check, gcry_prime_generate): New + functions. + (prime_generate_internal): New function, based on + _gcry_generate_elg_prime. + (_gcry_generate_elg_prime): Rewritten as a wrapper for + prime_generate_internal. + +2003-08-28 Werner Koch + + * pubkey.c (gcry_pk_encrypt): Don't include the flags list in the + return value. This does not make sense and breaks any programs + parsing the output strictly (e.g. current gpgsm). + (gcry_pk_encrypt): If aliases for the algorithm name exists, take + the first one instead of the regular name to adhere to SPKI + conventions. + (gcry_pk_genkey): Ditto. + (gcry_pk_sign): Ditto. Removed unused KEY_ALGO_NAME. + +2003-08-19 Moritz Schulte + + * cipher.c: Add support for Serpent + * serpent.c: New file. + +2003-08-10 Moritz Schulte + + * rsa.c (_gcry_rsa_blind, _gcry_rsa_unblind): Declare static. + +2003-08-09 Timo Schulz + + * random.c (getfnc_gather_random): Don't check NAME_OF_DEV_RANDOM + two times, but also the NAME_OF_DEV_URANDOM device. + +2003-08-08 Moritz Schulte + + * pubkey.c (sexp_to_enc): Fixed extraction of S-Expression: do not + fail if no `flags' sub S-Expression is found. + +2003-07-27 Werner Koch + + * md.c (gcry_md_lookup_func_oid): Allow for empty OID lists. + +2003-07-23 Moritz Schulte + + * ac.c (gcry_ac_data_construct): New argument: include_flags, only + include `flags' S-expression, if include_flags is true. Adjust + callers. Thanks for triggering a bug caused by `flags' + sub-S-expression where they are not expected to Ralf Schneider. + +2003-07-21 Moritz Schulte + + * pubkey.c (gcry_pk_lookup_func_name): Use new member name + `aliases' instead of `sexp_names'. + + * ac.c (gcry_ac_key_data_get): New function. + + * cipher.c (gcry_cipher_lookup_func_name): Fix return value. + +2003-07-20 Moritz Schulte + + * blowfish.c: Adjusted for new gcry_cipher_spec_t structure. + * cast5.c: Likewise. + * twofish.c: Likewise. + * arcfour.c: Likewise. + * rijndael.c (rijndael_oids, rijndael192_oids, rijndael256_oids): + New variables, adjust for new gcry_cipher_spec_t structure. + * des.c (oids_tripledes): New variable, adjust for new + gcry_cipher_spec_t structure. + + * md.c (oid_table): Removed. + + * tiger.c (oid_spec_tiger): New variable. + (digest_spec_tiger): Adjusted for new gry_md_spec_t structure. + + * sha512.c (oid_spec_sha512): New variable. + (digest_spec_sha512): Adjusted for new gry_md_spec_t structure. + + * sha512.c (oid_spec_sha384): New variable. + (digest_spec_sha384): Adjusted for new gry_md_spec_t structure. + + * sha256.c (oid_spec_sha256): New variable. + (digest_spec_sha256): Adjusted for new gry_md_spec_t structure. + + * sha1.c (oid_spec_sha1): New variable. + (digest_spec_sha1): Adjusted for new gry_md_spec_t structure. + + * rmd160.c (oid_spec_rmd160): New variable. + (digest_spec_rnd160): Adjusted for new gry_md_spec_t structure. + + * md5.c (oid_spec_md5): New variable. + (digest_spec_md5): Adjusted for new gry_md_spec_t structure. + + * md4.c (oid_spec_md4): New variable. + (digest_spec_md4): Adjusted for new gry_md_spec_t structure. + + * crc.c (digest_spec_crc32, digest_spec_crc32_rfc1510, + digest_spec_crc32_rfc2440): Adjusted for new gry_md_spec_t + structure. + +2003-07-19 Moritz Schulte + + * md.c (gcry_md_lookup_func_oid): New function. + (search_oid): New function, copied from cipher.c. + (gcry_md_map_name): Adjust for new search_oid_interface. + + * cipher.c (oid_table): Removed table. + (gcry_cipher_lookup_func_oid): New function. + (search_oid): Rewritten to use the module functions. + (gcry_cipher_map_name): Adjust for new search_oid interface. + (gcry_cipher_mode_from_oid): Likewise. + +2003-07-18 Werner Koch + + * md.c (gcry_md_hash_buffer): Convert ERR to gpg_error_t in + gpg_strerror. + +2003-07-14 Moritz Schulte + + * cipher.c (gcry_cipher_lookup_func_name): Also check the cipher + name aliases, not just the primary name. + (gcry_cipher_map_name): Remove kludge for aliasing Rijndael to + AES. + + * arcfour.c, blowfish.c, cast5.c, des.c, twofish.c: Adjust cipher + specification structures. + + * rijndael.c (rijndael_names, rijndael192_names, + rijndael256_names): New variables, use them in the cipher + specifications. + + * rmd160test.c: Removed file. + + * ac.c, arcfour.c, blowfish.c, cast5.c, cipher.c, des.c, dsa.c, + elgamal.c, md.c, pubkey.c, random.c, rijndael.c, rsa.c, twofish.c: + Used gcry_err* wrappers for libgpg symbols. + + * primegen.c (gen_prime): Correct the order arguments to + extra_check. + +2003-07-12 Moritz Schulte + + * ac.c: Replaced all public occurences of gpg_error_t with + gcry_error_t. + * cipher.c: Likewise. + * md.c: Likewise. + * pubkey.c: Likewise. + * random.c: Likewise. + + * cipher.c: Added support for TWOFISH128. + +2003-07-08 Moritz Schulte + + * ac.c (gcry_ac_data_copy_internal): New function, based on + gcry_ac_data_copy. + (gcry_ac_data_copy): Made public, use gcry_ac_data_copy_internal. + (gcry_ac_key_init): Use gcry_ac_data_copy_internal. + +2003-07-07 Moritz Schulte + + * ac.c (gcry_ac_data_set): Only release old MPI value if it is + different from the new value. Bug reported by Simon Josefsson + . + + * pubkey.c (gcry_pk_list): New function. + * md.c (gcry_md_list): New function. + + * ac.c (gcry_ac_key_pair_generate): Fix calculation of format + string size. + +2003-07-05 Moritz Schulte + + * md.c: Named struct of digest_table `digest_table_entry'. + (digest_table_entry): New member: algorithm; filled in. + (digest_table_entry): Removed unused member: flags. + (gcry_md_register): New argument: algorithm_id, filled in. + (gcry_md_register_default): Used algorithm ID from module + structure. + (gcry_md_map_name): Likewise. + (md_enable): Likewise. + (md_read): Likewise. + (gcry_md_info): Likewise. + + * pubkey.c: Named truct for pubkey_table `pubkey_table_entry'. + (pubkey_table_entry): New member: algorithm; filled in. + (gcry_pk_register_default): Used algorithm ID from pubkey_table. + (gcry_pk_register): New argument: algorithm_id, filled in. + (gcry_pk_map_name): Used algorithm ID from module structure. + (gcry_pk_decrypt): Likewise. + (gcry_pk_encrypt): Likewise. + (gcry_pk_verify): Likewise. + (gcry_pk_sign): Likewise. + (gcry_pk_testkey): Likewise. + (gcry_pk_genkey): Likewise. + (gcry_pk_get_nbits): Likewise. + (sexp_to_key): Removed unused variable: algo. + (sexp_to_sig): Likewise. + + * cipher.c: Named struct for cipher_table `cipher_table_entry'. + (cipher_table_entry): New member: algorithm; filled in. + (gcry_cipher_register_default): Used algorithm ID from + cipher_table. + (gcry_cipher_register): New argument: algorithm_id, filled in. + (gcry_cipher_map_name): Used algorithm ID from module structure. + + * arcfour.c (cipher_spec_arcfour): Removed algorithm ID. + * blowfish.c (cipher_spec_blowfish): Likewise. + * cast5.c (cipher_spec_cast5): Likewise. + * crc.c (digest_spec_crc32): Likewise. + * crc.c (digest_spec_crc32_rfc1510): Likewise. + * crc.c (digest_spec_crc32_rfc2440): Likewise. + * des.c (cipher_spec_des): Likewise. + * des.c (cipher_spec_tripledes): Likewise. + * dsa.c (pubkey_spec_dsa): Likewise. + * elgamal.c (pubkey_spec_elg): Likewise. + * md4.c (digest_spec_md4): Likewise. + * md5.c (digest_spec_md5): Likewise. + * aes.c (cipher_spec_aes): Likewise. + * aes.c (cipher_spec_aes192): Likewise. + * aes.c (cipher_spec_aes256): Likewise. + * rsa.c (pubkey_spec_rsa): Likewise. + * sha1.c (digest_spec_sha1): Likewise. + * sha256.c (digest_spec_sha256): Likewise. + * sha512.c (digest_spec_sha512): Likewise. + * tiger.c (digest_spec_tiger): Likewise. + * twofish.c (cipher_spec_twofish): Likewise. + * twofish.c (cipher_spec_twofish128): Likewise. + + * Makefile.am (EXTRA_libcipher_la_SOURCES): Fix list of source + files; reported by Simon Josefsson . + + * pubkey.c: Replaced all occurences of `id' with `algorithm', + since `id' is a keyword in obj-c. + * md.c: Likewise. + * cipher.c: Likewise. + + * crc.c, md4.c, md5.c, rmd160.c, sha1.c, sha256.c, tiger.c: + Replaced all occurences of gcry_digest_spec_t with gcry_md_spec_t. + + * dsa.c, rsa.c, elgamal.c: Replaced all occurencens of + gcry_pubkey_spec_t with gcry_pk_spec_t. + + * md.c: Replaced all occurences of gcry_digest_spec_t with + gcry_md_spec_t. + (gcry_digest_register_default): Renamed to ... + (gcry_md_register_default): ... this; adjusted callers. + (gcry_digest_lookup_func_name): Renamed to ... + (gcry_md_lookup_func_name): ... this; adjusted callers. + (gcry_digest_lookup_name): Renamed to ... + (gcry_md_lookup_name): ... this; adjusted callers. + (gcry_digest_register): Renamed to ... + (gcry_md_register): ... this. + (gcry_digest_unregister): Renamed to ... + (gcry_md_unregister): ... this. + + * pubkey.c (gcry_pubkey_register): Renamed to ... + (gcry_pk_register): ... this. + (gcry_pubkey_unregister): Renamed to ... + (gcry_pk_unregister): ... this. + Replaced all occurences of gcry_pubkey_spec_t with gcry_pk_spec_t. + (gcry_pubkey_register_default): Renamed to ... + (gcry_pk_register_default): ... this; adjusted callers. + (gcry_pubkey_lookup_func_name): Renamed to ... + (gcry_pk_lookup_func_name): ... this; adjusted callers. + (gcry_pubkey_lookup_name): Renamed to ... + (gcry_pk_lookup_name): ... this; adjusted callers. + + * md.c (gcry_md_hash_buffer): Fix error checking. Thanks to Simon + Josefsson . + +2003-07-04 Moritz Schulte + + * cipher.c (gcry_cipher_list): New function. + +2003-07-01 Moritz Schulte + + * pubkey.c (sexp_to_sig): Accept a `flags' S-expression to be more + consistent with sexp_to_enc. + +2003-06-30 Moritz Schulte + + * Makefile.am (libcipher_la_SOURCES): Added: ac.c. + + * pubkey.c (_gcry_pk_module_lookup): New function. + (_gcry_pk_module_release): New function. + +2003-06-29 Moritz Schulte + + * ac.c: New file. + +2003-06-26 Werner Koch + + * md.c (gcry_md_hash_buffer): Trigger BUG correcly with new API. + +2003-06-19 Werner Koch + + * md.c (gcry_md_is_enabled): Fixed. + +2003-06-18 Werner Koch + + * cipher.c (gcry_cipher_get_algo_keylen): New. + (gcry_cipher_get_algo_blklen): New. + +2003-06-18 Moritz Schulte + + * arcfour.c, cipher.c, blowfish.c, md.c, cast5.c, pubkey.c, crc.c, + des.c, dsa.c, elgamal.c, md4.c, md5.c, random.c, rijndael.c, + rmd160.c, rsa.c, sha1.c, sha256.c, sha512.c, tiger.c, twofish.c: + Replaced older types GcryDigestSpec, GcryCipherSpec and + GcryPubkeySpec with newer types: gcry_digest_spec_t, + gcry_cipher_spec_t and gcry_pubkey_spec_t. + + * md.c (gcry_digest_id_new): Removed function. + (gcry_digest_register): Removed code for generating a new module + ID. + + * pubkey.c (gcry_pubkey_id_new): Removed function. + (gcry_pubkey_register): Removed code for generating a new module + ID. + + * cipher.c, md.c, pubkey.c: Replace old type GcryModule with newer + one: gcry_module_t. + (gcry_cipher_id_new): Removed function. + (gcry_cipher_register): Removed code for generating a new module + ID. + + * cipher.c (gcry_cipher_register): Adjust call to + _gcry_module_add. + (gcry_cipher_register_default): Likewise. + * pubkey.c (gcry_pubkey_register_default): Likewise. + (gcry_pubkey_register): Likewise. + * md.c (gcry_digest_register_default): Likewise. + (gcry_digest_register): Likewise. + + * md.c (gcry_digest_lookup_func_id): Removed function. + (gcry_digest_lookup_id): Likewise. + (gcry_digest_id_new): Use _gcry_module_lookup_id instead of + gcry_digest_lookup_id. + (digest_algo_to_string): Likewise. + (check_digest_algo): Likewise. + (md_enable): Likewise. + (md_digest_length): Likewise. + (md_asn_oid): Likewise. + + * pubkey.c (gcry_pubkey_lookup_id): Removed function. + (gcry_pubkey_lookup_func_id): Likewise. + (gcry_pubkey_id_new): Use _gcry_module_lookup_id instead of + gcry_pubkey_id_new. + (gcry_pk_algo_name): Likewise. + (disable_pubkey_algo): Likewise. + (check_pubkey_algo): Likewise. + (pubkey_get_npkey): Likewise. + (pubkey_get_nskey): Likewise. + (pubkey_get_nsig): Likewise. + (pubkey_get_nenc): Likewise. + (pubkey_generate): Likewise. + (pubkey_check_secret_key): Likewise. + (pubkey_encrypt): Likewise. + (pubkey_decrypt): Likewise. + (pubkey_sign): Likewise. + (pubkey_verify): Likewise. + (gcry_pk_algo_info): Likewise. + + * cipher.c (gcry_cipher_lookup_func_id): Removed function. + (gcry_cipher_lookup_id): Likewise. + (cipher_algo_to_string): use _gcry_module_lookup_id instead of + gcry_cipher_lookup_id. + (disable_cipher_algo): Likewise. + (check_cipher_algo): Likewise. + (cipher_get_blocksize): Likewise. + (gcry_cipher_open): Likewise. + (gcry_cipher_id_new): Likewise. + +2003-06-17 Moritz Schulte + + * Makefile.am (GCRYPT_MODULES): Set to @GCRYPT_CIPHERS@, + @GCRYPT_PUBKEY_CIPHERS@, @GCRYPT_DIGESTS@ and @GCRYPT_RANDOM@. + (libcipher_la_DEPENDENCIES): Set to $(GCRYPT_MODULES). + (libcipher_la_LIBADD): Likewise. + (AM_CFLAGS): Added: @GPG_ERROR_CFLAGS@. + (EXTRA_libcipher_la_SOURCES): Added all conditional sources. + + * md.c (md_open): Use _gcry_fast_random_poll instead of + fast_random_poll. + * cipher.c (gcry_cipher_open): Likewise. + + * random.h (fast_random_poll): Removed macro. + + * blowfish.c, md4.c, md5.c, rmd160.c, sha1.c, sha256.c, sha512.c, + tiger.c: Use Autoconf's WORDS_BIGENDIAN instead of our own + BIG_ENDIAN_HOST. + +2003-06-16 Moritz Schulte + + * random.c (getfnc_gather_random): Do not special-case + USE_ALL_RANDOM_MODULES, make it the default. + + * dsa.c: Replace last occurences of old type names with newer + names (i.e. replace MPI with gcry_mpi_t). + * elgamal.c: Likewise. + * primegen.c: Likewise. + * pubkey.c: Likewise. + * rsa.c: Likewise. + +2003-06-14 Moritz Schulte + + * des.c (des_setkey): Add selftest check. + (tripledes_set3keys): Likewise. + (do_tripledes_setkey): Remove selftest check. + (do_des_setkey): Likewise. + +2003-06-11 Moritz Schulte + + * md.c (_gcry_md_init): New function. + * cipher.c (_gcry_cipher_init): New function. + * pubkey.c (_gcry_pk_init): New function. + +2003-06-13 Werner Koch + + * md.c (gcry_md_get_algo): Reverted to old API. This is a + convenience function anyway and error checking is not approriate. + (gcry_md_is_secure): New. + (gcry_md_is_enabled): New. + +2003-06-12 Werner Koch + + * cipher.c (gcry_cipher_open): Make sure HANDLE is set to NULL on + error. + +2003-06-11 Werner Koch + + * md.c (gcry_md_open): Make sure H receives either NULL or an + valid handle. + (gcry_md_copy): Swapped arguments so that it is more in lione with + md_open and most other API fucntions like memcpy (destination + comes first). Make sure HANDLE is set to NULL on error. + + * rijndael.c (do_encrypt): Hack to force correct alignment. It + seems not to be not sufficient, though. We should rework this + fucntions and remove all these ugly casts. Let the compiler + optimize or have an assembler implementation. + +2003-06-09 Moritz Schulte + + * Makefile.am: Removed rules serpent, since that is not commited + yet. + +2003-06-08 Moritz Schulte + + * pubkey.c (gcry_pk_encrypt): Improve calculation for size of the + format string. + +2003-06-07 Moritz Schulte + + * arcfour.c, bithelp.h, blowfish.c, cast5.c, cipher.c, crc.c, + des.c, dsa.c, elgamal.c, md4.c, md5.c, md.c, primegen.c, pubkey.c, + rand-internal.h, random.c, random.h, rijndael.c, rmd160.c, + rmd160test.c, rmd.h, rndeged.c, rndlinux.c, rndunix.c, rndw32.c, + rsa.c, sha1.c, sha256.c, sha512.c, tiger.c, twofish.c: Edited all + preprocessor instructions to remove whitespace before the '#'. + This is not required by C89, but there are some compilers out + there that don't like it. Replaced any occurence of the now + deprecated type names with the new ones. + +2003-06-04 Moritz Schulte + + * pubkey.c (gcry_pk_encrypt): Construct an arg_list and use + gcry_sexp_build_array instead of gcry_sexp_build. + (gcry_pk_sign): Likewise. + (gcry_pk_genkey): Likewise. + +2003-06-01 Moritz Schulte + + * dsa.c (_gcry_dsa_generate): Do not check wether the algorithm ID + does indeed belong to DSA. + (_gcry_dsa_sign): Likewise. + (_gcry_dsa_verify): Likewise. + (_gcry_dsa_get_nbits): Likewise. + + * elgamal.c (_gcry_elg_check_secret_key): Do not check wether the + algorithm ID does indeed belong to ElGamal. + (_gcry_elg_encrypt): Likewise. + (_gcry_elg_decrypt): Likewise. + (_gcry_elg_sign): Likewise. + (_gcry_elg_verify): Likewise. + (_gcry_elg_get_nbits): Likewise. + (_gcry_elg_generate): Likewise. + + * rsa.c (_gcry_rsa_generate): Do not check wether the algorithm ID + does indeed belong to RSA. + (_gcry_rsa_encrypt): Likewise. + (_gcry_rsa_decrypt): Likewise. + (_gcry_rsa_sign): Likewise. + (_gcry_rsa_verify): Likewise. + (_gcry_rsa_get_nbits): Likewise. + +2003-05-30 Moritz Schulte + + * md.c (md_get_algo): Return zero in case to algorithm is enabled. + + * md.c (gcry_md_info): Adjusted for new no-errno-API. + (md_final): Likewise. + (gcry_md_get_algo): Likewise. + * pubkey.c (gcry_pk_get_keygrip): Likewise. + (gcry_pk_ctl): Likewise. + (gcry_pk_algo_info): Likewise. + * des.c (selftest): Likewise. + +2003-05-29 Moritz Schulte + + * md.c (md_enable): Do not forget to release module on error. + (gcry_md_open): Adjusted for new no-errno-API. + (md_open): Likewise. + (md_copy): Likewise. + (gcry_md_copy): Likewise. + (gcry_md_setkey): Likewise. + (gcry_md_algo_info): Likewise. + + * cipher.c (gcry_cipher_open): Adjusted for new no-errno-API and + also fixed a locking bug. + (gcry_cipher_encrypt): Adjusted for new no-errno-API. + (gcry_cipher_decrypt): Likewise. + (gcry_cipher_ctl): Likewise. + (gcry_cipher_info): Likewise. + (gcry_cipher_algo_info): Likewise. + +2003-05-28 Moritz Schulte + + * md.c (md_enable): Adjusted for libgpg-error. + (gcry_md_enable): Likewise. + (gcry_digest_register_default): Likewise. + (gcry_digest_register): Likewise. + (check_digest_algo): Likewise. + (prepare_macpads): Likewise. + (gcry_md_setkey): Likewise. + (gcry_md_ctl): Likewise. + (gcry_md_get): Likewise. + (gcry_md_algo_info): Likewise. + (gcry_md_info): Likewise. + * dsa.c (_gcry_dsa_generate): Likewise. + (_gcry_dsa_check_secret_key): Likewise. + (_gcry_dsa_sign): Likewie. + (_gcry_dsa_verify): Likewise. + * twofish.c (do_twofish_setkey): Likewise. + (twofish_setkey): Likewise. + * cipher.c (gcry_cipher_register): Likewise. + +2003-05-25 Moritz Schulte + + * rijndael.c (do_setkey): Adjusted for libgpg-error. + (rijndael_setkey): Likewise. + * random.c (gcry_random_add_bytes): Likewise. + * elgamal.c (_gcry_elg_generate): Likewise. + (_gcry_elg_check_secret_key): Likewise. + (_gcry_elg_encrypt): Likewise. + (_gcry_elg_decrypt): Likewise. + (_gcry_elg_sign): Likewise. + (_gcry_elg_verify): Likewise. + * rsa.c (_gcry_rsa_generate): Likewise. + (_gcry_rsa_check_secret_key): Likewise. + (_gcry_rsa_encrypt): Likewise. + (_gcry_rsa_decrypt): Likewise. + (_gcry_rsa_sign): Likewise. + (_gcry_rsa_verify): Likewise. + * pubkey.c (dummy_generate, dummy_check_secret_key, dummy_encrypt, + dummy_decrypt, dummy_sign, dummy_verify): Likewise. + (gcry_pubkey_register): Likewise. + (check_pubkey_algo): Likewise. + (pubkey_generate): Likewise. + (pubkey_check_secret_key): Likewise. + (pubkey_encrypt): Likewise. + (pubkey_decrypt): Likewise. + (pubkey_sign): Likewise. + (pubkey_verify): Likewise. + (sexp_elements_extract): Likewise. + (sexp_to_key): Likewise. + (sexp_to_sig): Likewise. + (sexp_to_enc): Likewise. + (sexp_data_to_mpi): Likewise. + (gcry_pk_encrypt): Likewise. + (gcry_pk_decrypt): Likewise. + (gcry_pk_sign): Likewise. + (gcry_pk_verify): Likewise. + (gcry_pk_testkey): Likewise. + (gcry_pk_genkey): Likewise. + (gcry_pk_ctl): Likewise. + * cipher.c (dummy_setkey): Likewise. + (check_cipher_algo): Likewise. + (gcry_cipher_open): Likewise. + (cipher_setkey): Likewise. + (gcry_cipher_ctl): Likewise. + (cipher_encrypt): Likewise. + (gcry_cipher_encrypt): Likewise. + (cipher_decrypt): Likewise. + (gcry_cipher_decrypt): Likewise. + (gcry_cipher_info): Likewise. + (gcry_cipher_algo_info): Likewise. + * cast5.c (cast_setkey): Likewise. + (do_cast_setkey): Likewise. + * arcfour.c (arcfour_setkey): Likewise. + (do_arcfour_setkey): Likewise. + * blowfish.c (do_bf_setkey): Likewise. + (bf_setkey): Likewise. + * des.c (do_des_setkey): Likewise. + (do_tripledes_setkey): Likewise. + +2003-05-22 Moritz Schulte + + * tiger.c: Merged code ussing the U64_C macro from GnuPG. + + * sha512.c: Likewise. + +2003-05-17 Moritz Schulte + + * pubkey.c (gcry_pk_genkey): Fix type: acquire a lock, instead of + releasing it. + +2003-05-11 Moritz Schulte + + * pubkey.c (gcry_pk_testkey): Call REGISTER_DEFAULT_CIPHERS. + (gcry_pk_ctl): Likewise. + +2003-04-27 Moritz Schulte + + * pubkey.c (gcry_pk_genkey): Release sexp after extracted data has + been used. + + * md.c (gcry_md_get_algo_dlen): Simplified, simply call + md_digest_length to do the job. + + * des.c (do_des_setkey): Check for selftest failure not only + during initialization. + (do_tripledes_setkey): Include check for selftest failure. + + * pubkey.c (gcry_pubkey_register_default): New macro + `pubkey_use_dummy', use it. + + * elgamal.c (elg_names): New variable. + (pubkey_spec_elg): Include elg_names. + + * dsa.c (dsa_names): New variable. + (pubkey_spec_dsa): Include dsa_names. + + * rsa.c (rsa_names): New variable. + (pubkey_spec_rsa): Include rsa_names. + + * pubkey.c (gcry_pubkey_lookup_func_name): Compare name also with + the names listed in `sexp_names'. + +2003-04-24 Moritz Schulte + + * pubkey.c (sexp_to_key): New variables: module, pubkey. Adjusted + to new module interface. + (sexp_to_key): Changend type of argument `retalgo' from `int *' to + `GcryModule **'. Adjusted all callers. Removed argument: + r_algotblidx. + (sexp_to_sig): Changend type of argument `retalgo' from `int *' to + `GcryModule **'. Adjusted all callers. + (sexp_to_enc): Likewise. + + (pubkey_get_npkey, pubkey_get_nskey, pubkey_get_nsig, + pubkey_get_nenc): Use strlen to find out the number. + + * rsa.c: Adjust pubkey_spec_rsa to new internal interface. + * dsa.c: Likewise. + * elgamal.c: Likewise. + +2003-04-17 Moritz Schulte + + * pubkey.c (sexp_elements_extract): New function. + * pubkey.c (sexp_to_key): Removed variable `idx', added `err', use + sexp_elements_extract. + (sexp_to_sig): Likewise. + (sexp_to_enc): Likewise. + + * pubkey.c: Terminate list correctly. + * md.c: Include sha512/sha384 in digest_table. + +2003-04-16 Moritz Schulte + + * Makefile.am: Include support for sha512.c. + + * sha512.c: New file, merged from GnuPG, with few modifications + for libgcrypt. + + * rand-internal.h: Removed declarations for constructor functions. + + * md.c (md_copy): Call _gcry_module_use for incrementing the usage + counter of the digest modules. + + * rsa.c: Do not include "rsa.h". + * dsa.c: Do not include "dsa.h". + * elgamal.c: Do not include "elgamal.h". + * des.c: Do not include "des.h". + * cast5.c: Do not include "cast5.h". + * blowfish.c: Do not include "blowfish.h". + * arcfour.c: Do not include "arcfour.h". + + * Makefile.am (libcipher_la_DEPENDENCIES): Removed. + (libcipher_la_LIBADD): Removed. + Use Automake conditionals for conditional compilation. + +2003-04-13 Moritz Schulte + + * cipher.c (gcry_cipher_open): Call REGISTER_DEFAULT_CIPHERS. + + * md.c (gcry_md_list): New member: module. + (md_enable): New variable: module, changed use of module and + digest. + (md_enable): Initialize member: module. + (md_close): Call _gcry_module_release. + + * cipher.c (gcry_cipher_open): New variable: module, changed use of + module and cipher. + (struct gcry_cipher_handle): New member: module. + (gcry_cipher_open): Initialize member: module. + (gcry_cipher_close): Call _gcry_module_release. + +2003-04-09 Moritz Schulte + + * cipher.c: Include "ath.h". + * md.c: Likewise. + * pubkey.c: Likewise. + + * cipher.c (ciphers_registered_lock): New variable. + * md.c (digests_registered_lock): New variable. + * pubkey.c (pubkeys_registered_lock): New variable. + + * rndlinux.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_rndlinux_constructor): Removed function. + + * rndegd.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_rndegd_constructor): Removed function. + + * rndunix.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_rndunix_constructor): Removed function. + + * rndw32.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_rndw32_constructor): Removed function. + + * rndegd.c (rndegd_connect_socket): Simplify code for creating the + egd socket address. + (rndegd_connect_socket): Call log_fatal use instead of + g10_log_fatal. + (egd_gather_random): Renamed to ... + (rndegd_gather_random): ... here. + +2003-04-08 Moritz Schulte + + * rndlinux.c: Do not include "dynload.h". + * rndunix.c: Likewise. + * rndw32.c: Likewise. + + * rndegd.c (rndegd_connect_socket): Factored out from ... + (egd_gather_random): here; call it. + (egd_socket): New variable. + (egd_gather_random): Initialize fd with egd_socket, do not declare + fd static. + (do_read): Merged few changes from GnuPG. FIXME - not finished? + Do not include "dynload.h". + + * rndw32.c (gather_random): Renamed to rndw32_gather_random, do + not declare static. + (gather_random_fast): Renamed to rndw32_gather_random_fast, do not + declare static. + + * rndunix.c (gather_random): Renamed to rndunix_gather_random, do + not declare static. + * rndegd.c (gather_random): Renamed to rndegd_gather_random, do + not declare static. + * rndlinux.c (gather_random): Renamed to rndlinux_gather_random, + do not declare static. + +2003-04-07 Moritz Schulte + + * Makefile.am (libcipher_la_SOURCES): Removed construct.c. + (libcipher_la_SOURCES): Added sha1.c, sha256.c, rmd160.c, md4.c, + md5.c, tiger.c and crc.c + (EXTRA_PROGRAMS): Removed sha1, sha256, rmd160, md4, md5, tiger + and crc. Removed definitions: EXTRA_md4_SOURCES, + EXTRA_md5_SOURCES, EXTRA_rmd160_SOURCES, EXTRA_sha1_SOURCES, + EXTRA_sha256_SOURCES, EXTRA_tiger_SOURCES and EXTRA_crc_SOURCES, + BUILT_SOURCES, DISTCLEANFILES. + + * pubkey.c: Do not include "elgamal.h", "dsa.h" and "rsa.h". + + * Makefile.am (libcipher_la_SOURCES): Removed rsa.h, elgamal.h, + dsa.h, des.h, cast5.h, arcfour.h and blowfish.h. + + * rsa.h: Removed file. + * elgamal.h: Removed file. + * dsa.h: Removed file. + * des.h: Removed file. + * cast5.h: Removed file. + * arcfour.h: Removed file. + * blowfish.h: Removed file. + + * Makefile.am (libcipher_la_SOURCES): Removed dynload.c and + dynload.h. + + * rsa.c (pubkey_spec_rsa): New variable. + * dsa.c (pubkey_spec_rsa): New variable. + * elgamal.c (pubkey_spec_elg): New variable. + + * rsa.c (_gcry_rsa_get_info): Removed function. + * elgamal.c (_gcry_elg_get_info): Removed function. + * dsa.c (_gcry_dsa_get_info): Removed function. + + * tiger.c (tiger_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_tiger_constructor): Removed function. + + * sha1.c (sha1_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_sha1_constructor): Removed function. + + * sha256.c (sha256_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_sha256_constructor): Removed function. + + * rmd160.c (rmd160_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_rmd160_constructor): Removed function. + + * md5.c (md5_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_md5_constructor): Removed function. + + * md4.c (md4_get_info): Removed function. + (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func): Removed function. + (_gcry_md4_constructor): Removed function. + + * crc.c (crc_get_info): Removed function. + + * arcfour.c (do_arcfour_setkey): Changed type of context argument + to `void *', added local variable for cast, adjusted callers. + (arcfour_setkey): Likewise. + (encrypt_stream): Likewise. + * cast5.c (cast_setkey): Likewise. + (encrypt_block): Likewise. + * rijndael.c (rijndael_setkey): Likewise. + (rijndael_encrypt): Likewise. + (rijndael_decrypt): Likewise. + * twofish.c (twofish_setkey): Likewise. + (twofish_encrypt): Likewise. + (twofish_decrypt): Likewise. + * des.c (do_des_setkey): Likewise. + (do_des_encrypt): Likewise. + (do_des_encrypt): Likewise. + (do_tripledes_encrypt): Likewise. + (do_tripledes_encrypt): Likewise. + * blowfish.c (bf_setkey: Likewise. + (encrypt_block): Likewise. + (decrypt_block): Likewise. + + * arcfour.c (encrypt_stream): Likewise. + + * rijndael.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func) Removed function. + + * twofish.c (gnupgext_version, func_table): Removed definitions. + (gnupgext_enum_func) Removed function. + + * cast5.c (CIPHER_ALGO_CAST5): Removed. + + * blowfish.c (FNCCAST_SETKEY, FNCCAST_CRYPT): Removed macros. + (CIPHER_ALGO_BLOWFISH): Removed symbol. + * cast5.c (FNCCAST_SETKEY, FNCCAST_CRYPT): Likewise. + * des.c (selftest_failed): Removed. + (initialized): New variable. + (do_des_setkey): Run selftest, if not yet done. + (FNCCAST_SETKEY, FNCCAST_CRYPT): Removed macros. + + * arcfour.c (_gcry_arcfour_get_info): Removed function. + * blowfish.c (_gcry_blowfish_get_info): Removed function. + * cast5.c (_gcry_cast5_get_info): Removed function. + * des.c (_gcry_des_get_info): Removed function. + * rijndael.c (_gcry_rijndael_get_info): Removed function. + * twofish.c (_gcry_twofish_get_info): Removed function. + + * arcfour.c (cipher_spec_arcfour): New variable. + * twofish.c (cipher_spec_twofish, cipher_spec_twofish128): New + variables. + * rijndael.c (cipher_spec_aes, cipher_spec_aes192, + cipher_spec256): New variables. + * des.c (cipher_spec_des, cipher_spec_tripledes): New variables. + * cast5.c (cipher_spec_cast5): New variable. + * blowfish.c (cipher_spec_blowfish): Likewise. + + * twofish.c: Do not include "dynload.h". + * rijndael.c: Likewise. + * des.c: Likewise. + * cast5.c: Likewise. + * blowfish.c: Likewise. + * cipher.c: Likewise. + * crc.c: Likewise. + * md4.c: Likewise. + * md5.c: Likewise. + * md.c: Likewise. + * pubkey.c: Likewise. + * rijndael.c: Likewise. + * sha1.c: Likewise. + * sha256.c: Likewise. + + * arcfour.c: Include "cipher.h". + * twofish.c: Likewise. + * rijndael.c: Likewise. + * des.c: Likewise. + * cast5.c: Likewise. + * blowfish.c: Likewise. + + * twofish.c (twofish_setkey): Declared argument `key' const. + (twofish_encrypt): Declared argument `inbuf' const. + (twofish_decrypt): Likewise. + + * rijndael.c (rijndael_setkey): Declared argument `key' const. + (rijndael_encrypt): Declared argument `inbuf' const. + (rijndael_decrypt): Likewise. + + * des.c (do_des_setkey): Declared argument `key' const. + (do_tripledes_setkey): Likewise. + (do_des_encrypt): Declared argument `inbuf' const. + (do_des_decrypt): Likewise. + (do_tripledes_encrypt): Likewise. + (do_tripledes_decrypt): Likewise. + + * cast5.c (encrypt_block): Declared argument `inbuf' const. + (decrypt_block): Likewise. + (cast_setkey): Declared argument `key' const. + + * blowfish.c (do_bf_setkey): Declared argument `key' const. + (encrypt_block): Declared argument `inbuf' const. + (encrypt_block): Likewise. + + + + * cipher.c: Remove CIPHER_ALGO_DUMMY related code. + Removed struct cipher_table_s. + Changed definition of cipher_table. + Removed definition of disabled_algos. + (ciphers_registered, default_ciphers_registered): New variables. + (REGISTER_DEFAULT_CIPHERS): New macro. + (dummy_setkey): Declared argument `key' const. + (dummy_encrypt_block): Declared argument `inbuf' const. + (dummy_encrypt_block): Likewise. + (dummy_encrypt_stream): Likewise. + (dummy_encrypt_stream): Likewise. + (dummy_setkey): Use `unsigned char' instead of `byte'. + (dummy_encrypt_block): Likewise. + (dummy_decrypt_block): Likewise. + (dummy_encrypt_stream): Likewise. + (dummy_decrypt_stream): Likewise. + (gcry_cipher_register_default): New function. + (gcry_cipher_lookup_func_id): New function. + (gcry_cipher_lookup_func_name): New function. + (gcry_cipher_lookup_id): New function. + (gcry_cipher_lookup_name): New function. + (gcry_cipher_id_new): New function. + (gcry_cipher_register): New function. + (gcry_cipher_unregister): New function. + (setup_cipher_table): Removed function. + (load_cipher_modules): Removed function. + (gcry_cipher_map_name): Adjusted to use new module management. + (cipher_algo_to_string): Likewise. + (disable_cipher_algo): Likewise. + (check_cipher_algo): Likewise. + (cipher_get_keylen): Likewise. + (cipher_get_blocksize): Likewise. + (gcry_cipher_open): Likewise. + (struct gcry_cipher_handle): Replaced members algo, algo_index, + blocksize, setkey, encrypt, decrypt, stencrypt, stdecrypt with one + member: cipher. + (gcry_cipher_open): Adjusted code for new handle structure. + (cipher_setkey): Likewise. + (cipher_setiv): Likewise. + (cipher_reset): Likewise. + (do_ecb_encrypt): Likewise. + (do_ecb_decrypt): Likewise. + (do_cbc_encrypt): Likewise. + (do_cbc_decrypt): Likewise. + (do_cfb_encrypt): Likewise. + (do_cfb_decrypt): Likewise. + (do_ctr_encrypt): Likewise. + (cipher_encrypt): Likewise. + (gcry_cipher_encrypt): Likewise. + (cipher_decrypt): Likewise. + (gcry_cipher_decrypt): Likewise. + (cipher_sync): Likewise. + (gcry_cipher_ctl): Likewise. + + * pubkey.c: Removed struct pubkey_table_s. + Changed definition of pubkey_table. + Removed definition of disabled_algos. + (pubkeys_registered, default_pubkeys_registered): New variables. + (REGISTER_DEFAULT_PUBKEYS): New macro. + (setup_pubkey_table): Removed function. + (load_pubkey_modules): Removed function. + (gcry_pubkey_register_default): New function. + (gcry_pubkey_lookup_func_id): New function. + (gcry_pubkey_lookup_func_name): New function. + (gcry_pubkey_lookup_id): New function. + (gcry_pubkey_lookup_name): New function. + (gcry_pubkey_id_new): New function. + (gcry_pubkey_register): New function. + (gcry_pubkey_unregister): New function. + (gcry_pk_map_name): Adjusted to use new module management. + (gcry_pk_algo_name): Likewise. + (disable_pubkey_algo): Likewise. + (check_pubkey_algo): Likewise. + (pubkey_get_npkey): Likewise. + (pubkey_get_nskey): Likewise. + (pubkey_get_nsig): Likewise. + (pubkey_get_nenc): Likewise. + (pubkey_generate): Likewise. + (pubkey_check_secret_key): Likewise. + (pubkey_encrypt): Likewise. + (pubkey_decrypt): Likewise. + (pubkey_sign): Likewise. + (pubkey_verify): Likewise. + (gcry_pk_get_nbits): Likewise. + (gcry_pk_algo_info): Likewise. + + * md.c: Removed struct md_digest_list_s. + (digest_list): Changed definition. + (digests_registered, default_digests_registered): New variables. + (REGISTER_DEFAULT_DIGESTS): New macro. + (new_list_item): Removed function. + (setup_md_table): Removed function. + (load_digest_module): Removed function. + (gcry_digest_register_default): New function. + (gcry_digest_lookup_func_id): New function. + (gcry_digest_lookup_func_name): New function. + (gcry_digest_lookup_id): New function. + (gcry_digest_lookup_name): New function. + (gcry_digest_id_new): New function. + (gcry_digest_register): New function. + (gcry_digest_unregister): New function. + (GcryDigestEntry): New type. + (struct gcry_md_context): Adjusted type of `list'. + (gcry_md_map_name): Adjusted to use new module management. + (digest_algo_to_string): Likewise. + (check_digest_algo): Likewise. + (md_enable): Likewise. + (md_digest_length): Likewise. + (md_asn_oid): Likewise. + +2003-04-07 Moritz Schulte + + * pubkey.c: Replaced PUBKEY_ALGO_DSA with GCRY_PK_DSA, + PUBKEY_ALGO_RSA with GCRY_PK_RSA and PUBKEY_ALGO_ELGAMAL with + GCRY_PK_ELG. + + * dsa.c: Replaced PUBKEY_ALGO_DSA with GCRY_PK_DSA. + +2003-04-01 Moritz Schulte + + * des.c: Removed checks for GCRY_CIPHER_3DES and GCRY_CIPHER_DES. + +2003-03-31 Moritz Schulte + + * tiger.c (tiger_get_info): Do not declare static. + * sha256.c (sha256_get_info): Likewise. + * sha1.c (sha1_get_info): Likewise. + * rmd160.c (rmd160_get_info): Likewise. + * md5.c (md5_get_info): Likewise. + * md4.c (md4_get_info): Likewise. + * crc.c (crc_get_info): Likewise. + + * md.c (load_digest_module): Call setup_md_table during + initialization. + (new_list_item): Link new element into digest_list. + + * cipher.c (do_ctr_decrypt): Made do_ctr_encrypt act as a wrapper + for do_ctr_encrypt, since these functions are identical. + +2003-03-30 Simon Josefsson + + * cipher.c (struct gcry_cipher_handle): Add counter field. + (gcry_cipher_open): Add CTR. + (cipher_reset): Clear counter field. + (do_ctr_encrypt, do_ctr_decrypt): New functions. + (cipher_encrypt, cipher_decrypt): Call CTR functions. + (gcry_cipher_ctl): Add SET_CTR to set counter. + +2003-03-30 Moritz Schulte + + * rsa.c (_gcry_rsa_blind): New function. + (_gcry_rsa_unblind): New function. + (_gcry_rsa_decrypt): Use _gcry_rsa_blind and _gcry_rsa_decrypt. + +2003-03-26 Moritz Schulte + + * dynload.c (_gcry_enum_gnupgext_pubkeys): Adjust `encrypt' and + `decrypt' function arguments. + (_gcry_enum_gnupgext_pubkeys): Likewise. + * dynload.h: Likewise. + + * pubkey.c (dummy_decrypt): Add argument: int flags. + (dummy_encrypt): Likewise. + + * elgamal.c (_gcry_elg_encrypt): Add argument: int flags. + (_gcry_elg_decrypt): Likewise. + + * rsa.c (_gcry_rsa_encrypt): Add argument: int flags. + (_gcry_rsa_decrypt): Likewise. + + * pubkey.c: Add `flags' argument to members `encrypt' and + `decrypt' of struct `pubkey_table_s'. + + * rsa.h: Add `flags' argument to function declarations. + * elgamal.h: Likewise. + + * pubkey.c (sexp_data_to_mpi): New variable: int parsed_flags. + (sexp_data_to_mpi): Set `parsed_flags'. + (sexp_data_to_mpi): New argument: int *flags. + (gcry_pk_encrypt): New variable: int flags. + (gcry_pk_encrypt): Pass `flags' to pubkey_encrypt. + (pubkey_encrypt): New variable: int flags. + (pubkey_encrypt): Pass `flags' to pubkey encrypt function. + (pubkey_decrypt): Likewise. + (pubkey_decrypt): Pass `flags' to pubkey encrypt function. + (gcry_pk_encrypt): Include `flags' s-exp in return list. + (sexp_to_enc): New argument: int *flags. + (gcry_pk_decrypt): New variable: int flags. + (gcry_pk_decrypt): Pass `flags' to pubkey_decrypt. + (sexp_to_enc): New variable: int parsed_flags. + (sexp_to_enc): Set `parsed_flags'. + +2003-03-22 Simon Josefsson + + * cipher.c (gcry_cipher_open, do_cbc_encrypt) + (gcry_cipher_encrypt): Support GCRY_CIPHER_CBC_MAC. + (gcry_cipher_ctl): Support GCRYCTL_SET_CBC_MAC. + +2003-03-19 Werner Koch + + * primegen.c (gen_prime): New args EXTRA_CHECK and EXTRA_CHECK_ARG + to allow for a user callback. Changed all callers. + (_gcry_generate_secret_prime) + (_gcry_generate_public_prime): Ditto, pass them to gen_prime. + * rsa.c (check_exponent): New. + (generate): Use a callback to ensure that a given exponent is + actually generated. + +2003-03-12 Moritz Schulte + + * primegen.c: Initialize `no_of_small_prime_numbers' statically. + (gen_prime): Remove calculation of `no_of_small_prime_numbers'. + +2003-03-03 Moritz Schulte + + * md.c (gcry_md_ctl): Rewritten to use same style like the other + functions dispatchers. + +2003-03-02 Moritz Schulte + + * cipher.c (struct gcry_cipher_handle): New member: algo_index. + (gcry_cipher_open): Allocate memory for two cipher contexts. + Initialize algo_index. + (cipher_setkey): Duplicate context into reserved memory. + (cipher_reset): New function, which resets the context and clear + the IV. + (gcry_cipher_ctl): Call cipher_reset. + +2003-02-23 Moritz Schulte + + * cipher.c: Remove (bogus) `digitp' macro definition. + * md.c: Likewise. + + * blowfish.c (burn_stack): Removed. + * arcfour.c (burn_stack): Likewise. + * cast5.c (burn_stack): Likewise. + * des.c (burn_stack): Likewise. + * md4.c (burn_stack): Likewise. + * md5.c (burn_stack): Likewise. + * random.c (burn_stack): Likewise. + * rijndael.c (burn_stack): Likewise. + * rmd160.c (burn_stack): Likewise. + * sha1.c (burn_stack): Likewise. + * sha256.c (burn_stack): Likewise. + * tiger.c (burn_stack): Likewise. + * twofish.c (burn_stack): Likewise. + + * blowfish.c: Changed all occurences of burn_stack to + _gcry_burn_stack. + * arcfour.c: Likewise. + * cast5.c: Likewise. + * des.c: Likewise. + * md4.c: Likewise. + * md5.c: Likewise. + * random.c: Likewise. + * rijndael.c: Likewise. + * rmd160.c: Likewise. + * sha1.c: Likewise. + * sha256.c: Likewise. + * tiger.c: Likewise. + * twofish.c: Likewise. + + * arcfour.c (_gcry_arcfour_get_info): Use GCRY_CIPHER_ARCFOUR + instead of hard-coded value `301'. + +2003-01-24 Werner Koch + + * random.c (_gcry_register_random_progress): New. + (_gcry_random_progress): New. + + * rndlinux.c (gather_random): Call the random progress function. + +2003-01-23 Werner Koch + + * rsa.c (generate): New arg USE_E to request a specific public + exponent. + (_gcry_rsa_generate): Ditto. + * elgamal.c (_gcry_elg_generate): Must add an dummy argument + instead of USE_E. + * dsa.c (_gcry_dsa_generate): Ditto. + * pubkey.c (dummy_generate): Ditto. + (pubkey_generate): Add USE_E arg and pass it down. + (gcry_pk_genkey): Detect "rsa-use-e" parameter and pass it to generate. + + * pubkey.c (sexp_to_enc): New arg RET_MODERN. + (gcry_pk_decrypt): Make use of it to return a real S-expression. + Return better error codes. + (gcry_pk_verify): Return better error codes. + +2003-01-21 Werner Koch + + * random.c (gcry_random_add_bytes): Add QUALITY argument, let + function return an error code and disable its core for now. + +2003-01-21 Timo Schulz + + * random.c (gcry_random_add_bytes): New. Function to add external + random to the pool. + +2003-01-20 Simon Josefsson + + * crc.c: New. + * Makefile.am (EXTRA_PROGRAMS, EXTRA_crc_SOURCES): Add crc.c. + * md.c (gcry_md_get_algo_dlen): Add values for CRC. + +2003-01-20 Werner Koch + + * sha256.c: New. + * bithelp.h (ror): New. + * Makfile.am: Add sha256.c. + * md.c (oid_table): Add values for SHA256 et al. + (gcry_md_get_algo_dlen): Likewise + +2003-01-20 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): Implemented keygrips for DSA + and ElGamal. + +2003-01-17 Werner Koch + + * cipher.c (gcry_cipher_encrypt): Reworked so that the output will + never contain the plaintext even if the caller did not checked the + return value. + + * md.c (gcry_md_get_algo): Changed error code to GCRYERR_GENERAL + because we don't have an invalid md algo but no algorithm enabled. + + * pubkey.c (gcry_pk_genkey): Changed error code for bounds check + of table parameters to GCRYERR_INTERNAL. + + * md.c (gcry_md_open): Partly reverted Timo's change from + 2002-10-10 by removing the check for the algorithm. An algorithm + of 0 is allowed and anyway we should not double check it or check + it using a different function. Also fixed the flags check. + + * pubkey.c (gcry_pk_encrypt): Make sure that R_CIPH points to NULL + on error. + (gcry_pk_decrypt): Ditto for R_PLAIN. + (gcry_pk_sign): Ditto for R_SIG. + (gcry_pk_genkey): Ditto for R_KEY. + +2003-01-16 Werner Koch + + * md.c (gcry_md_write): Changed 2nd argument type to void*. + (gcry_md_hash_buffer): Changed type of boths buffers to void*. + (gcry_md_setkey): Changed 2nd argument type to void*. + +2003-01-15 Werner Koch + + * pubkey.c (sexp_data_to_mpi): New. This handles pkcs1 padding. + (gcry_pk_sign, gcry_pk_verify): Use it here. + (gcry_pk_encrypt): And here. + (pubkey_verify): Add debug code. + (sexp_to_enc): Handle flags in the input and return the pkcs1 flag + in a new parameter. + (gcry_pk_decrypt): Prepare for future pkcs1 handling. + +2002-12-19 Werner Koch + + * random.c (_gcry_random_initialize): New. + +2002-12-16 Werner Koch + + * cipher.c: Added a Teletrust specific OID for 3DES. + +2002-12-12 Werner Koch + + * md.c: Added another oddball OIW OID (sha-1WithRSAEncryption). + +2002-11-23 Werner Koch + + * md.c (load_digest_module): Enlarged checked_algos bitmap. + * md4.c (func_table): Fixed entry for md4. + Both by Simon Josephson. + (transform): Copy data to get the alignment straight. Tested only + on i386. + +2002-11-10 Simon Josefsson + + * cipher.c (gcry_cipher_open): Don't reject CTS flag. + (do_cbc_encrypt, do_cbc_decrypt, cipher_encrypt) + (gcry_cipher_encrypt, cipher_decrypt) + (gcry_cipher_decrypt): Support CTS flag. + (gcry_cipher_ctl): Toggle CTS flag. + +2002-11-10 Werner Koch + + * md4.c: New. By Simon Josefsson. + * Makefile.am (EXTRA_PROGRAMS): Add md4.c. + * md.c (oid_table,gcry_md_get_algo_dlen): MD4 support. + +2002-10-14 Werner Koch + + * arcfour.c (do_encrypt_stream): Don't use increment op when + assigning to the same variable. + +2002-10-10 Timo Schulz + + * pubkey.c (gcry_pk_genkey): Check boundaries. + + * md.c (gcry_md_open): Check that algo is available and only + valid flag values are used. + (gcry_md_get_algo): Add error handling. + +2002-09-26 Werner Koch + + * md.c: Include an OID for TIGER. + * tiger.c (tiger_get_info): Use a regular OID. + +2002-09-17 Werner Koch + + * random.c: Replaced mutex.h by the new ath.h. Changed all calls. + +2002-09-16 Werner Koch + + * arcfour.c (do_encrypt_stream): Use register modifier and modulo. + According to Nikos Mavroyanopoulos this increases perfromace on + i386 system noticable. And I always tought gcc is clever enough. + * md5.c (transform): Use register modifier. + * rmd160.c (transform): Ditto. + * sha1.c (transform): Ditto. We hope that there are 6 free registers. + * random.c (gcry_randomize): Rewrote to avoid malloc calls. + + * rndlinux.c (gather_random): Replaced remaining fprintfs by log_*. + * arcfour.c (do_arcfour_setkey): Ditto. + * twofish.c (do_twofish_setkey): Ditto. + * rndegd.c (gather_random): Ditto. + * rijndael.c (do_setkey): Ditto. + * random.c (_gcry_random_dump_stats): Ditto. + * primegen.c (_gcry_generate_elg_prime): Ditto. + * des.c (_gcry_des_get_info): Ditto. + * cast5.c (do_cast_setkey): Ditto. + * blowfish.c (do_bf_setkey): Ditto. + +2002-08-26 Werner Koch + + * des.c (weak_keys): Fixed one entry in the table and compared + all entries against the literature. + (selftest): Checksum the weak key table. + +2002-08-21 Werner Koch + + * pubkey.c: Enable keygrip calculation for "openpgp-rsa". + +2002-08-17 Werner Koch + + * cipher.c (setup_cipher_table): Don't overwrite the DES entry + with the entry for DUMMY. + +2002-08-14 Werner Koch + + * des.c (do_des_setkey,do_des_encrypt, do_des_decrypt): New. + (_gcry_des_get_info): Support plain old DES. + * cipher.c (setup_cipher_table): Put DES into the table. + +2002-07-25 Werner Koch + + * rndunix.c (_gcry_rndunix_constructor): Prefixed with _gcry_. + Noted by Stephan Austermuehle. + +2002-07-08 Timo Schulz + + * rndw32.c: Replaced the m_ memory functions with the real + gcry_ functions. Renamed all g10_ prefixed functions to log_. + +2002-06-12 Werner Koch + + * rsa.c (generate): Use e = 65537 for now. + +2002-06-11 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): Allow a "protected-private-key". + +2002-06-05 Timo Schulz + + * cipher.c (gcry_cipher_encrypt, gcry_cipher_decrypt): + Check that the input size is a multiple of the blocksize. + +2002-05-23 Werner Koch + + * md.c (oid_table): Add an rsadsi OID for MD5. + +2002-05-21 Werner Koch + + * primegen.c, elgamal.c, dsa.c (progress): Do not print anything + by default. Pass an extra identifying string to the callback and + reserved 2 argumenst for current and total counters. Changed the + register function prototype. + +2002-05-17 Werner Koch + + * rndegd.c (rndegd_constructor): Fixed name of register function + and prefixed the function name with _gcry_. + * rndw32.c (rndw32_constructor): Ditto. + * tiger.c (tiger_constructor): Ditto. + + * Makefile.am: Removed all dynamic loading stuff. + * dynload.c: Ditto. Now only used for the constructor system. + +2002-05-15 Werner Koch + + * random.c (gcry_random_bytes,gcry_random_bytes_secure) + (gcry_randomize): Make sure we are initialized. + +2002-05-14 Werner Koch + + Changed license of most files to the LGPL. + +2002-05-02 Werner Koch + + * random.c (_gcry_fast_random_poll): Initialize the module so the + mutex can be used. + + * primegen.c (small_prime_numbers): Moved table from smallprime.c + * smallprime.c: File removed. + + * des.c (leftkey_swap, rightkey_swap, working_memcmp): Made static. + + * cipher.c (gcry_cipher_map_name): Map "RIJNDAEL" to "AES". + * rijndael.c (rijndael_get_info): We do only support a 128 bit + blocksize so it makes sense to change the algorithm strings to + AES. + + * tiger.c (tiger_final): Removed superfluous token pasting operators. + * md5.c (md5_final): Ditto. + +2002-04-30 Werner Koch + + * cipher.c: Fixed list of copyright years. + +2002-03-18 Werner Koch + + * random.c (initialize): Initialize the new pool lock mutex. + (_gcry_fast_random_poll): Add locking and moved main + code out to... + (do_fast_random_poll): new function. + (read_pool): Use the new function here. + (get_random_bytes): Add locking. + (_gcry_update_random_seed_file): Ditto. + +2002-03-11 Werner Koch + + * md.c: Add rsaSignatureWithripemd160 to OID table. + +2002-02-20 Werner Koch + + * sha1.c: Removed a left over comment note. The code has been + rewritten from scratch in 1998. Thanks to Niels Möller for + reporting this misleading comment. + +2002-02-18 Werner Koch + + * rndunix.c (rndunix_constructor): Use the the new prefixed + function name. Reported by Jordi Mallach. + +2002-02-10 Werner Koch + + * random.c (mix_pool): Carry an extra failsafe_digest buffer + around to make the function more robust. + +2002-02-08 Werner Koch + + * random.c (add_randomness): Xor new data into the pool and not + just copy it. This avoids any choosen input attacks which are not + serious in our setting because an outsider won't be able to mix + data in and even then we keep going with a PRNG. Thanks to Stefan + Keller for pointing this out. + +2002-01-04 Werner Koch + + * pubkey.c (gcry_pk_genkey): Do not release skey - it is static. + + * primegen.c (gen_prime): Of course we should use set_bit + and not set_highbit to set the second high bit. + +2001-12-18 Werner Koch + + * rsa.c (generate): Loop until we find the exact modulus size. + Changed the exponent to 41. + (rsa_get_info): s/usage/r_usage/ to avoid shadow warnings. + * primegen.c (gen_prime): Set 2 high order bits for secret primes. + + * Makefile.am (DISTCLEANFILES): Include construct.c. + +2001-12-17 Werner Koch + + * pubkey.c (gcry_pk_get_keygrip): New - experimental. + +2001-12-11 Werner Koch + + * cipher.c: Added OIDs for AES. + (gcry_cipher_mode_from_oid): New. + (gcry_cipher_map_name): Moved OID search code to .. + (search_oid): .. new function. + +2001-12-10 Werner Koch + + * pubkey.c (gcry_pk_encrypt): Find the signature algorithm by name + and not by number. + + * pubkey.c (gcry_pk_encrypt,gcry_pk_decrypt,gcry_pk_sign) + (gcry_pk_verify,gcry_pk_testkey, gcry_pk_genkey) + (gcry_pk_get_nbits): Release the arrays. Noted by Nikos + Mavroyanopoulos. + +2001-12-06 Werner Koch + + * cipher.c (gcry_cipher_map_name): Look also for OIDs prefixed + with "oid." or "OID.". + +2001-12-05 Werner Koch + + * pubkey.c (algo_info_table): Fixed entry for openpgp-rsa. + +2001-11-24 Werner Koch + + * pubkey.c: Added the rsaEncryption OID to the tables. + (sexp_to_key): Add an arg to return the index of the algorithm, + changed all callers. + (gcry_pk_sign): Find the signature algorithm by name and not by + number. + (gcry_pk_get_nbits): Fixed so that we can now really pass a secret + key to get the result. + + * md.c (gcry_md_map_name): Look also for OIDs prefixed with "oid." + or "OID." so that an OID string can be used as an S-Exp token. + +2001-11-20 Werner Koch + + * md.c (gcry_md_map_name): Lookup by OID if the the name begins + with a digit. + (oid_table): New. + +2001-11-16 Werner Koch + + * md.c (gcry_md_info): New operator GCRYCTL_IS_ALGO_ENABLED. + +2001-11-07 Werner Koch + + * md.c (gcry_md_hash_buffer): Close the handle which was left open + for algorithms other than rmd160. + +2001-08-08 Werner Koch + + * rndw32.c (gather_random): Use toolhelp in addition to the NT + gatherer for Windows2000. Suggested by Sami Tolvanen. + + * random.c (read_pool): Fixed length check, this used to be one + byte to strict. Made an assert out of it because the caller has + already made sure that only poolsize bytes are requested. + Reported by Marcus Brinkmann. + +2001-08-03 Werner Koch + + * cipher.c (cipher_encrypt, cipher_decrypt): Prepare to return + errors. We have to change the interface to all ciphers to make + this really work but we should do so to prepare for hardware + encryption modules. + (gcry_cipher_encrypt, gcry_cipher_decrypt): Return the error and + set lasterr. + (gcry_cipher_ctl): Make sure that errors from setkey are returned. + +2001-08-02 Werner Koch + + * rndlinux.c (gather_random): casted a size_t arg to int so that + the format string is correct. Casting is okay here and avoids + translation changes. + + * random.c (fast_random_poll): Do not check the return code of + getrusage. + + * rndunix.c: Add a signal.h header to avoid warnings on Solaris 7 + and 8. + + * tiger.c (print_abc,print_data): Removed. + + * rijndael.c, des.c, blowfish.c, twofish.c, cast5.c, arcfour.c + (burn_stack): New. Add wrappers for most functions to be able to + call burn_stack after the function invocation. This methods seems + to be the most portable way to zeroise the stack used. It does + only work on stack frame based machines but it is highly portable + and has no side effects. Just setting the automatic variables at + the end of a function to zero does not work well because the + compiler will optimize them away - marking them as volatile would + be bad for performance. + * md5.c, sha1.c, rmd160.c, tiger.c (burn_stack): Likewise. + * random.c (burn_stack): New. + (mix_pool): Use it here to burn the stack of the mixblock function. + + * primegen.c (_gcry_generate_elg_prime): Freed q at 3 places. + Thanks to Tommi Komulainen. + + * arcfour.c (arcfour_setkey): Check the minimim keylength against + bytes and not bits. + (selftest): Must reset the key before decryption. + +2001-05-31 Werner Koch + + * sha1.c (sha1_init): Made static. + + Changed all g10_ prefixed function names as well as some mpi_ + function names to cope with the introduced naming changes. + + * md.c (prepare_macpads): Made key const. + +2001-05-28 Werner Koch + + * rndegd.c (gather_random): Removed the use of tty_printf. + +2001-03-29 Werner Koch + + * md5.c (md5_final): Fixed calculation of hashed length. Thanks + to disastry@saiknes.lv for pointing out that it was horrible wrong + for more than 512MB of input. + * sha1.c (sha1_final): Ditto. + * rmd160.c (rmd160_final): Ditto. + * tiger.c (tiger_final): Ditto. + + * blowfish.c (encrypt,do_encrypt): Changed name to do_encrypt to + avoid name clashes with an encrypt function in stdlib.h of + Dynix/PIX. Thanks to Gene Carter. + * elgamal.c (encrypt,do_encrypt): Ditto. + + * twofish.c (gnupgext_enum_func): Use only when when compiled as a + module. + * rijndael.c (gnupgext_enum_func): Ditto. + + * tiger.c (tiger_get_info): Return "TIGER192" and not just + "TIGER". By Edwin Woudt. + + * random.c: Always include time.h - standard requirement. Thanks + to James Troup. + + * rndw32.c: Fixes to the macros. + +2001-01-11 Werner Koch + + * cipher.c (cipher_encrypt,gcry_cipher_encrypt): Use blocksize and + not 8. + +2000-12-19 Werner Koch + + Major change: + Removed all GnuPG stuff and renamed this piece of software + to gcrypt. + +2000-11-14 Werner Koch + + * dsa.c (test_keys): Replaced mpi_alloc by gcry_mpi_new and + mpi_free by gcry_mpi_release. + * elgamal.c (test_keys,generate): Ditto, also for mpi_alloc_secure. + * rsa.c (test_keys,generate,rsa_verify): Ditto. + * primegen.c (generate_elg_prime): Ditto. + (gen_prime): Ditto and removed nlimbs. + + * rsa.c (generate): Allocate 2 more vars in secure memory. + + * Makefile.am (OMIT_DEPENDENCIES): Hack to work around dependency + problems. + +2000-10-09 Werner Koch + + * arcfour.c, arcfour.h: New. + * cipher.c (cipher_encrypt, cipher_decrypt): Add stream mode. + (setup_cipher_table): Add Arcfour. + (gcry_cipher_open): Kludge to allow stream mode. + +Wed Oct 4 13:16:18 CEST 2000 Werner Koch + + * sha1.c (transform): Use rol() macro. Actually this is not needed + for a newer gcc but there are still aoter compilers. + + * rsa.c (test_keys): Use new random function. + + * md.c (gcry_md_setkey): New function to overcome problems with + const conflics. + (gcry_md_ctl): Pass set key to the new functions. + + * rijndael.c: New. + * cipher.c: Add Rijndael support. + +Mon Sep 18 16:35:45 CEST 2000 Werner Koch + + * rndlinux.c (open_device): Loose random device checking. + By Nils Ellmenreich. + + * random.c (fast_random_poll): Check ENOSYS for getrusage. + * rndunix.c: Add 2 sources for QNX. By Sam Roberts. + + * pubkey.c (gcry_pk_algo_info): Add GCRYCTL_GET_ALGO_USAGE. + + * rsa.c: Changed the comment about the patent. + (secret): Speed up by using the CRT. For a 2k keys this + is about 3 times faster. + (stronger_key_check): New but unused code to check the secret key. + * Makefile.am: Included rsa.[ch]. + * pubkey.c: Enabled RSA support. + (pubkey_get_npkey): Removed RSA workaround. + +Mon Jul 31 10:04:47 CEST 2000 Werner Koch + + * pubkey.c: Replaced all gcry_sexp_{car,cdr}_{data,mpi} by the new + gcry_sexp_nth_{data,mpi} functions. + +Tue Jul 25 17:44:15 CEST 2000 Werner Koch + + * pubkey.c (exp_to_key,sexp_to_sig,sexp_to_enc,gcry_pk_encrypt, + gcry_pk_decrypt,gcry_pk_sign,gcry_pk_genkey): Changed to work with + the new S-Exp interface. + +Mon Jul 17 16:35:47 CEST 2000 Werner Koch + + * random.c (gather_faked): Replaced make_timestamp by time(2) again. + +Fri Jul 14 19:38:23 CEST 2000 Werner Koch + + * md.c (gcry_md_ctl): Support GCRYCTL_{START,STOP}_DUMP. + + * Makefile.am: Never compile mingw32 as module. + + * Makefile.am: Tweaked module build and removed libtool + + * Makefile.am: Replaced -O1 by -O. Suggested by Alec Habig. + + * elgamal.c (sign): Removed inactive code. + + * rsa.c, rsa.h: New based on the old module version (only in CVS for now). + * pubkey.c (setup_pubkey_table): Added commented support for RSA. + + * rndunix.c (waitpid): New. For UTS 2.1. All by Dave Dykstra. + (my_popen): Do the FD_CLOEXEC only if it is available + (start_gatherer): Cope with missing _SC_OPEN_MAX + + * rndunix.c: Add some more headers for QNX. By Sam Roberts. + + * rndegd.c (gather_random): Shortcut level 0. + * rndunix.c (gather_random): Ditto. + * rndw32.c (gather_random): Ditto. + + * rndw32.c: Replaced with code from Cryptlib and commented the old stuff. + * rndw32.c: Add some debuging code enabled by an environment variable. + + * random.c (read_seed_file): Binary open for DOSish system + (update_random_seed_file): Ditto. + * random.c [MINGW32]: Include process.h for getpid. + * random.c (fast_random_poll): Add clock_gettime() as fallback for + system which support this POSIX.4 fucntion. By Sam Roberts. + + * random.c (read_seed_file): Removed the S_ISLNK test becuase it + is already covered by !S_ISREG and is not defined in Unixware. + Reported by Dave Dykstra. + (update_random_seed_file): Silently ignore update request when pool + is not filled. + + * random.c (read_seed_file): New. + (set_random_seed_file): New. + (read_pool): Try to read the seeding file. + (update_random_seed_file): New. + + (read_pool): Do an initial extra seeding when level 2 quality random + is requested the first time. This requestes at least POOLSIZE/2 bytes + of entropy. Compined with the seeding file this should make normal + random bytes cheaper and increase the quality of the random bytes + used for key generation. + + * random.c (read_pool): Print a more friendly error message in + cases when too much random is requested in one call. + + * random.c (fast_random_poll): Check whether RUSAGE_SELF is defined; + this is not the case for some ESIX and Unixware, although they have + getrusage(). + + * primegen.c (generate_elg_prime): All primes are now generated with + the lowest random quality level. Because they are public anyway we + don't need stronger random and by this we do not drain the systems + entropy so much. + + * primegen.c (register_primegen_progress): New. + * dsa.c (register_pk_dsa_progress): New. + * elgamal.c (register_pk_elg_progress): New. + + * elgamal.c (wiener_map): New. + (gen_k): Use a much smaller k. + (generate): Calculate the qbits using the wiener map and + choose an x at a size comparable to the one choosen in gen_k + + * rmd160.c (rmd160_get_info): Moved casting to the left side due to a + problem with UTS4.3. Suggested by Dave Dykstra. + * sha1.c (sha1_get_info): Ditto. + * tiger.c (tiger_get_info): Ditto. + * md5.c (md5_get_info): Ditto + * des.c (des_get_info): Ditto. + * blowfish.c (blowfish_get_info): Ditto. + * cast5.c (cast5_get_info): Ditto. + * twofish.c (twofish_get_info): Ditto. + +Fri Mar 24 11:25:45 CET 2000 Werner Koch + + * md.c (md_open): Add hmac arg and allocate space for the pads. + (md_finalize): Add HMAC support. + (md_copy): Ditto. + (md_close): Ditto. + (gcry_md_reset): Ditto. + (gcry_md_ctl): Ditto. + (prepare_macpdas): New. + +Mon Mar 13 19:22:46 CET 2000 Werner Koch + + * md.c (gcry_md_hash_buffer): Add support for the other algorithms. + +Mon Jan 31 16:37:34 CET 2000 Werner Koch + + * genprime.c (generate_elg_prime): Fixed returned factors which never + worked for non-DSA keys. + +Thu Jan 27 18:00:44 CET 2000 Werner Koch + + * pubkey.c (sexp_to_key): Fixed mem leaks in case of errors. + +Mon Jan 24 22:24:38 CET 2000 Werner Koch + + * pubkey.c (gcry_pk_decrypt): Implemented. + (gcry_pk_encrypt): Implemented. + (gcry_pk_testkey): New. + (gcry_pk_genkey): New. + (pubkey_decrypt): Made static. + (pubkey_encrypt): Ditto. + (pubkey_check_secret_key): Ditto. + (pubkey_generate): Ditto. + +Mon Jan 24 13:04:28 CET 2000 Werner Koch + + * pubkey.c (pubkey_nbits): Removed and replaced by ... + (gcry_pk_get_nbits): this new one. + +Wed Dec 8 21:58:32 CET 1999 Werner Koch + + * dsa.c: s/mpi_powm/gcry_mpi_powm/g + * elgamal.c: Ditto. + * primegen.c: Ditto. + + * : Replaced g10_opt_verbose by g10_log_verbosity(). + + * Makefile.am (INCLUDES): removed intl, add ../gcrypt + +Fri Nov 19 17:15:20 CET 1999 Werner Koch + + * dynload.c (cmp_filenames): New to replaced compare_filename() in + module. + (register_cipher_extension): Removed the tilde expansion stuff. + * rndeg.c (my_make_filename): New. + + * : Replaced header util.h by g10lib.h + + * random.c (gather_faked): Replaced make_timestamp by time(2). + Disabled wrning printed with tty_printf. + * rndlinux.c (gather_random): Always use fprintf instead of tty_xxx; + this should be replaced by a callback function. + + * primegen.c (gen_prime): Use gcry_mpi_randomize. + (is_prime): Ditto. + * elgamal.c (test_keys): Ditto. + * dsa.c (test_keys): Ditto. + + * cipher.c (gcry_cipher_close): Die on invalid handle. + +Mon Nov 15 21:36:02 CET 1999 Werner Koch + + * elgamal.c (gen_k): Use the new random API. + (generate): Ditto. + * dsa.c (gen_k): Ditto. + (generate): Ditto. + +Sat Nov 13 17:44:23 CET 1999 Werner Koch + + * pubkey.c (disable_pubkey_algo): Made static. + (gcry_pk_ctl): New. + + * random.c (get_random_bits): Renamed to ... + (get_random_bytes): ... this and made static. + (gcry_random_bytes): New. + (gcry_random_bytes_secure): New. + (randomize_buffer): Renamed to ... + (gcry_randomize): ...this. + + * md.c (gcry_md_hash_buffer): New. + + * pubkey.c (gcry_pk_algo_info): 4 new commands. + (pubkey_get_npkey): Made static. + (pubkey_get_nskey): Made static. + (pubkey_get_nsig): Made static. + (pubkey_get_nenc): Made static. + + * pubkey.c: Removed all G10ERR_xxx. + * cipher.c: Changed all GCRYERR_INV_ALGO to GCRYERR_INV_CIPHER_ALGO. + * md.c: Changed all GCRYERR_INV_ALGO to GCRYERR_INV_MD_ALGO. + * cast5.c (cast_setkey): Changed errocodes to GCRYERR_xxx. + * blowfish.c: Ditto. + * des.c: Ditto. + * twofish.c: Ditto. + * dsa.c: Ditto. + * elgamal.c: Ditto. + + * g10c.c: Removed + + * cipher.c (gcry_cipher_open): Replaced alloc functions and return NULL + if we are out of core. + * dynload.c: Replaced all memory allocation functions. + * md.c: Ditto. + * primegen.c: Ditto. + * pubkey.c: Ditto. + * random.c: Ditto. + * rndw32.c: Ditto. + * elgamal.c: Ditto. + * dsa.c: Ditto. + +Tue Oct 26 14:10:21 CEST 1999 Werner Koch + + * elgamal.c (sign): Hugh found strange code here. Replaced by BUG(). + + * cipher.c: Merged with gcrypt/symapi.c. + + * pubkey.c (string_to_pubkey_algo): Renamed function to ... + (gcry_pk_map_name): ... this. + (pubkey_algo_to_string): Renamed function to ... + (gcry_pk_algo_name): ... this. + (gcry_pk_algo_info): New. + * pubkey.c: Merged with gcrypt/pkapi.c. + + * md.c (md_reset): Clear finalized; thanks to Ulf Moeller for + fixing this bug. + + * md.c: Merged with gcrypt/mdapi.c + +Wed Sep 15 14:39:59 CEST 1999 Michael Roth + + * des.c: Various speed improvements: One bit pre rotation + trick after initial permutation (Richard Outerbridge). + Finished test of SSLeay Tripple-DES patterns. + +Wed Sep 15 16:22:17 CEST 1999 Werner Koch + + * rndw32.c: New. + +Mon Sep 13 10:51:29 CEST 1999 Werner Koch + + * bithelp.h: New. + * rmd160.h, sha1.h, md5.h: Use the rol macro from bithelp.h + +Tue Sep 7 16:23:36 CEST 1999 Werner Koch + + * Makefile.am: Fixed seds for latest egcc. By Ollivier Robert. + +Mon Sep 6 19:59:08 CEST 1999 Werner Koch + + * des.c (selftest): Add some testpattern + +Mon Aug 30 20:38:33 CEST 1999 Werner Koch + + * cipher.c (do_cbc_encrypt): Fixed serious bug occuring when not using + in place encryption. Pointed out by Frank Stajano. + +Mon Jul 26 09:34:46 CEST 1999 Werner Koch + + * md5.c (md5_final): Fix for a SCO cpp bug. + +Thu Jul 15 10:15:35 CEST 1999 Werner Koch + + * elgamal.c (elg_check_secret_key,elg_encrypt + elg_decrypt,elg_sign,elg_verify): Sanity check on the args. + * dsa.c (dsa_check_secret_key,dsa_sign,dsa_verify): Ditto. + + * pubkey.c (disable_pubkey_algo): New. + (check_pubkey_algo2): Look at disabled algo table. + * cipher.c (disable_cipher_algo): New. + (check_cipher_algo): Look at disabled algo table. + +Wed Jul 7 13:08:40 CEST 1999 Werner Koch + + * Makefile.am: Support for libtool. + +Fri Jul 2 11:45:54 CEST 1999 Werner Koch + + * dsa.c (gen_k): Changed algorithm to consume less random bytes + * elgamal.c (gen_k): Ditto. + + * random.c (random_dump_stats): New. + +Thu Jul 1 12:47:31 CEST 1999 Werner Koch + + * primegen.c, elgamal.c, dsa.c (progess): New and replaced all + fputc with a call to this function. + +Sat Jun 26 12:15:59 CEST 1999 Werner Koch + + * rndegd.c (do_write): s/ssize_t/int/ due to SunOS 4.1 probs. + + * cipher.c (do_cbc_encrypt, do_cbc_decrypt): New. + + * dynload.c (HAVE_DL_SHL_LOAD): Map hpux API to dlopen (Dave Dykstra). + * Makefile.am (install-exec-hook): Removed. + +Sun May 23 14:20:22 CEST 1999 Werner Koch + + * cipher.c (setup_cipher_table): Enable Twofish + + * random.c (fast_random_poll): Disable use of times() for mingw32. + +Mon May 17 21:54:43 CEST 1999 Werner Koch + + * dynload.c (register_internal_cipher_extension): Minor init fix. + +Tue May 4 15:47:53 CEST 1999 Werner Koch + + * primegen.c (gen_prime): Readded the Fermat test. Fixed the bug + that we didn't correct for step when passing the prime to the + Rabin-Miller test which led to bad performance (Stefan Keller). + (check_prime): Add a first Fermat test. + +Sun Apr 18 10:11:28 CEST 1999 Werner Koch + + * cipher.c (cipher_setiv): Add ivlen arg, changed all callers. + + * random.c (randomize_buffer): alway use secure memory because + we can't use m_is_secure() on a statically allocated buffer. + + * twofish.c: Replaced some macros by a loop to reduce text size. + * Makefile.am (twofish): No more need for sed editing. + +Fri Apr 9 12:26:25 CEST 1999 Werner Koch + + * cipher.c (cipher_open): Reversed the changes for AUTO_CFB. + + * blowfish.c: Dropped the Blowfish 160 mode. + * cipher.c (cipher_open): Ditto. + (setup_cipher_table): Ditto. And removed support of twofish128 + +Wed Apr 7 20:51:39 CEST 1999 Werner Koch + + * random.c (get_random_bits): Can now handle requests > POOLSIZE + + * cipher.c (cipher_open): Now uses standard CFB for automode if + the blocksize is gt 8 (according to rfc2440). + + * twofish.c: Applied Matthew Skala's patches for 256 bit key. + +Tue Apr 6 19:58:12 CEST 1999 Werner Koch + + * random.c (get_random_bits): Can now handle requests > POOLSIZE + + * cipher.c (cipher_open): Now uses standard CFB for automode if + the blocksize is gt 8 (according to rfc2440). + +Sat Mar 20 11:44:21 CET 1999 Werner Koch + + * rndlinux.c (tty_printf) [IS_MODULE]: Removed. + + * rndegd.c (gather_random): Some fixes. + +Wed Mar 17 13:09:03 CET 1999 Werner Koch + + * rndegd.c (do_read): New. + (gather_random): Changed the implementation. + +Mon Mar 8 20:47:17 CET 1999 Werner Koch + + * dynload.c (DLSYM_NEEDS_UNDERSCORE): Renamed. + +Fri Feb 26 17:55:41 CET 1999 Werner Koch + + * md.c: Nearly a total rewrote. + +Wed Feb 24 11:07:27 CET 1999 Werner Koch + + * cipher.c (context): Fixed alignment + * md.c: Ditto. + + * rndegd.c: New + +Mon Feb 22 20:04:00 CET 1999 Werner Koch + + * rndegd.c: New. + +Wed Feb 10 17:15:39 CET 1999 Werner Koch + + * Makefile.am: Modules are now figured out by configure + * construct.c: New. Generated by configure. Changed all modules + to work with that. + * sha1.h: Removed. + * md5.h: Removed. + + * twofish.c: Changed interface to allow Twofish/256 + + * rndunix.c (start_gatherer): Die on SIGPIPE. + +Wed Jan 20 18:59:49 CET 1999 Werner Koch + + * rndunix.c (gather_random): Fix to avoid infinite loop. + +Sun Jan 17 11:04:33 CET 1999 Werner Koch + + * des.c (is_weak_key): Replace system memcmp due to bugs + in SunOS's memcmp. + (des_get_info): Return error on failed selftest. + * twofish.c (twofish_setkey): Return error on failed selftest or + invalid keylength. + * cast5.c (cast_setkey): Ditto. + * blowfish.c (bf_setkey): Return error on failed selftest. + +Tue Jan 12 11:17:18 CET 1999 Werner Koch + + * random.c (random_is_faked): New. + + * tiger.c: Only compile if we have the u64 type + +Sat Jan 9 16:02:23 CET 1999 Werner Koch + + * rndunix.c (gather_random): check for setuid. + + * Makefile.am: Add a way to staically link random modules + +Thu Jan 7 18:00:58 CET 1999 Werner Koch + + * md.c (md_stop_debug): Do a flush first. + (md_open): size of buffer now depends on the secure parameter + +Sun Jan 3 15:28:44 CET 1999 Werner Koch + + * rndunix.c (start_gatherer): Fixed stupid ==/= bug + +1998-12-31 Geoff Keating + + * des.c (is_weak_key): Rewrite loop end condition. + +Tue Dec 29 14:41:47 CET 1998 Werner Koch + + * random.c: add unistd.h for getpid(). + (RAND_MAX): Fallback value for Sun. + +Wed Dec 23 17:12:24 CET 1998 Werner Koch + + * md.c (md_copy): Reset debug. + +Mon Dec 14 21:18:49 CET 1998 Werner Koch + + * random.c (read_random_source): Changed the interface to the + random gathering function. + (gather_faked): Use new interface. + * dynload.c (dynload_getfnc_fast_random_poll): Ditto. + (dynload_getfnc_gather_random): Ditto. + * rndlinux.c (gather_random): Ditto. + * rndunix.c (gather_random): Ditto. + +Sat Dec 12 18:40:32 CET 1998 Werner Koch + + * dynload.c (SYMBOL_VERSION): New to cope with system which needs + underscores. + + * rndunix.c: Rewrote large parts + +Thu Dec 10 20:15:36 CET 1998 Werner Koch + + * dynload.c (load_extension): increased needed verbosity level. + + * random.c (fast_random_poll): Fallback to a default fast random + poll function. + (read_random_source): Always use the faked entroy gatherer if no + gather module is available. + * rndlinux.c (fast_poll): Removed. + * rndunix.c (fast_poll): Removed. + + +Wed Nov 25 12:33:41 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rand-*.c: Removed. + * rndlinux.c : New. + * rndunix.c : New. + * random.c : Restructured the interface to the gather modules. + (intialize): Call constructor functions + (read_radnom_source): Moved to here. + * dynload.c (dynload_getfnc_gather_random): New. + (dynload_getfnc_fast_random_poll): New. + (register_internal_cipher_extension): New. + (register_cipher_extension): Support of internal modules. + +Sun Nov 8 17:44:36 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rand-unix.c (read_random_source): Removed the assert. + +Mon Oct 19 18:34:30 1998 me,,, (wk@tobold) + + * pubkey.c: Hack to allow us to give some info about RSA keys back. + +Thu Oct 15 11:47:57 1998 Werner Koch (wk@isil.d.shuttle.de) + + * dynload.c: Support for DLD + +Wed Oct 14 12:13:07 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rand-unix.c: Now uses names from configure for /dev/random. + +1998-10-10 SL Baur + + * Makefile.am: fix sed -O substitutions to catch -O6, etc. + +Tue Oct 6 10:06:32 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rand-unix.c (HAVE_GETTIMEOFDAY): Fixed (was ..GETTIMEOFTIME :-) + * rand-dummy.c (HAVE_GETTIMEOFDAY): Ditto. + +Mon Sep 28 13:23:09 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c (md_digest): New. + (md_reset): New. + +Wed Sep 23 12:27:02 1998 Werner Koch (wk@isil.d.shuttle.de) + + * tiger.c (TIGER_CONTEXT): moved "buf", so that it is 64 bit aligned. + +Mon Sep 21 06:22:53 1998 Werner Koch (wk@(none)) + + * des.c: Some patches from Michael. + +Thu Sep 17 19:00:06 1998 Werner Koch (wk@(none)) + + * des.c : New file from Michael Roth + +Mon Sep 14 11:10:55 1998 Werner Koch (wk@(none)) + + * blowfish.c (bf_setkey): Niklas Hernaeus patch to detect weak keys. + +Mon Sep 14 09:19:25 1998 Werner Koch (wk@(none)) + + * dynload.c (RTLD_NOW): Now defined to 1 if it is undefined. + +Mon Sep 7 17:04:33 1998 Werner Koch (wk@(none)) + + * Makefile.am: Fixes to allow a different build directory + +Thu Aug 6 17:25:38 1998 Werner Koch,mobil,,, (wk@tobold) + + * random.c (get_random_byte): Removed and changed all callers + to use get_random_bits() + +Mon Jul 27 10:30:22 1998 Werner Koch (wk@(none)) + + * cipher.c : Support for other blocksizes + (cipher_get_blocksize): New. + * twofish.c: New. + * Makefile.am: Add twofish module. + +Mon Jul 13 21:30:52 1998 Werner Koch (wk@isil.d.shuttle.de) + + * random.c (read_pool): Simple alloc if secure_alloc is not set. + (get_random_bits): Ditto. + +Thu Jul 9 13:01:14 1998 Werner Koch (wk@isil.d.shuttle.de) + + * dynload.c (load_extension): Function now nbails out if + the program is run setuid. + +Wed Jul 8 18:58:23 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rmd160.c (rmd160_hash_buffer): New. + +Thu Jul 2 10:50:30 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.c (cipher_open): algos >=100 use standard CFB + +Thu Jun 25 11:18:25 1998 Werner Koch (wk@isil.d.shuttle.de) + + * Makefile.am: Support for extensions + +Thu Jun 18 12:09:38 1998 Werner Koch (wk@isil.d.shuttle.de) + + * random.c (mix_pool): simpler handling for level 0 + +Mon Jun 15 14:40:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * tiger.c: Removed from dist, will reappear as dynload module + +Sat Jun 13 14:16:57 1998 Werner Koch (wk@isil.d.shuttle.de) + + * pubkey.c: Major changes to allow extensions. Changed the inteface + of all public key ciphers and added the ability to load extensions + on demand. + + * misc.c: Removed. + +Wed Jun 10 07:52:08 1998 Werner Koch,mobil,,, (wk@tobold) + + * dynload.c: New. + * cipher.c: Major changes to allow extensions. + +Mon Jun 8 22:43:00 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.c: Major internal chnages to support extensions. + * blowfish.c (blowfish_get_info): New and made all internal + functions static, changed heder. + * cast5.c (cast5_get_info): Likewise. + +Mon Jun 8 12:27:52 1998 Werner Koch (wk@isil.d.shuttle.de) + + * tiger.c (transform): Fix for big endian + + * cipher.c (do_cfb_decrypt): Big endian fix. + +Fri May 22 07:30:39 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c (md_get_oid): Add a new one for TIGER. + +Thu May 21 13:24:52 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.c: Add support for a dummy cipher + +Thu May 14 15:40:36 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rmd160.c (transform): fixed sigbus - I should better + add Christian von Roques's new implemenation of rmd160_write. + +Fri May 8 18:07:44 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rand-internal.h, rand-unix.c, rand-w32.c, rand_dummy.c: New + * random.c: Moved system specific functions to rand-****.c + +Fri May 8 14:01:17 1998 Werner Koch (wk@isil.d.shuttle.de) + + * random.c (fast_random_poll): add call to gethrtime. + +Tue May 5 21:28:55 1998 Werner Koch (wk@isil.d.shuttle.de) + + * elgamal.c (elg_generate): choosing x was not correct, could + yield 6 bytes which are not from the random pool, tsss, tsss.. + +Tue May 5 14:09:06 1998 Werner Koch (wk@isil.d.shuttle.de) + + * primegen.c (generate_elg_prime): Add arg mode, changed all + callers and implemented mode 1. + +Mon Apr 27 14:41:58 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.c (cipher_get_keylen): New. + +Sun Apr 26 14:44:52 1998 Werner Koch (wk@isil.d.shuttle.de) + + * tiger.c, tiger.h: New. + +Wed Apr 8 14:57:11 1998 Werner Koch (wk@isil.d.shuttle.de) + + * misc.c (check_pubkey_algo2): New. + +Tue Apr 7 18:46:49 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cipher.c: New + * misc.c (check_cipher_algo): Moved to cipher.c + * cast5.c: Moved many functions to cipher.c + * blowfish.c: Likewise. + +Sat Apr 4 19:52:08 1998 Werner Koch (wk@isil.d.shuttle.de) + + * cast5.c: Implemented and tested. + +Wed Apr 1 16:38:27 1998 Werner Koch (wk@isil.d.shuttle.de) + + * elgamal.c (elg_generate): Faster generation of x in some cases. + +Thu Mar 19 13:54:48 1998 Werner Koch (wk@isil.d.shuttle.de) + + * blowfish.c (blowfish_decode_cfb): changed XOR operation + (blowfish_encode_cfb): Ditto. + +Thu Mar 12 14:04:05 1998 Werner Koch (wk@isil.d.shuttle.de) + + * sha1.c (transform): Rewrote + + * blowfish.c (encrypt): Unrolled for rounds == 16 + (decrypt): Ditto. + +Tue Mar 10 16:32:08 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rmd160.c (transform): Unrolled the loop. + +Tue Mar 10 13:05:14 1998 Werner Koch (wk@isil.d.shuttle.de) + + * random.c (read_pool): Add pool_balance stuff. + (get_random_bits): New. + + * elgamal.c (elg_generate): Now uses get_random_bits to generate x. + + +Tue Mar 10 11:33:51 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c (md_digest_length): New. + +Tue Mar 10 11:27:41 1998 Werner Koch (wk@isil.d.shuttle.de) + + * dsa.c (dsa_verify): Works. + +Mon Mar 9 12:59:08 1998 Werner Koch (wk@isil.d.shuttle.de) + + * dsa.c, dsa.h: Removed some unused code. + +Wed Mar 4 10:39:22 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c (md_open): Add call to fast_random_poll. + blowfish.c (blowfish_setkey): Ditto. + +Tue Mar 3 13:32:54 1998 Werner Koch (wk@isil.d.shuttle.de) + + * rmd160.c (rmd160_mixblock): New. + * random.c: Restructured to start with a new RNG implementation. + * random.h: New. + +Mon Mar 2 19:21:46 1998 Werner Koch (wk@isil.d.shuttle.de) + + * gost.c, gost.h: Removed because they did only contain trash. + +Sun Mar 1 16:42:29 1998 Werner Koch (wk@isil.d.shuttle.de) + + * random.c (fill_buffer): removed error message if n == -1. + +Fri Feb 27 16:39:34 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c (md_enable): No init if called twice. + +Thu Feb 26 07:57:02 1998 Werner Koch (wk@isil.d.shuttle.de) + + * primegen.c (generate_elg_prime): Changed the progress printing. + (gen_prime): Ditto. + +Tue Feb 24 12:28:42 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md5.c, md.5 : Replaced by a modified version of md5.c from + GNU textutils 1.22. + +Wed Feb 18 14:08:30 1998 Werner Koch (wk@isil.d.shuttle.de) + + * md.c, md.h : New debugging support + +Mon Feb 16 10:08:47 1998 Werner Koch (wk@isil.d.shuttle.de) + + * misc.c (cipher_algo_to_string): New + (pubkey_algo_to_string): New. + (digest_algo_to_string): New. + + + Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. + + This file is free software; as a special exception the author gives + unlimited permission to copy and/or distribute it, with or without + modifications, as long as this notice is preserved. + + This file is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. diff --git a/grub-core/lib/libgcrypt/cipher/Makefile.am b/grub-core/lib/libgcrypt/cipher/Makefile.am new file mode 100644 index 000000000..76cdc96ad --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/Makefile.am @@ -0,0 +1,83 @@ +# Makefile for cipher modules +# Copyright (C) 1998, 1999, 2000, 2001, 2002, +# 2003, 2009 Free Software Foundation, Inc. +# +# This file is part of Libgcrypt. +# +# Libgcrypt is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# Libgcrypt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, see . + +# Process this file with automake to produce Makefile.in + +EXTRA_DIST = Manifest + +# Need to include ../src in addition to top_srcdir because gcrypt.h is +# a built header. +AM_CPPFLAGS = -I../src -I$(top_srcdir)/src +AM_CFLAGS = $(GPG_ERROR_CFLAGS) + + +noinst_LTLIBRARIES = libcipher.la + +GCRYPT_MODULES = @GCRYPT_CIPHERS@ @GCRYPT_PUBKEY_CIPHERS@ @GCRYPT_DIGESTS@ + +libcipher_la_DEPENDENCIES = $(GCRYPT_MODULES) +libcipher_la_LIBADD = $(GCRYPT_MODULES) + +libcipher_la_SOURCES = \ +cipher.c pubkey.c ac.c md.c kdf.c \ +hmac-tests.c \ +bithelp.h \ +primegen.c \ +hash-common.c hash-common.h \ +rmd.h + +EXTRA_libcipher_la_SOURCES = \ +arcfour.c \ +blowfish.c \ +cast5.c \ +crc.c \ +des.c \ +dsa.c \ +elgamal.c \ +ecc.c \ +idea.c \ +md4.c \ +md5.c \ +rijndael.c rijndael-tables.h \ +rmd160.c \ +rsa.c \ +seed.c \ +serpent.c \ +sha1.c \ +sha256.c \ +sha512.c \ +tiger.c \ +whirlpool.c \ +twofish.c \ +rfc2268.c \ +camellia.c camellia.h camellia-glue.c + +if ENABLE_O_FLAG_MUNGING +o_flag_munging = sed -e 's/-O\([2-9s][2-9s]*\)/-O1/' -e 's/-Ofast/-O1/g' +else +o_flag_munging = cat +endif + + +# We need to lower the optimization for this module. +tiger.o: $(srcdir)/tiger.c + `echo $(COMPILE) -c $(srcdir)/tiger.c | $(o_flag_munging) ` + +tiger.lo: $(srcdir)/tiger.c + `echo $(LTCOMPILE) -c $(srcdir)/tiger.c | $(o_flag_munging) ` diff --git a/grub-core/lib/libgcrypt/cipher/Manifest b/grub-core/lib/libgcrypt/cipher/Manifest new file mode 100644 index 000000000..0cd64f71f --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/Manifest @@ -0,0 +1,73 @@ +# Manifest - checksums of the cipher directory +# Copyright 2003 Free Software Foundation, Inc. +# +# This file is part of Libgcrypt. +# +# Libgcrypt is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser general Public License as +# published by the Free Software Foundation; either version 2.1 of +# the License, or (at your option) any later version. +# +# Libgcrypt is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +# Checksums for all source files in this directory. Format is +# filename, blanks, base-64 part of an OpenPGP detached signature +# without the header lines. Blank lines and lines beginning with a +# hash mark are ignored. A tool to process this file is available by +# cvs -d :pserver:anoncvs@cvs.gnupg.org:/cvs/wk co misc-scripts/manifest-tool +# +# The special entry "$names$" holds a signature over all sorted +# filenames excluding itself. + + +# Algorithm API +cipher.c iQCVAwUAQDzrVjEAnp832S/7AQIPDgP+OVJ/YNWY5m7c09EBbPAzL/WsGoj6wrBNMmkRlMOqTHeh+OOtjuFHt1f9uhfM2Nzl7sJ5+h4ryZKLEZmQPRMTZTnAqkvGdsrJWJnigUA9QwYdV0ONqC9C63gpuG465gO9TZVOqlQu/FTxSRuTQYUulkaBNG71n8nZEOusBVwV2YA==58xH +pubkey.c iQCVAwUAP9XQ3jEAnp832S/7AQJ5UgQAyHfEBvPVJ8wTRg8c7ixS2GiVmIgwIo5tvQaiQJTPWASevvYrB+2Z2qa9cATyu50ACjLzbaquGBgPzjJV3dU/qttT1gCqRuN/LCNvXFe5qnIZezejc3RAadFNTw/pOTHq0wxD1Keg66ruei9R36Nba59pEQIWIBXTfubRft2hMYk==E09t +ac.c iQCVAwUAQDzsOzEAnp832S/7AQJCBQP/WI6EV/dsR4rmha6RVhvkjZo17kQ8z6pIl5J3cXOvqEkIFeD2HYu3HHrWST5l7yXlffhpDkVHkfMih4ruK76q6Fm0dxZ98pO4C/dVtgimlvvcy/wOQjpzsE0fYAe1BYdg81LJ09X33vW5x6C29lunfKROO2tPlV5i8ffeoFvmMF8==j26g +md.c iQCVAwUAP+NFGjEAnp832S/7AQJs8wP/Qdk0EAKsyr3O1/pmOSN8AG4rPKbd6KDTzvoBPAN4upFwKYY4hWwvy12Q3YU9DmECrzZkRCXHR7mljVQKs6B7CRZJKjFKmOELpcJDtKvu40vTs1bOH4k9iJYZpGgRA83nkQ+ELAcphAbCA+KIpVr2K4mCJAB0FhpC2uOQ50JHAko==BeF6 +primegen.c iQCVAwUAQDzsoDEAnp832S/7AQKYRwP/TqAQBm1rHTnF0HYE05PqXfWlOqa6EosqVpaOcs/OIW6PaqX0xH1UlrukK7jNOjK3xC4o1qNQ1UKzz2dvQaq1bMvNNizeavxAh10SJZc0hIc/ofc83IbjLh8SZVWQ67JxjsUd3DOXmSmhPZ+Pqd7cUIiw8fDoF+I9EZqy3COu1wY==1ebT + +# Algorithm implementations +arcfour.c iQCVAwUAP9XR/TEAnp832S/7AQJcRwP6AlvYEx++fpT4mIYo0xRDqKEQeqMQvbaRhIg2eV74JxItpHa3q5YsYIl+n1yUz5g35JRWWXSWmAZBwO5wLKsHii4kRUhgrKWnSoQZoPpl49L5+N3R58ON3S0ru5lsBiEJEze3xplf2vqwrH9v1QHVD+gU7UTlfNqrIJoOUXN+1O4==Tq+x +blowfish.c iQCVAwUAP9XTETEAnp832S/7AQJaEgQAgiqqfuO+zQtscgTB0rvOzVymIKjRKjYhFuLjVuc79G4z1RCAffvIn/YM2d7kt+Z/QF7zjcTAOgETCQL1XokpX2zz9HPAMi2tlDY5zsDufTNqj0n4WBL9nM7w6XAvsiwP1B3bqCTv9SjJV4KbxJ58vw1yQE+sqW74R/QIHFvC7mU==wZnX +cast5.c iQCVAwUAP9XT6DEAnp832S/7AQJ3xgP/ehLjEN3GELGudbqeo91Xd+PqitHrkuBbtRIYX7Udd/fyXLN+h8rMJVyIQX2m+mpxbBxudVU3x8/DNT8B0ZHAwK6qqJmEBLLhEYPgIuF76i9LMrP1KqUPhAwRZ2OppjIIugBQ+rP74aD4eLyd/aKQHNuXML8QGWR6KwQShohXM5I==/BRh +crc.c iQCVAwUAP7ouejEAnp832S/7AQIgwQQApg5Nm63tH5DQkbN+zPzMO9Ygoj3ukxfFTyTBPYSXYKMiTjEbESegaU40uN8jnz2vprcIQWcgZfzO4+opEJMcI35aPwzEk0vKOp0S/PrBLUY2rJfnDVkX5XgJFZa2Q7LLe826UEBzTVYW924utiCCe8oOaOEWVNpg1mqdknu3M9o==kz5D +des.c iQCVAwUAQCN2oDEAnp832S/7AQL/jwP6Auoq6nZCDBjpgc9tDzuIRwa9DqyuM3gX94uvgEpUwdHszb2bG43dz03kVmcYxtj1MzXbyCeCZOwox0b2SKmLgxIbrNP6yGbzVdTj6592gDYuf/ZXmc1ZNJ1DDldcPQ0n9fXUipUPwyPaNWo3mSZaNcMKSWWzdK0J6ciG6nk7SWI==9k/t +dsa.c iQCVAwUAP9XZHDEAnp832S/7AQLBRgP/XrBzTEYx5ccMj1MMb6sg37liEHdIyyy49zjvt6jUqxj4RuwVEN8S6v3u4q/QyJkHAi1E0EkREgENlyHW6PKWhYbcrd0vPIAN15yjnl2yqtrCrJImexUCoqJJewK0E4JOicGbabTil8MZjk+mbhEPnjJBqOkyP1w0i31pEDgE/8M==pC8s +elgamal.c iQCVAwUAP9XbYzEAnp832S/7AQLXagQA3HrvspZfbTGgmUH0IqLQTJ0exUPxJv5DET2TvoIy62trDmMN6lTAj5P+a7jQ8udcu0w+mR2vXUHcxUpNA2PxLaMwGzNSY4zRDNe9r3SFTDrFm6m4y9Ko2e8XtEA+WF6P/XLpck4Jn7vMEDmVGPwkNd22kXFFE8dBGwG6i5Hk1Mk==oBUs +md4.c iQCVAwUAP9h50DEAnp832S/7AQJhHgQAzNA/B6MWFDlCtPkIVaW8RpP1Eg0ZNMsy0s7SJkopOCBlu6CwXUOKe+8ppcSxhjYKh4i4uQr/QtfipYlBjzKJGnrafoF/NugXNCOHSTGT11TvK7mCiBuUMVgvZGAlOJImk6eTTfUjRrMfaXM/SWl8bdJ4ZpzdjEyVh89r7I5JrGk==x2UD +md5.c iQCVAwUAP9h7LzEAnp832S/7AQJUGQP/c0cbf6WZXCzmjufHxiE9FAQBzTsA0WtaNqdFcHl7fhmikGtknlaED8n5a7eYd/C481UQW6Wgq/oZdsvgoPWPhG3fOCy2CFP9cZVXITuMSf0ucyZTFUJNO15fnZ+nDfsUv+JPdv1aSeRinAUtfAcSKfkSyR9BCPZvkx+tgU6cphU==Zv+h +rijndael.c iQCVAwUAP9h9cTEAnp832S/7AQKF1AP+P2L/tPqDJRDg+/fwbOk8Ts0MNxnvvYEm3gE73TKuLt1S+B2+jkrZcKNvM5VGPnVMJbnS0lmIK04nmedHCOftGTOwhGulZAHHIaKGystT3Jql4iPws/JMgAjE7Fyxh5WZMtB9yEljKBpJ5XNqhrMvvxcHpnyP3+YzIXNwzk34V+c==dJ5k +rmd160.c iQCVAwUAP9h+bTEAnp832S/7AQK1OgP+PNKF6Nzi6X93easVlksdLqKEsArCAw2QjGWDGyxTnbiJM55qAl9JxR1mn3V+oOL7izLLwTt6EYK9evhzfcxY5N5Mni85RAcsLPsuAfQDEzjI6GUWHtQUKPbM+BaorzfhQjYFSZyvum/dZYJ/WfiwwwhqqIKyVU2ZFSqA38YGC/c==9jdA +rsa.c iQCVAwUAP9iHIzEAnp832S/7AQKAYwQAuWtnMte54QHN+Hij9t4sGuypXogajOb1vQQwGgS0fKsaBZsuSP2amze4o5diIvsQTsFQ4CzjvqoCVuBDoHM3xkSD8wGDizgvtCamAxkdbF7wmzldKFn8SpJqlVwWQMP6kk1IjXHEuYb4IDWGTbVMhfEu+eOlU8+PSK4IhZqNvt4==/3hp +serpent.c iQCVAwUAP9h/VzEAnp832S/7AQLyCwP/d1zbmb7l/PriZNa9/Z7mo01XFe5MnAqCfIwhl9GjeaMszcoS37jECNq5nLvrTTFIIJpm3rvBePwiCG4Wwx1I18HCxaP198pcSaR+BLOJ3Aj52EZPrxtqlDKuFr38ZOP5giyUqUYVYGVdrz4kRMNWAZQK53GeJnGhXCnhxojLEgA==ck46 +sha1.c iQCVAwUAP9iATTEAnp832S/7AQKcSwQAwAs/HnNqho3lU1ZUgCPNt5P2/Brm6W21+wWWGKJkSrra/c4NYVKJGDDwlsFE0b9ln1uZt7bHReFkKXK3JnrKTmNVcx/Cy64iCMRNMhaM72Mqy7wWx5yHBAmMBxzFGnNQKbmeY52zeGih5HsNLSibc2pPuOViWo2JPJ5Ci/wIwl8==/wtO +sha256.c iQCVAwUAP9iAtzEAnp832S/7AQJD2QP/UqvL0hhjG1wEFbGrdkV9tba1sMDXdnnK6X7HdLuRpVAgNiQiFf8JDmntd/dZ2Q71p4Uae2ctqve4WoEijPUZPjACnpuZfx0SEQL0lQBkwxzJp7lz9ujVtwQ2cM/aYexJkXcWgGcloJNLM3JbWPGIJnuYbr/IwJ6RQF9vgj0357o==UWO1 +sha512.c iQCVAwUAP9iBTDEAnp832S/7AQIPBAQA28CJSUQLiW0s2x9u8/OH2eKnxPjA4sZmb50WP7920Lem66P31C3BrOqwfBot4RLhjL+zh/+Uc4s3HPwApZuj9E4BxNMlqLv+Tqk++DAbdaOeYT4jeUt+mlhQQ6mH/RDsy32rZsNsGQ2bUGxazZmfG++PL3JyhawqCy00SUDr/o0==H+0X +tiger.c iQCVAwUAP9iCfjEAnp832S/7AQKufwP/fryv3MqSOYY+90325DH7X3/CtekxeooN0scGsHX0fxBakWSMecTNrj33KPddLS46gU/S89zIc2N/Bw/7EVIAXVFA3/3Ip+OrFOuIMO4Py1sCdB8o2Y+5ygv8iXLcsXIq1O0av79i9g774V3uaXa2qN9ZnXe0AEhcy8FHJ2i/wro==5XVB +twofish.c iQCVAwUAP9iD6TEAnp832S/7AQKUnQP/Rq8FaYeHTG7HbZuqAs9pbPitzjDbkdZddmInWR7NmevBkKvhsJALjVooc0KGQfo2lAAmy3Xi/4QQN8VPn51DVjDIgf7x+DQh/9TFJHMccxI9asUgi4+TNnmMqLU1k3N8S2PjyZ1sjeC8B79fKPpwCzj72WkqPkzZw3l2jArr+dU==NdJT +rfc2268.c iQCVAwUAQCN+3jEAnp832S/7AQLv1gQA1hJh29hAjKi4uLSGxXvJ6cyYmPdmevdKrbLnuHZWtHe4xvCgy/nTdEojEpxgLp/hL/ogasuWRC1W16Wiz9ryxf7YR0uhZWayO/bQNagpfU5MIkJTLuKqqgpwYumCSQfOugXVAqcgEzj+13eeyJaFVrzwrNa67sh84nmbjOjNjvE==0zBq + +# Random number related +random.c iQCVAwUAP7nsITEAnp832S/7AQK4SAQAtvfUgrtGOQ2PlxGMla0qJLPHjJacMwgq0ecusiI79elPdDsFfCCk6dK1Ug2kFbNm22nCGHNcUquqbX7noi7ZVQnmPBQXzyLNZd7GmrawRZfdlRerTUDBpSnR8V8ui/5+YYp627E7kKGC0hPSgqXFql6oBMIfno0LZwFJTjIevRY==L419 +random.h iQCVAwUAP7ovKDEAnp832S/7AQJ3bQQAjnPebnyTC7sphAv2I7uIz+yPgw1ZfbVhLv+OiWDlO9ish+fRyyMpy+HELBOgZjJdgRegqhlZC6qyns5arM/VglYi+PzvdLO3hIqHE/YFfpIFPz8wBrcmlqrYyd3CsGqcYsfjocXNttCBLeSWmoJ09ltKQH8yzJf3oAgN6X1yuc4==eNoU +rand-internal.h iQCVAwUAP7ouvDEAnp832S/7AQLYnAQAhdI7ERoJVCkV8GiV7MjaUxv1WIL7iZ+jIOvVhv4fNyhCGCGoEtTjkyput/lj7Nsh3FXEqRhypGGrCLf47x/gua5n+BwffogxVyUDqiOyyGhNTPpe3fQcNBvbPCtco8yMK4GJO5G3BqzlPyN+BMeogLymyV6Sm1mvh5LZDyAFbfQ==tZSE +rndlinux.c iQCVAwUAP9iPYTEAnp832S/7AQL6/AP/ZDrbOkVuB9qJ7sKeX1MImZEsz3mi0xPovJzaBtBU7a0idcUKrWYOvQFWRlLUeq0iCT6+h2l5bniP7q7hepzlKa+VPY9VWaQthqeJm2l5LN6QQ5PyMfBq04QuBncw9BJnCGmEyTLt3RxIXBAPdxmiVxtcRIFUqCBtQvoUXGLvemw==t37k +rndegd.c iQCVAwUAP9iPRDEAnp832S/7AQImBQP/WHKg+hKXcm1pQvilzML0jZpwK5PAMM4uBnnPJNIXWOYBO6I/Xg9d/tPLg8NlmmtyQCo2Eu0ybDSt+8mu+dWveAys+0LTi0MIqeP9BMzCKz8dnWH6+S8huLXwTF3m0IrqM0JLb6b71GK9SOq6sWQ22yW5vf61hXP8kH9dhIaoMZs==FaHV +rndunix.c iQCVAwUAP9iQlzEAnp832S/7AQL/KgQA29GnvcD4Xb5qjDMBgW9THEE4+4lfex/6k+Fh0IT61OLJsWVLJ7bJpRntburw4uQm4Tf7CO8vaiDFDYhKKrzXeOF1fmdpcL8hA+fNp9I/MUOc4e9kN9+YJ9wikVa0SZj1OBfhzgcFLd1xOtulkr3ii52HLF9vhrxzkgVwvD10Bi8==2cML +rndw32.c iQCVAwUAP9iRKDEAnp832S/7AQIuaAQA3AJr3WqnxNDsWCIdvehf8Suotthj+laX8nJsvDfFhXPKcXDpsg0wTTXSnnKgyED53+uYiMDnVRsxeWAyhKwvx1MjjlaSMMjzbH6isWTH8FaWpLgrxEkXoPeNqYf5FXpdUkcUxGX2RkQeuX/cIfiHLNE9CV0usaF2jysjBX2iERY==EEnO + +# Helper +bithelp.h iQCVAwUAP7ouPTEAnp832S/7AQKXggQAqjcgvihIF3WclOgw1JV2rbARw4ISIDRMFqdaNCqBRx6BwEz3UGsEIlz6+iR1sS/reqN61WvtjLb+D0+tujAkGrgQJhFLG85WtG2tB5UVoI3am1fpkwiRm+bR4rv0rGk0BYk81bC7+l4KrK9o5lVp4lCsrorlUKsd48lNmBHyAXM==mDDN +rmd.h iQCVAwUAP7oumjEAnp832S/7AQJiJQP/V4bJwjZaYndJzV+KRnIDbl1koHuw+ZK5heMYVu8Qk4ylqv//BGyeRa3jZCcfPHI35q6HilCs2VBm8hiBMjHSqY/VPn2ZQ0yg/lt6qEvl7YjsLmyMICvjG+ncszHoq9pRvnF3vTnM18sPIioXLk8fskuM0XOCNBs0ARBAQjY9UGI==olUN + +# Configuration +Makefile.am iQCVAwUAQCN33TEAnp832S/7AQKFJAQAz7BDkC814q+QiuE/jnutJHR5qlgbrm3ikGbQwdRzYUscst4bCCWy3uKL/sIPGLg+JQXtF5FnsQy3s4D9BOYhp72cA9ktYK65hhi4pNm/JQ0lXkZMNfk8Go5lNzKezlWwHvkMwRXR0Fep0wPdyeaKW5BfaW2ABvgep6Bp+hHEbyg==zSyi +$names$ iQCVAwUAQCN3EDEAnp832S/7AQJXLAP8DvHTpm5DkTF35EmzeKpi9ie59AZcZanD19ir/e/7+PaQxr2riuLHDGwFKTju+dcvvBsqrygXOC378GXVWzIF2OZwS4EdDcJ+pgojo9UpsqpKsJHouY4Ugx5cQialxba462kUn8hcihSBnMyc4LzbJ5WQ4puQuqy544d2x94+2ms==G4Ls diff --git a/lib/libgcrypt/cipher/ac.c b/grub-core/lib/libgcrypt/cipher/ac.c similarity index 99% rename from lib/libgcrypt/cipher/ac.c rename to grub-core/lib/libgcrypt/cipher/ac.c index ee9498b23..63f6fcd11 100644 --- a/lib/libgcrypt/cipher/ac.c +++ b/grub-core/lib/libgcrypt/cipher/ac.c @@ -1,19 +1,19 @@ /* ac.c - Alternative interface for asymmetric cryptography. Copyright (C) 2003, 2004, 2005, 2006 2007, 2008 Free Software Foundation, Inc. - + This file is part of Libgcrypt. - + Libgcrypt is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser general Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + Libgcrypt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this program; if not, see . */ @@ -115,7 +115,7 @@ struct gcry_ac_key_pair -/* +/* * Functions for working with data sets. */ @@ -151,7 +151,7 @@ static void ac_data_values_destroy (gcry_ac_data_t data) { unsigned int i; - + for (i = 0; i < data->data_n; i++) if (data->data[i].flags & GCRY_AC_FLAG_DEALLOC) { @@ -185,7 +185,7 @@ ac_data_mpi_copy (gcry_ac_mpi_t *data_mpis, unsigned int data_mpis_n, gcry_mpi_t mpi; char *label; - data_mpis_new = gcry_malloc (sizeof (*data_mpis_new) * data_mpis_n); + data_mpis_new = gcry_calloc (data_mpis_n, sizeof (*data_mpis_new)); if (! data_mpis_new) { err = gcry_error_from_errno (errno); @@ -256,7 +256,7 @@ _gcry_ac_data_copy (gcry_ac_data_t *data_cp, gcry_ac_data_t data) err = ac_data_mpi_copy (data->data, data->data_n, &data_mpis); if (err) goto out; - + data_new->data_n = data->data_n; data_new->data = data_mpis; *data_cp = data_new; @@ -524,13 +524,13 @@ _gcry_ac_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp, i++; } identifiers_n = i; - + if (! identifiers_n) /* If there are NO identifiers, we still add surrounding braces so that we have a list of named MPI value lists. Otherwise it wouldn't be too much fun to process these lists. */ sexp_buffer_n += 2; - + data_n = _gcry_ac_data_length (data); for (i = 0; i < data_n; i++) { @@ -572,7 +572,7 @@ _gcry_ac_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp, } /* Add MPI list. */ - arg_list = gcry_malloc (sizeof (*arg_list) * (data_n + 1)); + arg_list = gcry_calloc (data_n + 1, sizeof (*arg_list)); if (! arg_list) { err = gcry_error_from_errno (errno); @@ -666,7 +666,7 @@ _gcry_ac_data_from_sexp (gcry_ac_data_t *data_set, gcry_sexp_t sexp, /* Identifier matches. Now we have to distinguish two cases: - + (i) we are at the last identifier: leave loop @@ -725,7 +725,7 @@ _gcry_ac_data_from_sexp (gcry_ac_data_t *data_set, gcry_sexp_t sexp, skip_name = 0; /* Create data set from S-expression data. */ - + err = gcry_ac_data_new (&data_set_new); if (err) goto out; @@ -793,7 +793,7 @@ _gcry_ac_data_from_sexp (gcry_ac_data_t *data_set, gcry_sexp_t sexp, gcry_sexp_release (sexp_tmp); gcry_mpi_release (mpi); gcry_free (string); - + if (err) gcry_ac_data_destroy (data_set_new); @@ -1005,7 +1005,7 @@ _gcry_ac_io_read (gcry_ac_io_t *ac_io, unsigned int nread, unsigned char *buffer, size_t *buffer_n) { gcry_error_t err; - + gcry_assert (ac_io->mode == GCRY_AC_IO_READABLE); err = 0; @@ -1072,7 +1072,7 @@ _gcry_ac_io_read_all (gcry_ac_io_t *ac_io, unsigned char **buffer, size_t *buffe err = gcry_error_from_errno (errno); break; } - + if (buffer_new != p) buffer_new = p; @@ -1132,7 +1132,7 @@ _gcry_ac_io_process (gcry_ac_io_t *ac_io, -/* +/* * Functions for converting data between the native ac and the * S-expression structure used by the pk interface. */ @@ -1283,7 +1283,7 @@ ac_data_construct (const char *identifier, int include_flags, /* We build a list of arguments to pass to gcry_sexp_build_array(). */ data_length = _gcry_ac_data_length (data); - arg_list = gcry_malloc (sizeof (*arg_list) * (data_length * 2)); + arg_list = gcry_calloc (data_length, sizeof (*arg_list) * 2); if (! arg_list) { err = gcry_error_from_errno (errno); @@ -1403,7 +1403,7 @@ _gcry_ac_open (gcry_ac_handle_t *handle, err = _gcry_pk_module_lookup (algorithm, &module); if (err) goto out; - + /* Allocate. */ handle_new = gcry_malloc (sizeof (*handle_new)); if (! handle_new) @@ -1420,7 +1420,7 @@ _gcry_ac_open (gcry_ac_handle_t *handle, *handle = handle_new; out: - + /* Deallocate resources. */ if (err) _gcry_pk_module_release (module); @@ -1443,7 +1443,7 @@ _gcry_ac_close (gcry_ac_handle_t handle) -/* +/* * Key management. */ @@ -1593,7 +1593,7 @@ _gcry_ac_key_pair_generate (gcry_ac_handle_t handle, unsigned int nbits, arg_list_n += 2; /* Allocate list. */ - arg_list = gcry_malloc (sizeof (*arg_list) * arg_list_n); + arg_list = gcry_calloc (arg_list_n, sizeof (*arg_list)); if (! arg_list) { err = gcry_error_from_errno (errno); @@ -1662,7 +1662,7 @@ _gcry_ac_key_pair_generate (gcry_ac_handle_t handle, unsigned int nbits, out: /* Deallocate resources. */ - + gcry_free (genkey_format); gcry_free (arg_list); gcry_sexp_release (genkey_sexp_request); @@ -1682,7 +1682,7 @@ _gcry_ac_key_pair_generate (gcry_ac_handle_t handle, unsigned int nbits, /* Returns the key of type WHICH out of the key pair KEY_PAIR. */ gcry_ac_key_t -_gcry_ac_key_pair_extract (gcry_ac_key_pair_t key_pair, +_gcry_ac_key_pair_extract (gcry_ac_key_pair_t key_pair, gcry_ac_key_type_t which) { gcry_ac_key_t key; @@ -1851,7 +1851,7 @@ _gcry_ac_key_get_grip (gcry_ac_handle_t handle, -/* +/* * Functions performing cryptographic operations. */ @@ -2176,7 +2176,7 @@ em_randomize_nonzero (unsigned char *buffer, size_t buffer_n, for (i = 0; i < buffer_n; i++) buffer[i] = 0; - + do { /* Count zeros. */ @@ -2384,7 +2384,7 @@ emsa_pkcs_v1_5_encode (unsigned int flags, void *opts, unsigned int i; (void)flags; - + options = opts; buffer = NULL; md = NULL; @@ -2499,7 +2499,7 @@ typedef enum dencode_action dencode_action_t; /* Encode or decode a message according to the the encoding method - METHOD; ACTION specifies wether the message that is contained in + METHOD; ACTION specifies whether the message that is contained in BUFFER_IN and of length BUFFER_IN_N should be encoded or decoded. The resulting message will be stored in a newly allocated buffer in BUFFER_OUT and BUFFER_OUT_N. */ @@ -2656,7 +2656,7 @@ _gcry_ac_mpi_to_os_alloc (gcry_mpi_t mpi, unsigned char **os, size_t *os_n) err = gcry_error_from_errno (errno); goto out; } - + _gcry_ac_mpi_to_os (mpi, buffer, buffer_n); *os = buffer; *os_n = buffer_n; @@ -2676,7 +2676,7 @@ _gcry_ac_os_to_mpi (gcry_mpi_t mpi, unsigned char *os, size_t os_n) gcry_mpi_t xi; gcry_mpi_t x; gcry_mpi_t a; - + if (fips_mode ()) return; @@ -2692,7 +2692,7 @@ _gcry_ac_os_to_mpi (gcry_mpi_t mpi, unsigned char *os, size_t os_n) gcry_mpi_add (x, x, xi); gcry_mpi_mul_ui (a, a, 256); } - + gcry_mpi_release (xi); gcry_mpi_release (a); @@ -2702,7 +2702,7 @@ _gcry_ac_os_to_mpi (gcry_mpi_t mpi, unsigned char *os, size_t os_n) -/* +/* * Implementation of Encryption Schemes (ES) and Signature Schemes * with Appendix (SSA). */ @@ -2824,7 +2824,7 @@ ac_dencode_prepare (gcry_ac_handle_t handle, gcry_ac_key_t key, void *opts, err = gcry_error_from_errno (errno); goto out; } - + err = (*scheme.dencode_prepare) (handle, key, opts, options_em); if (err) goto out; @@ -3065,7 +3065,7 @@ _gcry_ac_data_decrypt_scheme (gcry_ac_handle_t handle, goto out; out: - + _gcry_ac_data_destroy (data_encrypted); gcry_mpi_release (mpi_encrypted); gcry_mpi_release (mpi_decrypted); @@ -3270,7 +3270,7 @@ _gcry_ac_data_verify_scheme (gcry_ac_handle_t handle, gcry_mpi_release (mpi_signature); mpi_signature = NULL; - + err = _gcry_ac_data_verify (handle, key, mpi_data, data_signed); out: @@ -3287,7 +3287,7 @@ _gcry_ac_data_verify_scheme (gcry_ac_handle_t handle, } -/* +/* * General functions. */ diff --git a/lib/libgcrypt/cipher/arcfour.c b/grub-core/lib/libgcrypt/cipher/arcfour.c similarity index 97% rename from lib/libgcrypt/cipher/arcfour.c rename to grub-core/lib/libgcrypt/cipher/arcfour.c index 6bb0555c6..6ef07fb20 100644 --- a/lib/libgcrypt/cipher/arcfour.c +++ b/grub-core/lib/libgcrypt/cipher/arcfour.c @@ -45,7 +45,7 @@ do_encrypt_stream( ARCFOUR_context *ctx, register int i = ctx->idx_i; register int j = ctx->idx_j; register byte *sbox = ctx->sbox; - register int t; + register int t; while ( length-- ) { @@ -56,7 +56,7 @@ do_encrypt_stream( ARCFOUR_context *ctx, t = sbox[i]; sbox[i] = sbox[j]; sbox[j] = t; *outbuf++ = *inbuf++ ^ sbox[(sbox[i] + sbox[j]) & 255]; } - + ctx->idx_i = i; ctx->idx_j = j; } @@ -80,7 +80,7 @@ do_arcfour_setkey (void *context, const byte *key, unsigned int keylen) byte karr[256]; ARCFOUR_context *ctx = (ARCFOUR_context *) context; - if (!initialized ) + if (!initialized ) { initialized = 1; selftest_failed = selftest(); @@ -98,14 +98,14 @@ do_arcfour_setkey (void *context, const byte *key, unsigned int keylen) ctx->sbox[i] = i; for (i=0; i < 256; i++ ) karr[i] = key[i%keylen]; - for (i=j=0; i < 256; i++ ) + for (i=j=0; i < 256; i++ ) { int t; j = (j + ctx->sbox[i] + karr[i]) % 256; t = ctx->sbox[i]; ctx->sbox[i] = ctx->sbox[j]; ctx->sbox[j] = t; - } + } memset( karr, 0, 256 ); return GPG_ERR_NO_ERROR; @@ -125,8 +125,8 @@ static const char* selftest(void) { ARCFOUR_context ctx; - byte scratch[16]; - + byte scratch[16]; + /* Test vector from Cryptlib labeled there: "from the State/Commerce Department". */ static byte key_1[] = @@ -153,4 +153,3 @@ gcry_cipher_spec_t _gcry_cipher_spec_arcfour = "ARCFOUR", NULL, NULL, 1, 128, sizeof (ARCFOUR_context), arcfour_setkey, NULL, NULL, encrypt_stream, encrypt_stream, }; - diff --git a/lib/libgcrypt/cipher/bithelp.h b/grub-core/lib/libgcrypt/cipher/bithelp.h similarity index 100% rename from lib/libgcrypt/cipher/bithelp.h rename to grub-core/lib/libgcrypt/cipher/bithelp.h diff --git a/lib/libgcrypt/cipher/blowfish.c b/grub-core/lib/libgcrypt/cipher/blowfish.c similarity index 99% rename from lib/libgcrypt/cipher/blowfish.c rename to grub-core/lib/libgcrypt/cipher/blowfish.c index 6ef68e371..b4d2b9c9a 100644 --- a/lib/libgcrypt/cipher/blowfish.c +++ b/grub-core/lib/libgcrypt/cipher/blowfish.c @@ -501,7 +501,7 @@ do_bf_setkey (BLOWFISH_context *c, const byte *key, unsigned keylen) static int initialized; static const char *selftest_failed; - if( !initialized ) + if( !initialized ) { initialized = 1; selftest_failed = selftest(); @@ -513,7 +513,7 @@ do_bf_setkey (BLOWFISH_context *c, const byte *key, unsigned keylen) for(i=0; i < BLOWFISH_ROUNDS+2; i++ ) c->p[i] = ps[i]; - for(i=0; i < 256; i++ ) + for(i=0; i < 256; i++ ) { c->s0[i] = ks0[i]; c->s1[i] = ks1[i]; @@ -521,7 +521,7 @@ do_bf_setkey (BLOWFISH_context *c, const byte *key, unsigned keylen) c->s3[i] = ks3[i]; } - for(i=j=0; i < BLOWFISH_ROUNDS+2; i++ ) + for(i=j=0; i < BLOWFISH_ROUNDS+2; i++ ) { #ifdef WORDS_BIGENDIAN ((byte*)&data)[0] = key[j]; @@ -545,7 +545,7 @@ do_bf_setkey (BLOWFISH_context *c, const byte *key, unsigned keylen) c->p[i] = datal; c->p[i+1] = datar; } - for(i=0; i < 256; i += 2 ) + for(i=0; i < 256; i += 2 ) { do_encrypt( c, &datal, &datar ); c->s0[i] = datal; diff --git a/grub-core/lib/libgcrypt/cipher/bufhelp.h b/grub-core/lib/libgcrypt/cipher/bufhelp.h new file mode 100644 index 000000000..df3559472 --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/bufhelp.h @@ -0,0 +1,432 @@ +/* bufhelp.h - Some buffer manipulation helpers + * Copyright (C) 2012 Jussi Kivilinna + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ +#ifndef GCRYPT_BUFHELP_H +#define GCRYPT_BUFHELP_H + + +#include "bithelp.h" + + +#undef BUFHELP_FAST_UNALIGNED_ACCESS +#if defined(HAVE_GCC_ATTRIBUTE_PACKED) && \ + defined(HAVE_GCC_ATTRIBUTE_ALIGNED) && \ + (defined(__i386__) || defined(__x86_64__) || \ + (defined(__arm__) && defined(__ARM_FEATURE_UNALIGNED)) || \ + defined(__aarch64__)) +/* These architectures are able of unaligned memory accesses and can + handle those fast. + */ +# define BUFHELP_FAST_UNALIGNED_ACCESS 1 +#endif + + +#ifdef BUFHELP_FAST_UNALIGNED_ACCESS +/* Define type with one-byte alignment on architectures with fast unaligned + memory accesses. + */ +typedef struct bufhelp_int_s +{ + uintptr_t a; +} __attribute__((packed, aligned(1))) bufhelp_int_t; +#else +/* Define type with default alignment for other architectures (unaligned + accessed handled in per byte loops). + */ +typedef struct bufhelp_int_s +{ + uintptr_t a; +} bufhelp_int_t; +#endif + + +/* Optimized function for small buffer copying */ +static inline void +buf_cpy(void *_dst, const void *_src, size_t len) +{ +#if __GNUC__ >= 4 && (defined(__x86_64__) || defined(__i386__)) + /* For AMD64 and i386, memcpy is faster. */ + memcpy(_dst, _src, len); +#else + byte *dst = _dst; + const byte *src = _src; + bufhelp_int_t *ldst; + const bufhelp_int_t *lsrc; +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + const unsigned int longmask = sizeof(bufhelp_int_t) - 1; + + /* Skip fast processing if buffers are unaligned. */ + if (((uintptr_t)dst | (uintptr_t)src) & longmask) + goto do_bytes; +#endif + + ldst = (bufhelp_int_t *)(void *)dst; + lsrc = (const bufhelp_int_t *)(const void *)src; + + for (; len >= sizeof(bufhelp_int_t); len -= sizeof(bufhelp_int_t)) + (ldst++)->a = (lsrc++)->a; + + dst = (byte *)ldst; + src = (const byte *)lsrc; + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS +do_bytes: +#endif + /* Handle tail. */ + for (; len; len--) + *dst++ = *src++; +#endif /*__GNUC__ >= 4 && (__x86_64__ || __i386__)*/ +} + + +/* Optimized function for buffer xoring */ +static inline void +buf_xor(void *_dst, const void *_src1, const void *_src2, size_t len) +{ + byte *dst = _dst; + const byte *src1 = _src1; + const byte *src2 = _src2; + bufhelp_int_t *ldst; + const bufhelp_int_t *lsrc1, *lsrc2; +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + const unsigned int longmask = sizeof(bufhelp_int_t) - 1; + + /* Skip fast processing if buffers are unaligned. */ + if (((uintptr_t)dst | (uintptr_t)src1 | (uintptr_t)src2) & longmask) + goto do_bytes; +#endif + + ldst = (bufhelp_int_t *)(void *)dst; + lsrc1 = (const bufhelp_int_t *)(const void *)src1; + lsrc2 = (const bufhelp_int_t *)(const void *)src2; + + for (; len >= sizeof(bufhelp_int_t); len -= sizeof(bufhelp_int_t)) + (ldst++)->a = (lsrc1++)->a ^ (lsrc2++)->a; + + dst = (byte *)ldst; + src1 = (const byte *)lsrc1; + src2 = (const byte *)lsrc2; + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS +do_bytes: +#endif + /* Handle tail. */ + for (; len; len--) + *dst++ = *src1++ ^ *src2++; +} + + +/* Optimized function for in-place buffer xoring. */ +static inline void +buf_xor_1(void *_dst, const void *_src, size_t len) +{ + byte *dst = _dst; + const byte *src = _src; + bufhelp_int_t *ldst; + const bufhelp_int_t *lsrc; +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + const unsigned int longmask = sizeof(bufhelp_int_t) - 1; + + /* Skip fast processing if buffers are unaligned. */ + if (((uintptr_t)dst | (uintptr_t)src) & longmask) + goto do_bytes; +#endif + + ldst = (bufhelp_int_t *)(void *)dst; + lsrc = (const bufhelp_int_t *)(const void *)src; + + for (; len >= sizeof(bufhelp_int_t); len -= sizeof(bufhelp_int_t)) + (ldst++)->a ^= (lsrc++)->a; + + dst = (byte *)ldst; + src = (const byte *)lsrc; + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS +do_bytes: +#endif + /* Handle tail. */ + for (; len; len--) + *dst++ ^= *src++; +} + + +/* Optimized function for buffer xoring with two destination buffers. Used + mainly by CFB mode encryption. */ +static inline void +buf_xor_2dst(void *_dst1, void *_dst2, const void *_src, size_t len) +{ + byte *dst1 = _dst1; + byte *dst2 = _dst2; + const byte *src = _src; + bufhelp_int_t *ldst1, *ldst2; + const bufhelp_int_t *lsrc; +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + const unsigned int longmask = sizeof(bufhelp_int_t) - 1; + + /* Skip fast processing if buffers are unaligned. */ + if (((uintptr_t)src | (uintptr_t)dst1 | (uintptr_t)dst2) & longmask) + goto do_bytes; +#endif + + ldst1 = (bufhelp_int_t *)(void *)dst1; + ldst2 = (bufhelp_int_t *)(void *)dst2; + lsrc = (const bufhelp_int_t *)(const void *)src; + + for (; len >= sizeof(bufhelp_int_t); len -= sizeof(bufhelp_int_t)) + (ldst1++)->a = ((ldst2++)->a ^= (lsrc++)->a); + + dst1 = (byte *)ldst1; + dst2 = (byte *)ldst2; + src = (const byte *)lsrc; + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS +do_bytes: +#endif + /* Handle tail. */ + for (; len; len--) + *dst1++ = (*dst2++ ^= *src++); +} + + +/* Optimized function for combined buffer xoring and copying. Used by mainly + CBC mode decryption. */ +static inline void +buf_xor_n_copy_2(void *_dst_xor, const void *_src_xor, void *_srcdst_cpy, + const void *_src_cpy, size_t len) +{ + byte *dst_xor = _dst_xor; + byte *srcdst_cpy = _srcdst_cpy; + const byte *src_xor = _src_xor; + const byte *src_cpy = _src_cpy; + byte temp; + bufhelp_int_t *ldst_xor, *lsrcdst_cpy; + const bufhelp_int_t *lsrc_cpy, *lsrc_xor; + uintptr_t ltemp; +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + const unsigned int longmask = sizeof(bufhelp_int_t) - 1; + + /* Skip fast processing if buffers are unaligned. */ + if (((uintptr_t)src_cpy | (uintptr_t)src_xor | (uintptr_t)dst_xor | + (uintptr_t)srcdst_cpy) & longmask) + goto do_bytes; +#endif + + ldst_xor = (bufhelp_int_t *)(void *)dst_xor; + lsrc_xor = (const bufhelp_int_t *)(void *)src_xor; + lsrcdst_cpy = (bufhelp_int_t *)(void *)srcdst_cpy; + lsrc_cpy = (const bufhelp_int_t *)(const void *)src_cpy; + + for (; len >= sizeof(bufhelp_int_t); len -= sizeof(bufhelp_int_t)) + { + ltemp = (lsrc_cpy++)->a; + (ldst_xor++)->a = (lsrcdst_cpy)->a ^ (lsrc_xor++)->a; + (lsrcdst_cpy++)->a = ltemp; + } + + dst_xor = (byte *)ldst_xor; + src_xor = (const byte *)lsrc_xor; + srcdst_cpy = (byte *)lsrcdst_cpy; + src_cpy = (const byte *)lsrc_cpy; + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS +do_bytes: +#endif + /* Handle tail. */ + for (; len; len--) + { + temp = *src_cpy++; + *dst_xor++ = *srcdst_cpy ^ *src_xor++; + *srcdst_cpy++ = temp; + } +} + + +/* Optimized function for combined buffer xoring and copying. Used by mainly + CFB mode decryption. */ +static inline void +buf_xor_n_copy(void *_dst_xor, void *_srcdst_cpy, const void *_src, size_t len) +{ + buf_xor_n_copy_2(_dst_xor, _src, _srcdst_cpy, _src, len); +} + + +/* Constant-time compare of two buffers. Returns 1 if buffers are equal, + and 0 if buffers differ. */ +static inline int +buf_eq_const(const void *_a, const void *_b, size_t len) +{ + const byte *a = _a; + const byte *b = _b; + size_t diff, i; + + /* Constant-time compare. */ + for (i = 0, diff = 0; i < len; i++) + diff -= !!(a[i] - b[i]); + + return !diff; +} + + +#ifndef BUFHELP_FAST_UNALIGNED_ACCESS + +/* Functions for loading and storing unaligned u32 values of different + endianness. */ +static inline u32 buf_get_be32(const void *_buf) +{ + const byte *in = _buf; + return ((u32)in[0] << 24) | ((u32)in[1] << 16) | \ + ((u32)in[2] << 8) | (u32)in[3]; +} + +static inline u32 buf_get_le32(const void *_buf) +{ + const byte *in = _buf; + return ((u32)in[3] << 24) | ((u32)in[2] << 16) | \ + ((u32)in[1] << 8) | (u32)in[0]; +} + +static inline void buf_put_be32(void *_buf, u32 val) +{ + byte *out = _buf; + out[0] = val >> 24; + out[1] = val >> 16; + out[2] = val >> 8; + out[3] = val; +} + +static inline void buf_put_le32(void *_buf, u32 val) +{ + byte *out = _buf; + out[3] = val >> 24; + out[2] = val >> 16; + out[1] = val >> 8; + out[0] = val; +} + + +/* Functions for loading and storing unaligned u64 values of different + endianness. */ +static inline u64 buf_get_be64(const void *_buf) +{ + const byte *in = _buf; + return ((u64)in[0] << 56) | ((u64)in[1] << 48) | \ + ((u64)in[2] << 40) | ((u64)in[3] << 32) | \ + ((u64)in[4] << 24) | ((u64)in[5] << 16) | \ + ((u64)in[6] << 8) | (u64)in[7]; +} + +static inline u64 buf_get_le64(const void *_buf) +{ + const byte *in = _buf; + return ((u64)in[7] << 56) | ((u64)in[6] << 48) | \ + ((u64)in[5] << 40) | ((u64)in[4] << 32) | \ + ((u64)in[3] << 24) | ((u64)in[2] << 16) | \ + ((u64)in[1] << 8) | (u64)in[0]; +} + +static inline void buf_put_be64(void *_buf, u64 val) +{ + byte *out = _buf; + out[0] = val >> 56; + out[1] = val >> 48; + out[2] = val >> 40; + out[3] = val >> 32; + out[4] = val >> 24; + out[5] = val >> 16; + out[6] = val >> 8; + out[7] = val; +} + +static inline void buf_put_le64(void *_buf, u64 val) +{ + byte *out = _buf; + out[7] = val >> 56; + out[6] = val >> 48; + out[5] = val >> 40; + out[4] = val >> 32; + out[3] = val >> 24; + out[2] = val >> 16; + out[1] = val >> 8; + out[0] = val; +} + +#else /*BUFHELP_FAST_UNALIGNED_ACCESS*/ + +typedef struct bufhelp_u32_s +{ + u32 a; +} __attribute__((packed, aligned(1))) bufhelp_u32_t; + +/* Functions for loading and storing unaligned u32 values of different + endianness. */ +static inline u32 buf_get_be32(const void *_buf) +{ + return be_bswap32(((const bufhelp_u32_t *)_buf)->a); +} + +static inline u32 buf_get_le32(const void *_buf) +{ + return le_bswap32(((const bufhelp_u32_t *)_buf)->a); +} + +static inline void buf_put_be32(void *_buf, u32 val) +{ + bufhelp_u32_t *out = _buf; + out->a = be_bswap32(val); +} + +static inline void buf_put_le32(void *_buf, u32 val) +{ + bufhelp_u32_t *out = _buf; + out->a = le_bswap32(val); +} + + +typedef struct bufhelp_u64_s +{ + u64 a; +} __attribute__((packed, aligned(1))) bufhelp_u64_t; + +/* Functions for loading and storing unaligned u64 values of different + endianness. */ +static inline u64 buf_get_be64(const void *_buf) +{ + return be_bswap64(((const bufhelp_u64_t *)_buf)->a); +} + +static inline u64 buf_get_le64(const void *_buf) +{ + return le_bswap64(((const bufhelp_u64_t *)_buf)->a); +} + +static inline void buf_put_be64(void *_buf, u64 val) +{ + bufhelp_u64_t *out = _buf; + out->a = be_bswap64(val); +} + +static inline void buf_put_le64(void *_buf, u64 val) +{ + bufhelp_u64_t *out = _buf; + out->a = le_bswap64(val); +} + + +#endif /*BUFHELP_FAST_UNALIGNED_ACCESS*/ + +#endif /*GCRYPT_BUFHELP_H*/ diff --git a/lib/libgcrypt/cipher/camellia-glue.c b/grub-core/lib/libgcrypt/cipher/camellia-glue.c similarity index 99% rename from lib/libgcrypt/cipher/camellia-glue.c rename to grub-core/lib/libgcrypt/cipher/camellia-glue.c index 067af85bc..a26362177 100644 --- a/lib/libgcrypt/cipher/camellia-glue.c +++ b/grub-core/lib/libgcrypt/cipher/camellia-glue.c @@ -32,7 +32,7 @@ * space of the library clean. The following macro is thus useful: * * #define CAMELLIA_EXT_SYM_PREFIX foo_ - * + * * This prefixes all external symbols with "foo_". */ #ifdef HAVE_CONFIG_H @@ -50,7 +50,7 @@ #define camellia_encrypt128 CAMELLIA_PREFIX(camellia_encrypt128) #define camellia_encrypt256 CAMELLIA_PREFIX(camellia_encrypt256) #define camellia_setup128 CAMELLIA_PREFIX(camellia_setup128) -#define camellia_setup192 CAMELLIA_PREFIX(camellia_setup192) +#define camellia_setup192 CAMELLIA_PREFIX(camellia_setup192) #define camellia_setup256 CAMELLIA_PREFIX(camellia_setup256) #endif /*CAMELLIA_EXT_SYM_PREFIX*/ @@ -99,7 +99,7 @@ camellia_setkey(void *c, const byte *key, unsigned keylen) +(4+32)*sizeof(u32)+2*sizeof(void*) /* camellia_setup192 */ +0+sizeof(int)+2*sizeof(void*) /* Camellia_Ekeygen */ +3*2*sizeof(void*) /* Function calls. */ - ); + ); return 0; } @@ -137,7 +137,7 @@ selftest(void) { CAMELLIA_context ctx; byte scratch[16]; - + /* These test vectors are from RFC-3713 */ const byte plaintext[]= { diff --git a/lib/libgcrypt/cipher/camellia.c b/grub-core/lib/libgcrypt/cipher/camellia.c similarity index 99% rename from lib/libgcrypt/cipher/camellia.c rename to grub-core/lib/libgcrypt/cipher/camellia.c index 79cd49b7c..2e28bce2a 100644 --- a/lib/libgcrypt/cipher/camellia.c +++ b/grub-core/lib/libgcrypt/cipher/camellia.c @@ -19,7 +19,7 @@ */ /* - * Algorithm Specification + * Algorithm Specification * http://info.isl.ntt.co.jp/crypt/eng/camellia/specifications.html */ @@ -937,7 +937,7 @@ void camellia_setup256(const unsigned char *key, u32 *subkey) CamelliaSubkeyR(30) = CamelliaSubkeyL(30) ^ dw, CamelliaSubkeyL(30) = dw; dw = CamelliaSubkeyL(31) ^ CamelliaSubkeyR(31), dw = CAMELLIA_RL8(dw); CamelliaSubkeyR(31) = CamelliaSubkeyL(31) ^ dw,CamelliaSubkeyL(31) = dw; - + return; } @@ -1049,14 +1049,14 @@ void camellia_encrypt128(const u32 *subkey, u32 *io) io[1] = io[3]; io[2] = t0; io[3] = t1; - + return; } void camellia_decrypt128(const u32 *subkey, u32 *io) { u32 il,ir,t0,t1; /* temporary valiables */ - + /* pre whitening but absorb kw2*/ io[0] ^= CamelliaSubkeyL(24); io[1] ^= CamelliaSubkeyR(24); @@ -1267,7 +1267,7 @@ void camellia_decrypt256(const u32 *subkey, u32 *io) /* pre whitening but absorb kw2*/ io[0] ^= CamelliaSubkeyL(32); io[1] ^= CamelliaSubkeyR(32); - + /* main iteration */ CAMELLIA_ROUNDSM(io[0],io[1], CamelliaSubkeyL(31),CamelliaSubkeyR(31), @@ -1379,8 +1379,8 @@ void camellia_decrypt256(const u32 *subkey, u32 *io) * API for compatibility */ -void Camellia_Ekeygen(const int keyBitLength, - const unsigned char *rawKey, +void Camellia_Ekeygen(const int keyBitLength, + const unsigned char *rawKey, KEY_TABLE_TYPE keyTable) { switch(keyBitLength) { @@ -1399,9 +1399,9 @@ void Camellia_Ekeygen(const int keyBitLength, } -void Camellia_EncryptBlock(const int keyBitLength, - const unsigned char *plaintext, - const KEY_TABLE_TYPE keyTable, +void Camellia_EncryptBlock(const int keyBitLength, + const unsigned char *plaintext, + const KEY_TABLE_TYPE keyTable, unsigned char *ciphertext) { u32 tmp[4]; @@ -1430,9 +1430,9 @@ void Camellia_EncryptBlock(const int keyBitLength, PUTU32(ciphertext + 12, tmp[3]); } -void Camellia_DecryptBlock(const int keyBitLength, - const unsigned char *ciphertext, - const KEY_TABLE_TYPE keyTable, +void Camellia_DecryptBlock(const int keyBitLength, + const unsigned char *ciphertext, + const KEY_TABLE_TYPE keyTable, unsigned char *plaintext) { u32 tmp[4]; diff --git a/lib/libgcrypt/cipher/camellia.h b/grub-core/lib/libgcrypt/cipher/camellia.h similarity index 91% rename from lib/libgcrypt/cipher/camellia.h rename to grub-core/lib/libgcrypt/cipher/camellia.h index 4425a3a2b..cccf786ca 100644 --- a/lib/libgcrypt/cipher/camellia.h +++ b/grub-core/lib/libgcrypt/cipher/camellia.h @@ -25,7 +25,7 @@ * space of the library clean. The following macro is thus useful: * * #define CAMELLIA_EXT_SYM_PREFIX foo_ - * + * * This prefixes all external symbols with "foo_". */ #ifdef HAVE_CONFIG_H @@ -43,7 +43,7 @@ #define camellia_encrypt128 CAMELLIA_PREFIX(camellia_encrypt128) #define camellia_encrypt256 CAMELLIA_PREFIX(camellia_encrypt256) #define camellia_setup128 CAMELLIA_PREFIX(camellia_setup128) -#define camellia_setup192 CAMELLIA_PREFIX(camellia_setup192) +#define camellia_setup192 CAMELLIA_PREFIX(camellia_setup192) #define camellia_setup256 CAMELLIA_PREFIX(camellia_setup256) #endif /*CAMELLIA_EXT_SYM_PREFIX*/ @@ -60,17 +60,17 @@ typedef unsigned int KEY_TABLE_TYPE[CAMELLIA_TABLE_WORD_LEN]; void Camellia_Ekeygen(const int keyBitLength, - const unsigned char *rawKey, + const unsigned char *rawKey, KEY_TABLE_TYPE keyTable); void Camellia_EncryptBlock(const int keyBitLength, - const unsigned char *plaintext, - const KEY_TABLE_TYPE keyTable, + const unsigned char *plaintext, + const KEY_TABLE_TYPE keyTable, unsigned char *cipherText); -void Camellia_DecryptBlock(const int keyBitLength, - const unsigned char *cipherText, - const KEY_TABLE_TYPE keyTable, +void Camellia_DecryptBlock(const int keyBitLength, + const unsigned char *cipherText, + const KEY_TABLE_TYPE keyTable, unsigned char *plaintext); diff --git a/lib/libgcrypt/cipher/cast5.c b/grub-core/lib/libgcrypt/cipher/cast5.c similarity index 99% rename from lib/libgcrypt/cipher/cast5.c rename to grub-core/lib/libgcrypt/cipher/cast5.c index 333d55e91..9905f5cb9 100644 --- a/lib/libgcrypt/cipher/cast5.c +++ b/grub-core/lib/libgcrypt/cipher/cast5.c @@ -569,7 +569,7 @@ do_cast_setkey( CAST5_context *c, const byte *key, unsigned keylen ) u32 z[4]; u32 k[16]; - if( !initialized ) + if( !initialized ) { initialized = 1; selftest_failed = selftest(); diff --git a/lib/libgcrypt/cipher/cipher.c b/grub-core/lib/libgcrypt/cipher/cipher.c similarity index 68% rename from lib/libgcrypt/cipher/cipher.c rename to grub-core/lib/libgcrypt/cipher/cipher.c index 2c33ee94f..9852d6a5a 100644 --- a/lib/libgcrypt/cipher/cipher.c +++ b/grub-core/lib/libgcrypt/cipher/cipher.c @@ -1,6 +1,6 @@ /* cipher.c - cipher dispatcher * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 - * 2005, 2007, 2008 Free Software Foundation, Inc. + * 2005, 2007, 2008, 2009, 2011 Free Software Foundation, Inc. * * This file is part of Libgcrypt. * @@ -33,9 +33,12 @@ #define CTX_MAGIC_NORMAL 0x24091964 #define CTX_MAGIC_SECURE 0x46919042 +/* Try to use 16 byte aligned cipher context for better performance. + We use the aligned attribute, thus it is only possible to implement + this with gcc. */ #undef NEED_16BYTE_ALIGNED_CONTEXT -#if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__) -#define NEED_16BYTE_ALIGNED_CONTEXT 1 +#if defined (__GNUC__) +# define NEED_16BYTE_ALIGNED_CONTEXT 1 #endif /* A dummy extraspec so that we do not need to tests the extraspec @@ -58,39 +61,39 @@ static struct cipher_table_entry &dummy_extra_spec, GCRY_CIPHER_BLOWFISH }, #endif #if USE_DES - { &_gcry_cipher_spec_des, + { &_gcry_cipher_spec_des, &dummy_extra_spec, GCRY_CIPHER_DES }, { &_gcry_cipher_spec_tripledes, &_gcry_cipher_extraspec_tripledes, GCRY_CIPHER_3DES, 1 }, #endif #if USE_ARCFOUR - { &_gcry_cipher_spec_arcfour, + { &_gcry_cipher_spec_arcfour, &dummy_extra_spec, GCRY_CIPHER_ARCFOUR }, #endif #if USE_CAST5 - { &_gcry_cipher_spec_cast5, + { &_gcry_cipher_spec_cast5, &dummy_extra_spec, GCRY_CIPHER_CAST5 }, #endif #if USE_AES - { &_gcry_cipher_spec_aes, + { &_gcry_cipher_spec_aes, &_gcry_cipher_extraspec_aes, GCRY_CIPHER_AES, 1 }, - { &_gcry_cipher_spec_aes192, + { &_gcry_cipher_spec_aes192, &_gcry_cipher_extraspec_aes192, GCRY_CIPHER_AES192, 1 }, - { &_gcry_cipher_spec_aes256, + { &_gcry_cipher_spec_aes256, &_gcry_cipher_extraspec_aes256, GCRY_CIPHER_AES256, 1 }, #endif #if USE_TWOFISH { &_gcry_cipher_spec_twofish, &dummy_extra_spec, GCRY_CIPHER_TWOFISH }, - { &_gcry_cipher_spec_twofish128, + { &_gcry_cipher_spec_twofish128, &dummy_extra_spec, GCRY_CIPHER_TWOFISH128 }, #endif #if USE_SERPENT - { &_gcry_cipher_spec_serpent128, + { &_gcry_cipher_spec_serpent128, &dummy_extra_spec, GCRY_CIPHER_SERPENT128 }, { &_gcry_cipher_spec_serpent192, &dummy_extra_spec, GCRY_CIPHER_SERPENT192 }, - { &_gcry_cipher_spec_serpent256, + { &_gcry_cipher_spec_serpent256, &dummy_extra_spec, GCRY_CIPHER_SERPENT256 }, #endif #if USE_RFC2268 @@ -98,16 +101,20 @@ static struct cipher_table_entry &dummy_extra_spec, GCRY_CIPHER_RFC2268_40 }, #endif #if USE_SEED - { &_gcry_cipher_spec_seed, + { &_gcry_cipher_spec_seed, &dummy_extra_spec, GCRY_CIPHER_SEED }, #endif #if USE_CAMELLIA { &_gcry_cipher_spec_camellia128, &dummy_extra_spec, GCRY_CIPHER_CAMELLIA128 }, - { &_gcry_cipher_spec_camellia192, + { &_gcry_cipher_spec_camellia192, &dummy_extra_spec, GCRY_CIPHER_CAMELLIA192 }, { &_gcry_cipher_spec_camellia256, &dummy_extra_spec, GCRY_CIPHER_CAMELLIA256 }, +#endif +#ifdef USE_IDEA + { &_gcry_cipher_spec_idea, + &dummy_extra_spec, GCRY_CIPHER_IDEA }, #endif { NULL } }; @@ -118,7 +125,7 @@ static gcry_module_t ciphers_registered; /* This is the lock protecting CIPHERS_REGISTERED. */ static ath_mutex_t ciphers_registered_lock = ATH_MUTEX_INITIALIZER; -/* Flag to check wether the default ciphers have already been +/* Flag to check whether the default ciphers have already been registered. */ static int default_ciphers_registered; @@ -137,19 +144,20 @@ static int default_ciphers_registered; while (0) -/* A VIA processor with the Padlock engine requires an alignment of - most data on a 16 byte boundary. Because we trick out the compiler - while allocating the context, the align attribute as used in - rijndael.c does not work on its own. Thus we need to make sure - that the entire context structure is a aligned on that boundary. - We achieve this by defining a new type and use that instead of our - usual alignment type. */ -typedef union +/* A VIA processor with the Padlock engine as well as the Intel AES_NI + instructions require an alignment of most data on a 16 byte + boundary. Because we trick out the compiler while allocating the + context, the align attribute as used in rijndael.c does not work on + its own. Thus we need to make sure that the entire context + structure is a aligned on that boundary. We achieve this by + defining a new type and use that instead of our usual alignment + type. */ +typedef union { PROPERLY_ALIGNED_TYPE foo; #ifdef NEED_16BYTE_ALIGNED_CONTEXT char bar[16] __attribute__ ((aligned (16))); -#endif +#endif char c[1]; } cipher_context_alignment_t; @@ -166,7 +174,7 @@ struct gcry_cipher_handle /* The algorithm id. This is a hack required because the module interface does not easily allow to retrieve this value. */ - int algo; + int algo; /* A structure with function pointers for bulk operations. Due to limitations of the module system (we don't want to change the @@ -174,16 +182,19 @@ struct gcry_cipher_handle open function intializes them and the actual encryption routines use them if they are not NULL. */ struct { - void (*cfb_enc)(void *context, unsigned char *iv, + void (*cfb_enc)(void *context, unsigned char *iv, void *outbuf_arg, const void *inbuf_arg, unsigned int nblocks); - void (*cfb_dec)(void *context, unsigned char *iv, + void (*cfb_dec)(void *context, unsigned char *iv, void *outbuf_arg, const void *inbuf_arg, unsigned int nblocks); - void (*cbc_enc)(void *context, unsigned char *iv, + void (*cbc_enc)(void *context, unsigned char *iv, void *outbuf_arg, const void *inbuf_arg, unsigned int nblocks, int cbc_mac); - void (*cbc_dec)(void *context, unsigned char *iv, + void (*cbc_dec)(void *context, unsigned char *iv, + void *outbuf_arg, const void *inbuf_arg, + unsigned int nblocks); + void (*ctr_enc)(void *context, unsigned char *iv, void *outbuf_arg, const void *inbuf_arg, unsigned int nblocks); } bulk; @@ -192,19 +203,29 @@ struct gcry_cipher_handle int mode; unsigned int flags; - /* The initialization vector. To help code optimization we make - sure that it is aligned on an unsigned long and u32 boundary. */ + struct { + unsigned int key:1; /* Set to 1 if a key has been set. */ + unsigned int iv:1; /* Set to 1 if a IV has been set. */ + } marks; + + /* The initialization vector. For best performance we make sure + that it is properly aligned. In particular some implementations + of bulk operations expect an 16 byte aligned IV. */ union { - unsigned long dummy_iv; - u32 dummy_u32_iv; - unsigned char iv[MAX_BLOCKSIZE]; + cipher_context_alignment_t iv_align; + unsigned char iv[MAX_BLOCKSIZE]; } u_iv; + /* The counter for CTR mode. This field is also used by AESWRAP and + thus we can't use the U_IV union. */ + union { + cipher_context_alignment_t iv_align; + unsigned char ctr[MAX_BLOCKSIZE]; + } u_ctr; + + /* Space to save an IV or CTR for chaining operations. */ unsigned char lastiv[MAX_BLOCKSIZE]; - int unused; /* Number of unused bytes in the IV. */ - - unsigned char ctr[MAX_BLOCKSIZE]; /* For Counter (CTR) mode. */ - + int unused; /* Number of unused bytes in LASTIV. */ /* What follows are two contexts of the cipher in use. The first one needs to be aligned well enough for the cipher operation @@ -222,7 +243,7 @@ struct gcry_cipher_handle static gcry_err_code_t dummy_setkey (void *c, const unsigned char *key, unsigned int keylen) { - (void)c; + (void)c; (void)key; (void)keylen; return GPG_ERR_NO_ERROR; @@ -281,7 +302,7 @@ cipher_register_default (void) { gcry_err_code_t err = GPG_ERR_NO_ERROR; int i; - + for (i = 0; !err && cipher_table[i].cipher; i++) { if (! cipher_table[i].cipher->setkey) @@ -384,8 +405,8 @@ _gcry_cipher_register (gcry_cipher_spec_t *cipher, ath_mutex_lock (&ciphers_registered_lock); err = _gcry_module_add (&ciphers_registered, 0, - (void *)cipher, - (void *)(extraspec? extraspec : &dummy_extra_spec), + (void *)cipher, + (void *)(extraspec? extraspec : &dummy_extra_spec), &mod); ath_mutex_unlock (&ciphers_registered_lock); @@ -415,7 +436,7 @@ gcry_cipher_unregister (gcry_module_t module) ispassed as NULL. A pointer to the specification of the module implementing this algorithm is return in OID_SPEC unless passed as NULL.*/ -static int +static int search_oid (const char *oid, int *algorithm, gcry_cipher_oid_spec_t *oid_spec) { gcry_module_t module; @@ -479,7 +500,7 @@ gcry_cipher_map_name (const char *string) } ath_mutex_unlock (&ciphers_registered_lock); - + return algorithm; } @@ -588,15 +609,13 @@ check_cipher_algo (int algorithm) else err = GPG_ERR_CIPHER_ALGO; ath_mutex_unlock (&ciphers_registered_lock); - + return err; } -/* Return the standard length of the key for the cipher algorithm with - the identifier ALGORITHM. This function expects a valid algorithm - and will abort if the algorithm is not available or the length of - the key is not known. */ +/* Return the standard length in bits of the key for the cipher + algorithm with the identifier ALGORITHM. */ static unsigned int cipher_get_keylen (int algorithm) { @@ -614,17 +633,13 @@ cipher_get_keylen (int algorithm) log_bug ("cipher %d w/o key length\n", algorithm); _gcry_module_release (cipher); } - else - log_bug ("cipher %d not found\n", algorithm); ath_mutex_unlock (&ciphers_registered_lock); return len; } /* Return the block length of the cipher algorithm with the identifier - ALGORITHM. This function expects a valid algorithm and will abort - if the algorithm is not available or the length of the key is not - known. */ + ALGORITHM. This function return 0 for an invalid algorithm. */ static unsigned int cipher_get_blocksize (int algorithm) { @@ -642,8 +657,6 @@ cipher_get_blocksize (int algorithm) log_bug ("cipher %d w/o blocksize\n", algorithm); _gcry_module_release (cipher); } - else - log_bug ("cipher %d not found\n", algorithm); ath_mutex_unlock (&ciphers_registered_lock); return len; @@ -678,10 +691,10 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, /* If the application missed to call the random poll function, we do it here to ensure that it is used once in a while. */ _gcry_fast_random_poll (); - + REGISTER_DEFAULT_CIPHERS; - /* Fetch the according module and check wether the cipher is marked + /* Fetch the according module and check whether the cipher is marked available for use. */ ath_mutex_lock (&ciphers_registered_lock); module = _gcry_module_lookup_id (ciphers_registered, algo); @@ -693,7 +706,6 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, { /* Not available for use. */ err = GPG_ERR_CIPHER_ALGO; - _gcry_module_release (module); } else { @@ -707,7 +719,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, /* check flags */ if ((! err) - && ((flags & ~(0 + && ((flags & ~(0 | GCRY_CIPHER_SECURE | GCRY_CIPHER_ENABLE_SYNC | GCRY_CIPHER_CBC_CTS @@ -724,6 +736,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, case GCRY_CIPHER_MODE_CFB: case GCRY_CIPHER_MODE_OFB: case GCRY_CIPHER_MODE_CTR: + case GCRY_CIPHER_MODE_AESWRAP: if ((cipher->encrypt == dummy_encrypt_block) || (cipher->decrypt == dummy_decrypt_block)) err = GPG_ERR_INV_CIPHER_MODE; @@ -769,7 +782,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, h = gcry_calloc (1, size); if (! h) - err = gpg_err_code_from_errno (errno); + err = gpg_err_code_from_syserror (); else { size_t off = 0; @@ -805,9 +818,10 @@ gcry_cipher_open (gcry_cipher_hd_t *handle, h->bulk.cfb_dec = _gcry_aes_cfb_dec; h->bulk.cbc_enc = _gcry_aes_cbc_enc; h->bulk.cbc_dec = _gcry_aes_cbc_dec; + h->bulk.ctr_enc = _gcry_aes_ctr_enc; break; #endif /*USE_AES*/ - + default: break; } @@ -882,7 +896,10 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen) memcpy ((void *) ((char *) &c->context.c + c->cipher->contextsize), (void *) &c->context.c, c->cipher->contextsize); + c->marks.key = 1; } + else + c->marks.key = 0; return gcry_error (ret); } @@ -894,7 +911,7 @@ static void cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen ) { memset (c->u_iv.iv, 0, c->cipher->blocksize); - if (iv) + if (iv) { if (ivlen != c->cipher->blocksize) { @@ -905,7 +922,10 @@ cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen ) if (ivlen > c->cipher->blocksize) ivlen = c->cipher->blocksize; memcpy (c->u_iv.iv, iv, ivlen); + c->marks.iv = 1; } + else + c->marks.iv = 0; c->unused = 0; } @@ -918,61 +938,92 @@ cipher_reset (gcry_cipher_hd_t c) memcpy (&c->context.c, (char *) &c->context.c + c->cipher->contextsize, c->cipher->contextsize); + memset (&c->marks, 0, sizeof c->marks); memset (c->u_iv.iv, 0, c->cipher->blocksize); memset (c->lastiv, 0, c->cipher->blocksize); - memset (c->ctr, 0, c->cipher->blocksize); + memset (c->u_ctr.ctr, 0, c->cipher->blocksize); } -static void -do_ecb_encrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, - unsigned int nblocks ) + +static gcry_err_code_t +do_ecb_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { - unsigned int n; - + unsigned int blocksize = c->cipher->blocksize; + unsigned int n, nblocks; + + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + if ((inbuflen % blocksize)) + return GPG_ERR_INV_LENGTH; + + nblocks = inbuflen / c->cipher->blocksize; + for (n=0; n < nblocks; n++ ) { - c->cipher->encrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf ); - inbuf += c->cipher->blocksize; - outbuf += c->cipher->blocksize; + c->cipher->encrypt (&c->context.c, outbuf, (byte*)/*arggg*/inbuf); + inbuf += blocksize; + outbuf += blocksize; } + return 0; } -static void -do_ecb_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, - unsigned int nblocks ) +static gcry_err_code_t +do_ecb_decrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { - unsigned int n; + unsigned int blocksize = c->cipher->blocksize; + unsigned int n, nblocks; - for (n=0; n < nblocks; n++ ) + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + if ((inbuflen % blocksize)) + return GPG_ERR_INV_LENGTH; + nblocks = inbuflen / c->cipher->blocksize; + + for (n=0; n < nblocks; n++ ) { - c->cipher->decrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf ); - inbuf += c->cipher->blocksize; - outbuf += c->cipher->blocksize; + c->cipher->decrypt (&c->context.c, outbuf, (byte*)/*arggg*/inbuf ); + inbuf += blocksize; + outbuf += blocksize; } + + return 0; } -static void -do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf, - const unsigned char *inbuf, unsigned int nbytes ) +static gcry_err_code_t +do_cbc_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { unsigned int n; unsigned char *ivp; int i; size_t blocksize = c->cipher->blocksize; - unsigned nblocks = nbytes / blocksize; + unsigned nblocks = inbuflen / blocksize; - if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) + if (outbuflen < ((c->flags & GCRY_CIPHER_CBC_MAC)? blocksize : inbuflen)) + return GPG_ERR_BUFFER_TOO_SHORT; + + if ((inbuflen % c->cipher->blocksize) + && !(inbuflen > c->cipher->blocksize + && (c->flags & GCRY_CIPHER_CBC_CTS))) + return GPG_ERR_INV_LENGTH; + + if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize) { - if ((nbytes % blocksize) == 0) + if ((inbuflen % blocksize) == 0) nblocks--; } if (c->bulk.cbc_enc) { c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks, - (c->flags & GCRY_CIPHER_CBC_MAC)); + (c->flags & GCRY_CIPHER_CBC_MAC)); inbuf += nblocks * blocksize; if (!(c->flags & GCRY_CIPHER_CBC_MAC)) outbuf += nblocks * blocksize; @@ -991,17 +1042,17 @@ do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf, } } - if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) + if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize) { /* We have to be careful here, since outbuf might be equal to inbuf. */ int restbytes; unsigned char b; - if ((nbytes % blocksize) == 0) + if ((inbuflen % blocksize) == 0) restbytes = blocksize; else - restbytes = nbytes % blocksize; + restbytes = inbuflen % blocksize; outbuf -= blocksize; for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++) @@ -1012,40 +1063,51 @@ do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf, } for (; i < blocksize; i++) outbuf[i] = 0 ^ *ivp++; - + c->cipher->encrypt (&c->context.c, outbuf, outbuf); memcpy (c->u_iv.iv, outbuf, blocksize); } + + return 0; } -static void -do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf, - const unsigned char *inbuf, unsigned int nbytes) +static gcry_err_code_t +do_cbc_decrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { unsigned int n; unsigned char *ivp; int i; size_t blocksize = c->cipher->blocksize; - unsigned int nblocks = nbytes / blocksize; + unsigned int nblocks = inbuflen / blocksize; - if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + if ((inbuflen % c->cipher->blocksize) + && !(inbuflen > c->cipher->blocksize + && (c->flags & GCRY_CIPHER_CBC_CTS))) + return GPG_ERR_INV_LENGTH; + + if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize) { nblocks--; - if ((nbytes % blocksize) == 0) + if ((inbuflen % blocksize) == 0) nblocks--; memcpy (c->lastiv, c->u_iv.iv, blocksize); } if (c->bulk.cbc_dec) { - c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); + c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); inbuf += nblocks * blocksize; outbuf += nblocks * blocksize; } else { - for (n=0; n < nblocks; n++ ) + for (n=0; n < nblocks; n++ ) { /* Because outbuf and inbuf might be the same, we have to * save the original ciphertext block. We use LASTIV for @@ -1060,22 +1122,22 @@ do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf, } } - if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) + if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize) { int restbytes; - - if ((nbytes % blocksize) == 0) + + if ((inbuflen % blocksize) == 0) restbytes = blocksize; else - restbytes = nbytes % blocksize; - + restbytes = inbuflen % blocksize; + memcpy (c->lastiv, c->u_iv.iv, blocksize ); /* Save Cn-2. */ memcpy (c->u_iv.iv, inbuf + blocksize, restbytes ); /* Save Cn. */ c->cipher->decrypt ( &c->context.c, outbuf, inbuf ); for (ivp=c->u_iv.iv,i=0; i < restbytes; i++ ) outbuf[i] ^= *ivp++; - + memcpy(outbuf + blocksize, outbuf, restbytes); for(i=restbytes; i < blocksize; i++) c->u_iv.iv[i] = outbuf[i]; @@ -1084,32 +1146,38 @@ do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf, outbuf[i] ^= *ivp++; /* c->lastiv is now really lastlastiv, does this matter? */ } + + return 0; } -static void -do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf, - const unsigned char *inbuf, unsigned int nbytes ) +static gcry_err_code_t +do_cfb_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { unsigned char *ivp; size_t blocksize = c->cipher->blocksize; size_t blocksize_x_2 = blocksize + blocksize; - - if ( nbytes <= c->unused ) + + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + if ( inbuflen <= c->unused ) { /* Short enough to be encoded by the remaining XOR mask. */ /* XOR the input with the IV and store input into IV. */ for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused; - nbytes; - nbytes--, c->unused-- ) + inbuflen; + inbuflen--, c->unused-- ) *outbuf++ = (*ivp++ ^= *inbuf++); - return; + return 0; } if ( c->unused ) { /* XOR the input with the IV and store input into IV */ - nbytes -= c->unused; + inbuflen -= c->unused; for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- ) *outbuf++ = (*ivp++ ^= *inbuf++); } @@ -1117,17 +1185,17 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf, /* Now we can process complete blocks. We use a loop as long as we have at least 2 blocks and use conditions for the rest. This also allows to use a bulk encryption function if available. */ - if (nbytes >= blocksize_x_2 && c->bulk.cfb_enc) + if (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc) { - unsigned int nblocks = nbytes / blocksize; - c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); + unsigned int nblocks = inbuflen / blocksize; + c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); outbuf += nblocks * blocksize; inbuf += nblocks * blocksize; - nbytes -= nblocks * blocksize; + inbuflen -= nblocks * blocksize; } else { - while ( nbytes >= blocksize_x_2 ) + while ( inbuflen >= blocksize_x_2 ) { int i; /* Encrypt the IV. */ @@ -1135,11 +1203,11 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf, /* XOR the input with the IV and store input into IV. */ for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ ) *outbuf++ = (*ivp++ ^= *inbuf++); - nbytes -= blocksize; + inbuflen -= blocksize; } } - if ( nbytes >= blocksize ) + if ( inbuflen >= blocksize ) { int i; /* Save the current IV and then encrypt the IV. */ @@ -1148,51 +1216,56 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf, /* XOR the input with the IV and store input into IV */ for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ ) *outbuf++ = (*ivp++ ^= *inbuf++); - nbytes -= blocksize; + inbuflen -= blocksize; } - if ( nbytes ) + if ( inbuflen ) { /* Save the current IV and then encrypt the IV. */ memcpy( c->lastiv, c->u_iv.iv, blocksize ); c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); c->unused = blocksize; /* Apply the XOR. */ - c->unused -= nbytes; - for(ivp=c->u_iv.iv; nbytes; nbytes-- ) + c->unused -= inbuflen; + for(ivp=c->u_iv.iv; inbuflen; inbuflen-- ) *outbuf++ = (*ivp++ ^= *inbuf++); } + return 0; } -static void -do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf, - const unsigned char *inbuf, unsigned int nbytes ) +static gcry_err_code_t +do_cfb_decrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { unsigned char *ivp; unsigned long temp; int i; size_t blocksize = c->cipher->blocksize; size_t blocksize_x_2 = blocksize + blocksize; - - if (nbytes <= c->unused) + + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + if (inbuflen <= c->unused) { /* Short enough to be encoded by the remaining XOR mask. */ /* XOR the input with the IV and store input into IV. */ for (ivp=c->u_iv.iv+blocksize - c->unused; - nbytes; - nbytes--, c->unused--) + inbuflen; + inbuflen--, c->unused--) { temp = *inbuf++; *outbuf++ = *ivp ^ temp; *ivp++ = temp; } - return; + return 0; } - + if (c->unused) { /* XOR the input with the IV and store input into IV. */ - nbytes -= c->unused; + inbuflen -= c->unused; for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- ) { temp = *inbuf++; @@ -1200,21 +1273,21 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf, *ivp++ = temp; } } - + /* Now we can process complete blocks. We use a loop as long as we have at least 2 blocks and use conditions for the rest. This also allows to use a bulk encryption function if available. */ - if (nbytes >= blocksize_x_2 && c->bulk.cfb_dec) + if (inbuflen >= blocksize_x_2 && c->bulk.cfb_dec) { - unsigned int nblocks = nbytes / blocksize; - c->bulk.cfb_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); + unsigned int nblocks = inbuflen / blocksize; + c->bulk.cfb_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); outbuf += nblocks * blocksize; inbuf += nblocks * blocksize; - nbytes -= nblocks * blocksize; + inbuflen -= nblocks * blocksize; } else { - while (nbytes >= blocksize_x_2 ) + while (inbuflen >= blocksize_x_2 ) { /* Encrypt the IV. */ c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); @@ -1225,11 +1298,11 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf, *outbuf++ = *ivp ^ temp; *ivp++ = temp; } - nbytes -= blocksize; + inbuflen -= blocksize; } } - if (nbytes >= blocksize ) + if (inbuflen >= blocksize ) { /* Save the current IV and then encrypt the IV. */ memcpy ( c->lastiv, c->u_iv.iv, blocksize); @@ -1241,99 +1314,109 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf, *outbuf++ = *ivp ^ temp; *ivp++ = temp; } - nbytes -= blocksize; + inbuflen -= blocksize; } - if (nbytes) - { + if (inbuflen) + { /* Save the current IV and then encrypt the IV. */ memcpy ( c->lastiv, c->u_iv.iv, blocksize ); c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); c->unused = blocksize; /* Apply the XOR. */ - c->unused -= nbytes; - for (ivp=c->u_iv.iv; nbytes; nbytes-- ) + c->unused -= inbuflen; + for (ivp=c->u_iv.iv; inbuflen; inbuflen-- ) { temp = *inbuf++; *outbuf++ = *ivp ^ temp; *ivp++ = temp; } } + return 0; } -static void -do_ofb_encrypt( gcry_cipher_hd_t c, - byte *outbuf, const byte *inbuf, unsigned nbytes ) +static gcry_err_code_t +do_ofb_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { - byte *ivp; + unsigned char *ivp; size_t blocksize = c->cipher->blocksize; - if ( nbytes <= c->unused ) + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + if ( inbuflen <= c->unused ) { /* Short enough to be encoded by the remaining XOR mask. */ /* XOR the input with the IV */ for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused; - nbytes; - nbytes--, c->unused-- ) + inbuflen; + inbuflen--, c->unused-- ) *outbuf++ = (*ivp++ ^ *inbuf++); - return; + return 0; } if( c->unused ) { - nbytes -= c->unused; + inbuflen -= c->unused; for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- ) *outbuf++ = (*ivp++ ^ *inbuf++); } /* Now we can process complete blocks. */ - while ( nbytes >= blocksize ) + while ( inbuflen >= blocksize ) { int i; /* Encrypt the IV (and save the current one). */ memcpy( c->lastiv, c->u_iv.iv, blocksize ); c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); - + for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ ) *outbuf++ = (*ivp++ ^ *inbuf++); - nbytes -= blocksize; + inbuflen -= blocksize; } - if ( nbytes ) + if ( inbuflen ) { /* process the remaining bytes */ memcpy( c->lastiv, c->u_iv.iv, blocksize ); c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); c->unused = blocksize; - c->unused -= nbytes; - for(ivp=c->u_iv.iv; nbytes; nbytes-- ) + c->unused -= inbuflen; + for(ivp=c->u_iv.iv; inbuflen; inbuflen-- ) *outbuf++ = (*ivp++ ^ *inbuf++); } + return 0; } -static void -do_ofb_decrypt( gcry_cipher_hd_t c, - byte *outbuf, const byte *inbuf, unsigned int nbytes ) +static gcry_err_code_t +do_ofb_decrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { - byte *ivp; + unsigned char *ivp; size_t blocksize = c->cipher->blocksize; - - if( nbytes <= c->unused ) + + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + if( inbuflen <= c->unused ) { /* Short enough to be encoded by the remaining XOR mask. */ - for (ivp=c->u_iv.iv+blocksize - c->unused; nbytes; nbytes--,c->unused--) + for (ivp=c->u_iv.iv+blocksize - c->unused; inbuflen; inbuflen--,c->unused--) *outbuf++ = *ivp++ ^ *inbuf++; - return; + return 0; } if ( c->unused ) { - nbytes -= c->unused; + inbuflen -= c->unused; for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- ) *outbuf++ = *ivp++ ^ *inbuf++; } /* Now we can process complete blocks. */ - while ( nbytes >= blocksize ) + while ( inbuflen >= blocksize ) { int i; /* Encrypt the IV (and save the current one). */ @@ -1341,113 +1424,335 @@ do_ofb_decrypt( gcry_cipher_hd_t c, c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ ) *outbuf++ = *ivp++ ^ *inbuf++; - nbytes -= blocksize; + inbuflen -= blocksize; } - if ( nbytes ) + if ( inbuflen ) { /* Process the remaining bytes. */ /* Encrypt the IV (and save the current one). */ memcpy( c->lastiv, c->u_iv.iv, blocksize ); c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv ); c->unused = blocksize; - c->unused -= nbytes; - for (ivp=c->u_iv.iv; nbytes; nbytes-- ) + c->unused -= inbuflen; + for (ivp=c->u_iv.iv; inbuflen; inbuflen-- ) *outbuf++ = *ivp++ ^ *inbuf++; } + return 0; } -static void -do_ctr_encrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, - unsigned int nbytes ) +static gcry_err_code_t +do_ctr_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { unsigned int n; - byte tmp[MAX_BLOCKSIZE]; int i; + unsigned int blocksize = c->cipher->blocksize; + unsigned int nblocks; - for(n=0; n < nbytes; n++) + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + + /* First process a left over encrypted counter. */ + if (c->unused) { - if ((n % c->cipher->blocksize) == 0) - { - c->cipher->encrypt (&c->context.c, tmp, c->ctr); - - for (i = c->cipher->blocksize; i > 0; i--) - { - c->ctr[i-1]++; - if (c->ctr[i-1] != 0) - break; - } - } - - /* XOR input with encrypted counter and store in output. */ - outbuf[n] = inbuf[n] ^ tmp[n % c->cipher->blocksize]; + gcry_assert (c->unused < blocksize); + i = blocksize - c->unused; + for (n=0; c->unused && n < inbuflen; c->unused--, n++, i++) + { + /* XOR input with encrypted counter and store in output. */ + outbuf[n] = inbuf[n] ^ c->lastiv[i]; + } + inbuf += n; + outbuf += n; + inbuflen -= n; } + + + /* Use a bulk method if available. */ + nblocks = inbuflen / blocksize; + if (nblocks && c->bulk.ctr_enc) + { + c->bulk.ctr_enc (&c->context.c, c->u_ctr.ctr, outbuf, inbuf, nblocks); + inbuf += nblocks * blocksize; + outbuf += nblocks * blocksize; + inbuflen -= nblocks * blocksize; + } + + /* If we don't have a bulk method use the standard method. We also + use this method for the a remaining partial block. */ + if (inbuflen) + { + unsigned char tmp[MAX_BLOCKSIZE]; + + for (n=0; n < inbuflen; n++) + { + if ((n % blocksize) == 0) + { + c->cipher->encrypt (&c->context.c, tmp, c->u_ctr.ctr); + + for (i = blocksize; i > 0; i--) + { + c->u_ctr.ctr[i-1]++; + if (c->u_ctr.ctr[i-1] != 0) + break; + } + } + + /* XOR input with encrypted counter and store in output. */ + outbuf[n] = inbuf[n] ^ tmp[n % blocksize]; + } + + /* Save the unused bytes of the counter. */ + n %= blocksize; + c->unused = (blocksize - n) % blocksize; + if (c->unused) + memcpy (c->lastiv+n, tmp+n, c->unused); + + wipememory (tmp, sizeof tmp); + } + + return 0; } -static void -do_ctr_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, - unsigned int nbytes ) +static gcry_err_code_t +do_ctr_decrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, unsigned int outbuflen, + const unsigned char *inbuf, unsigned int inbuflen) { - do_ctr_encrypt (c, outbuf, inbuf, nbytes); + return do_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); +} + + +/* Perform the AES-Wrap algorithm as specified by RFC3394. We + implement this as a mode usable with any cipher algorithm of + blocksize 128. */ +static gcry_err_code_t +do_aeswrap_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen, + const byte *inbuf, unsigned int inbuflen ) +{ + int j, x; + unsigned int n, i; + unsigned char *r, *a, *b; + unsigned char t[8]; + +#if MAX_BLOCKSIZE < 8 +#error Invalid block size +#endif + /* We require a cipher with a 128 bit block length. */ + if (c->cipher->blocksize != 16) + return GPG_ERR_INV_LENGTH; + + /* The output buffer must be able to hold the input data plus one + additional block. */ + if (outbuflen < inbuflen + 8) + return GPG_ERR_BUFFER_TOO_SHORT; + /* Input data must be multiple of 64 bits. */ + if (inbuflen % 8) + return GPG_ERR_INV_ARG; + + n = inbuflen / 8; + + /* We need at least two 64 bit blocks. */ + if (n < 2) + return GPG_ERR_INV_ARG; + + r = outbuf; + a = outbuf; /* We store A directly in OUTBUF. */ + b = c->u_ctr.ctr; /* B is also used to concatenate stuff. */ + + /* If an IV has been set we use that IV as the Alternative Initial + Value; if it has not been set we use the standard value. */ + if (c->marks.iv) + memcpy (a, c->u_iv.iv, 8); + else + memset (a, 0xa6, 8); + + /* Copy the inbuf to the outbuf. */ + memmove (r+8, inbuf, inbuflen); + + memset (t, 0, sizeof t); /* t := 0. */ + + for (j = 0; j <= 5; j++) + { + for (i = 1; i <= n; i++) + { + /* B := AES_k( A | R[i] ) */ + memcpy (b, a, 8); + memcpy (b+8, r+i*8, 8); + c->cipher->encrypt (&c->context.c, b, b); + /* t := t + 1 */ + for (x = 7; x >= 0; x--) + { + t[x]++; + if (t[x]) + break; + } + /* A := MSB_64(B) ^ t */ + for (x=0; x < 8; x++) + a[x] = b[x] ^ t[x]; + /* R[i] := LSB_64(B) */ + memcpy (r+i*8, b+8, 8); + } + } + + return 0; +} + +/* Perform the AES-Unwrap algorithm as specified by RFC3394. We + implement this as a mode usable with any cipher algorithm of + blocksize 128. */ +static gcry_err_code_t +do_aeswrap_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen, + const byte *inbuf, unsigned int inbuflen) +{ + int j, x; + unsigned int n, i; + unsigned char *r, *a, *b; + unsigned char t[8]; + +#if MAX_BLOCKSIZE < 8 +#error Invalid block size +#endif + /* We require a cipher with a 128 bit block length. */ + if (c->cipher->blocksize != 16) + return GPG_ERR_INV_LENGTH; + + /* The output buffer must be able to hold the input data minus one + additional block. Fixme: The caller has more restrictive checks + - we may want to fix them for this mode. */ + if (outbuflen + 8 < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + /* Input data must be multiple of 64 bits. */ + if (inbuflen % 8) + return GPG_ERR_INV_ARG; + + n = inbuflen / 8; + + /* We need at least three 64 bit blocks. */ + if (n < 3) + return GPG_ERR_INV_ARG; + + r = outbuf; + a = c->lastiv; /* We use c->LASTIV as buffer for A. */ + b = c->u_ctr.ctr; /* B is also used to concatenate stuff. */ + + /* Copy the inbuf to the outbuf and save A. */ + memcpy (a, inbuf, 8); + memmove (r, inbuf+8, inbuflen-8); + n--; /* Reduce to actual number of data blocks. */ + + /* t := 6 * n */ + i = n * 6; /* The range is valid because: n = inbuflen / 8 - 1. */ + for (x=0; x < 8 && x < sizeof (i); x++) + t[7-x] = i >> (8*x); + for (; x < 8; x++) + t[7-x] = 0; + + for (j = 5; j >= 0; j--) + { + for (i = n; i >= 1; i--) + { + /* B := AES_k^1( (A ^ t)| R[i] ) */ + for (x = 0; x < 8; x++) + b[x] = a[x] ^ t[x]; + memcpy (b+8, r+(i-1)*8, 8); + c->cipher->decrypt (&c->context.c, b, b); + /* t := t - 1 */ + for (x = 7; x >= 0; x--) + { + t[x]--; + if (t[x] != 0xff) + break; + } + /* A := MSB_64(B) */ + memcpy (a, b, 8); + /* R[i] := LSB_64(B) */ + memcpy (r+(i-1)*8, b+8, 8); + } + } + + /* If an IV has been set we compare against this Alternative Initial + Value; if it has not been set we compare against the standard IV. */ + if (c->marks.iv) + j = memcmp (a, c->u_iv.iv, 8); + else + { + for (j=0, x=0; x < 8; x++) + if (a[x] != 0xa6) + { + j=1; + break; + } + } + return j? GPG_ERR_CHECKSUM : 0; } /**************** * Encrypt INBUF to OUTBUF with the mode selected at open. * inbuf and outbuf may overlap or be the same. - * Depending on the mode some contraints apply to NBYTES. + * Depending on the mode some constraints apply to INBUFLEN. */ static gcry_err_code_t -cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, - const byte *inbuf, unsigned int nbytes) +cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen, + const byte *inbuf, unsigned int inbuflen) { - gcry_err_code_t rc = GPG_ERR_NO_ERROR; + gcry_err_code_t rc; - switch( c->mode ) { - case GCRY_CIPHER_MODE_ECB: - if (!(nbytes%c->cipher->blocksize)) - do_ecb_encrypt(c, outbuf, inbuf, nbytes/c->cipher->blocksize ); - else - rc = GPG_ERR_INV_ARG; - break; - case GCRY_CIPHER_MODE_CBC: - if (!(nbytes%c->cipher->blocksize) - || (nbytes > c->cipher->blocksize - && (c->flags & GCRY_CIPHER_CBC_CTS))) - do_cbc_encrypt(c, outbuf, inbuf, nbytes ); - else - rc = GPG_ERR_INV_ARG; - break; - case GCRY_CIPHER_MODE_CFB: - do_cfb_encrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_OFB: - do_ofb_encrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_CTR: - do_ctr_encrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_STREAM: - c->cipher->stencrypt ( &c->context.c, - outbuf, (byte*)/*arggg*/inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_NONE: - if (fips_mode () || !_gcry_get_debug_flag (0)) - { - fips_signal_error ("cipher mode NONE used"); - rc = GPG_ERR_INV_CIPHER_MODE; - } - else - { - if ( inbuf != outbuf ) - memmove (outbuf, inbuf, nbytes); - } - break; - default: - log_fatal("cipher_encrypt: invalid mode %d\n", c->mode ); - rc = GPG_ERR_INV_CIPHER_MODE; - break; + switch (c->mode) + { + case GCRY_CIPHER_MODE_ECB: + rc = do_ecb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CBC: + rc = do_cbc_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CFB: + rc = do_cfb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_OFB: + rc = do_ofb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CTR: + rc = do_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_AESWRAP: + rc = do_aeswrap_encrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_STREAM: + c->cipher->stencrypt (&c->context.c, + outbuf, (byte*)/*arggg*/inbuf, inbuflen); + rc = 0; + break; + + case GCRY_CIPHER_MODE_NONE: + if (fips_mode () || !_gcry_get_debug_flag (0)) + { + fips_signal_error ("cipher mode NONE used"); + rc = GPG_ERR_INV_CIPHER_MODE; + } + else + { + if (inbuf != outbuf) + memmove (outbuf, inbuf, inbuflen); + rc = 0; + } + break; + + default: + log_fatal ("cipher_encrypt: invalid mode %d\n", c->mode ); + rc = GPG_ERR_INV_CIPHER_MODE; + break; } - return rc; + + return rc; } @@ -1461,29 +1766,15 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize, { gcry_err_code_t err; - if (!in) - { - /* Caller requested in-place encryption. */ - /* Actually cipher_encrypt() does not need to know about it, but - * we may change it in the future to get better performance. */ - err = cipher_encrypt (h, out, out, outsize); - } - else if (outsize < ((h->flags & GCRY_CIPHER_CBC_MAC) ? - h->cipher->blocksize : inlen)) - err = GPG_ERR_TOO_SHORT; - else if ((h->mode == GCRY_CIPHER_MODE_ECB - || (h->mode == GCRY_CIPHER_MODE_CBC - && (! ((h->flags & GCRY_CIPHER_CBC_CTS) - && (inlen > h->cipher->blocksize))))) - && (inlen % h->cipher->blocksize)) - err = GPG_ERR_INV_ARG; + if (!in) /* Caller requested in-place encryption. */ + err = cipher_encrypt (h, out, outsize, out, outsize); else - err = cipher_encrypt (h, out, in, inlen); + err = cipher_encrypt (h, out, outsize, in, inlen); + /* Failsafe: Make sure that the plaintext will never make it into + OUT if the encryption returned an error. */ if (err && out) - memset (out, 0x42, outsize); /* Failsafe: Make sure that the - plaintext will never make it into - OUT. */ + memset (out, 0x42, outsize); return gcry_error (err); } @@ -1493,60 +1784,67 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize, /**************** * Decrypt INBUF to OUTBUF with the mode selected at open. * inbuf and outbuf may overlap or be the same. - * Depending on the mode some some contraints apply to NBYTES. + * Depending on the mode some some contraints apply to INBUFLEN. */ static gcry_err_code_t -cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf, - unsigned int nbytes) +cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen, + const byte *inbuf, unsigned int inbuflen) { - gcry_err_code_t rc = GPG_ERR_NO_ERROR; + gcry_err_code_t rc; - switch( c->mode ) { - case GCRY_CIPHER_MODE_ECB: - if (!(nbytes%c->cipher->blocksize)) - do_ecb_decrypt(c, outbuf, inbuf, nbytes/c->cipher->blocksize ); - else - rc = GPG_ERR_INV_ARG; - break; - case GCRY_CIPHER_MODE_CBC: - if (!(nbytes%c->cipher->blocksize) - || (nbytes > c->cipher->blocksize - && (c->flags & GCRY_CIPHER_CBC_CTS))) - do_cbc_decrypt(c, outbuf, inbuf, nbytes ); - else - rc = GPG_ERR_INV_ARG; - break; - case GCRY_CIPHER_MODE_CFB: - do_cfb_decrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_OFB: - do_ofb_decrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_CTR: - do_ctr_decrypt(c, outbuf, inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_STREAM: - c->cipher->stdecrypt ( &c->context.c, - outbuf, (byte*)/*arggg*/inbuf, nbytes ); - break; - case GCRY_CIPHER_MODE_NONE: - if (fips_mode () || !_gcry_get_debug_flag (0)) - { - fips_signal_error ("cipher mode NONE used"); - rc = GPG_ERR_INV_CIPHER_MODE; - } - else - { - if (inbuf != outbuf) - memmove (outbuf, inbuf, nbytes); - } - break; - default: - log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode ); - rc = GPG_ERR_INV_CIPHER_MODE; - break; + switch (c->mode) + { + case GCRY_CIPHER_MODE_ECB: + rc = do_ecb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CBC: + rc = do_cbc_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CFB: + rc = do_cfb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_OFB: + rc = do_ofb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_CTR: + rc = do_ctr_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_AESWRAP: + rc = do_aeswrap_decrypt (c, outbuf, outbuflen, inbuf, inbuflen); + break; + + case GCRY_CIPHER_MODE_STREAM: + c->cipher->stdecrypt (&c->context.c, + outbuf, (byte*)/*arggg*/inbuf, inbuflen); + rc = 0; + break; + + case GCRY_CIPHER_MODE_NONE: + if (fips_mode () || !_gcry_get_debug_flag (0)) + { + fips_signal_error ("cipher mode NONE used"); + rc = GPG_ERR_INV_CIPHER_MODE; + } + else + { + if (inbuf != outbuf) + memmove (outbuf, inbuf, inbuflen); + rc = 0; + } + break; + + default: + log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode ); + rc = GPG_ERR_INV_CIPHER_MODE; + break; } - return rc; + + return rc; } @@ -1554,25 +1852,12 @@ gcry_error_t gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize, const void *in, size_t inlen) { - gcry_err_code_t err = 0; + gcry_err_code_t err; - if (!in) - { - /* Caller requested in-place encryption. */ - /* Actually cipher_encrypt() does not need to know about it, but - * we may change it in the future to get better performance. */ - err = cipher_decrypt (h, out, out, outsize); - } - else if (outsize < inlen) - err = GPG_ERR_TOO_SHORT; - else if (((h->mode == GCRY_CIPHER_MODE_ECB) - || ((h->mode == GCRY_CIPHER_MODE_CBC) - && (! ((h->flags & GCRY_CIPHER_CBC_CTS) - && (inlen > h->cipher->blocksize))))) - && (inlen % h->cipher->blocksize) != 0) - err = GPG_ERR_INV_ARG; + if (!in) /* Caller requested in-place encryption. */ + err = cipher_decrypt (h, out, outsize, out, outsize); else - err = cipher_decrypt (h, out, in, inlen); + err = cipher_decrypt (h, out, outsize, in, inlen); return gcry_error (err); } @@ -1618,9 +1903,15 @@ gpg_error_t _gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen) { if (ctr && ctrlen == hd->cipher->blocksize) - memcpy (hd->ctr, ctr, hd->cipher->blocksize); + { + memcpy (hd->u_ctr.ctr, ctr, hd->cipher->blocksize); + hd->unused = 0; + } else if (!ctr || !ctrlen) - memset (hd->ctr, 0, hd->cipher->blocksize); + { + memset (hd->u_ctr.ctr, 0, hd->cipher->blocksize); + hd->unused = 0; + } else return gpg_error (GPG_ERR_INV_ARG); return 0; @@ -1679,17 +1970,12 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen) break; case GCRYCTL_SET_CTR: /* Deprecated; use gcry_cipher_setctr. */ - if (buffer && buflen == h->cipher->blocksize) - memcpy (h->ctr, buffer, h->cipher->blocksize); - else if (buffer == NULL || buflen == 0) - memset (h->ctr, 0, h->cipher->blocksize); - else - rc = GPG_ERR_INV_ARG; + rc = gpg_err_code (_gcry_cipher_setctr (h, buffer, buflen)); break; case 61: /* Disable weak key detection (private). */ if (h->extraspec->set_extra_info) - rc = h->extraspec->set_extra_info + rc = h->extraspec->set_extra_info (&h->context.c, CIPHER_INFO_NO_WEAK_KEY, NULL, 0); else rc = GPG_ERR_NOT_SUPPORTED; @@ -1697,7 +1983,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen) case 62: /* Return current input vector (private). */ /* This is the input block as used in CFB and OFB mode which has - initially been set as IV. The returned format is: + initially been set as IV. The returned format is: 1 byte Actual length of the block in bytes. n byte The block. If the provided buffer is too short, an error is returned. */ @@ -1708,7 +1994,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen) unsigned char *ivp; unsigned char *dst = buffer; int n = h->unused; - + if (!n) n = h->cipher->blocksize; gcry_assert (n <= h->cipher->blocksize); @@ -1730,10 +2016,10 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen) /* Return information about the cipher handle H. CMD is the kind of information requested. BUFFER and NBYTES are reserved for now. - There are no values for CMD yet defined. + There are no values for CMD yet defined. + + The function always returns GPG_ERR_INV_OP. - The fucntion always returns GPG_ERR_INV_OP. - */ gcry_error_t gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes) @@ -1770,11 +2056,11 @@ gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes) GCRYCTL_TEST_ALGO: Returns 0 if the specified algorithm ALGO is available for use. BUFFER and NBYTES must be zero. - + Note: Because this function is in most cases used to return an integer value, we can make it easier for the caller to just look at the return value. The caller will in all cases consult the value - and thereby detecting whether a error occured or not (i.e. while + and thereby detecting whether a error occurred or not (i.e. while checking the block size) */ gcry_error_t @@ -1794,8 +2080,7 @@ gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes) if ((ui > 0) && (ui <= 512)) *nbytes = (size_t) ui / 8; else - /* The only reason is an invalid algo or a strange - blocksize. */ + /* The only reason for an error is an invalid algo. */ err = GPG_ERR_CIPHER_ALGO; } break; @@ -1839,7 +2124,7 @@ gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes) gcry_cipher_algo_info because it allows for proper type checking. */ size_t -gcry_cipher_get_algo_keylen (int algo) +gcry_cipher_get_algo_keylen (int algo) { size_t n; @@ -1855,7 +2140,7 @@ gcry_cipher_get_algo_keylen (int algo) gcry_cipher_algo_info because it allows for proper type checking. */ size_t -gcry_cipher_get_algo_blklen (int algo) +gcry_cipher_get_algo_blklen (int algo) { size_t n; @@ -1916,7 +2201,7 @@ _gcry_cipher_selftest (int algo, int extended, selftest_report_func_t report) { ec = GPG_ERR_CIPHER_ALGO; if (report) - report ("cipher", algo, "module", + report ("cipher", algo, "module", module && !(module->flags & FLAG_MODULE_DISABLED)? "no selftest available" : module? "algorithm disabled" : "algorithm not found"); diff --git a/grub-core/lib/libgcrypt/cipher/crc.c b/grub-core/lib/libgcrypt/cipher/crc.c new file mode 100644 index 000000000..28454f8ab --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/crc.c @@ -0,0 +1,793 @@ +/* crc.c - Cyclic redundancy checks. + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + */ + +#include +#include +#include +#include + +#include "g10lib.h" +#include "cipher.h" + +#include "bithelp.h" +#include "bufhelp.h" + + +typedef struct +{ + u32 CRC; + byte buf[4]; +} +CRC_CONTEXT; + + +/* + * Code generated by universal_crc by Danjel McGougan + * + * CRC parameters used: + * bits: 32 + * poly: 0x04c11db7 + * init: 0xffffffff + * xor: 0xffffffff + * reverse: true + * non-direct: false + * + * CRC of the string "123456789" is 0xcbf43926 + */ + +static const u32 crc32_table[1024] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + 0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, + 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7, + 0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, + 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf, + 0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, + 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496, + 0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, + 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e, + 0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, + 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265, + 0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, + 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d, + 0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, + 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034, + 0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, + 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c, + 0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, + 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2, + 0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, + 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca, + 0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, + 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93, + 0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, + 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b, + 0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, + 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60, + 0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, + 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768, + 0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, + 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31, + 0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, + 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539, + 0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, + 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c, + 0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, + 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484, + 0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, + 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd, + 0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, + 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5, + 0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, + 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e, + 0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, + 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026, + 0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, + 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f, + 0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, + 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277, + 0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, + 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189, + 0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, + 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81, + 0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, + 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8, + 0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, + 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0, + 0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, + 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b, + 0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, + 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23, + 0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, + 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a, + 0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, + 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72, + 0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, + 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685, + 0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, + 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d, + 0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, + 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5, + 0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, + 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d, + 0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, + 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065, + 0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, + 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd, + 0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, + 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315, + 0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, + 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad, + 0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, + 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45, + 0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, + 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd, + 0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, + 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835, + 0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, + 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d, + 0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, + 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5, + 0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, + 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d, + 0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, + 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5, + 0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, + 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d, + 0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, + 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05, + 0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, + 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd, + 0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, + 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75, + 0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, + 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd, + 0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, + 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5, + 0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, + 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d, + 0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, + 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895, + 0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, + 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d, + 0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, + 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5, + 0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, + 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d, + 0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, + 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5, + 0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, + 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d, + 0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, + 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625, + 0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, + 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d, + 0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, + 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555, + 0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, + 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed, + 0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, + 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9, + 0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, + 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056, + 0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, + 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26, + 0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, + 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9, + 0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, + 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787, + 0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, + 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68, + 0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, + 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018, + 0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, + 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7, + 0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, + 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084, + 0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, + 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b, + 0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, + 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b, + 0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, + 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4, + 0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, + 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba, + 0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, + 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755, + 0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, + 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825, + 0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, + 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca, + 0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, + 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82, + 0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, + 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d, + 0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, + 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d, + 0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, + 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2, + 0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, + 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc, + 0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, + 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953, + 0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, + 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623, + 0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, + 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc, + 0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, + 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf, + 0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, + 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50, + 0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, + 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120, + 0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, + 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf, + 0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, + 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981, + 0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, + 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e, + 0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, + 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e, + 0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, + 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1 +}; + +/* CRC32 */ + +static inline u32 +crc32_next (u32 crc, byte data) +{ + return (crc >> 8) ^ crc32_table[(crc & 0xff) ^ data]; +} + +/* + * Process 4 bytes in one go + */ +static inline u32 +crc32_next4 (u32 crc, u32 data) +{ + crc ^= data; + crc = crc32_table[(crc & 0xff) + 0x300] ^ + crc32_table[((crc >> 8) & 0xff) + 0x200] ^ + crc32_table[((crc >> 16) & 0xff) + 0x100] ^ + crc32_table[(crc >> 24) & 0xff]; + return crc; +} + +static void +crc32_init (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + ctx->CRC = 0 ^ 0xffffffffL; +} + +static void +crc32_write (void *context, const void *inbuf_arg, size_t inlen) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + const byte *inbuf = inbuf_arg; + u32 crc; + + if (!inbuf || !inlen) + return; + + crc = ctx->CRC; + + while (inlen >= 16) + { + inlen -= 16; + crc = crc32_next4(crc, buf_get_le32(&inbuf[0])); + crc = crc32_next4(crc, buf_get_le32(&inbuf[4])); + crc = crc32_next4(crc, buf_get_le32(&inbuf[8])); + crc = crc32_next4(crc, buf_get_le32(&inbuf[12])); + inbuf += 16; + } + + while (inlen >= 4) + { + inlen -= 4; + crc = crc32_next4(crc, buf_get_le32(inbuf)); + inbuf += 4; + } + + while (inlen--) + { + crc = crc32_next(crc, *inbuf++); + } + + ctx->CRC = crc; +} + +static byte * +crc32_read (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + return ctx->buf; +} + +static void +crc32_final (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + ctx->CRC ^= 0xffffffffL; + buf_put_be32 (ctx->buf, ctx->CRC); +} + +/* CRC32 a'la RFC 1510 */ +/* CRC of the string "123456789" is 0x2dfd2d88 */ + +static void +crc32rfc1510_init (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + ctx->CRC = 0; +} + +static void +crc32rfc1510_final (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + buf_put_be32(ctx->buf, ctx->CRC); +} + +/* CRC24 a'la RFC 2440 */ +/* + * Code generated by universal_crc by Danjel McGougan + * + * CRC parameters used: + * bits: 24 + * poly: 0x864cfb + * init: 0xb704ce + * xor: 0x000000 + * reverse: false + * non-direct: false + * + * CRC of the string "123456789" is 0x21cf02 + */ + +static const u32 crc24_table[1024] = +{ + 0x00000000, 0x00fb4c86, 0x000dd58a, 0x00f6990c, + 0x00e1e693, 0x001aaa15, 0x00ec3319, 0x00177f9f, + 0x003981a1, 0x00c2cd27, 0x0034542b, 0x00cf18ad, + 0x00d86732, 0x00232bb4, 0x00d5b2b8, 0x002efe3e, + 0x00894ec5, 0x00720243, 0x00849b4f, 0x007fd7c9, + 0x0068a856, 0x0093e4d0, 0x00657ddc, 0x009e315a, + 0x00b0cf64, 0x004b83e2, 0x00bd1aee, 0x00465668, + 0x005129f7, 0x00aa6571, 0x005cfc7d, 0x00a7b0fb, + 0x00e9d10c, 0x00129d8a, 0x00e40486, 0x001f4800, + 0x0008379f, 0x00f37b19, 0x0005e215, 0x00feae93, + 0x00d050ad, 0x002b1c2b, 0x00dd8527, 0x0026c9a1, + 0x0031b63e, 0x00cafab8, 0x003c63b4, 0x00c72f32, + 0x00609fc9, 0x009bd34f, 0x006d4a43, 0x009606c5, + 0x0081795a, 0x007a35dc, 0x008cacd0, 0x0077e056, + 0x00591e68, 0x00a252ee, 0x0054cbe2, 0x00af8764, + 0x00b8f8fb, 0x0043b47d, 0x00b52d71, 0x004e61f7, + 0x00d2a319, 0x0029ef9f, 0x00df7693, 0x00243a15, + 0x0033458a, 0x00c8090c, 0x003e9000, 0x00c5dc86, + 0x00eb22b8, 0x00106e3e, 0x00e6f732, 0x001dbbb4, + 0x000ac42b, 0x00f188ad, 0x000711a1, 0x00fc5d27, + 0x005beddc, 0x00a0a15a, 0x00563856, 0x00ad74d0, + 0x00ba0b4f, 0x004147c9, 0x00b7dec5, 0x004c9243, + 0x00626c7d, 0x009920fb, 0x006fb9f7, 0x0094f571, + 0x00838aee, 0x0078c668, 0x008e5f64, 0x007513e2, + 0x003b7215, 0x00c03e93, 0x0036a79f, 0x00cdeb19, + 0x00da9486, 0x0021d800, 0x00d7410c, 0x002c0d8a, + 0x0002f3b4, 0x00f9bf32, 0x000f263e, 0x00f46ab8, + 0x00e31527, 0x001859a1, 0x00eec0ad, 0x00158c2b, + 0x00b23cd0, 0x00497056, 0x00bfe95a, 0x0044a5dc, + 0x0053da43, 0x00a896c5, 0x005e0fc9, 0x00a5434f, + 0x008bbd71, 0x0070f1f7, 0x008668fb, 0x007d247d, + 0x006a5be2, 0x00911764, 0x00678e68, 0x009cc2ee, + 0x00a44733, 0x005f0bb5, 0x00a992b9, 0x0052de3f, + 0x0045a1a0, 0x00beed26, 0x0048742a, 0x00b338ac, + 0x009dc692, 0x00668a14, 0x00901318, 0x006b5f9e, + 0x007c2001, 0x00876c87, 0x0071f58b, 0x008ab90d, + 0x002d09f6, 0x00d64570, 0x0020dc7c, 0x00db90fa, + 0x00ccef65, 0x0037a3e3, 0x00c13aef, 0x003a7669, + 0x00148857, 0x00efc4d1, 0x00195ddd, 0x00e2115b, + 0x00f56ec4, 0x000e2242, 0x00f8bb4e, 0x0003f7c8, + 0x004d963f, 0x00b6dab9, 0x004043b5, 0x00bb0f33, + 0x00ac70ac, 0x00573c2a, 0x00a1a526, 0x005ae9a0, + 0x0074179e, 0x008f5b18, 0x0079c214, 0x00828e92, + 0x0095f10d, 0x006ebd8b, 0x00982487, 0x00636801, + 0x00c4d8fa, 0x003f947c, 0x00c90d70, 0x003241f6, + 0x00253e69, 0x00de72ef, 0x0028ebe3, 0x00d3a765, + 0x00fd595b, 0x000615dd, 0x00f08cd1, 0x000bc057, + 0x001cbfc8, 0x00e7f34e, 0x00116a42, 0x00ea26c4, + 0x0076e42a, 0x008da8ac, 0x007b31a0, 0x00807d26, + 0x009702b9, 0x006c4e3f, 0x009ad733, 0x00619bb5, + 0x004f658b, 0x00b4290d, 0x0042b001, 0x00b9fc87, + 0x00ae8318, 0x0055cf9e, 0x00a35692, 0x00581a14, + 0x00ffaaef, 0x0004e669, 0x00f27f65, 0x000933e3, + 0x001e4c7c, 0x00e500fa, 0x001399f6, 0x00e8d570, + 0x00c62b4e, 0x003d67c8, 0x00cbfec4, 0x0030b242, + 0x0027cddd, 0x00dc815b, 0x002a1857, 0x00d154d1, + 0x009f3526, 0x006479a0, 0x0092e0ac, 0x0069ac2a, + 0x007ed3b5, 0x00859f33, 0x0073063f, 0x00884ab9, + 0x00a6b487, 0x005df801, 0x00ab610d, 0x00502d8b, + 0x00475214, 0x00bc1e92, 0x004a879e, 0x00b1cb18, + 0x00167be3, 0x00ed3765, 0x001bae69, 0x00e0e2ef, + 0x00f79d70, 0x000cd1f6, 0x00fa48fa, 0x0001047c, + 0x002ffa42, 0x00d4b6c4, 0x00222fc8, 0x00d9634e, + 0x00ce1cd1, 0x00355057, 0x00c3c95b, 0x003885dd, + 0x00000000, 0x00488f66, 0x00901ecd, 0x00d891ab, + 0x00db711c, 0x0093fe7a, 0x004b6fd1, 0x0003e0b7, + 0x00b6e338, 0x00fe6c5e, 0x0026fdf5, 0x006e7293, + 0x006d9224, 0x00251d42, 0x00fd8ce9, 0x00b5038f, + 0x006cc771, 0x00244817, 0x00fcd9bc, 0x00b456da, + 0x00b7b66d, 0x00ff390b, 0x0027a8a0, 0x006f27c6, + 0x00da2449, 0x0092ab2f, 0x004a3a84, 0x0002b5e2, + 0x00015555, 0x0049da33, 0x00914b98, 0x00d9c4fe, + 0x00d88ee3, 0x00900185, 0x0048902e, 0x00001f48, + 0x0003ffff, 0x004b7099, 0x0093e132, 0x00db6e54, + 0x006e6ddb, 0x0026e2bd, 0x00fe7316, 0x00b6fc70, + 0x00b51cc7, 0x00fd93a1, 0x0025020a, 0x006d8d6c, + 0x00b44992, 0x00fcc6f4, 0x0024575f, 0x006cd839, + 0x006f388e, 0x0027b7e8, 0x00ff2643, 0x00b7a925, + 0x0002aaaa, 0x004a25cc, 0x0092b467, 0x00da3b01, + 0x00d9dbb6, 0x009154d0, 0x0049c57b, 0x00014a1d, + 0x004b5141, 0x0003de27, 0x00db4f8c, 0x0093c0ea, + 0x0090205d, 0x00d8af3b, 0x00003e90, 0x0048b1f6, + 0x00fdb279, 0x00b53d1f, 0x006dacb4, 0x002523d2, + 0x0026c365, 0x006e4c03, 0x00b6dda8, 0x00fe52ce, + 0x00279630, 0x006f1956, 0x00b788fd, 0x00ff079b, + 0x00fce72c, 0x00b4684a, 0x006cf9e1, 0x00247687, + 0x00917508, 0x00d9fa6e, 0x00016bc5, 0x0049e4a3, + 0x004a0414, 0x00028b72, 0x00da1ad9, 0x009295bf, + 0x0093dfa2, 0x00db50c4, 0x0003c16f, 0x004b4e09, + 0x0048aebe, 0x000021d8, 0x00d8b073, 0x00903f15, + 0x00253c9a, 0x006db3fc, 0x00b52257, 0x00fdad31, + 0x00fe4d86, 0x00b6c2e0, 0x006e534b, 0x0026dc2d, + 0x00ff18d3, 0x00b797b5, 0x006f061e, 0x00278978, + 0x002469cf, 0x006ce6a9, 0x00b47702, 0x00fcf864, + 0x0049fbeb, 0x0001748d, 0x00d9e526, 0x00916a40, + 0x00928af7, 0x00da0591, 0x0002943a, 0x004a1b5c, + 0x0096a282, 0x00de2de4, 0x0006bc4f, 0x004e3329, + 0x004dd39e, 0x00055cf8, 0x00ddcd53, 0x00954235, + 0x002041ba, 0x0068cedc, 0x00b05f77, 0x00f8d011, + 0x00fb30a6, 0x00b3bfc0, 0x006b2e6b, 0x0023a10d, + 0x00fa65f3, 0x00b2ea95, 0x006a7b3e, 0x0022f458, + 0x002114ef, 0x00699b89, 0x00b10a22, 0x00f98544, + 0x004c86cb, 0x000409ad, 0x00dc9806, 0x00941760, + 0x0097f7d7, 0x00df78b1, 0x0007e91a, 0x004f667c, + 0x004e2c61, 0x0006a307, 0x00de32ac, 0x0096bdca, + 0x00955d7d, 0x00ddd21b, 0x000543b0, 0x004dccd6, + 0x00f8cf59, 0x00b0403f, 0x0068d194, 0x00205ef2, + 0x0023be45, 0x006b3123, 0x00b3a088, 0x00fb2fee, + 0x0022eb10, 0x006a6476, 0x00b2f5dd, 0x00fa7abb, + 0x00f99a0c, 0x00b1156a, 0x006984c1, 0x00210ba7, + 0x00940828, 0x00dc874e, 0x000416e5, 0x004c9983, + 0x004f7934, 0x0007f652, 0x00df67f9, 0x0097e89f, + 0x00ddf3c3, 0x00957ca5, 0x004ded0e, 0x00056268, + 0x000682df, 0x004e0db9, 0x00969c12, 0x00de1374, + 0x006b10fb, 0x00239f9d, 0x00fb0e36, 0x00b38150, + 0x00b061e7, 0x00f8ee81, 0x00207f2a, 0x0068f04c, + 0x00b134b2, 0x00f9bbd4, 0x00212a7f, 0x0069a519, + 0x006a45ae, 0x0022cac8, 0x00fa5b63, 0x00b2d405, + 0x0007d78a, 0x004f58ec, 0x0097c947, 0x00df4621, + 0x00dca696, 0x009429f0, 0x004cb85b, 0x0004373d, + 0x00057d20, 0x004df246, 0x009563ed, 0x00ddec8b, + 0x00de0c3c, 0x0096835a, 0x004e12f1, 0x00069d97, + 0x00b39e18, 0x00fb117e, 0x002380d5, 0x006b0fb3, + 0x0068ef04, 0x00206062, 0x00f8f1c9, 0x00b07eaf, + 0x0069ba51, 0x00213537, 0x00f9a49c, 0x00b12bfa, + 0x00b2cb4d, 0x00fa442b, 0x0022d580, 0x006a5ae6, + 0x00df5969, 0x0097d60f, 0x004f47a4, 0x0007c8c2, + 0x00042875, 0x004ca713, 0x009436b8, 0x00dcb9de, + 0x00000000, 0x00d70983, 0x00555f80, 0x00825603, + 0x0051f286, 0x0086fb05, 0x0004ad06, 0x00d3a485, + 0x0059a88b, 0x008ea108, 0x000cf70b, 0x00dbfe88, + 0x00085a0d, 0x00df538e, 0x005d058d, 0x008a0c0e, + 0x00491c91, 0x009e1512, 0x001c4311, 0x00cb4a92, + 0x0018ee17, 0x00cfe794, 0x004db197, 0x009ab814, + 0x0010b41a, 0x00c7bd99, 0x0045eb9a, 0x0092e219, + 0x0041469c, 0x00964f1f, 0x0014191c, 0x00c3109f, + 0x006974a4, 0x00be7d27, 0x003c2b24, 0x00eb22a7, + 0x00388622, 0x00ef8fa1, 0x006dd9a2, 0x00bad021, + 0x0030dc2f, 0x00e7d5ac, 0x006583af, 0x00b28a2c, + 0x00612ea9, 0x00b6272a, 0x00347129, 0x00e378aa, + 0x00206835, 0x00f761b6, 0x007537b5, 0x00a23e36, + 0x00719ab3, 0x00a69330, 0x0024c533, 0x00f3ccb0, + 0x0079c0be, 0x00aec93d, 0x002c9f3e, 0x00fb96bd, + 0x00283238, 0x00ff3bbb, 0x007d6db8, 0x00aa643b, + 0x0029a4ce, 0x00fead4d, 0x007cfb4e, 0x00abf2cd, + 0x00785648, 0x00af5fcb, 0x002d09c8, 0x00fa004b, + 0x00700c45, 0x00a705c6, 0x002553c5, 0x00f25a46, + 0x0021fec3, 0x00f6f740, 0x0074a143, 0x00a3a8c0, + 0x0060b85f, 0x00b7b1dc, 0x0035e7df, 0x00e2ee5c, + 0x00314ad9, 0x00e6435a, 0x00641559, 0x00b31cda, + 0x003910d4, 0x00ee1957, 0x006c4f54, 0x00bb46d7, + 0x0068e252, 0x00bfebd1, 0x003dbdd2, 0x00eab451, + 0x0040d06a, 0x0097d9e9, 0x00158fea, 0x00c28669, + 0x001122ec, 0x00c62b6f, 0x00447d6c, 0x009374ef, + 0x001978e1, 0x00ce7162, 0x004c2761, 0x009b2ee2, + 0x00488a67, 0x009f83e4, 0x001dd5e7, 0x00cadc64, + 0x0009ccfb, 0x00dec578, 0x005c937b, 0x008b9af8, + 0x00583e7d, 0x008f37fe, 0x000d61fd, 0x00da687e, + 0x00506470, 0x00876df3, 0x00053bf0, 0x00d23273, + 0x000196f6, 0x00d69f75, 0x0054c976, 0x0083c0f5, + 0x00a9041b, 0x007e0d98, 0x00fc5b9b, 0x002b5218, + 0x00f8f69d, 0x002fff1e, 0x00ada91d, 0x007aa09e, + 0x00f0ac90, 0x0027a513, 0x00a5f310, 0x0072fa93, + 0x00a15e16, 0x00765795, 0x00f40196, 0x00230815, + 0x00e0188a, 0x00371109, 0x00b5470a, 0x00624e89, + 0x00b1ea0c, 0x0066e38f, 0x00e4b58c, 0x0033bc0f, + 0x00b9b001, 0x006eb982, 0x00ecef81, 0x003be602, + 0x00e84287, 0x003f4b04, 0x00bd1d07, 0x006a1484, + 0x00c070bf, 0x0017793c, 0x00952f3f, 0x004226bc, + 0x00918239, 0x00468bba, 0x00c4ddb9, 0x0013d43a, + 0x0099d834, 0x004ed1b7, 0x00cc87b4, 0x001b8e37, + 0x00c82ab2, 0x001f2331, 0x009d7532, 0x004a7cb1, + 0x00896c2e, 0x005e65ad, 0x00dc33ae, 0x000b3a2d, + 0x00d89ea8, 0x000f972b, 0x008dc128, 0x005ac8ab, + 0x00d0c4a5, 0x0007cd26, 0x00859b25, 0x005292a6, + 0x00813623, 0x00563fa0, 0x00d469a3, 0x00036020, + 0x0080a0d5, 0x0057a956, 0x00d5ff55, 0x0002f6d6, + 0x00d15253, 0x00065bd0, 0x00840dd3, 0x00530450, + 0x00d9085e, 0x000e01dd, 0x008c57de, 0x005b5e5d, + 0x0088fad8, 0x005ff35b, 0x00dda558, 0x000aacdb, + 0x00c9bc44, 0x001eb5c7, 0x009ce3c4, 0x004bea47, + 0x00984ec2, 0x004f4741, 0x00cd1142, 0x001a18c1, + 0x009014cf, 0x00471d4c, 0x00c54b4f, 0x001242cc, + 0x00c1e649, 0x0016efca, 0x0094b9c9, 0x0043b04a, + 0x00e9d471, 0x003eddf2, 0x00bc8bf1, 0x006b8272, + 0x00b826f7, 0x006f2f74, 0x00ed7977, 0x003a70f4, + 0x00b07cfa, 0x00677579, 0x00e5237a, 0x00322af9, + 0x00e18e7c, 0x003687ff, 0x00b4d1fc, 0x0063d87f, + 0x00a0c8e0, 0x0077c163, 0x00f59760, 0x00229ee3, + 0x00f13a66, 0x002633e5, 0x00a465e6, 0x00736c65, + 0x00f9606b, 0x002e69e8, 0x00ac3feb, 0x007b3668, + 0x00a892ed, 0x007f9b6e, 0x00fdcd6d, 0x002ac4ee, + 0x00000000, 0x00520936, 0x00a4126c, 0x00f61b5a, + 0x004825d8, 0x001a2cee, 0x00ec37b4, 0x00be3e82, + 0x006b0636, 0x00390f00, 0x00cf145a, 0x009d1d6c, + 0x002323ee, 0x00712ad8, 0x00873182, 0x00d538b4, + 0x00d60c6c, 0x0084055a, 0x00721e00, 0x00201736, + 0x009e29b4, 0x00cc2082, 0x003a3bd8, 0x006832ee, + 0x00bd0a5a, 0x00ef036c, 0x00191836, 0x004b1100, + 0x00f52f82, 0x00a726b4, 0x00513dee, 0x000334d8, + 0x00ac19d8, 0x00fe10ee, 0x00080bb4, 0x005a0282, + 0x00e43c00, 0x00b63536, 0x00402e6c, 0x0012275a, + 0x00c71fee, 0x009516d8, 0x00630d82, 0x003104b4, + 0x008f3a36, 0x00dd3300, 0x002b285a, 0x0079216c, + 0x007a15b4, 0x00281c82, 0x00de07d8, 0x008c0eee, + 0x0032306c, 0x0060395a, 0x00962200, 0x00c42b36, + 0x00111382, 0x00431ab4, 0x00b501ee, 0x00e708d8, + 0x0059365a, 0x000b3f6c, 0x00fd2436, 0x00af2d00, + 0x00a37f36, 0x00f17600, 0x00076d5a, 0x0055646c, + 0x00eb5aee, 0x00b953d8, 0x004f4882, 0x001d41b4, + 0x00c87900, 0x009a7036, 0x006c6b6c, 0x003e625a, + 0x00805cd8, 0x00d255ee, 0x00244eb4, 0x00764782, + 0x0075735a, 0x00277a6c, 0x00d16136, 0x00836800, + 0x003d5682, 0x006f5fb4, 0x009944ee, 0x00cb4dd8, + 0x001e756c, 0x004c7c5a, 0x00ba6700, 0x00e86e36, + 0x005650b4, 0x00045982, 0x00f242d8, 0x00a04bee, + 0x000f66ee, 0x005d6fd8, 0x00ab7482, 0x00f97db4, + 0x00474336, 0x00154a00, 0x00e3515a, 0x00b1586c, + 0x006460d8, 0x003669ee, 0x00c072b4, 0x00927b82, + 0x002c4500, 0x007e4c36, 0x0088576c, 0x00da5e5a, + 0x00d96a82, 0x008b63b4, 0x007d78ee, 0x002f71d8, + 0x00914f5a, 0x00c3466c, 0x00355d36, 0x00675400, + 0x00b26cb4, 0x00e06582, 0x00167ed8, 0x004477ee, + 0x00fa496c, 0x00a8405a, 0x005e5b00, 0x000c5236, + 0x0046ff6c, 0x0014f65a, 0x00e2ed00, 0x00b0e436, + 0x000edab4, 0x005cd382, 0x00aac8d8, 0x00f8c1ee, + 0x002df95a, 0x007ff06c, 0x0089eb36, 0x00dbe200, + 0x0065dc82, 0x0037d5b4, 0x00c1ceee, 0x0093c7d8, + 0x0090f300, 0x00c2fa36, 0x0034e16c, 0x0066e85a, + 0x00d8d6d8, 0x008adfee, 0x007cc4b4, 0x002ecd82, + 0x00fbf536, 0x00a9fc00, 0x005fe75a, 0x000dee6c, + 0x00b3d0ee, 0x00e1d9d8, 0x0017c282, 0x0045cbb4, + 0x00eae6b4, 0x00b8ef82, 0x004ef4d8, 0x001cfdee, + 0x00a2c36c, 0x00f0ca5a, 0x0006d100, 0x0054d836, + 0x0081e082, 0x00d3e9b4, 0x0025f2ee, 0x0077fbd8, + 0x00c9c55a, 0x009bcc6c, 0x006dd736, 0x003fde00, + 0x003cead8, 0x006ee3ee, 0x0098f8b4, 0x00caf182, + 0x0074cf00, 0x0026c636, 0x00d0dd6c, 0x0082d45a, + 0x0057ecee, 0x0005e5d8, 0x00f3fe82, 0x00a1f7b4, + 0x001fc936, 0x004dc000, 0x00bbdb5a, 0x00e9d26c, + 0x00e5805a, 0x00b7896c, 0x00419236, 0x00139b00, + 0x00ada582, 0x00ffacb4, 0x0009b7ee, 0x005bbed8, + 0x008e866c, 0x00dc8f5a, 0x002a9400, 0x00789d36, + 0x00c6a3b4, 0x0094aa82, 0x0062b1d8, 0x0030b8ee, + 0x00338c36, 0x00618500, 0x00979e5a, 0x00c5976c, + 0x007ba9ee, 0x0029a0d8, 0x00dfbb82, 0x008db2b4, + 0x00588a00, 0x000a8336, 0x00fc986c, 0x00ae915a, + 0x0010afd8, 0x0042a6ee, 0x00b4bdb4, 0x00e6b482, + 0x00499982, 0x001b90b4, 0x00ed8bee, 0x00bf82d8, + 0x0001bc5a, 0x0053b56c, 0x00a5ae36, 0x00f7a700, + 0x00229fb4, 0x00709682, 0x00868dd8, 0x00d484ee, + 0x006aba6c, 0x0038b35a, 0x00cea800, 0x009ca136, + 0x009f95ee, 0x00cd9cd8, 0x003b8782, 0x00698eb4, + 0x00d7b036, 0x0085b900, 0x0073a25a, 0x0021ab6c, + 0x00f493d8, 0x00a69aee, 0x005081b4, 0x00028882, + 0x00bcb600, 0x00eebf36, 0x0018a46c, 0x004aad5a +}; + +static inline +u32 crc24_init (void) +{ + return 0xce04b7; +} + +static inline +u32 crc24_next (u32 crc, byte data) +{ + return (crc >> 8) ^ crc24_table[(crc & 0xff) ^ data]; +} + +/* + * Process 4 bytes in one go + */ +static inline +u32 crc24_next4 (u32 crc, u32 data) +{ + crc ^= data; + crc = crc24_table[(crc & 0xff) + 0x300] ^ + crc24_table[((crc >> 8) & 0xff) + 0x200] ^ + crc24_table[((crc >> 16) & 0xff) + 0x100] ^ + crc24_table[(crc >> 24) & 0xff]; + return crc; +} + +static inline +u32 crc24_final (u32 crc) +{ + return crc & 0xffffff; +} + +static void +crc24rfc2440_init (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + ctx->CRC = crc24_init(); +} + +static void +crc24rfc2440_write (void *context, const void *inbuf_arg, size_t inlen) +{ + const unsigned char *inbuf = inbuf_arg; + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + u32 crc; + + if (!inbuf || !inlen) + return; + + crc = ctx->CRC; + + while (inlen >= 16) + { + inlen -= 16; + crc = crc24_next4(crc, buf_get_le32(&inbuf[0])); + crc = crc24_next4(crc, buf_get_le32(&inbuf[4])); + crc = crc24_next4(crc, buf_get_le32(&inbuf[8])); + crc = crc24_next4(crc, buf_get_le32(&inbuf[12])); + inbuf += 16; + } + + while (inlen >= 4) + { + inlen -= 4; + crc = crc24_next4(crc, buf_get_le32(inbuf)); + inbuf += 4; + } + + while (inlen--) + { + crc = crc24_next(crc, *inbuf++); + } + + ctx->CRC = crc; +} + +static void +crc24rfc2440_final (void *context) +{ + CRC_CONTEXT *ctx = (CRC_CONTEXT *) context; + ctx->CRC = crc24_final(ctx->CRC); + buf_put_le32 (ctx->buf, ctx->CRC); +} + +gcry_md_spec_t _gcry_digest_spec_crc32 = + { + "CRC32", NULL, 0, NULL, 4, + crc32_init, crc32_write, crc32_final, crc32_read, + sizeof (CRC_CONTEXT) + }; + +gcry_md_spec_t _gcry_digest_spec_crc32_rfc1510 = + { + "CRC32RFC1510", NULL, 0, NULL, 4, + crc32rfc1510_init, crc32_write, + crc32rfc1510_final, crc32_read, + sizeof (CRC_CONTEXT) + }; + +gcry_md_spec_t _gcry_digest_spec_crc24_rfc2440 = + { + "CRC24RFC2440", NULL, 0, NULL, 3, + crc24rfc2440_init, crc24rfc2440_write, + crc24rfc2440_final, crc32_read, + sizeof (CRC_CONTEXT) + }; diff --git a/lib/libgcrypt/cipher/des.c b/grub-core/lib/libgcrypt/cipher/des.c similarity index 99% rename from lib/libgcrypt/cipher/des.c rename to grub-core/lib/libgcrypt/cipher/des.c index f91df7771..96b06ae36 100644 --- a/lib/libgcrypt/cipher/des.c +++ b/grub-core/lib/libgcrypt/cipher/des.c @@ -22,7 +22,7 @@ * Bruce Schneier: Applied Cryptography. Second Edition. * John Wiley & Sons, 1996. ISBN 0-471-12845-7. Pages 358 ff. * This implementation is according to the definition of DES in FIPS - * PUB 46-2 from December 1993. + * PUB 46-2 from December 1993. */ @@ -106,7 +106,7 @@ * * if ( (error_msg = selftest()) ) * { - * fprintf(stderr, "An error in the DES/Tripple-DES implementation occured: %s\n", error_msg); + * fprintf(stderr, "An error in the DES/Triple-DES implementation occurred: %s\n", error_msg); * abort(); * } */ @@ -388,7 +388,7 @@ static byte weak_keys[64][8] = }; static unsigned char weak_keys_chksum[20] = { 0xD0, 0xCF, 0x07, 0x38, 0x93, 0x70, 0x8A, 0x83, 0x7D, 0xD7, - 0x8A, 0x36, 0x65, 0x29, 0x6C, 0x1F, 0x7C, 0x3F, 0xD3, 0x41 + 0x8A, 0x36, 0x65, 0x29, 0x6C, 0x1F, 0x7C, 0x3F, 0xD3, 0x41 }; @@ -888,12 +888,12 @@ selftest (void) if (memcmp (input, result, 8)) return "Triple-DES test failed."; } - + /* * More Triple-DES test. These are testvectors as used by SSLeay, * thanks to Jeroen C. van Gelderen. */ - { + { struct { byte key[24]; byte plain[8]; byte cipher[8]; } testdata[] = { { { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, @@ -901,7 +901,7 @@ selftest (void) { 0x95,0xF8,0xA5,0xE5,0xDD,0x31,0xD9,0x00 }, { 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } }, - + { { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 }, @@ -966,7 +966,7 @@ selftest (void) { tripledes_set3keys (des3, testdata[i].key, testdata[i].key + 8, testdata[i].key + 16); - + tripledes_ecb_encrypt (des3, testdata[i].plain, result); if (memcmp (testdata[i].cipher, result, 8)) return "Triple-DES SSLeay test failed on encryption."; @@ -1047,7 +1047,7 @@ do_tripledes_set_extra_info (void *context, int what, break; default: - ec = GPG_ERR_INV_OP; + ec = GPG_ERR_INV_OP; break; } return ec; @@ -1112,7 +1112,7 @@ do_des_decrypt( void *context, byte *outbuf, const byte *inbuf ) -/* +/* Self-test section. */ @@ -1123,7 +1123,7 @@ selftest_fips (int extended, selftest_report_func_t report) { const char *what; const char *errtxt; - + (void)extended; /* No extended tests available. */ what = "low-level"; @@ -1160,7 +1160,7 @@ run_selftests (int algo, int extended, selftest_report_func_t report) default: ec = GPG_ERR_CIPHER_ALGO; break; - + } return ec; } @@ -1189,7 +1189,7 @@ gcry_cipher_spec_t _gcry_cipher_spec_tripledes = do_tripledes_setkey, do_tripledes_encrypt, do_tripledes_decrypt }; -cipher_extra_spec_t _gcry_cipher_extraspec_tripledes = +cipher_extra_spec_t _gcry_cipher_extraspec_tripledes = { run_selftests, do_tripledes_set_extra_info diff --git a/lib/libgcrypt/cipher/dsa.c b/grub-core/lib/libgcrypt/cipher/dsa.c similarity index 94% rename from lib/libgcrypt/cipher/dsa.c rename to grub-core/lib/libgcrypt/cipher/dsa.c index 100710f97..883a815f2 100644 --- a/lib/libgcrypt/cipher/dsa.c +++ b/grub-core/lib/libgcrypt/cipher/dsa.c @@ -74,7 +74,7 @@ static const char sample_secret_key[] = " 42CAA7DC289F0C5A9D155F02D3D551DB741A81695B74D4C8F477F9C7838EB0FB#)" " (x #11D54E4ADBD3034160F2CED4B7CD292A4EBF3EC0#)))"; /* A sample 1024 bit DSA key used for the selftests (public only). */ -static const char sample_public_key[] = +static const char sample_public_key[] = "(public-key" " (dsa" " (p #00AD7C0025BA1A15F775F3F2D673718391D00456978D347B33D7B49E7F32EDAB" @@ -141,14 +141,19 @@ gen_k( gcry_mpi_t q ) unsigned int nbytes = (nbits+7)/8; char *rndbuf = NULL; + /* To learn why we don't use mpi_mod to get the requested bit size, + read the paper: "The Insecurity of the Digital Signature + Algorithm with Partially Known Nonces" by Nguyen and Shparlinski. + Journal of Cryptology, New York. Vol 15, nr 3 (2003) */ + if ( DBG_CIPHER ) log_debug("choosing a random k "); - for (;;) + for (;;) { if( DBG_CIPHER ) progress('.'); - if ( !rndbuf || nbits < 32 ) + if ( !rndbuf || nbits < 32 ) { gcry_free(rndbuf); rndbuf = gcry_random_bytes_secure( (nbits+7)/8, GCRY_STRONG_RANDOM ); @@ -156,13 +161,20 @@ gen_k( gcry_mpi_t q ) else { /* Change only some of the higher bits. We could improve this by directly requesting more memory at the first call - to get_random_bytes() and use this the here maybe it is - easier to do this directly in random.c. */ + to get_random_bytes() and use these extra bytes here. + However the required management code is more complex and + thus we better use this simple method. */ char *pp = gcry_random_bytes_secure( 4, GCRY_STRONG_RANDOM ); memcpy( rndbuf,pp, 4 ); gcry_free(pp); } _gcry_mpi_set_buffer( k, rndbuf, nbytes, 0 ); + + /* Make sure we have the requested number of bits. This code + looks a bit funny but it is easy to understand if you + consider that mpi_set_highbit clears all higher bits. We + don't have a clear_highbit, thus we first set the high bit + and then clear it again. */ if ( mpi_test_bit( k, nbits-1 ) ) mpi_set_highbit( k, nbits-1 ); else @@ -172,7 +184,7 @@ gen_k( gcry_mpi_t q ) } if( !(mpi_cmp( k, q ) < 0) ) /* check: k < q */ - { + { if( DBG_CIPHER ) progress('+'); continue; /* no */ @@ -188,7 +200,7 @@ gen_k( gcry_mpi_t q ) gcry_free(rndbuf); if( DBG_CIPHER ) progress('\n'); - + return k; } @@ -315,7 +327,7 @@ generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, mpi_add_ui (h, h, 1); /* g = h^e mod p */ gcry_mpi_powm (g, h, e, p); - } + } while (!mpi_cmp_ui (g, 1)); /* Continue until g != 1. */ } @@ -330,13 +342,13 @@ generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, x = mpi_alloc_secure( mpi_get_nlimbs(q) ); mpi_sub_ui( h, q, 1 ); /* put q-1 into h */ rndbuf = NULL; - do + do { if( DBG_CIPHER ) progress('.'); if( !rndbuf ) rndbuf = gcry_random_bytes_secure ((qbits+7)/8, random_level); - else + else { /* Change only some of the higher bits (= 2 bytes)*/ char *r = gcry_random_bytes_secure (2, random_level); memcpy(rndbuf, r, 2 ); @@ -345,7 +357,7 @@ generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, _gcry_mpi_set_buffer( x, rndbuf, (qbits+7)/8, 0 ); mpi_clear_highbit( x, qbits+1 ); - } + } while ( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, h )<0 ) ); gcry_free(rndbuf); mpi_free( e ); @@ -355,7 +367,7 @@ generate (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, y = mpi_alloc( mpi_get_nlimbs(p) ); gcry_mpi_powm( y, g, x, p ); - if( DBG_CIPHER ) + if( DBG_CIPHER ) { progress('\n'); log_mpidump("dsa p", p ); @@ -406,8 +418,8 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, const void *seed; size_t seedlen; } initial_seed = { NULL, NULL, 0 }; - gcry_mpi_t prime_q = NULL; - gcry_mpi_t prime_p = NULL; + gcry_mpi_t prime_q = NULL; + gcry_mpi_t prime_p = NULL; gcry_mpi_t value_g = NULL; /* The generator. */ gcry_mpi_t value_y = NULL; /* g^x mod p */ gcry_mpi_t value_x = NULL; /* The secret exponent. */ @@ -467,15 +479,15 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, initial_seed.seed = gcry_sexp_nth_data (initial_seed.sexp, 1, &initial_seed.seedlen); } - + /* Fixme: Enable 186-3 after it has been approved and after fixing the generation function. */ /* if (use_fips186_2) */ (void)use_fips186_2; - ec = _gcry_generate_fips186_2_prime (nbits, qbits, - initial_seed.seed, + ec = _gcry_generate_fips186_2_prime (nbits, qbits, + initial_seed.seed, initial_seed.seedlen, - &prime_q, &prime_p, + &prime_q, &prime_p, r_counter, r_seed, r_seedlen); /* else */ @@ -493,33 +505,33 @@ generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits, mpi_sub_ui (value_e, prime_p, 1); mpi_fdiv_q (value_e, value_e, prime_q ); value_g = mpi_alloc_like (prime_p); - value_h = mpi_alloc_set_ui (1); + value_h = mpi_alloc_set_ui (1); do { mpi_add_ui (value_h, value_h, 1); /* g = h^e mod p */ mpi_powm (value_g, value_h, value_e, prime_p); - } + } while (!mpi_cmp_ui (value_g, 1)); /* Continue until g != 1. */ } /* Select a random number x with: 0 < x < q */ value_x = gcry_mpi_snew (qbits); - do + do { if( DBG_CIPHER ) progress('.'); gcry_mpi_randomize (value_x, qbits, GCRY_VERY_STRONG_RANDOM); mpi_clear_highbit (value_x, qbits+1); - } + } while (!(mpi_cmp_ui (value_x, 0) > 0 && mpi_cmp (value_x, prime_q) < 0)); /* y = g^x mod p */ value_y = mpi_alloc_like (prime_p); gcry_mpi_powm (value_y, value_g, value_x, prime_p); - if (DBG_CIPHER) + if (DBG_CIPHER) { progress('\n'); log_mpidump("dsa p", prime_p ); @@ -691,7 +703,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, int use_fips186_2 = 0; int use_fips186 = 0; dsa_domain_t domain; - + (void)algo; /* No need to check it. */ (void)evalue; /* Not required for DSA. */ @@ -700,7 +712,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, if (genparms) { gcry_sexp_t domainsexp; - + /* Parse the optional qbits element. */ l1 = gcry_sexp_find_token (genparms, "qbits", 0); if (l1) @@ -708,7 +720,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, char buf[50]; const char *s; size_t n; - + s = gcry_sexp_nth_data (l1, 1, &n); if (!s || n >= DIM (buf) - 1 ) { @@ -760,7 +772,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, gcry_sexp_release (deriveparms); return GPG_ERR_INV_VALUE; } - + /* Put all domain parameters into the domain object. */ l1 = gcry_sexp_find_token (domainsexp, "p", 0); domain.p = gcry_sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); @@ -804,7 +816,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, { /* Format the seed-values unless domain parameters are used for which a H_VALUE of NULL is an indication. */ - ec = gpg_err_code (gcry_sexp_build + ec = gpg_err_code (gcry_sexp_build (&seedinfo, NULL, "(seed-values(counter %d)(seed %b)(h %m))", counter, (int)seedlen, seed, h_value)); @@ -879,7 +891,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, p = stpcpy (p, ")"); } p = stpcpy (p, ")"); - + /* Allocate space for the list of factors plus one for an S-expression plus an extra NULL entry for safety and fill it with the factors. */ @@ -894,8 +906,8 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, for (j=0; j < nfactors; j++) arg_list[i++] = (*retfactors) + j; arg_list[i] = NULL; - - ec = gpg_err_code (gcry_sexp_build_array + + ec = gpg_err_code (gcry_sexp_build_array (r_extrainfo, NULL, format, arg_list)); } } @@ -907,6 +919,7 @@ dsa_generate_ext (int algo, unsigned int nbits, unsigned long evalue, gcry_mpi_release ((*retfactors)[i]); (*retfactors)[i] = NULL; } + gcry_free (*retfactors); *retfactors = NULL; if (ec) { @@ -1022,19 +1035,19 @@ dsa_get_nbits (int algo, gcry_mpi_t *pkey) -/* +/* Self-test section. */ static const char * selftest_sign_1024 (gcry_sexp_t pkey, gcry_sexp_t skey) { - static const char sample_data[] = - "(data (flags pkcs1)" - " (hash sha1 #a0b1c2d3e4f500102030405060708090a1b2c3d4#))"; - static const char sample_data_bad[] = - "(data (flags pkcs1)" - " (hash sha1 #a0b1c2d3e4f510102030405060708090a1b2c3d4#))"; + static const char sample_data[] = + "(data (flags raw)" + " (value #a0b1c2d3e4f500102030405060708090a1b2c3d4#))"; + static const char sample_data_bad[] = + "(data (flags raw)" + " (value #a0b1c2d3e4f510102030405060708090a1b2c3d4#))"; const char *errtxt = NULL; gcry_error_t err; @@ -1045,7 +1058,7 @@ selftest_sign_1024 (gcry_sexp_t pkey, gcry_sexp_t skey) err = gcry_sexp_sscan (&data, NULL, sample_data, strlen (sample_data)); if (!err) - err = gcry_sexp_sscan (&data_bad, NULL, + err = gcry_sexp_sscan (&data_bad, NULL, sample_data_bad, strlen (sample_data_bad)); if (err) { @@ -1092,10 +1105,10 @@ selftests_dsa (selftest_report_func_t report) /* Convert the S-expressions into the internal representation. */ what = "convert"; - err = gcry_sexp_sscan (&skey, NULL, + err = gcry_sexp_sscan (&skey, NULL, sample_secret_key, strlen (sample_secret_key)); if (!err) - err = gcry_sexp_sscan (&pkey, NULL, + err = gcry_sexp_sscan (&pkey, NULL, sample_public_key, strlen (sample_public_key)); if (err) { @@ -1145,7 +1158,7 @@ run_selftests (int algo, int extended, selftest_report_func_t report) default: ec = GPG_ERR_PUBKEY_ALGO; break; - + } return ec; } @@ -1162,7 +1175,7 @@ static const char *dsa_names[] = gcry_pk_spec_t _gcry_pubkey_spec_dsa = { - "DSA", dsa_names, + "DSA", dsa_names, "pqgy", "pqgyx", "", "rs", "pqgy", GCRY_PK_USAGE_SIGN, dsa_generate, @@ -1173,9 +1186,8 @@ gcry_pk_spec_t _gcry_pubkey_spec_dsa = dsa_verify, dsa_get_nbits }; -pk_extra_spec_t _gcry_pubkey_extraspec_dsa = +pk_extra_spec_t _gcry_pubkey_extraspec_dsa = { run_selftests, dsa_generate_ext }; - diff --git a/lib/libgcrypt/cipher/ecc.c b/grub-core/lib/libgcrypt/cipher/ecc.c similarity index 73% rename from lib/libgcrypt/cipher/ecc.c rename to grub-core/lib/libgcrypt/cipher/ecc.c index fcbd8e3a9..b8487dc13 100644 --- a/lib/libgcrypt/cipher/ecc.c +++ b/grub-core/lib/libgcrypt/cipher/ecc.c @@ -1,18 +1,18 @@ /* ecc.c - Elliptic Curve Cryptography - Copyright (C) 2007, 2008 Free Software Foundation, Inc. + Copyright (C) 2007, 2008, 2010, 2011 Free Software Foundation, Inc. This file is part of Libgcrypt. - + Libgcrypt is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. - + Libgcrypt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, @@ -33,20 +33,21 @@ up. In fact there is not much left of the orginally code except for some variable names and the text book implementaion of the sign and verification algorithms. The arithmetic functions have entirely - been rewritten and moved to mpi/ec.c. */ + been rewritten and moved to mpi/ec.c. + + ECDH encrypt and decrypt code written by Andrey Jivsov, +*/ /* TODO: - - If we support point compression we need to decide how to compute - the keygrip - it should not change due to compression. + - If we support point compression we need to uncompress before + computing the keygrip - In mpi/ec.c we use mpi_powm for x^2 mod p: Either implement a special case in mpi_powm or check whether mpi_mulm is faster. - Decide whether we should hide the mpi_point_t definition. - - - Support more than just ECDSA. */ @@ -59,7 +60,6 @@ #include "mpi.h" #include "cipher.h" - /* Definition of a curve. */ typedef struct { @@ -68,7 +68,8 @@ typedef struct gcry_mpi_t b; /* Second coefficient of the Weierstrass equation. */ mpi_point_t G; /* Base point (generator). */ gcry_mpi_t n; /* Order of G. */ -} elliptic_curve_t; + const char *name; /* Name of curve or NULL. */ +} elliptic_curve_t; typedef struct @@ -90,7 +91,7 @@ static const struct { const char *name; /* Our name. */ const char *other; /* Other name. */ -} curve_aliases[] = +} curve_aliases[] = { { "NIST P-192", "1.2.840.10045.3.1.1" }, /* X9.62 OID */ { "NIST P-192", "prime192v1" }, /* X9.62 name. */ @@ -100,11 +101,11 @@ static const struct { "NIST P-224", "1.3.132.0.33" }, /* SECP OID. */ { "NIST P-256", "1.2.840.10045.3.1.7" }, /* From NIST SP 800-78-1. */ - { "NIST P-256", "prime256v1" }, + { "NIST P-256", "prime256v1" }, { "NIST P-256", "secp256r1" }, { "NIST P-384", "secp384r1" }, - { "NIST P-384", "1.3.132.0.34" }, + { "NIST P-384", "1.3.132.0.34" }, { "NIST P-521", "secp521r1" }, { "NIST P-521", "1.3.132.0.35" }, @@ -120,11 +121,7 @@ static const struct { NULL, NULL} }; - - -/* This static table defines all available curves. */ -static const struct -{ +typedef struct { const char *desc; /* Description of the curve. */ unsigned int nbits; /* Number of bits. */ unsigned int fips:1; /* True if this is a FIPS140-2 approved curve. */ @@ -132,7 +129,10 @@ static const struct const char *a, *b; /* The coefficients. */ const char *n; /* The order of the base point. */ const char *g_x, *g_y; /* Base point. */ -} domain_parms[] = +} ecc_domain_parms_t; + +/* This static table defines all available curves. */ +static const ecc_domain_parms_t domain_parms[] = { { "NIST P-192", 192, 1, @@ -197,7 +197,7 @@ static const struct "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650" }, - { "brainpoolP160r1", 160, 0, + { "brainpoolP160r1", 160, 0, "0xe95e4a5f737059dc60dfc7ad95b3d8139515620f", "0x340e7be2a280eb74e2be61bada745d97e8f7c300", "0x1e589a8595423412134faa2dbdec95c8d8675e58", @@ -206,7 +206,7 @@ static const struct "0x1667cb477a1a8ec338f94741669c976316da6321" }, - { "brainpoolP192r1", 192, 0, + { "brainpoolP192r1", 192, 0, "0xc302f41d932a36cda7a3463093d18db78fce476de1a86297", "0x6a91174076b1e0e19c39c031fe8685c1cae040e5c69a28ef", "0x469a28ef7c28cca3dc721d044f4496bcca7ef4146fbf25c9", @@ -369,7 +369,6 @@ curve_copy (elliptic_curve_t E) } - /* Helper to scan a hex string. */ static gcry_mpi_t scanval (const char *string) @@ -400,10 +399,10 @@ gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base) axb = mpi_new (0); y = mpi_new (0); - mpi_powm (x_3, x, three, base->p); - mpi_mulm (axb, base->a, x, base->p); - mpi_addm (axb, axb, base->b, base->p); - mpi_addm (y, x_3, axb, base->p); + mpi_powm (x_3, x, three, base->p); + mpi_mulm (axb, base->a, x, base->p); + mpi_addm (axb, axb, base->b, base->p); + mpi_addm (y, x_3, axb, base->p); mpi_free (x_3); mpi_free (axb); @@ -412,9 +411,6 @@ gen_y_2 (gcry_mpi_t x, elliptic_curve_t *base) } - - - /* Generate a random secret scalar k with an order of p At the beginning this was identical to the code is in elgamal.c. @@ -428,7 +424,8 @@ gen_k (gcry_mpi_t p, int security_level) nbits = mpi_get_nbits (p); k = mpi_snew (nbits); if (DBG_CIPHER) - log_debug ("choosing a random k of %u bits\n", nbits); + log_debug ("choosing a random k of %u bits at seclevel %d\n", + nbits, security_level); gcry_mpi_randomize (k, nbits, security_level); @@ -437,23 +434,27 @@ gen_k (gcry_mpi_t p, int security_level) return k; } -/**************** - * Generate the crypto system setup. - * As of now the fix NIST recommended values are used. - * The subgroup generator point is in another function: gen_big_point. - */ + +/* Generate the crypto system setup. This function takes the NAME of + a curve or the desired number of bits and stores at R_CURVE the + parameters of the named curve or those of a suitable curve. The + chosen number of bits is stored on R_NBITS. */ static gpg_err_code_t -generate_curve (unsigned int nbits, const char *name, - elliptic_curve_t *curve, unsigned int *r_nbits) +fill_in_curve (unsigned int nbits, const char *name, + elliptic_curve_t *curve, unsigned int *r_nbits) { int idx, aliasno; + const char *resname = NULL; /* Set to a found curve name. */ if (name) { - /* First check nor native curves. */ + /* First check our native curves. */ for (idx = 0; domain_parms[idx].desc; idx++) if (!strcmp (name, domain_parms[idx].desc)) - break; + { + resname = domain_parms[idx].desc; + break; + } /* If not found consult the alias table. */ if (!domain_parms[idx].desc) { @@ -465,7 +466,10 @@ generate_curve (unsigned int nbits, const char *name, for (idx = 0; domain_parms[idx].desc; idx++) if (!strcmp (curve_aliases[aliasno].name, domain_parms[idx].desc)) - break; + { + resname = domain_parms[idx].desc; + break; + } } } } @@ -482,8 +486,7 @@ generate_curve (unsigned int nbits, const char *name, possible to bypass this check by specifying the curve parameters directly. */ if (fips_mode () && !domain_parms[idx].fips ) - return GPG_ERR_NOT_SUPPORTED; - + return GPG_ERR_NOT_SUPPORTED; *r_nbits = domain_parms[idx].nbits; curve->p = scanval (domain_parms[idx].p); @@ -493,6 +496,7 @@ generate_curve (unsigned int nbits, const char *name, curve->G.x = scanval (domain_parms[idx].g_x); curve->G.y = scanval (domain_parms[idx].g_y); curve->G.z = mpi_alloc_set_ui (1); + curve->name = resname; return 0; } @@ -504,33 +508,39 @@ generate_curve (unsigned int nbits, const char *name, */ static gpg_err_code_t generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, + int transient_key, gcry_mpi_t g_x, gcry_mpi_t g_y, - gcry_mpi_t q_x, gcry_mpi_t q_y) + gcry_mpi_t q_x, gcry_mpi_t q_y, + const char **r_usedcurve) { gpg_err_code_t err; elliptic_curve_t E; gcry_mpi_t d; mpi_point_t Q; mpi_ec_t ctx; + gcry_random_level_t random_level; - err = generate_curve (nbits, name, &E, &nbits); + *r_usedcurve = NULL; + + err = fill_in_curve (nbits, name, &E, &nbits); if (err) return err; if (DBG_CIPHER) { - log_mpidump ("ecc generation p", E.p); - log_mpidump ("ecc generation a", E.a); - log_mpidump ("ecc generation b", E.b); - log_mpidump ("ecc generation n", E.n); - log_mpidump ("ecc generation Gx", E.G.x); - log_mpidump ("ecc generation Gy", E.G.y); - log_mpidump ("ecc generation Gz", E.G.z); + log_mpidump ("ecgen curve p", E.p); + log_mpidump ("ecgen curve a", E.a); + log_mpidump ("ecgen curve b", E.b); + log_mpidump ("ecgen curve n", E.n); + log_mpidump ("ecgen curve Gx", E.G.x); + log_mpidump ("ecgen curve Gy", E.G.y); + log_mpidump ("ecgen curve Gz", E.G.z); + if (E.name) + log_debug ("ecgen curve used: %s\n", E.name); } - if (DBG_CIPHER) - log_debug ("choosing a random x of size %u\n", nbits); - d = gen_k (E.n, GCRY_VERY_STRONG_RANDOM); + random_level = transient_key ? GCRY_STRONG_RANDOM : GCRY_VERY_STRONG_RANDOM; + d = gen_k (E.n, random_level); /* Compute Q. */ point_init (&Q); @@ -552,27 +562,29 @@ generate_key (ECC_secret_key *sk, unsigned int nbits, const char *name, if (g_x && g_y) { if (_gcry_mpi_ec_get_affine (g_x, g_y, &sk->E.G, ctx)) - log_fatal ("ecc generate: Failed to get affine coordinates\n"); + log_fatal ("ecgen: Failed to get affine coordinates\n"); } if (q_x && q_y) { if (_gcry_mpi_ec_get_affine (q_x, q_y, &sk->Q, ctx)) - log_fatal ("ecc generate: Failed to get affine coordinates\n"); + log_fatal ("ecgen: Failed to get affine coordinates\n"); } _gcry_mpi_ec_free (ctx); point_free (&Q); mpi_free (d); + + *r_usedcurve = E.name; curve_free (&E); - /* Now we can test our keys (this should never fail!). */ + /* Now we can test our keys (this should never fail!). */ test_keys (sk, nbits - 64); return 0; } -/**************** +/* * To verify correct skey it use a random information. * First, encrypt and decrypt this dummy value, * test if the information is recuperated. @@ -622,54 +634,56 @@ test_keys (ECC_secret_key *sk, unsigned int nbits) mpi_free (test); } -/**************** + +/* * To check the validity of the value, recalculate the correspondence * between the public value and the secret one. */ static int check_secret_key (ECC_secret_key * sk) { + int rc = 1; mpi_point_t Q; - gcry_mpi_t y_2, y2 = mpi_alloc (0); - mpi_ec_t ctx; + gcry_mpi_t y_2, y2; + mpi_ec_t ctx = NULL; + + point_init (&Q); /* ?primarity test of 'p' */ /* (...) //!! */ /* G in E(F_p) */ y_2 = gen_y_2 (sk->E.G.x, &sk->E); /* y^2=x^3+a*x+b */ + y2 = mpi_alloc (0); mpi_mulm (y2, sk->E.G.y, sk->E.G.y, sk->E.p); /* y^2=y*y */ if (mpi_cmp (y_2, y2)) { if (DBG_CIPHER) log_debug ("Bad check: Point 'G' does not belong to curve 'E'!\n"); - return (1); + goto leave; } /* G != PaI */ if (!mpi_cmp_ui (sk->E.G.z, 0)) { if (DBG_CIPHER) log_debug ("Bad check: 'G' cannot be Point at Infinity!\n"); - return (1); + goto leave; } - point_init (&Q); ctx = _gcry_mpi_ec_init (sk->E.p, sk->E.a); + _gcry_mpi_ec_mul_point (&Q, sk->E.n, &sk->E.G, ctx); if (mpi_cmp_ui (Q.z, 0)) { if (DBG_CIPHER) log_debug ("check_secret_key: E is not a curve of order n\n"); - point_free (&Q); - _gcry_mpi_ec_free (ctx); - return 1; + goto leave; } /* pubkey cannot be PaI */ if (!mpi_cmp_ui (sk->Q.z, 0)) { if (DBG_CIPHER) log_debug ("Bad check: Q can not be a Point at Infinity!\n"); - _gcry_mpi_ec_free (ctx); - return (1); + goto leave; } /* pubkey = [d]G over E */ _gcry_mpi_ec_mul_point (&Q, sk->d, &sk->E.G, ctx); @@ -678,12 +692,16 @@ check_secret_key (ECC_secret_key * sk) if (DBG_CIPHER) log_debug ("Bad check: There is NO correspondence between 'd' and 'Q'!\n"); - _gcry_mpi_ec_free (ctx); - return (1); + goto leave; } + rc = 0; /* Okay. */ + + leave: _gcry_mpi_ec_free (ctx); + mpi_free (y2); + mpi_free (y_2); point_free (&Q); - return 0; + return rc; } @@ -699,6 +717,9 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s) mpi_point_t I; mpi_ec_t ctx; + if (DBG_CIPHER) + log_mpidump ("ecdsa sign hash ", input ); + k = NULL; dr = mpi_alloc (0); sum = mpi_alloc (0); @@ -721,7 +742,7 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s) has to be recomputed. */ mpi_free (k); k = gen_k (skey->E.n, GCRY_STRONG_RANDOM); - _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx); + _gcry_mpi_ec_mul_point (&I, k, &skey->E.G, ctx); if (_gcry_mpi_ec_get_affine (x, NULL, &I, ctx)) { if (DBG_CIPHER) @@ -737,6 +758,12 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s) mpi_mulm (s, k_1, sum, skey->E.n); /* s = k^(-1)*(hash+(d*r)) mod n */ } + if (DBG_CIPHER) + { + log_mpidump ("ecdsa sign result r ", r); + log_mpidump ("ecdsa sign result s ", s); + } + leave: _gcry_mpi_ec_free (ctx); point_free (&I); @@ -749,6 +776,7 @@ sign (gcry_mpi_t input, ECC_secret_key *skey, gcry_mpi_t r, gcry_mpi_t s) return err; } + /* * Check if R and S verifies INPUT. */ @@ -820,10 +848,10 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s) { if (DBG_CIPHER) { - log_mpidump (" x", x); - log_mpidump (" y", y); - log_mpidump (" r", r); - log_mpidump (" s", s); + log_mpidump (" x", x); + log_mpidump (" y", y); + log_mpidump (" r", r); + log_mpidump (" s", s); log_debug ("ecc verify: Not verified\n"); } err = GPG_ERR_BAD_SIGNATURE; @@ -846,7 +874,7 @@ verify (gcry_mpi_t input, ECC_public_key *pkey, gcry_mpi_t r, gcry_mpi_t s) } - + /********************************************* ************** interface ****************** *********************************************/ @@ -879,18 +907,16 @@ ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p) memmove (ptr+(pbytes-n), ptr, n); memset (ptr, 0, (pbytes-n)); } - + err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL); if (err) log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err)); gcry_free (buf); - mpi_free (x); - mpi_free (y); - return result; } + /* RESULT must have been initialized and is set on success to the point given by VALUE. */ static gcry_error_t @@ -909,7 +935,7 @@ os2ec (mpi_point_t *result, gcry_mpi_t value) gcry_free (buf); return err; } - if (n < 1) + if (n < 1) { gcry_free (buf); return GPG_ERR_INV_OBJ; @@ -919,7 +945,7 @@ os2ec (mpi_point_t *result, gcry_mpi_t value) gcry_free (buf); return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression. */ } - if ( ((n-1)%2) ) + if ( ((n-1)%2) ) { gcry_free (buf); return GPG_ERR_INV_OBJ; @@ -945,7 +971,7 @@ os2ec (mpi_point_t *result, gcry_mpi_t value) mpi_free (x); mpi_free (y); - + return 0; } @@ -962,10 +988,11 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, gcry_mpi_t g_x, g_y, q_x, q_y; char *curve_name = NULL; gcry_sexp_t l1; + int transient_key = 0; + const char *usedcurve = NULL; (void)algo; (void)evalue; - (void)r_extrainfo; if (genparms) { @@ -978,6 +1005,14 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, if (!curve_name) return GPG_ERR_INV_OBJ; /* No curve name or value too large. */ } + + /* Parse the optional transient-key flag. */ + l1 = gcry_sexp_find_token (genparms, "transient-key", 0); + if (l1) + { + transient_key = 1; + gcry_sexp_release (l1); + } } /* NBITS is required if no curve name has been given. */ @@ -988,28 +1023,45 @@ ecc_generate_ext (int algo, unsigned int nbits, unsigned long evalue, g_y = mpi_new (0); q_x = mpi_new (0); q_y = mpi_new (0); - ec = generate_key (&sk, nbits, curve_name, g_x, g_y, q_x, q_y); + ec = generate_key (&sk, nbits, curve_name, transient_key, g_x, g_y, q_x, q_y, + &usedcurve); gcry_free (curve_name); if (ec) return ec; + if (usedcurve) /* Fixme: No error return checking. */ + gcry_sexp_build (r_extrainfo, NULL, "(curve %s)", usedcurve); skey[0] = sk.E.p; skey[1] = sk.E.a; skey[2] = sk.E.b; - /* The function ec2os releases g_x and g_y. */ skey[3] = ec2os (g_x, g_y, sk.E.p); skey[4] = sk.E.n; - /* The function ec2os releases g_x and g_y. */ skey[5] = ec2os (q_x, q_y, sk.E.p); skey[6] = sk.d; + mpi_free (g_x); + mpi_free (g_y); + mpi_free (q_x); + mpi_free (q_y); + point_free (&sk.E.G); point_free (&sk.Q); /* Make an empty list of factors. */ *retfactors = gcry_calloc ( 1, sizeof **retfactors ); if (!*retfactors) - return gpg_err_code_from_syserror (); + return gpg_err_code_from_syserror (); /* Fixme: relase mem? */ + + if (DBG_CIPHER) + { + log_mpidump ("ecgen result p", skey[0]); + log_mpidump ("ecgen result a", skey[1]); + log_mpidump ("ecgen result b", skey[2]); + log_mpidump ("ecgen result G", skey[3]); + log_mpidump ("ecgen result n", skey[4]); + log_mpidump ("ecgen result Q", skey[5]); + log_mpidump ("ecgen result d", skey[6]); + } return 0; } @@ -1024,7 +1076,7 @@ ecc_generate (int algo, unsigned int nbits, unsigned long evalue, } -/* Return the parameters of the curve NAME. */ +/* Return the parameters of the curve NAME in an MPI array. */ static gcry_err_code_t ecc_get_param (const char *name, gcry_mpi_t *pkey) { @@ -1033,8 +1085,8 @@ ecc_get_param (const char *name, gcry_mpi_t *pkey) elliptic_curve_t E; mpi_ec_t ctx; gcry_mpi_t g_x, g_y; - - err = generate_curve (0, name, &E, &nbits); + + err = fill_in_curve (0, name, &E, &nbits); if (err) return err; @@ -1053,10 +1105,120 @@ ecc_get_param (const char *name, gcry_mpi_t *pkey) pkey[4] = E.n; pkey[5] = NULL; + mpi_free (g_x); + mpi_free (g_y); + return 0; } +/* Return the parameters of the curve NAME as an S-expression. */ +static gcry_sexp_t +ecc_get_param_sexp (const char *name) +{ + gcry_mpi_t pkey[6]; + gcry_sexp_t result; + int i; + + if (ecc_get_param (name, pkey)) + return NULL; + + if (gcry_sexp_build (&result, NULL, + "(public-key(ecc(p%m)(a%m)(b%m)(g%m)(n%m)))", + pkey[0], pkey[1], pkey[2], pkey[3], pkey[4])) + result = NULL; + + for (i=0; pkey[i]; i++) + gcry_mpi_release (pkey[i]); + + return result; +} + + +/* Return the name matching the parameters in PKEY. */ +static const char * +ecc_get_curve (gcry_mpi_t *pkey, int iterator, unsigned int *r_nbits) +{ + gpg_err_code_t err; + elliptic_curve_t E; + int idx; + gcry_mpi_t tmp; + const char *result = NULL; + + if (r_nbits) + *r_nbits = 0; + + if (!pkey) + { + idx = iterator; + if (idx >= 0 && idx < DIM (domain_parms)) + { + result = domain_parms[idx].desc; + if (r_nbits) + *r_nbits = domain_parms[idx].nbits; + } + return result; + } + + if (!pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4]) + return NULL; + + E.p = pkey[0]; + E.a = pkey[1]; + E.b = pkey[2]; + point_init (&E.G); + err = os2ec (&E.G, pkey[3]); + if (err) + { + point_free (&E.G); + return NULL; + } + E.n = pkey[4]; + + for (idx = 0; domain_parms[idx].desc; idx++) + { + tmp = scanval (domain_parms[idx].p); + if (!mpi_cmp (tmp, E.p)) + { + mpi_free (tmp); + tmp = scanval (domain_parms[idx].a); + if (!mpi_cmp (tmp, E.a)) + { + mpi_free (tmp); + tmp = scanval (domain_parms[idx].b); + if (!mpi_cmp (tmp, E.b)) + { + mpi_free (tmp); + tmp = scanval (domain_parms[idx].n); + if (!mpi_cmp (tmp, E.n)) + { + mpi_free (tmp); + tmp = scanval (domain_parms[idx].g_x); + if (!mpi_cmp (tmp, E.G.x)) + { + mpi_free (tmp); + tmp = scanval (domain_parms[idx].g_y); + if (!mpi_cmp (tmp, E.G.y)) + { + result = domain_parms[idx].desc; + if (r_nbits) + *r_nbits = domain_parms[idx].nbits; + break; + } + } + } + } + } + } + mpi_free (tmp); + } + + point_free (&E.G); + + return result; +} + + static gcry_err_code_t ecc_check_secret_key (int algo, gcry_mpi_t *skey) { @@ -1065,8 +1227,9 @@ ecc_check_secret_key (int algo, gcry_mpi_t *skey) (void)algo; + /* FIXME: This check looks a bit fishy: Now long is the array? */ if (!skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] || !skey[5] - || !skey[6] || !skey[7] || !skey[8] || !skey[9] || !skey[10]) + || !skey[6]) return GPG_ERR_BAD_MPI; sk.E.p = skey[0]; @@ -1150,6 +1313,7 @@ ecc_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey) return err; } + static gcry_err_code_t ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, int (*cmp)(void *, gcry_mpi_t), void *opaquev) @@ -1193,6 +1357,221 @@ ecc_verify (int algo, gcry_mpi_t hash, gcry_mpi_t *data, gcry_mpi_t *pkey, } +/* ecdh raw is classic 2-round DH protocol published in 1976. + * + * Overview of ecc_encrypt_raw and ecc_decrypt_raw. + * + * As with any PK operation, encrypt version uses a public key and + * decrypt -- private. + * + * Symbols used below: + * G - field generator point + * d - private long-term scalar + * dG - public long-term key + * k - ephemeral scalar + * kG - ephemeral public key + * dkG - shared secret + * + * ecc_encrypt_raw description: + * input: + * data[0] : private scalar (k) + * output: + * result[0] : shared point (kdG) + * result[1] : generated ephemeral public key (kG) + * + * ecc_decrypt_raw description: + * input: + * data[0] : a point kG (ephemeral public key) + * output: + * result[0] : shared point (kdG) + */ +static gcry_err_code_t +ecc_encrypt_raw (int algo, gcry_mpi_t *resarr, gcry_mpi_t k, + gcry_mpi_t *pkey, int flags) +{ + ECC_public_key pk; + mpi_ec_t ctx; + gcry_mpi_t result[2]; + int err; + + (void)algo; + (void)flags; + + if (!k + || !pkey[0] || !pkey[1] || !pkey[2] || !pkey[3] || !pkey[4] || !pkey[5]) + return GPG_ERR_BAD_MPI; + + pk.E.p = pkey[0]; + pk.E.a = pkey[1]; + pk.E.b = pkey[2]; + point_init (&pk.E.G); + err = os2ec (&pk.E.G, pkey[3]); + if (err) + { + point_free (&pk.E.G); + return err; + } + pk.E.n = pkey[4]; + point_init (&pk.Q); + err = os2ec (&pk.Q, pkey[5]); + if (err) + { + point_free (&pk.E.G); + point_free (&pk.Q); + return err; + } + + ctx = _gcry_mpi_ec_init (pk.E.p, pk.E.a); + + /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so */ + { + mpi_point_t R; /* Result that we return. */ + gcry_mpi_t x, y; + + x = mpi_new (0); + y = mpi_new (0); + + point_init (&R); + + /* R = kQ <=> R = kdG */ + _gcry_mpi_ec_mul_point (&R, k, &pk.Q, ctx); + + if (_gcry_mpi_ec_get_affine (x, y, &R, ctx)) + log_fatal ("ecdh: Failed to get affine coordinates for kdG\n"); + + result[0] = ec2os (x, y, pk.E.p); + + /* R = kG */ + _gcry_mpi_ec_mul_point (&R, k, &pk.E.G, ctx); + + if (_gcry_mpi_ec_get_affine (x, y, &R, ctx)) + log_fatal ("ecdh: Failed to get affine coordinates for kG\n"); + + result[1] = ec2os (x, y, pk.E.p); + + mpi_free (x); + mpi_free (y); + + point_free (&R); + } + + _gcry_mpi_ec_free (ctx); + point_free (&pk.E.G); + point_free (&pk.Q); + + if (!result[0] || !result[1]) + { + mpi_free (result[0]); + mpi_free (result[1]); + return GPG_ERR_ENOMEM; + } + + /* Success. */ + resarr[0] = result[0]; + resarr[1] = result[1]; + + return 0; +} + +/* input: + * data[0] : a point kG (ephemeral public key) + * output: + * resaddr[0] : shared point kdG + * + * see ecc_encrypt_raw for details. + */ +static gcry_err_code_t +ecc_decrypt_raw (int algo, gcry_mpi_t *result, gcry_mpi_t *data, + gcry_mpi_t *skey, int flags) +{ + ECC_secret_key sk; + mpi_point_t R; /* Result that we return. */ + mpi_point_t kG; + mpi_ec_t ctx; + gcry_mpi_t r; + int err; + + (void)algo; + (void)flags; + + *result = NULL; + + if (!data || !data[0] + || !skey[0] || !skey[1] || !skey[2] || !skey[3] || !skey[4] + || !skey[5] || !skey[6] ) + return GPG_ERR_BAD_MPI; + + point_init (&kG); + err = os2ec (&kG, data[0]); + if (err) + { + point_free (&kG); + return err; + } + + + sk.E.p = skey[0]; + sk.E.a = skey[1]; + sk.E.b = skey[2]; + point_init (&sk.E.G); + err = os2ec (&sk.E.G, skey[3]); + if (err) + { + point_free (&kG); + point_free (&sk.E.G); + return err; + } + sk.E.n = skey[4]; + point_init (&sk.Q); + err = os2ec (&sk.Q, skey[5]); + if (err) + { + point_free (&kG); + point_free (&sk.E.G); + point_free (&sk.Q); + return err; + } + sk.d = skey[6]; + + ctx = _gcry_mpi_ec_init (sk.E.p, sk.E.a); + + /* R = dkG */ + point_init (&R); + _gcry_mpi_ec_mul_point (&R, sk.d, &kG, ctx); + + point_free (&kG); + + /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so: */ + { + gcry_mpi_t x, y; + + x = mpi_new (0); + y = mpi_new (0); + + if (_gcry_mpi_ec_get_affine (x, y, &R, ctx)) + log_fatal ("ecdh: Failed to get affine coordinates\n"); + + r = ec2os (x, y, sk.E.p); + mpi_free (x); + mpi_free (y); + } + + point_free (&R); + _gcry_mpi_ec_free (ctx); + point_free (&kG); + point_free (&sk.E.G); + point_free (&sk.Q); + + if (!r) + return GPG_ERR_ENOMEM; + + /* Success. */ + + *result = r; + + return 0; +} + static unsigned int ecc_get_nbits (int algo, gcry_mpi_t *pkey) @@ -1203,23 +1582,23 @@ ecc_get_nbits (int algo, gcry_mpi_t *pkey) } - /* See rsa.c for a description of this function. */ static gpg_err_code_t compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) { - static const char names[] = "pabgnq"; +#define N_COMPONENTS 6 + static const char names[N_COMPONENTS+1] = "pabgnq"; gpg_err_code_t ec = 0; gcry_sexp_t l1; - gcry_mpi_t values[6]; + gcry_mpi_t values[N_COMPONENTS]; int idx; /* Clear the values for easier error cleanup. */ - for (idx=0; idx < 6; idx++) + for (idx=0; idx < N_COMPONENTS; idx++) values[idx] = NULL; - - /* Fill values with all available parameters. */ - for (idx=0; idx < 6; idx++) + + /* Fill values with all provided parameters. */ + for (idx=0; idx < N_COMPONENTS; idx++) { l1 = gcry_sexp_find_token (keyparam, names+idx, 1); if (l1) @@ -1233,19 +1612,20 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } } } - + /* Check whether a curve parameter is available and use that to fill in missing values. */ l1 = gcry_sexp_find_token (keyparam, "curve", 5); if (l1) { char *curve; - gcry_mpi_t tmpvalues[6]; - - for (idx = 0; idx < 6; idx++) + gcry_mpi_t tmpvalues[N_COMPONENTS]; + + for (idx = 0; idx < N_COMPONENTS; idx++) tmpvalues[idx] = NULL; curve = _gcry_sexp_nth_string (l1, 1); + gcry_sexp_release (l1); if (!curve) { ec = GPG_ERR_INV_OBJ; /* Name missing or out of core. */ @@ -1256,7 +1636,7 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) if (ec) goto leave; - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < N_COMPONENTS; idx++) { if (!values[idx]) values[idx] = tmpvalues[idx]; @@ -1266,9 +1646,9 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } /* Check that all parameters are known and normalize all MPIs (that - should not be required but we use an internal fucntion later and + should not be required but we use an internal function later and thus we better make 100% sure that they are normalized). */ - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < N_COMPONENTS; idx++) if (!values[idx]) { ec = GPG_ERR_NO_OBJ; @@ -1276,14 +1656,14 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } else _gcry_mpi_normalize (values[idx]); - + /* Hash them all. */ - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < N_COMPONENTS; idx++) { char buf[30]; unsigned char *rawmpi; unsigned int rawmpilen; - + rawmpi = _gcry_mpi_get_buffer (values[idx], &rawmpilen, NULL); if (!rawmpi) { @@ -1298,17 +1678,18 @@ compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam) } leave: - for (idx = 0; idx < 6; idx++) + for (idx = 0; idx < N_COMPONENTS; idx++) _gcry_mpi_release (values[idx]); - + return ec; +#undef N_COMPONENTS } -/* +/* Self-test section. */ @@ -1318,7 +1699,7 @@ selftests_ecdsa (selftest_report_func_t report) { const char *what; const char *errtxt; - + what = "low-level"; errtxt = NULL; /*selftest ();*/ if (errtxt) @@ -1351,7 +1732,7 @@ run_selftests (int algo, int extended, selftest_report_func_t report) default: ec = GPG_ERR_PUBKEY_ALGO; break; - + } return ec; } @@ -1365,6 +1746,12 @@ static const char *ecdsa_names[] = "ecc", NULL, }; +static const char *ecdh_names[] = + { + "ecdh", + "ecc", + NULL, + }; gcry_pk_spec_t _gcry_pubkey_spec_ecdsa = { @@ -1380,11 +1767,27 @@ gcry_pk_spec_t _gcry_pubkey_spec_ecdsa = ecc_get_nbits }; -pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa = +gcry_pk_spec_t _gcry_pubkey_spec_ecdh = + { + "ECDH", ecdh_names, + "pabgnq", "pabgnqd", "se", "", "pabgnq", + GCRY_PK_USAGE_ENCR, + ecc_generate, + ecc_check_secret_key, + ecc_encrypt_raw, + ecc_decrypt_raw, + NULL, + NULL, + ecc_get_nbits + }; + + +pk_extra_spec_t _gcry_pubkey_extraspec_ecdsa = { run_selftests, ecc_generate_ext, compute_keygrip, - ecc_get_param + ecc_get_param, + ecc_get_curve, + ecc_get_param_sexp }; - diff --git a/lib/libgcrypt/cipher/elgamal.c b/grub-core/lib/libgcrypt/cipher/elgamal.c similarity index 98% rename from lib/libgcrypt/cipher/elgamal.c rename to grub-core/lib/libgcrypt/cipher/elgamal.c index 0b0c07cb4..ce4be8524 100644 --- a/lib/libgcrypt/cipher/elgamal.c +++ b/grub-core/lib/libgcrypt/cipher/elgamal.c @@ -115,7 +115,7 @@ wiener_map( unsigned int n ) }; int i; - for(i=0; t[i].p_n; i++ ) + for(i=0; t[i].p_n; i++ ) { if( n <= t[i].p_n ) return t[i].q_n; @@ -158,7 +158,7 @@ test_keys ( ELG_secret_key *sk, unsigned int nbits, int nodie ) log_fatal ("Elgamal test key for %s %s failed\n", (failed & 1)? "encrypt+decrypt":"", (failed & 2)? "sign+verify":""); - if (failed && DBG_CIPHER) + if (failed && DBG_CIPHER) log_debug ("Elgamal test key for %s %s failed\n", (failed & 1)? "encrypt+decrypt":"", (failed & 2)? "sign+verify":""); @@ -199,15 +199,15 @@ gen_k( gcry_mpi_t p, int small_k ) if( DBG_CIPHER ) log_debug("choosing a random k "); mpi_sub_ui( p_1, p, 1); - for(;;) + for(;;) { - if( !rndbuf || nbits < 32 ) + if( !rndbuf || nbits < 32 ) { gcry_free(rndbuf); rndbuf = gcry_random_bytes_secure( nbytes, GCRY_STRONG_RANDOM ); } else - { + { /* Change only some of the higher bits. We could improve this by directly requesting more memory at the first call to get_random_bytes() and use this the here maybe it is @@ -218,7 +218,7 @@ gen_k( gcry_mpi_t p, int small_k ) gcry_free(pp); } _gcry_mpi_set_buffer( k, rndbuf, nbytes, 0 ); - + for(;;) { if( !(mpi_cmp( k, p_1 ) < 0) ) /* check: k < (p-1) */ @@ -294,7 +294,7 @@ generate ( ELG_secret_key *sk, unsigned int nbits, gcry_mpi_t **ret_factors ) if( DBG_CIPHER ) log_debug("choosing a random x of size %u", xbits ); rndbuf = NULL; - do + do { if( DBG_CIPHER ) progress('.'); @@ -314,21 +314,21 @@ generate ( ELG_secret_key *sk, unsigned int nbits, gcry_mpi_t **ret_factors ) gcry_free(r); } } - else + else { rndbuf = gcry_random_bytes_secure( (xbits+7)/8, GCRY_VERY_STRONG_RANDOM ); } _gcry_mpi_set_buffer( x, rndbuf, (xbits+7)/8, 0 ); mpi_clear_highbit( x, xbits+1 ); - } + } while( !( mpi_cmp_ui( x, 0 )>0 && mpi_cmp( x, p_min1 )<0 ) ); gcry_free(rndbuf); y = gcry_mpi_new (nbits); gcry_mpi_powm( y, g, x, p ); - if( DBG_CIPHER ) + if( DBG_CIPHER ) { progress('\n'); log_mpidump("elg p= ", p ); @@ -354,7 +354,7 @@ generate ( ELG_secret_key *sk, unsigned int nbits, gcry_mpi_t **ret_factors ) value for the secret key but the one given as X. This is useful to implement a passphrase based decryption for a public key based encryption. It has appliactions in backup systems. - + Returns: A structure filled with all needed values and an array with n-1 factors of (p-1). */ static gcry_err_code_t @@ -399,7 +399,7 @@ generate_using_x (ELG_secret_key *sk, unsigned int nbits, gcry_mpi_t x, y = gcry_mpi_new (nbits); gcry_mpi_powm ( y, g, x, p ); - if ( DBG_CIPHER ) + if ( DBG_CIPHER ) { progress ('\n'); log_mpidump ("elg p= ", p ); @@ -493,7 +493,7 @@ decrypt(gcry_mpi_t output, gcry_mpi_t a, gcry_mpi_t b, ELG_secret_key *skey ) mpi_invm( t1, t1, skey->p ); mpi_mulm( output, b, t1, skey->p ); #if 0 - if( DBG_CIPHER ) + if( DBG_CIPHER ) { log_mpidump("elg decrypted x= ", skey->x); log_mpidump("elg decrypted p= ", skey->p); @@ -533,7 +533,7 @@ sign(gcry_mpi_t a, gcry_mpi_t b, gcry_mpi_t input, ELG_secret_key *skey ) mpi_mulm(b, t, inv, p_1 ); #if 0 - if( DBG_CIPHER ) + if( DBG_CIPHER ) { log_mpidump("elg sign p= ", skey->p); log_mpidump("elg sign g= ", skey->g); @@ -652,7 +652,7 @@ elg_generate_ext (int algo, unsigned int nbits, unsigned long evalue, skey[1] = sk.g; skey[2] = sk.y; skey[3] = sk.x; - + return ec; } @@ -671,7 +671,7 @@ elg_generate (int algo, unsigned int nbits, unsigned long evalue, skey[1] = sk.g; skey[2] = sk.y; skey[3] = sk.x; - + return GPG_ERR_NO_ERROR; } @@ -692,7 +692,7 @@ elg_check_secret_key (int algo, gcry_mpi_t *skey) sk.g = skey[1]; sk.y = skey[2]; sk.x = skey[3]; - + if (! check_secret_key (&sk)) err = GPG_ERR_BAD_SECKEY; } @@ -773,7 +773,7 @@ elg_sign (int algo, gcry_mpi_t *resarr, gcry_mpi_t data, gcry_mpi_t *skey) resarr[1] = mpi_alloc (mpi_get_nlimbs (sk.p)); sign (resarr[0], resarr[1], data, &sk); } - + return err; } @@ -837,10 +837,9 @@ gcry_pk_spec_t _gcry_pubkey_spec_elg = elg_get_nbits }; -pk_extra_spec_t _gcry_pubkey_extraspec_elg = +pk_extra_spec_t _gcry_pubkey_extraspec_elg = { NULL, elg_generate_ext, NULL }; - diff --git a/lib/libgcrypt/cipher/hash-common.c b/grub-core/lib/libgcrypt/cipher/hash-common.c similarity index 97% rename from lib/libgcrypt/cipher/hash-common.c rename to grub-core/lib/libgcrypt/cipher/hash-common.c index 656e180e2..8c413bcba 100644 --- a/lib/libgcrypt/cipher/hash-common.c +++ b/grub-core/lib/libgcrypt/cipher/hash-common.c @@ -21,7 +21,7 @@ #include #include #include -#ifdef HAVE_STDINT_H +#ifdef HAVE_STDINT_H # include #endif @@ -35,10 +35,10 @@ describing the error. DATAMODE controls what will be hashed according to this table: - + 0 - Hash the supplied DATA of DATALEN. 1 - Hash one million times a 'a'. DATA and DATALEN are ignored. - + */ const char * _gcry_hash_selftest_check_one (int algo, @@ -49,14 +49,14 @@ _gcry_hash_selftest_check_one (int algo, gcry_error_t err = 0; gcry_md_hd_t hd; unsigned char *digest; - + if (_gcry_md_get_algo_dlen (algo) != expectlen) return "digest size does not match expected size"; - + err = _gcry_md_open (&hd, algo, 0); if (err) return "gcry_md_open failed"; - + switch (datamode) { case 0: @@ -64,7 +64,7 @@ _gcry_hash_selftest_check_one (int algo, break; case 1: /* Hash one million times an "a". */ - { + { char aaa[1000]; int i; @@ -82,7 +82,7 @@ _gcry_hash_selftest_check_one (int algo, if (!result) { digest = _gcry_md_read (hd, algo); - + if ( memcmp (digest, expect, expectlen) ) result = "digest mismatch"; } @@ -91,4 +91,3 @@ _gcry_hash_selftest_check_one (int algo, return result; } - diff --git a/lib/libgcrypt/cipher/hash-common.h b/grub-core/lib/libgcrypt/cipher/hash-common.h similarity index 93% rename from lib/libgcrypt/cipher/hash-common.h rename to grub-core/lib/libgcrypt/cipher/hash-common.h index 9c4e33359..fdebef42a 100644 --- a/lib/libgcrypt/cipher/hash-common.h +++ b/grub-core/lib/libgcrypt/cipher/hash-common.h @@ -21,11 +21,11 @@ #define GCRY_HASH_COMMON_H -const char * _gcry_hash_selftest_check_one -/**/ (int algo, +const char * _gcry_hash_selftest_check_one +/**/ (int algo, int datamode, const void *data, size_t datalen, const void *expect, size_t expectlen); - + diff --git a/lib/libgcrypt/cipher/hmac-tests.c b/grub-core/lib/libgcrypt/cipher/hmac-tests.c similarity index 98% rename from lib/libgcrypt/cipher/hmac-tests.c rename to grub-core/lib/libgcrypt/cipher/hmac-tests.c index 56c9b203c..a32ece75d 100644 --- a/lib/libgcrypt/cipher/hmac-tests.c +++ b/grub-core/lib/libgcrypt/cipher/hmac-tests.c @@ -17,7 +17,7 @@ * License along with this program; if not, see . */ -/* +/* Although algorithm self-tests are usually implemented in the module implementing the algorithm, the case for HMAC is different because HMAC is implemnetd on a higher level using a special feature of the @@ -33,7 +33,7 @@ #include #include #include -#ifdef HAVE_STDINT_H +#ifdef HAVE_STDINT_H # include #endif @@ -47,7 +47,7 @@ succdess or a string describing the failure. */ static const char * check_one (int algo, - const void *data, size_t datalen, + const void *data, size_t datalen, const void *key, size_t keylen, const void *expect, size_t expectlen) { @@ -88,7 +88,7 @@ check_one (int algo, return "does not match"; } _gcry_md_close (hd); - return NULL; + return NULL; } @@ -123,7 +123,7 @@ selftests_sha1 (int extended, selftest_report_func_t report) "\xa4\x58\x30\x73\x7d\x5c\xc6\xc7\x5d\x24", 20); if (errtxt) goto failed; - + what = "FIPS-198a, A.3"; for (i=0, j=0x50; i < 100; i++) key[i] = j++; @@ -134,7 +134,7 @@ selftests_sha1 (int extended, selftest_report_func_t report) "\x5c\xaf\x7c\xb0\x92\xec\xf8\xd1\xa3\xaa", 20 ); if (errtxt) goto failed; - + what = "FIPS-198a, A.4"; for (i=0, j=0x70; i < 49; i++) key[i] = j++; @@ -160,7 +160,7 @@ selftests_sha1 (int extended, selftest_report_func_t report) static gpg_err_code_t selftests_sha224 (int extended, selftest_report_func_t report) { - static struct + static struct { const char * const desc; const char * const data; @@ -169,7 +169,7 @@ selftests_sha224 (int extended, selftest_report_func_t report) } tv[] = { { "data-28 key-4", - "what do ya want for nothing?", + "what do ya want for nothing?", "Jefe", { 0xa3, 0x0e, 0x01, 0x09, 0x8b, 0xc6, 0xdb, 0xbf, 0x45, 0x69, 0x0f, 0x3a, 0x7e, 0x9e, 0x6d, 0x0f, @@ -248,7 +248,7 @@ selftests_sha224 (int extended, selftest_report_func_t report) const char *what; const char *errtxt; int tvidx; - + for (tvidx=0; tv[tvidx].desc; tvidx++) { what = tv[tvidx].desc; @@ -274,7 +274,7 @@ selftests_sha224 (int extended, selftest_report_func_t report) static gpg_err_code_t selftests_sha256 (int extended, selftest_report_func_t report) { - static struct + static struct { const char * const desc; const char * const data; @@ -283,7 +283,7 @@ selftests_sha256 (int extended, selftest_report_func_t report) } tv[] = { { "data-28 key-4", - "what do ya want for nothing?", + "what do ya want for nothing?", "Jefe", { 0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, @@ -362,7 +362,7 @@ selftests_sha256 (int extended, selftest_report_func_t report) const char *what; const char *errtxt; int tvidx; - + for (tvidx=0; tv[tvidx].desc; tvidx++) { hmac256_context_t hmachd; @@ -416,7 +416,7 @@ selftests_sha256 (int extended, selftest_report_func_t report) static gpg_err_code_t selftests_sha384 (int extended, selftest_report_func_t report) { - static struct + static struct { const char * const desc; const char * const data; @@ -425,7 +425,7 @@ selftests_sha384 (int extended, selftest_report_func_t report) } tv[] = { { "data-28 key-4", - "what do ya want for nothing?", + "what do ya want for nothing?", "Jefe", { 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, @@ -516,7 +516,7 @@ selftests_sha384 (int extended, selftest_report_func_t report) const char *what; const char *errtxt; int tvidx; - + for (tvidx=0; tv[tvidx].desc; tvidx++) { what = tv[tvidx].desc; @@ -542,7 +542,7 @@ selftests_sha384 (int extended, selftest_report_func_t report) static gpg_err_code_t selftests_sha512 (int extended, selftest_report_func_t report) { - static struct + static struct { const char * const desc; const char * const data; @@ -551,7 +551,7 @@ selftests_sha512 (int extended, selftest_report_func_t report) } tv[] = { { "data-28 key-4", - "what do ya want for nothing?", + "what do ya want for nothing?", "Jefe", { 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, @@ -654,7 +654,7 @@ selftests_sha512 (int extended, selftest_report_func_t report) const char *what; const char *errtxt; int tvidx; - + for (tvidx=0; tv[tvidx].desc; tvidx++) { what = tv[tvidx].desc; diff --git a/grub-core/lib/libgcrypt/cipher/idea.c b/grub-core/lib/libgcrypt/cipher/idea.c new file mode 100644 index 000000000..3c5578f95 --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/idea.c @@ -0,0 +1,378 @@ +/* idea.c - IDEA function + * Copyright 1997, 1998, 1999, 2001 Werner Koch (dd9jn) + * Copyright 2013 g10 Code GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * WERNER KOCH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Werner Koch shall not be + * used in advertising or otherwise to promote the sale, use or other dealings + * in this Software without prior written authorization from Werner Koch. + * + * Patents on IDEA have expired: + * Europe: EP0482154 on 2011-05-16, + * Japan: JP3225440 on 2011-05-16, + * U.S.: 5,214,703 on 2012-01-07. + */ + +/* + * Please see http://www.noepatents.org/ to learn why software patents + * are bad for society and what you can do to fight them. + * + * The code herein is based on the one from: + * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996. + * ISBN 0-471-11709-9. + */ + + +#include +#include +#include +#include +#include + +#include "types.h" /* for byte and u32 typedefs */ +#include "g10lib.h" +#include "cipher.h" + + +#define IDEA_KEYSIZE 16 +#define IDEA_BLOCKSIZE 8 +#define IDEA_ROUNDS 8 +#define IDEA_KEYLEN (6*IDEA_ROUNDS+4) + +typedef struct { + u16 ek[IDEA_KEYLEN]; + u16 dk[IDEA_KEYLEN]; + int have_dk; +} IDEA_context; + +static const char *selftest(void); + + +static u16 +mul_inv( u16 x ) +{ + u16 t0, t1; + u16 q, y; + + if( x < 2 ) + return x; + t1 = 0x10001UL / x; + y = 0x10001UL % x; + if( y == 1 ) + return (1-t1) & 0xffff; + + t0 = 1; + do { + q = x / y; + x = x % y; + t0 += q * t1; + if( x == 1 ) + return t0; + q = y / x; + y = y % x; + t1 += q * t0; + } while( y != 1 ); + return (1-t1) & 0xffff; +} + + + +static void +expand_key( const byte *userkey, u16 *ek ) +{ + int i,j; + + for(j=0; j < 8; j++ ) { + ek[j] = (*userkey << 8) + userkey[1]; + userkey += 2; + } + for(i=0; j < IDEA_KEYLEN; j++ ) { + i++; + ek[i+7] = ek[i&7] << 9 | ek[(i+1)&7] >> 7; + ek += i & 8; + i &= 7; + } +} + + +static void +invert_key( u16 *ek, u16 dk[IDEA_KEYLEN] ) +{ + int i; + u16 t1, t2, t3; + u16 temp[IDEA_KEYLEN]; + u16 *p = temp + IDEA_KEYLEN; + + t1 = mul_inv( *ek++ ); + t2 = -*ek++; + t3 = -*ek++; + *--p = mul_inv( *ek++ ); + *--p = t3; + *--p = t2; + *--p = t1; + + for(i=0; i < IDEA_ROUNDS-1; i++ ) { + t1 = *ek++; + *--p = *ek++; + *--p = t1; + + t1 = mul_inv( *ek++ ); + t2 = -*ek++; + t3 = -*ek++; + *--p = mul_inv( *ek++ ); + *--p = t2; + *--p = t3; + *--p = t1; + } + t1 = *ek++; + *--p = *ek++; + *--p = t1; + + t1 = mul_inv( *ek++ ); + t2 = -*ek++; + t3 = -*ek++; + *--p = mul_inv( *ek++ ); + *--p = t3; + *--p = t2; + *--p = t1; + memcpy(dk, temp, sizeof(temp) ); + memset(temp, 0, sizeof(temp) ); /* burn temp */ +} + + +static void +cipher( byte *outbuf, const byte *inbuf, u16 *key ) +{ + u16 s2, s3; + u16 in[4]; + int r = IDEA_ROUNDS; +#define x1 (in[0]) +#define x2 (in[1]) +#define x3 (in[2]) +#define x4 (in[3]) +#define MUL(x,y) \ + do {u16 _t16; u32 _t32; \ + if( (_t16 = (y)) ) { \ + if( (x = (x)&0xffff) ) { \ + _t32 = (u32)x * _t16; \ + x = _t32 & 0xffff; \ + _t16 = _t32 >> 16; \ + x = ((x)-_t16) + (x<_t16?1:0); \ + } \ + else { \ + x = 1 - _t16; \ + } \ + } \ + else { \ + x = 1 - x; \ + } \ + } while(0) + + memcpy (in, inbuf, sizeof in); +#ifndef WORDS_BIGENDIAN + x1 = (x1>>8) | (x1<<8); + x2 = (x2>>8) | (x2<<8); + x3 = (x3>>8) | (x3<<8); + x4 = (x4>>8) | (x4<<8); +#endif + do { + MUL(x1, *key++); + x2 += *key++; + x3 += *key++; + MUL(x4, *key++ ); + + s3 = x3; + x3 ^= x1; + MUL(x3, *key++); + s2 = x2; + x2 ^=x4; + x2 += x3; + MUL(x2, *key++); + x3 += x2; + + x1 ^= x2; + x4 ^= x3; + + x2 ^= s3; + x3 ^= s2; + } while( --r ); + MUL(x1, *key++); + x3 += *key++; + x2 += *key++; + MUL(x4, *key); + +#ifndef WORDS_BIGENDIAN + x1 = (x1>>8) | (x1<<8); + x2 = (x2>>8) | (x2<<8); + x3 = (x3>>8) | (x3<<8); + x4 = (x4>>8) | (x4<<8); +#endif + memcpy (outbuf+0, &x1, 2); + memcpy (outbuf+2, &x3, 2); + memcpy (outbuf+4, &x2, 2); + memcpy (outbuf+6, &x4, 2); +#undef MUL +#undef x1 +#undef x2 +#undef x3 +#undef x4 +} + + +static int +do_setkey( IDEA_context *c, const byte *key, unsigned int keylen ) +{ + static int initialized = 0; + static const char *selftest_failed = 0; + + if( !initialized ) { + initialized = 1; + selftest_failed = selftest(); + if( selftest_failed ) + log_error( "%s\n", selftest_failed ); + } + if( selftest_failed ) + return GPG_ERR_SELFTEST_FAILED; + + assert(keylen == 16); + c->have_dk = 0; + expand_key( key, c->ek ); + invert_key( c->ek, c->dk ); + return 0; +} + +static gcry_err_code_t +idea_setkey (void *context, const byte *key, unsigned int keylen) +{ + IDEA_context *ctx = context; + int rc = do_setkey (ctx, key, keylen); + _gcry_burn_stack (23+6*sizeof(void*)); + return rc; +} + +static void +encrypt_block( IDEA_context *c, byte *outbuf, const byte *inbuf ) +{ + cipher( outbuf, inbuf, c->ek ); +} + +static void +idea_encrypt (void *context, byte *out, const byte *in) +{ + IDEA_context *ctx = context; + encrypt_block (ctx, out, in); + _gcry_burn_stack (24+3*sizeof (void*)); +} + +static void +decrypt_block( IDEA_context *c, byte *outbuf, const byte *inbuf ) +{ + if( !c->have_dk ) { + c->have_dk = 1; + invert_key( c->ek, c->dk ); + } + cipher( outbuf, inbuf, c->dk ); +} + +static void +idea_decrypt (void *context, byte *out, const byte *in) +{ + IDEA_context *ctx = context; + decrypt_block (ctx, out, in); + _gcry_burn_stack (24+3*sizeof (void*)); +} + + +static const char * +selftest( void ) +{ +static struct { + byte key[16]; + byte plain[8]; + byte cipher[8]; +} test_vectors[] = { + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03 }, + { 0x11, 0xFB, 0xED, 0x2B, 0x01, 0x98, 0x6D, 0xE5 } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }, + { 0x54, 0x0E, 0x5F, 0xEA, 0x18, 0xC2, 0xF8, 0xB1 } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0x00, 0x19, 0x32, 0x4B, 0x64, 0x7D, 0x96, 0xAF }, + { 0x9F, 0x0A, 0x0A, 0xB6, 0xE1, 0x0C, 0xED, 0x78 } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0xF5, 0x20, 0x2D, 0x5B, 0x9C, 0x67, 0x1B, 0x08 }, + { 0xCF, 0x18, 0xFD, 0x73, 0x55, 0xE2, 0xC5, 0xC5 } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0xFA, 0xE6, 0xD2, 0xBE, 0xAA, 0x96, 0x82, 0x6E }, + { 0x85, 0xDF, 0x52, 0x00, 0x56, 0x08, 0x19, 0x3D } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0x0A, 0x14, 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50 }, + { 0x2F, 0x7D, 0xE7, 0x50, 0x21, 0x2F, 0xB7, 0x34 } }, + { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 }, + { 0x05, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x23, 0x28 }, + { 0x7B, 0x73, 0x14, 0x92, 0x5D, 0xE5, 0x9C, 0x09 } }, + { { 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0F, 0x00, 0x14, + 0x00, 0x19, 0x00, 0x1E, 0x00, 0x23, 0x00, 0x28 }, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }, + { 0x3E, 0xC0, 0x47, 0x80, 0xBE, 0xFF, 0x6E, 0x20 } }, + { { 0x3A, 0x98, 0x4E, 0x20, 0x00, 0x19, 0x5D, 0xB3, + 0x2E, 0xE5, 0x01, 0xC8, 0xC4, 0x7C, 0xEA, 0x60 }, + { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }, + { 0x97, 0xBC, 0xD8, 0x20, 0x07, 0x80, 0xDA, 0x86 } }, + { { 0x00, 0x64, 0x00, 0xC8, 0x01, 0x2C, 0x01, 0x90, + 0x01, 0xF4, 0x02, 0x58, 0x02, 0xBC, 0x03, 0x20 }, + { 0x05, 0x32, 0x0A, 0x64, 0x14, 0xC8, 0x19, 0xFA }, + { 0x65, 0xBE, 0x87, 0xE7, 0xA2, 0x53, 0x8A, 0xED } }, + { { 0x9D, 0x40, 0x75, 0xC1, 0x03, 0xBC, 0x32, 0x2A, + 0xFB, 0x03, 0xE7, 0xBE, 0x6A, 0xB3, 0x00, 0x06 }, + { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, + { 0xF5, 0xDB, 0x1A, 0xC4, 0x5E, 0x5E, 0xF9, 0xF9 } } +}; + IDEA_context c; + byte buffer[8]; + int i; + + for(i=0; i < DIM(test_vectors); i++ ) { + do_setkey( &c, test_vectors[i].key, 16 ); + encrypt_block( &c, buffer, test_vectors[i].plain ); + if( memcmp( buffer, test_vectors[i].cipher, 8 ) ) + return "IDEA test encryption failed."; + decrypt_block( &c, buffer, test_vectors[i].cipher ); + if( memcmp( buffer, test_vectors[i].plain, 8 ) ) + return "IDEA test decryption failed."; + } + + return NULL; +} + + +gcry_cipher_spec_t _gcry_cipher_spec_idea = +{ + "IDEA", NULL, NULL, IDEA_BLOCKSIZE, 128, + sizeof (IDEA_context), + idea_setkey, idea_encrypt, idea_decrypt +}; diff --git a/grub-core/lib/libgcrypt/cipher/kdf.c b/grub-core/lib/libgcrypt/cipher/kdf.c new file mode 100644 index 000000000..46e8550df --- /dev/null +++ b/grub-core/lib/libgcrypt/cipher/kdf.c @@ -0,0 +1,278 @@ +/* kdf.c - Key Derivation Functions + * Copyright (C) 1998, 2011 Free Software Foundation, Inc. + * + * This file is part of Libgcrypt. + * + * Libgcrypt is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser general Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * Libgcrypt is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +#include "g10lib.h" +#include "cipher.h" +#include "ath.h" + + +/* Transform a passphrase into a suitable key of length KEYSIZE and + store this key in the caller provided buffer KEYBUFFER. The caller + must provide an HASHALGO, a valid ALGO and depending on that algo a + SALT of 8 bytes and the number of ITERATIONS. Code taken from + gnupg/agent/protect.c:hash_passphrase. */ +gpg_err_code_t +openpgp_s2k (const void *passphrase, size_t passphraselen, + int algo, int hashalgo, + const void *salt, size_t saltlen, + unsigned long iterations, + size_t keysize, void *keybuffer) +{ + gpg_err_code_t ec; + gcry_md_hd_t md; + char *key = keybuffer; + int pass, i; + int used = 0; + int secmode; + + if ((algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K) + && (!salt || saltlen != 8)) + return GPG_ERR_INV_VALUE; + + secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer); + + ec = gpg_err_code (gcry_md_open (&md, hashalgo, + secmode? GCRY_MD_FLAG_SECURE : 0)); + if (ec) + return ec; + + for (pass=0; used < keysize; pass++) + { + if (pass) + { + gcry_md_reset (md); + for (i=0; i < pass; i++) /* Preset the hash context. */ + gcry_md_putc (md, 0); + } + + if (algo == GCRY_KDF_SALTED_S2K || algo == GCRY_KDF_ITERSALTED_S2K) + { + int len2 = passphraselen + 8; + unsigned long count = len2; + + if (algo == GCRY_KDF_ITERSALTED_S2K) + { + count = iterations; + if (count < len2) + count = len2; + } + + while (count > len2) + { + gcry_md_write (md, salt, saltlen); + gcry_md_write (md, passphrase, passphraselen); + count -= len2; + } + if (count < saltlen) + gcry_md_write (md, salt, count); + else + { + gcry_md_write (md, salt, saltlen); + count -= saltlen; + gcry_md_write (md, passphrase, count); + } + } + else + gcry_md_write (md, passphrase, passphraselen); + + gcry_md_final (md); + i = gcry_md_get_algo_dlen (hashalgo); + if (i > keysize - used) + i = keysize - used; + memcpy (key+used, gcry_md_read (md, hashalgo), i); + used += i; + } + gcry_md_close (md); + return 0; +} + + +/* Transform a passphrase into a suitable key of length KEYSIZE and + store this key in the caller provided buffer KEYBUFFER. The caller + must provide PRFALGO which indicates the pseudorandom function to + use: This shall be the algorithms id of a hash algorithm; it is + used in HMAC mode. SALT is a salt of length SALTLEN and ITERATIONS + gives the number of iterations. */ +gpg_err_code_t +pkdf2 (const void *passphrase, size_t passphraselen, + int hashalgo, + const void *salt, size_t saltlen, + unsigned long iterations, + size_t keysize, void *keybuffer) +{ + gpg_err_code_t ec; + gcry_md_hd_t md; + int secmode; + unsigned int dklen = keysize; + char *dk = keybuffer; + unsigned int hlen; /* Output length of the digest function. */ + unsigned int l; /* Rounded up number of blocks. */ + unsigned int r; /* Number of octets in the last block. */ + char *sbuf; /* Malloced buffer to concatenate salt and iter + as well as space to hold TBUF and UBUF. */ + char *tbuf; /* Buffer for T; ptr into SBUF, size is HLEN. */ + char *ubuf; /* Buffer for U; ptr into SBUF, size is HLEN. */ + unsigned int lidx; /* Current block number. */ + unsigned long iter; /* Current iteration number. */ + unsigned int i; + + if (!salt || !saltlen || !iterations || !dklen) + return GPG_ERR_INV_VALUE; + + hlen = gcry_md_get_algo_dlen (hashalgo); + if (!hlen) + return GPG_ERR_DIGEST_ALGO; + + secmode = gcry_is_secure (passphrase) || gcry_is_secure (keybuffer); + + /* We ignore step 1 from pksc5v2.1 which demands a check that dklen + is not larger that 0xffffffff * hlen. */ + + /* Step 2 */ + l = ((dklen - 1)/ hlen) + 1; + r = dklen - (l - 1) * hlen; + + /* Setup buffers and prepare a hash context. */ + sbuf = (secmode + ? gcry_malloc_secure (saltlen + 4 + hlen + hlen) + : gcry_malloc (saltlen + 4 + hlen + hlen)); + if (!sbuf) + return gpg_err_code_from_syserror (); + tbuf = sbuf + saltlen + 4; + ubuf = tbuf + hlen; + + ec = gpg_err_code (gcry_md_open (&md, hashalgo, + (GCRY_MD_FLAG_HMAC + | (secmode?GCRY_MD_FLAG_SECURE:0)))); + if (ec) + { + gcry_free (sbuf); + return ec; + } + + /* Step 3 and 4. */ + memcpy (sbuf, salt, saltlen); + for (lidx = 1; lidx <= l; lidx++) + { + for (iter = 0; iter < iterations; iter++) + { + ec = gpg_err_code (gcry_md_setkey (md, passphrase, passphraselen)); + if (ec) + { + gcry_md_close (md); + gcry_free (sbuf); + return ec; + } + if (!iter) /* Compute U_1: */ + { + sbuf[saltlen] = (lidx >> 24); + sbuf[saltlen + 1] = (lidx >> 16); + sbuf[saltlen + 2] = (lidx >> 8); + sbuf[saltlen + 3] = lidx; + gcry_md_write (md, sbuf, saltlen + 4); + memcpy (ubuf, gcry_md_read (md, 0), hlen); + memcpy (tbuf, ubuf, hlen); + } + else /* Compute U_(2..c): */ + { + gcry_md_write (md, ubuf, hlen); + memcpy (ubuf, gcry_md_read (md, 0), hlen); + for (i=0; i < hlen; i++) + tbuf[i] ^= ubuf[i]; + } + } + if (lidx == l) /* Last block. */ + memcpy (dk, tbuf, r); + else + { + memcpy (dk, tbuf, hlen); + dk += hlen; + } + } + + gcry_md_close (md); + gcry_free (sbuf); + return 0; +} + + +/* Derive a key from a passphrase. KEYSIZE gives the requested size + of the keys in octets. KEYBUFFER is a caller provided buffer + filled on success with the derived key. The input passphrase is + taken from (PASSPHRASE,PASSPHRASELEN) which is an arbitrary memory + buffer. ALGO specifies the KDF algorithm to use; these are the + constants GCRY_KDF_*. SUBALGO specifies an algorithm used + internally by the KDF algorithms; this is usually a hash algorithm + but certain KDF algorithm may use it differently. {SALT,SALTLEN} + is a salt as needed by most KDF algorithms. ITERATIONS is a + positive integer parameter to most KDFs. 0 is returned on success, + or an error code on failure. */ +gpg_error_t +gcry_kdf_derive (const void *passphrase, size_t passphraselen, + int algo, int subalgo, + const void *salt, size_t saltlen, + unsigned long iterations, + size_t keysize, void *keybuffer) +{ + gpg_err_code_t ec; + + if (!passphrase || (!passphraselen && algo != GCRY_KDF_PBKDF2)) + { + ec = GPG_ERR_INV_DATA; + goto leave; + } + if (!keybuffer || !keysize) + { + ec = GPG_ERR_INV_VALUE; + goto leave; + } + + + switch (algo) + { + case GCRY_KDF_SIMPLE_S2K: + case GCRY_KDF_SALTED_S2K: + case GCRY_KDF_ITERSALTED_S2K: + ec = openpgp_s2k (passphrase, passphraselen, algo, subalgo, + salt, saltlen, iterations, keysize, keybuffer); + break; + + case GCRY_KDF_PBKDF1: + ec = GPG_ERR_UNSUPPORTED_ALGORITHM; + break; + + case GCRY_KDF_PBKDF2: + ec = pkdf2 (passphrase, passphraselen, subalgo, + salt, saltlen, iterations, keysize, keybuffer); + break; + + default: + ec = GPG_ERR_UNKNOWN_ALGORITHM; + break; + } + + leave: + return gpg_error (ec); +} diff --git a/lib/libgcrypt/cipher/md.c b/grub-core/lib/libgcrypt/cipher/md.c similarity index 96% rename from lib/libgcrypt/cipher/md.c rename to grub-core/lib/libgcrypt/cipher/md.c index 5dfbbd95a..c3b3a4f3a 100644 --- a/lib/libgcrypt/cipher/md.c +++ b/grub-core/lib/libgcrypt/cipher/md.c @@ -43,15 +43,15 @@ static struct digest_table_entry gcry_md_spec_t *digest; md_extra_spec_t *extraspec; unsigned int algorithm; - int fips_allowed; + int fips_allowed; } digest_table[] = { -#if USE_CRC +#if USE_CRC /* We allow the CRC algorithms even in FIPS mode because they are actually no cryptographic primitives. */ - { &_gcry_digest_spec_crc32, + { &_gcry_digest_spec_crc32, &dummy_extra_spec, GCRY_MD_CRC32, 1 }, - { &_gcry_digest_spec_crc32_rfc1510, + { &_gcry_digest_spec_crc32_rfc1510, &dummy_extra_spec, GCRY_MD_CRC32_RFC1510, 1 }, { &_gcry_digest_spec_crc24_rfc2440, &dummy_extra_spec, GCRY_MD_CRC24_RFC2440, 1 }, @@ -69,7 +69,7 @@ static struct digest_table_entry &dummy_extra_spec, GCRY_MD_RMD160 }, #endif #if USE_SHA1 - { &_gcry_digest_spec_sha1, + { &_gcry_digest_spec_sha1, &_gcry_digest_extraspec_sha1, GCRY_MD_SHA1, 1 }, #endif #if USE_SHA256 @@ -87,6 +87,10 @@ static struct digest_table_entry #if USE_TIGER { &_gcry_digest_spec_tiger, &dummy_extra_spec, GCRY_MD_TIGER }, + { &_gcry_digest_spec_tiger1, + &dummy_extra_spec, GCRY_MD_TIGER1 }, + { &_gcry_digest_spec_tiger2, + &dummy_extra_spec, GCRY_MD_TIGER2 }, #endif #if USE_WHIRLPOOL { &_gcry_digest_spec_whirlpool, @@ -101,7 +105,7 @@ static gcry_module_t digests_registered; /* This is the lock protecting DIGESTS_REGISTERED. */ static ath_mutex_t digests_registered_lock = ATH_MUTEX_INITIALIZER; -/* Flag to check wether the default ciphers have already been +/* Flag to check whether the default ciphers have already been registered. */ static int default_digests_registered; @@ -173,7 +177,7 @@ md_register_default (void) { gcry_err_code_t err = 0; int i; - + for (i = 0; !err && digest_table[i].digest; i++) { if ( fips_mode ()) @@ -226,7 +230,7 @@ gcry_md_lookup_func_oid (void *spec, void *data) } /* Internal function. Lookup a digest entry by it's name. */ -static gcry_module_t +static gcry_module_t gcry_md_lookup_name (const char *name) { gcry_module_t digest; @@ -267,11 +271,11 @@ _gcry_md_register (gcry_md_spec_t *digest, ath_mutex_lock (&digests_registered_lock); err = _gcry_module_add (&digests_registered, 0, - (void *) digest, - (void *)(extraspec? extraspec : &dummy_extra_spec), + (void *) digest, + (void *)(extraspec? extraspec : &dummy_extra_spec), &mod); ath_mutex_unlock (&digests_registered_lock); - + if (! err) { *module = mod; @@ -292,7 +296,7 @@ gcry_md_unregister (gcry_module_t module) } -static int +static int search_oid (const char *oid, int *algorithm, gcry_md_oid_spec_t *oid_spec) { gcry_module_t module; @@ -563,7 +567,7 @@ md_enable (gcry_md_hd_t hd, int algorithm) else digest = (gcry_md_spec_t *) module->spec; - + if (!err && algorithm == GCRY_MD_MD5 && fips_mode ()) { _gcry_inactivate_fips_mode ("MD5 used"); @@ -574,7 +578,7 @@ md_enable (gcry_md_hd_t hd, int algorithm) err = GPG_ERR_DIGEST_ALGO; } } - + if (!err) { size_t size = (sizeof (*entry) @@ -631,7 +635,7 @@ md_copy (gcry_md_hd_t ahd, gcry_md_hd_t *b_hd) GcryDigestEntry *ar, *br; gcry_md_hd_t bhd; size_t n; - + if (ahd->bufpos) md_write (ahd, NULL, 0); @@ -693,7 +697,7 @@ md_copy (gcry_md_hd_t ahd, gcry_md_hd_t *b_hd) - sizeof (ar->context))); br->next = b->list; b->list = br; - + /* Add a reference to the module. */ ath_mutex_lock (&digests_registered_lock); _gcry_module_use (br->module); @@ -783,7 +787,7 @@ static void md_write (gcry_md_hd_t a, const void *inbuf, size_t inlen) { GcryDigestEntry *r; - + if (a->ctx->debug) { if (a->bufpos && fwrite (a->buf, a->bufpos, 1, a->ctx->debug) != 1) @@ -834,8 +838,8 @@ md_final (gcry_md_hd_t a) if (err) _gcry_fatal_error (err, NULL); - md_write (om, - (a->ctx->macpads)+(a->ctx->macpads_Bsize), + md_write (om, + (a->ctx->macpads)+(a->ctx->macpads_Bsize), a->ctx->macpads_Bsize); md_write (om, p, dlen); md_final (om); @@ -856,7 +860,7 @@ prepare_macpads (gcry_md_hd_t hd, const unsigned char *key, size_t keylen) if (!algo) return GPG_ERR_DIGEST_ALGO; /* Might happen if no algo is enabled. */ - if ( keylen > hd->ctx->macpads_Bsize ) + if ( keylen > hd->ctx->macpads_Bsize ) { helpkey = gcry_malloc_secure (md_digest_length (algo)); if (!helpkey) @@ -872,7 +876,7 @@ prepare_macpads (gcry_md_hd_t hd, const unsigned char *key, size_t keylen) opad = (hd->ctx->macpads)+(hd->ctx->macpads_Bsize); memcpy ( ipad, key, keylen ); memcpy ( opad, key, keylen ); - for (i=0; i < hd->ctx->macpads_Bsize; i++ ) + for (i=0; i < hd->ctx->macpads_Bsize; i++ ) { ipad[i] ^= 0x36; opad[i] ^= 0x5c; @@ -886,7 +890,7 @@ gcry_error_t gcry_md_ctl (gcry_md_hd_t hd, int cmd, void *buffer, size_t buflen) { gcry_err_code_t rc = 0; - + switch (cmd) { case GCRYCTL_FINALIZE: @@ -948,10 +952,13 @@ md_read( gcry_md_hd_t a, int algo ) if (! algo) { - /* return the first algorithm */ - if (r && r->next) - log_debug ("more than one algorithm in md_read(0)\n"); - return r->digest->read( &r->context.c ); + /* Return the first algorithm */ + if (r) + { + if (r->next) + log_debug ("more than one algorithm in md_read(0)\n"); + return r->digest->read (&r->context.c); + } } else { @@ -1135,7 +1142,7 @@ md_asn_oid (int algorithm, size_t *asnlen, size_t *mdlen) * Note: Because this function is in most cases used to return an * integer value, we can make it easier for the caller to just look at * the return value. The caller will in all cases consult the value - * and thereby detecting whether a error occured or not (i.e. while checking + * and thereby detecting whether a error occurred or not (i.e. while checking * the block size) */ gcry_error_t @@ -1160,7 +1167,7 @@ gcry_md_algo_info (int algo, int what, void *buffer, size_t *nbytes) { const char unsigned *asn; size_t asnlen; - + asn = md_asn_oid (algo, &asnlen, NULL); if (buffer && (*nbytes >= asnlen)) { @@ -1195,7 +1202,7 @@ md_start_debug ( gcry_md_hd_t md, const char *suffix ) if (fips_mode ()) return; - + if ( md->ctx->debug ) { log_debug("Oops: md debug already started\n"); @@ -1225,6 +1232,7 @@ md_stop_debug( gcry_md_hd_t md ) volatile u64 b = 42; volatile u64 c; c = a * b; + (void)c; } #endif } @@ -1261,7 +1269,7 @@ gcry_md_info (gcry_md_hd_t h, int cmd, void *buffer, size_t *nbytes) else { algo = *(int*)buffer; - + *nbytes = 0; for(r=h->ctx->list; r; r = r->next ) { if (r->module->mod_id == algo) @@ -1295,7 +1303,7 @@ _gcry_md_init (void) int -gcry_md_is_secure (gcry_md_hd_t a) +gcry_md_is_secure (gcry_md_hd_t a) { size_t value; @@ -1307,7 +1315,7 @@ gcry_md_is_secure (gcry_md_hd_t a) int -gcry_md_is_enabled (gcry_md_hd_t a, int algo) +gcry_md_is_enabled (gcry_md_hd_t a, int algo) { size_t value; @@ -1359,7 +1367,7 @@ _gcry_md_selftest (int algo, int extended, selftest_report_func_t report) { ec = GPG_ERR_DIGEST_ALGO; if (report) - report ("digest", algo, "module", + report ("digest", algo, "module", module && !(module->flags & FLAG_MODULE_DISABLED)? "no selftest available" : module? "algorithm disabled" : "algorithm not found"); diff --git a/lib/libgcrypt/cipher/md4.c b/grub-core/lib/libgcrypt/cipher/md4.c similarity index 98% rename from lib/libgcrypt/cipher/md4.c rename to grub-core/lib/libgcrypt/cipher/md4.c index 680cf87f5..22fbf8d90 100644 --- a/lib/libgcrypt/cipher/md4.c +++ b/grub-core/lib/libgcrypt/cipher/md4.c @@ -53,7 +53,6 @@ #include #include "g10lib.h" -#include "memory.h" #include "cipher.h" #include "bithelp.h" @@ -101,7 +100,8 @@ transform ( MD4_CONTEXT *ctx, const unsigned char *data ) #ifdef WORDS_BIGENDIAN { int i; - byte *p2, *p1; + byte *p2; + const byte *p1; for(i=0, p1=data, p2=(byte*)in; i < 16; i++, p2 += 4 ) { p2[3] = *p1++; @@ -198,7 +198,7 @@ md4_write ( void *context, const void *inbuf_arg, size_t inlen) MD4_CONTEXT *hd = context; if( hd->count == 64 ) /* flush the buffer */ - { + { transform( hd, hd->buf ); _gcry_burn_stack (80+6*sizeof(void*)); hd->count = 0; @@ -259,15 +259,15 @@ md4_final( void *context ) lsb <<= 3; msb <<= 3; msb |= t >> 29; - + if( hd->count < 56 ) /* enough room */ { hd->buf[hd->count++] = 0x80; /* pad */ while( hd->count < 56 ) hd->buf[hd->count++] = 0; /* pad */ } - else /* need one extra block */ - { + else /* need one extra block */ + { hd->buf[hd->count++] = 0x80; /* pad character */ while( hd->count < 64 ) hd->buf[hd->count++] = 0; @@ -325,4 +325,3 @@ gcry_md_spec_t _gcry_digest_spec_md4 = md4_init, md4_write, md4_final, md4_read, sizeof (MD4_CONTEXT) }; - diff --git a/lib/libgcrypt/cipher/md5.c b/grub-core/lib/libgcrypt/cipher/md5.c similarity index 97% rename from lib/libgcrypt/cipher/md5.c rename to grub-core/lib/libgcrypt/cipher/md5.c index 899dce89a..a98678a9b 100644 --- a/lib/libgcrypt/cipher/md5.c +++ b/grub-core/lib/libgcrypt/cipher/md5.c @@ -20,8 +20,8 @@ * * According to the definition of MD5 in RFC 1321 from April 1992. * NOTE: This is *not* the same file as the one from glibc. - * Written by Ulrich Drepper , 1995. - * heavily modified for GnuPG by Werner Koch + * Written by Ulrich Drepper , 1995. + * heavily modified for GnuPG by Werner Koch */ /* Test values: @@ -37,7 +37,6 @@ #include #include "g10lib.h" -#include "memory.h" #include "cipher.h" #include "bithelp.h" @@ -88,11 +87,12 @@ transform ( MD5_CONTEXT *ctx, const unsigned char *data ) register u32 C = ctx->C; register u32 D = ctx->D; u32 *cwp = correct_words; - + #ifdef WORDS_BIGENDIAN - { + { int i; - byte *p2, *p1; + byte *p2; + const byte *p1; for(i=0, p1=data, p2=(byte*)correct_words; i < 16; i++, p2 += 4 ) { p2[3] = *p1++; @@ -221,7 +221,7 @@ md5_write( void *context, const void *inbuf_arg , size_t inlen) { const unsigned char *inbuf = inbuf_arg; MD5_CONTEXT *hd = context; - + if( hd->count == 64 ) /* flush the buffer */ { transform( hd, hd->buf ); @@ -242,7 +242,7 @@ md5_write( void *context, const void *inbuf_arg , size_t inlen) } _gcry_burn_stack (80+6*sizeof(void*)); - while( inlen >= 64 ) + while( inlen >= 64 ) { transform( hd, inbuf ); hd->count = 0; @@ -269,7 +269,7 @@ md5_final( void *context) MD5_CONTEXT *hd = context; u32 t, msb, lsb; byte *p; - + md5_write(hd, NULL, 0); /* flush */; t = hd->nblocks; diff --git a/lib/libgcrypt/cipher/primegen.c b/grub-core/lib/libgcrypt/cipher/primegen.c similarity index 96% rename from lib/libgcrypt/cipher/primegen.c rename to grub-core/lib/libgcrypt/cipher/primegen.c index b869bee83..b12e79b19 100644 --- a/lib/libgcrypt/cipher/primegen.c +++ b/grub-core/lib/libgcrypt/cipher/primegen.c @@ -31,7 +31,7 @@ #include "cipher.h" #include "ath.h" -static gcry_mpi_t gen_prime (unsigned int nbits, int secret, int randomlevel, +static gcry_mpi_t gen_prime (unsigned int nbits, int secret, int randomlevel, int (*extra_check)(void *, gcry_mpi_t), void *extra_check_arg); static int check_prime( gcry_mpi_t prime, gcry_mpi_t val_2, int rm_rounds, @@ -132,7 +132,7 @@ static int no_of_small_prime_numbers = DIM (small_prime_numbers) - 1; /* An object and a list to build up a global pool of primes. See save_pool_prime and get_pool_prime. */ -struct primepool_s +struct primepool_s { struct primepool_s *next; gcry_mpi_t prime; /* If this is NULL the entry is not used. */ @@ -163,7 +163,7 @@ save_pool_prime (gcry_mpi_t prime, gcry_random_level_t randomlevel) /* Remove some of the entries. Our strategy is removing the last third from the list. */ int i; - + for (i=0, item2 = primepool; item2; item2 = item2->next) { if (i >= n/3*2) @@ -182,7 +182,7 @@ save_pool_prime (gcry_mpi_t prime, gcry_random_level_t randomlevel) { /* Out of memory. Silently giving up. */ gcry_mpi_release (prime); - return; + return; } item->next = primepool; primepool = item; @@ -359,7 +359,7 @@ prime_generate_internal (int need_q_factor, fbits = (pbits - req_qbits -1) / n; qbits = pbits - n * fbits; } - + if (DBG_CIPHER) log_debug ("gen prime: pbits=%u qbits=%u fbits=%u/%u n=%d\n", pbits, req_qbits, qbits, fbits, n); @@ -373,7 +373,7 @@ prime_generate_internal (int need_q_factor, /* Generate a specific Q-Factor if requested. */ if (need_q_factor) q_factor = gen_prime (req_qbits, is_secret, randomlevel, NULL, NULL); - + /* Allocate an array to hold all factors + 2 for later usage. */ factors = gcry_calloc (n + 2, sizeof (*factors)); if (!factors) @@ -383,7 +383,7 @@ prime_generate_internal (int need_q_factor, } /* Allocate an array to track pool usage. */ - pool_in_use = gcry_malloc (n * sizeof *pool_in_use); + pool_in_use = gcry_calloc (n, sizeof *pool_in_use); if (!pool_in_use) { err = gpg_err_code_from_errno (errno); @@ -391,10 +391,10 @@ prime_generate_internal (int need_q_factor, } for (i=0; i < n; i++) pool_in_use[i] = -1; - + /* Make a pool of 3n+5 primes (this is an arbitrary value). We - require at least 30 primes for are useful selection process. - + require at least 30 primes for are useful selection process. + Fixme: We need to research the best formula for sizing the pool. */ m = n * 3 + 5; @@ -443,7 +443,7 @@ prime_generate_internal (int need_q_factor, is_locked = 1; for (i = 0; i < n; i++) { - perms[i] = 1; + perms[i] = 1; /* At a maximum we use strong random for the factors. This saves us a lot of entropy. Given that Q and possible Q-factor are also used in the final prime @@ -523,12 +523,12 @@ prime_generate_internal (int need_q_factor, gcry_free (perms); perms = NULL; progress ('!'); - goto next_try; + goto next_try; } } /* Generate next prime candidate: - p = 2 * q [ * q_factor] * factor_0 * factor_1 * ... * factor_n + 1. + p = 2 * q [ * q_factor] * factor_0 * factor_1 * ... * factor_n + 1. */ mpi_set (prime, q); mpi_mul_ui (prime, prime, 2); @@ -553,7 +553,7 @@ prime_generate_internal (int need_q_factor, } else count1 = 0; - + if (nprime > pbits) { if (++count2 > 20) @@ -624,14 +624,14 @@ prime_generate_internal (int need_q_factor, factors_new[i] = mpi_copy (factors[i]); } } - + if (g) { /* Create a generator (start with 3). */ gcry_mpi_t tmp = mpi_alloc (mpi_get_nlimbs (prime)); gcry_mpi_t b = mpi_alloc (mpi_get_nlimbs (prime)); gcry_mpi_t pmin1 = mpi_alloc (mpi_get_nlimbs (prime)); - + if (need_q_factor) err = GPG_ERR_NOT_IMPLEMENTED; else @@ -662,7 +662,7 @@ prime_generate_internal (int need_q_factor, } if (DBG_CIPHER) progress('\n'); - } + } while (i < n + 2); mpi_free (factors[n+1]); @@ -671,7 +671,7 @@ prime_generate_internal (int need_q_factor, mpi_free (pmin1); } } - + if (! DBG_CIPHER) progress ('\n'); @@ -738,19 +738,19 @@ gcry_mpi_t _gcry_generate_elg_prime (int mode, unsigned pbits, unsigned qbits, gcry_mpi_t g, gcry_mpi_t **ret_factors) { - gcry_err_code_t err = GPG_ERR_NO_ERROR; gcry_mpi_t prime = NULL; - - err = prime_generate_internal ((mode == 1), &prime, pbits, qbits, g, - ret_factors, GCRY_WEAK_RANDOM, 0, 0, - NULL, NULL); + + if (prime_generate_internal ((mode == 1), &prime, pbits, qbits, g, + ret_factors, GCRY_WEAK_RANDOM, 0, 0, + NULL, NULL)) + prime = NULL; /* (Should be NULL in the error case anyway.) */ return prime; } static gcry_mpi_t -gen_prime (unsigned int nbits, int secret, int randomlevel, +gen_prime (unsigned int nbits, int secret, int randomlevel, int (*extra_check)(void *, gcry_mpi_t), void *extra_check_arg) { gcry_mpi_t prime, ptest, pminus1, val_2, val_3, result; @@ -758,14 +758,14 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, unsigned int x, step; unsigned int count1, count2; int *mods; - + /* if ( DBG_CIPHER ) */ /* log_debug ("generate a prime of %u bits ", nbits ); */ if (nbits < 16) log_fatal ("can't generate a prime with less than %d bits\n", 16); - mods = gcry_xmalloc( no_of_small_prime_numbers * sizeof *mods ); + mods = gcry_xcalloc( no_of_small_prime_numbers, sizeof *mods); /* Make nbits fit into gcry_mpi_t implementation. */ val_2 = mpi_alloc_set_ui( 2 ); val_3 = mpi_alloc_set_ui( 3); @@ -777,10 +777,10 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, for (;;) { /* try forvever */ int dotcount=0; - + /* generate a random number */ gcry_mpi_randomize( prime, nbits, randomlevel ); - + /* Set high order bit to 1, set low order bit to 1. If we are generating a secret prime we are most probably doing that for RSA, to make sure that the modulus does have the @@ -789,17 +789,17 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, if (secret) mpi_set_bit (prime, nbits-2); mpi_set_bit(prime, 0); - + /* Calculate all remainders. */ for (i=0; (x = small_prime_numbers[i]); i++ ) mods[i] = mpi_fdiv_r_ui(NULL, prime, x); - + /* Now try some primes starting with prime. */ - for(step=0; step < 20000; step += 2 ) + for(step=0; step < 20000; step += 2 ) { /* Check against all the small primes we have in mods. */ count1++; - for (i=0; (x = small_prime_numbers[i]); i++ ) + for (i=0; (x = small_prime_numbers[i]); i++ ) { while ( mods[i] + step >= x ) mods[i] -= x; @@ -808,7 +808,7 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, } if ( x ) continue; /* Found a multiple of an already known prime. */ - + mpi_add_ui( ptest, prime, step ); /* Do a fast Fermat test now. */ @@ -816,7 +816,7 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, mpi_sub_ui( pminus1, ptest, 1); gcry_mpi_powm( result, val_2, pminus1, ptest ); if ( !mpi_cmp_ui( result, 1 ) ) - { + { /* Not composite, perform stronger tests */ if (is_prime(ptest, 5, &count2 )) { @@ -828,13 +828,13 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, } if (extra_check && extra_check (extra_check_arg, ptest)) - { + { /* The extra check told us that this prime is not of the caller's taste. */ progress ('/'); } else - { + { /* Got it. */ mpi_free(val_2); mpi_free(val_3); @@ -842,7 +842,7 @@ gen_prime (unsigned int nbits, int secret, int randomlevel, mpi_free(pminus1); mpi_free(prime); gcry_free(mods); - return ptest; + return ptest; } } } @@ -883,7 +883,7 @@ check_prime( gcry_mpi_t prime, gcry_mpi_t val_2, int rm_rounds, gcry_mpi_powm( result, val_2, pminus1, prime ); mpi_free( pminus1 ); if ( mpi_cmp_ui( result, 1 ) ) - { + { /* Is composite. */ mpi_free( result ); progress('.'); @@ -924,7 +924,7 @@ is_prime (gcry_mpi_t n, int steps, unsigned int *count) unsigned nbits = mpi_get_nbits( n ); if (steps < 5) /* Make sure that we do at least 5 rounds. */ - steps = 5; + steps = 5; mpi_sub_ui( nminus1, n, 1 ); @@ -988,7 +988,7 @@ is_prime (gcry_mpi_t n, int steps, unsigned int *count) /* Given ARRAY of size N with M elements set to true produce a modified array with the next permutation of M elements. Note, that ARRAY is used in a one-bit-per-byte approach. To detected the last - permutation it is useful to intialize the array with the first M + permutation it is useful to initialize the array with the first M element set to true and use this test: m_out_of_n (array, m, n); for (i = j = 0; i < n && j < m; i++) @@ -996,7 +996,7 @@ is_prime (gcry_mpi_t n, int steps, unsigned int *count) j++; if (j == m) goto ready; - + This code is based on the algorithm 452 from the "Collected Algorithms From ACM, Volume II" by C. N. Liu and D. T. Tang. */ @@ -1010,7 +1010,7 @@ m_out_of_n ( char *array, int m, int n ) /* Need to handle this simple case separately. */ if( m == 1 ) - { + { for (i=0; i < n; i++ ) { if ( array[i] ) @@ -1060,7 +1060,7 @@ m_out_of_n ( char *array, int m, int n ) else k1 = k2 + 1; } - else + else { /* M is even. */ if( !array[n-1] ) @@ -1069,7 +1069,7 @@ m_out_of_n ( char *array, int m, int n ) k2 = k1 + 1; goto leave; } - + if( !(j1 & 1) ) { k1 = n - j1; @@ -1080,7 +1080,7 @@ m_out_of_n ( char *array, int m, int n ) } scan: jp = n - j1 - 1; - for (i=1; i <= jp; i++ ) + for (i=1; i <= jp; i++ ) { i1 = jp + 2 - i; if( array[i1-1] ) @@ -1128,7 +1128,7 @@ gcry_prime_generate (gcry_mpi_t *prime, unsigned int prime_bits, if (!prime) return gpg_error (GPG_ERR_INV_ARG); - *prime = NULL; + *prime = NULL; if (flags & GCRY_PRIME_FLAG_SPECIAL_FACTOR) mode = 1; @@ -1156,7 +1156,7 @@ gcry_prime_generate (gcry_mpi_t *prime, unsigned int prime_bits, mpi_free (factors_generated[i]); gcry_free (factors_generated); } - err = GPG_ERR_GENERAL; + err = GPG_ERR_GENERAL; } } @@ -1170,7 +1170,7 @@ gcry_prime_generate (gcry_mpi_t *prime, unsigned int prime_bits, return gcry_error (err); } -/* Check wether the number X is prime. */ +/* Check whether the number X is prime. */ gcry_error_t gcry_prime_check (gcry_mpi_t x, unsigned int flags) { @@ -1207,29 +1207,29 @@ gcry_prime_group_generator (gcry_mpi_t *r_g, if (!factors || !r_g || !prime) return gpg_error (GPG_ERR_INV_ARG); - *r_g = NULL; + *r_g = NULL; for (n=0; factors[n]; n++) ; if (n < 2) return gpg_error (GPG_ERR_INV_ARG); - /* Extra sanity check - usually disabled. */ + /* Extra sanity check - usually disabled. */ /* mpi_set (tmp, factors[0]); */ /* for(i = 1; i < n; i++) */ /* mpi_mul (tmp, tmp, factors[i]); */ /* mpi_add_ui (tmp, tmp, 1); */ /* if (mpi_cmp (prime, tmp)) */ /* return gpg_error (GPG_ERR_INV_ARG); */ - - gcry_mpi_sub_ui (pmin1, prime, 1); - do + + gcry_mpi_sub_ui (pmin1, prime, 1); + do { if (first) first = 0; else gcry_mpi_add_ui (g, g, 1); - + if (DBG_CIPHER) { log_debug ("checking g:"); @@ -1238,7 +1238,7 @@ gcry_prime_group_generator (gcry_mpi_t *r_g, } else progress('^'); - + for (i = 0; i < n; i++) { mpi_fdiv_q (tmp, pmin1, factors[i]); @@ -1250,13 +1250,13 @@ gcry_prime_group_generator (gcry_mpi_t *r_g, progress('\n'); } while (i < n); - - gcry_mpi_release (tmp); - gcry_mpi_release (b); - gcry_mpi_release (pmin1); - *r_g = g; - return 0; + gcry_mpi_release (tmp); + gcry_mpi_release (b); + gcry_mpi_release (pmin1); + *r_g = g; + + return 0; } /* Convenience function to release the factors array. */ @@ -1266,7 +1266,7 @@ gcry_prime_release_factors (gcry_mpi_t *factors) if (factors) { int i; - + for (i=0; factors[i]; i++) mpi_free (factors[i]); gcry_free (factors); @@ -1279,11 +1279,11 @@ gcry_prime_release_factors (gcry_mpi_t *factors) static gcry_mpi_t find_x931_prime (const gcry_mpi_t pfirst) { - gcry_mpi_t val_2 = mpi_alloc_set_ui (2); + gcry_mpi_t val_2 = mpi_alloc_set_ui (2); gcry_mpi_t prime; - + prime = gcry_mpi_copy (pfirst); - /* If P is even add 1. */ + /* If P is even add 1. */ mpi_set_bit (prime, 0); /* We use 64 Rabin-Miller rounds which is better and thus @@ -1299,7 +1299,7 @@ find_x931_prime (const gcry_mpi_t pfirst) } -/* Generate a prime using the algorithm from X9.31 appendix B.4. +/* Generate a prime using the algorithm from X9.31 appendix B.4. This function requires that the provided public exponent E is odd. XP, XP1 and XP2 are the seed values. All values are mandatory. @@ -1308,7 +1308,7 @@ find_x931_prime (const gcry_mpi_t pfirst) internal values P1 and P2 are saved at these addresses. On error NULL is returned. */ gcry_mpi_t -_gcry_derive_x931_prime (const gcry_mpi_t xp, +_gcry_derive_x931_prime (const gcry_mpi_t xp, const gcry_mpi_t xp1, const gcry_mpi_t xp2, const gcry_mpi_t e, gcry_mpi_t *r_p1, gcry_mpi_t *r_p2) @@ -1327,20 +1327,20 @@ _gcry_derive_x931_prime (const gcry_mpi_t xp, { gcry_mpi_t r1, tmp; - + /* r1 = (p2^{-1} mod p1)p2 - (p1^{-1} mod p2) */ tmp = mpi_alloc_like (p1); mpi_invm (tmp, p2, p1); mpi_mul (tmp, tmp, p2); r1 = tmp; - + tmp = mpi_alloc_like (p2); mpi_invm (tmp, p1, p2); mpi_mul (tmp, tmp, p1); mpi_sub (r1, r1, tmp); /* Fixup a negative value. */ - if (mpi_is_neg (r1)) + if (mpi_is_neg (r1)) mpi_add (r1, r1, p1p2); /* yp0 = xp + (r1 - xp mod p1*p2) */ @@ -1350,7 +1350,7 @@ _gcry_derive_x931_prime (const gcry_mpi_t xp, mpi_free (r1); /* Fixup a negative value. */ - if (mpi_cmp (yp0, xp) < 0 ) + if (mpi_cmp (yp0, xp) < 0 ) mpi_add (yp0, yp0, p1p2); } @@ -1378,10 +1378,10 @@ _gcry_derive_x931_prime (const gcry_mpi_t xp, */ { - gcry_mpi_t val_2 = mpi_alloc_set_ui (2); + gcry_mpi_t val_2 = mpi_alloc_set_ui (2); gcry_mpi_t gcdtmp = mpi_alloc_like (yp0); int gcdres; - + mpi_sub_ui (p1p2, p1p2, 1); /* Adjust for loop body. */ mpi_sub_ui (yp0, yp0, 1); /* Ditto. */ for (;;) @@ -1453,7 +1453,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, ; /* No seed value given: We are asked to generate it. */ else if (!seed || seedlen < qbits/8) return GPG_ERR_INV_ARG; - + /* Allocate a buffer to later compute SEED+some_increment. */ seed_plus = gcry_malloc (seedlen < 20? 20:seedlen); if (!seed_plus) @@ -1468,7 +1468,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, value_w = gcry_mpi_new (pbits); value_x = gcry_mpi_new (pbits); - restart: + restart: /* Generate Q. */ for (;;) { @@ -1479,7 +1479,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, gcry_create_nonce (seed_help_buffer, seedlen); seed = seed_help_buffer; } - + /* Step 2: U = sha1(seed) ^ sha1((seed+1) mod 2^{qbits}) */ memcpy (seed_plus, seed, seedlen); for (i=seedlen-1; i >= 0; i--) @@ -1492,16 +1492,16 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, gcry_md_hash_buffer (GCRY_MD_SHA1, digest, seed_plus, seedlen); for (i=0; i < sizeof value_u; i++) value_u[i] ^= digest[i]; - + /* Step 3: Form q from U */ gcry_mpi_release (prime_q); prime_q = NULL; - ec = gpg_err_code (gcry_mpi_scan (&prime_q, GCRYMPI_FMT_USG, + ec = gpg_err_code (gcry_mpi_scan (&prime_q, GCRYMPI_FMT_USG, value_u, sizeof value_u, NULL)); if (ec) goto leave; mpi_set_highbit (prime_q, qbits-1 ); mpi_set_bit (prime_q, 0); - + /* Step 4: Test whether Q is prime using 64 round of Rabin-Miller. */ if (check_prime (prime_q, val_2, 64, NULL, NULL)) break; /* Yes, Q is prime. */ @@ -1509,7 +1509,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, /* Step 5. */ seed = NULL; /* Force a new seed at Step 1. */ } - + /* Step 6. Note that we do no use an explicit offset but increment SEED_PLUS accordingly. SEED_PLUS is currently SEED+1. */ counter = 0; @@ -1518,12 +1518,12 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, prime_p = gcry_mpi_new (pbits); for (;;) { - /* Step 7: For k = 0,...n let - V_k = sha1(seed+offset+k) mod 2^{qbits} - Step 8: W = V_0 + V_1*2^160 + - ... + /* Step 7: For k = 0,...n let + V_k = sha1(seed+offset+k) mod 2^{qbits} + Step 8: W = V_0 + V_1*2^160 + + ... + V_{n-1}*2^{(n-1)*160} - + (V_{n} mod 2^b)*2^{n*160} + + (V_{n} mod 2^b)*2^{n*160} */ mpi_set_ui (value_w, 0); for (value_k=0; value_k <= value_n; value_k++) @@ -1542,7 +1542,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, break; } gcry_md_hash_buffer (GCRY_MD_SHA1, digest, seed_plus, seedlen); - + gcry_mpi_release (tmpval); tmpval = NULL; ec = gpg_err_code (gcry_mpi_scan (&tmpval, GCRYMPI_FMT_USG, digest, sizeof digest, NULL)); @@ -1631,7 +1631,7 @@ _gcry_generate_fips186_2_prime (unsigned int pbits, unsigned int qbits, value is stored at R_COUNTER and the seed actually used for generation is stored at R_SEED and R_SEEDVALUE. The hash algorithm used is stored at R_HASHALGO. - + Note that this function is very similar to the fips186_2 code. Due to the minor differences, other buffer sizes and for documentarion, we use a separate function. @@ -1652,7 +1652,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, gcry_mpi_t tmpval = NULL; /* Helper variable. */ int hashalgo; /* The id of the Approved Hash Function. */ int i; - + unsigned char value_u[256/8]; int value_n, value_b, value_j; int counter; @@ -1690,10 +1690,10 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, ; /* No seed value given: We are asked to generate it. */ else if (!seed || seedlen < qbits/8) return GPG_ERR_INV_ARG; - + /* Allocate a buffer to later compute SEED+some_increment and a few helper variables. */ - seed_plus = gcry_malloc (seedlen < sizeof seed_help_buffer? + seed_plus = gcry_malloc (seedlen < sizeof seed_help_buffer? sizeof seed_help_buffer : seedlen); if (!seed_plus) { @@ -1709,7 +1709,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, /* Step 4: b = L - 1 - (n * outlen) */ value_b = pbits - 1 - (value_n * qbits); - restart: + restart: /* Generate Q. */ for (;;) { @@ -1721,7 +1721,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, gcry_create_nonce (seed_help_buffer, seedlen); seed = seed_help_buffer; } - + /* Step 6: U = hash(seed) */ gcry_md_hash_buffer (hashalgo, value_u, seed, seedlen); @@ -1736,12 +1736,12 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, } } gcry_mpi_release (prime_q); prime_q = NULL; - ec = gpg_err_code (gcry_mpi_scan (&prime_q, GCRYMPI_FMT_USG, + ec = gpg_err_code (gcry_mpi_scan (&prime_q, GCRYMPI_FMT_USG, value_u, sizeof value_u, NULL)); if (ec) goto leave; mpi_set_highbit (prime_q, qbits-1 ); - + /* Step 8: Test whether Q is prime using 64 round of Rabin-Miller. According to table C.1 this is sufficient for all supported prime sizes (i.e. up 3072/256). */ @@ -1751,7 +1751,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, /* Step 8. */ seed = NULL; /* Force a new seed at Step 5. */ } - + /* Step 11. Note that we do no use an explicit offset but increment SEED_PLUS accordingly. */ memcpy (seed_plus, seed, seedlen); @@ -1761,12 +1761,12 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, prime_p = gcry_mpi_new (pbits); for (;;) { - /* Step 11.1: For j = 0,...n let - V_j = hash(seed+offset+j) - Step 11.2: W = V_0 + V_1*2^outlen + - ... + /* Step 11.1: For j = 0,...n let + V_j = hash(seed+offset+j) + Step 11.2: W = V_0 + V_1*2^outlen + + ... + V_{n-1}*2^{(n-1)*outlen} - + (V_{n} mod 2^b)*2^{n*outlen} + + (V_{n} mod 2^b)*2^{n*outlen} */ mpi_set_ui (value_w, 0); for (value_j=0; value_j <= value_n; value_j++) @@ -1783,7 +1783,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, break; } gcry_md_hash_buffer (GCRY_MD_SHA1, digest, seed_plus, seedlen); - + gcry_mpi_release (tmpval); tmpval = NULL; ec = gpg_err_code (gcry_mpi_scan (&tmpval, GCRYMPI_FMT_USG, digest, sizeof digest, NULL)); @@ -1813,7 +1813,7 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, if (mpi_get_nbits (prime_p) >= pbits-1 && check_prime (prime_p, val_2, 64, NULL, NULL) ) break; /* Yes, P is prime, continue with Step 15. */ - + /* Step 11.9: counter = counter + 1, offset = offset + n + 1. If counter >= 4L goto Step 5. */ counter++; @@ -1859,4 +1859,3 @@ _gcry_generate_fips186_3_prime (unsigned int pbits, unsigned int qbits, gcry_mpi_release (val_2); return ec; } - diff --git a/lib/libgcrypt/cipher/pubkey.c b/grub-core/lib/libgcrypt/cipher/pubkey.c similarity index 52% rename from lib/libgcrypt/cipher/pubkey.c rename to grub-core/lib/libgcrypt/cipher/pubkey.c index 08abcbfde..ca087ad75 100644 --- a/lib/libgcrypt/cipher/pubkey.c +++ b/grub-core/lib/libgcrypt/cipher/pubkey.c @@ -1,6 +1,6 @@ /* pubkey.c - pubkey dispatcher - * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005, - * 2007, 2008 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2002, 2003, 2005, + * 2007, 2008, 2011 Free Software Foundation, Inc. * * This file is part of Libgcrypt. * @@ -55,7 +55,7 @@ static struct pubkey_table_entry gcry_pk_spec_t *pubkey; pk_extra_spec_t *extraspec; unsigned int algorithm; - int fips_allowed; + int fips_allowed; } pubkey_table[] = { #if USE_RSA @@ -75,6 +75,8 @@ static struct pubkey_table_entry #if USE_ECC { &_gcry_pubkey_spec_ecdsa, &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDSA, 0 }, + { &_gcry_pubkey_spec_ecdh, + &_gcry_pubkey_extraspec_ecdsa, GCRY_PK_ECDH, 0 }, #endif { NULL, 0 }, }; @@ -85,7 +87,7 @@ static gcry_module_t pubkeys_registered; /* This is the lock protecting PUBKEYS_REGISTERED. */ static ath_mutex_t pubkeys_registered_lock = ATH_MUTEX_INITIALIZER;; -/* Flag to check wether the default pubkeys have already been +/* Flag to check whether the default pubkeys have already been registered. */ static int default_pubkeys_registered; @@ -197,7 +199,7 @@ pk_register_default (void) { gcry_err_code_t err = 0; int i; - + for (i = 0; (! err) && pubkey_table[i].pubkey; i++) { #define pubkey_use_dummy(func) \ @@ -215,8 +217,8 @@ pk_register_default (void) err = _gcry_module_add (&pubkeys_registered, pubkey_table[i].algorithm, - (void *) pubkey_table[i].pubkey, - (void *) pubkey_table[i].extraspec, + (void *) pubkey_table[i].pubkey, + (void *) pubkey_table[i].extraspec, NULL); } @@ -240,7 +242,7 @@ gcry_pk_lookup_func_name (void *spec, void *data) } /* Internal function. Lookup a pubkey entry by it's name. */ -static gcry_module_t +static gcry_module_t gcry_pk_lookup_name (const char *name) { gcry_module_t pubkey; @@ -269,8 +271,8 @@ _gcry_pk_register (gcry_pk_spec_t *pubkey, ath_mutex_lock (&pubkeys_registered_lock); err = _gcry_module_add (&pubkeys_registered, 0, - (void *) pubkey, - (void *)(extraspec? extraspec : &dummy_extra_spec), + (void *) pubkey, + (void *)(extraspec? extraspec : &dummy_extra_spec), &mod); ath_mutex_unlock (&pubkeys_registered_lock); @@ -559,13 +561,13 @@ pubkey_generate (int algorithm, if (extraspec && extraspec->ext_generate) { /* Use the extended generate function. */ - ec = extraspec->ext_generate + ec = extraspec->ext_generate (algorithm, nbits, use_e, genparms, skey, retfactors, r_extrainfo); } else { /* Use the standard generate function. */ - ec = ((gcry_pk_spec_t *) pubkey->spec)->generate + ec = ((gcry_pk_spec_t *) pubkey->spec)->generate (algorithm, nbits, use_e, skey, retfactors); } _gcry_module_release (pubkey); @@ -615,7 +617,7 @@ pubkey_encrypt (int algorithm, gcry_mpi_t *resarr, gcry_mpi_t data, /* Note: In fips mode DBG_CIPHER will enver evaluate to true but as an extra failsafe protection we explicitly test for fips mode - here. */ + here. */ if (DBG_CIPHER && !fips_mode ()) { log_debug ("pubkey_encrypt: algo=%d\n", algorithm); @@ -684,7 +686,7 @@ pubkey_decrypt (int algorithm, gcry_mpi_t *result, gcry_mpi_t *data, } rc = GPG_ERR_PUBKEY_ALGO; - + ready: ath_mutex_unlock (&pubkeys_registered_lock); @@ -758,10 +760,10 @@ pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data, { log_debug ("pubkey_verify: algo=%d\n", algorithm); for (i = 0; i < pubkey_get_npkey (algorithm); i++) - log_mpidump (" pkey:", pkey[i]); + log_mpidump (" pkey", pkey[i]); for (i = 0; i < pubkey_get_nsig (algorithm); i++) - log_mpidump (" sig:", data[i]); - log_mpidump (" hash:", hash); + log_mpidump (" sig", data[i]); + log_mpidump (" hash", hash); } ath_mutex_lock (&pubkeys_registered_lock); @@ -782,6 +784,1018 @@ pubkey_verify (int algorithm, gcry_mpi_t hash, gcry_mpi_t *data, } +/* Turn VALUE into an octet string and store it in an allocated buffer + at R_FRAME or - if R_RAME is NULL - copy it into the caller + provided buffer SPACE; either SPACE or R_FRAME may be used. If + SPACE if not NULL, the caller must provide a buffer of at least + NBYTES. If the resulting octet string is shorter than NBYTES pad + it to the left with zeroes. If VALUE does not fit into NBYTES + return an error code. */ +static gpg_err_code_t +octet_string_from_mpi (unsigned char **r_frame, void *space, + gcry_mpi_t value, size_t nbytes) +{ + gpg_err_code_t rc; + size_t nframe, noff, n; + unsigned char *frame; + + if (!r_frame == !space) + return GPG_ERR_INV_ARG; /* Only one may be used. */ + + if (r_frame) + *r_frame = NULL; + + rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG, + NULL, 0, &nframe, value)); + if (rc) + return rc; + if (nframe > nbytes) + return GPG_ERR_TOO_LARGE; /* Value too long to fit into NBYTES. */ + + noff = (nframe < nbytes)? nbytes - nframe : 0; + n = nframe + noff; + if (space) + frame = space; + else + { + frame = mpi_is_secure (value)? gcry_malloc_secure (n) : gcry_malloc (n); + if (!frame) + { + rc = gpg_err_code_from_syserror (); + return rc; + } + } + if (noff) + memset (frame, 0, noff); + nframe += noff; + rc = gcry_err_code (gcry_mpi_print (GCRYMPI_FMT_USG, + frame+noff, nframe-noff, NULL, value)); + if (rc) + { + gcry_free (frame); + return rc; + } + + if (r_frame) + *r_frame = frame; + return 0; +} + + +/* Encode {VALUE,VALUELEN} for an NBITS keys using the pkcs#1 block + type 2 padding. On sucess the result is stored as a new MPI at + R_RESULT. On error the value at R_RESULT is undefined. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the seed instead of using a random string for it. This feature is + only useful for regression tests. Note that this value may not + contain zero bytes. + + We encode the value in this way: + + 0 2 RND(n bytes) 0 VALUE + + 0 is a marker we unfortunately can't encode because we return an + MPI which strips all leading zeroes. + 2 is the block type. + RND are non-zero random bytes. + + (Note that OpenPGP includes the cipher algorithm and a checksum in + VALUE; the caller needs to prepare the value accordingly.) + */ +static gcry_err_code_t +pkcs1_encode_for_encryption (gcry_mpi_t *r_result, unsigned int nbits, + const unsigned char *value, size_t valuelen, + const unsigned char *random_override, + size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + int i; + size_t n; + unsigned char *p; + + if (valuelen + 7 > nframe || !nframe) + { + /* Can't encode a VALUELEN value in a NFRAME bytes frame. */ + return GPG_ERR_TOO_SHORT; /* The key is too short. */ + } + + if ( !(frame = gcry_malloc_secure (nframe))) + return gpg_err_code_from_syserror (); + + n = 0; + frame[n++] = 0; + frame[n++] = 2; /* block type */ + i = nframe - 3 - valuelen; + gcry_assert (i > 0); + + if (random_override) + { + int j; + + if (random_override_len != i) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + /* Check that random does not include a zero byte. */ + for (j=0; j < random_override_len; j++) + if (!random_override[j]) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + memcpy (frame + n, random_override, random_override_len); + n += random_override_len; + } + else + { + p = gcry_random_bytes_secure (i, GCRY_STRONG_RANDOM); + /* Replace zero bytes by new values. */ + for (;;) + { + int j, k; + unsigned char *pp; + + /* Count the zero bytes. */ + for (j=k=0; j < i; j++) + { + if (!p[j]) + k++; + } + if (!k) + break; /* Okay: no (more) zero bytes. */ + + k += k/128 + 3; /* Better get some more. */ + pp = gcry_random_bytes_secure (k, GCRY_STRONG_RANDOM); + for (j=0; j < i && k; ) + { + if (!p[j]) + p[j] = pp[--k]; + if (p[j]) + j++; + } + gcry_free (pp); + } + memcpy (frame+n, p, i); + n += i; + gcry_free (p); + } + + frame[n++] = 0; + memcpy (frame+n, value, valuelen); + n += valuelen; + gcry_assert (n == nframe); + + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("PKCS#1 block type 2 encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* Decode a plaintext in VALUE assuming pkcs#1 block type 2 padding. + NBITS is the size of the secret key. On success the result is + stored as a newly allocated buffer at R_RESULT and its valid length at + R_RESULTLEN. On error NULL is stored at R_RESULT. */ +static gcry_err_code_t +pkcs1_decode_for_encryption (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, gcry_mpi_t value) +{ + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + size_t n; + + *r_result = NULL; + + if ( !(frame = gcry_malloc_secure (nframe))) + return gpg_err_code_from_syserror (); + + err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &n, value); + if (err) + { + gcry_free (frame); + return gcry_err_code (err); + } + + nframe = n; /* Set NFRAME to the actual length. */ + + /* FRAME = 0x00 || 0x02 || PS || 0x00 || M + + pkcs#1 requires that the first byte is zero. Our MPIs usually + strip leading zero bytes; thus we are not able to detect them. + However due to the way gcry_mpi_print is implemented we may see + leading zero bytes nevertheless. We handle this by making the + first zero byte optional. */ + if (nframe < 4) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* Too short. */ + } + n = 0; + if (!frame[0]) + n++; + if (frame[n++] != 0x02) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* Wrong block type. */ + } + + /* Skip the non-zero random bytes and the terminating zero byte. */ + for (; n < nframe && frame[n] != 0x00; n++) + ; + if (n+1 >= nframe) + { + gcry_free (frame); + return GPG_ERR_ENCODING_PROBLEM; /* No zero byte. */ + } + n++; /* Skip the zero byte. */ + + /* To avoid an extra allocation we reuse the frame buffer. The only + caller of this function will anyway free the result soon. */ + memmove (frame, frame + n, nframe - n); + *r_result = frame; + *r_resultlen = nframe - n; + + if (DBG_CIPHER) + log_printhex ("value extracted from PKCS#1 block type 2 encoded data:", + *r_result, *r_resultlen); + + return 0; +} + + +/* Encode {VALUE,VALUELEN} for an NBITS keys and hash algorith ALGO + using the pkcs#1 block type 1 padding. On success the result is + stored as a new MPI at R_RESULT. On error the value at R_RESULT is + undefined. + + We encode the value in this way: + + 0 1 PAD(n bytes) 0 ASN(asnlen bytes) VALUE(valuelen bytes) + + 0 is a marker we unfortunately can't encode because we return an + MPI which strips all leading zeroes. + 1 is the block type. + PAD consists of 0xff bytes. + 0 marks the end of the padding. + ASN is the DER encoding of the hash algorithm; along with the VALUE + it yields a valid DER encoding. + + (Note that PGP prior to version 2.3 encoded the message digest as: + 0 1 MD(16 bytes) 0 PAD(n bytes) 1 + The MD is always 16 bytes here because it's always MD5. GnuPG + does not not support pre-v2.3 signatures, but I'm including this + comment so the information is easily found if needed.) +*/ +static gcry_err_code_t +pkcs1_encode_for_signature (gcry_mpi_t *r_result, unsigned int nbits, + const unsigned char *value, size_t valuelen, + int algo) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + byte asn[100]; + byte *frame = NULL; + size_t nframe = (nbits+7) / 8; + int i; + size_t n; + size_t asnlen, dlen; + + asnlen = DIM(asn); + dlen = gcry_md_get_algo_dlen (algo); + + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + /* We don't have yet all of the above algorithms. */ + return GPG_ERR_NOT_IMPLEMENTED; + } + + if ( valuelen != dlen ) + { + /* Hash value does not match the length of digest for + the given algorithm. */ + return GPG_ERR_CONFLICT; + } + + if ( !dlen || dlen + asnlen + 4 > nframe) + { + /* Can't encode an DLEN byte digest MD into an NFRAME byte + frame. */ + return GPG_ERR_TOO_SHORT; + } + + if ( !(frame = gcry_malloc (nframe)) ) + return gpg_err_code_from_syserror (); + + /* Assemble the pkcs#1 block type 1. */ + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - valuelen - asnlen - 3 ; + gcry_assert (i > 1); + memset (frame+n, 0xff, i ); + n += i; + frame[n++] = 0; + memcpy (frame+n, asn, asnlen); + n += asnlen; + memcpy (frame+n, value, valuelen ); + n += valuelen; + gcry_assert (n == nframe); + + /* Convert it into an MPI. */ + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, n, &nframe); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("PKCS#1 block type 1 encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* Mask generation function for OAEP. See RFC-3447 B.2.1. */ +static gcry_err_code_t +mgf1 (unsigned char *output, size_t outlen, unsigned char *seed, size_t seedlen, + int algo) +{ + size_t dlen, nbytes, n; + int idx; + gcry_md_hd_t hd; + gcry_error_t err; + + err = gcry_md_open (&hd, algo, 0); + if (err) + return gpg_err_code (err); + + dlen = gcry_md_get_algo_dlen (algo); + + /* We skip step 1 which would be assert(OUTLEN <= 2^32). The loop + in step 3 is merged with step 4 by concatenating no more octets + than what would fit into OUTPUT. The ceiling for the counter IDX + is implemented indirectly. */ + nbytes = 0; /* Step 2. */ + idx = 0; + while ( nbytes < outlen ) + { + unsigned char c[4], *digest; + + if (idx) + gcry_md_reset (hd); + + c[0] = (idx >> 24) & 0xFF; + c[1] = (idx >> 16) & 0xFF; + c[2] = (idx >> 8) & 0xFF; + c[3] = idx & 0xFF; + idx++; + + gcry_md_write (hd, seed, seedlen); + gcry_md_write (hd, c, 4); + digest = gcry_md_read (hd, 0); + + n = (outlen - nbytes < dlen)? (outlen - nbytes) : dlen; + memcpy (output+nbytes, digest, n); + nbytes += n; + } + + gcry_md_close (hd); + return GPG_ERR_NO_ERROR; +} + + +/* RFC-3447 (pkcs#1 v2.1) OAEP encoding. NBITS is the length of the + key measured in bits. ALGO is the hash function; it must be a + valid and usable algorithm. {VALUE,VALUELEN} is the message to + encrypt. {LABEL,LABELLEN} is the optional label to be associated + with the message, if LABEL is NULL the default is to use the empty + string as label. On success the encoded ciphertext is returned at + R_RESULT. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the seed instead of using a random string for it. This feature is + only useful for regression tests. + + Here is figure 1 from the RFC depicting the process: + + +----------+---------+-------+ + DB = | lHash | PS | M | + +----------+---------+-------+ + | + +----------+ V + | seed |--> MGF ---> xor + +----------+ | + | | + +--+ V | + |00| xor <----- MGF <-----| + +--+ | | + | | | + V V V + +--+----------+----------------------------+ + EM = |00|maskedSeed| maskedDB | + +--+----------+----------------------------+ + */ +static gcry_err_code_t +oaep_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + const unsigned char *value, size_t valuelen, + const unsigned char *label, size_t labellen, + const void *random_override, size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + unsigned char *frame = NULL; + size_t nframe = (nbits+7) / 8; + unsigned char *p; + size_t hlen; + size_t n; + + *r_result = NULL; + + /* Set defaults for LABEL. */ + if (!label || !labellen) + { + label = (const unsigned char*)""; + labellen = 0; + } + + hlen = gcry_md_get_algo_dlen (algo); + + /* We skip step 1a which would be to check that LABELLEN is not + greater than 2^61-1. See rfc-3447 7.1.1. */ + + /* Step 1b. Note that the obsolete rfc-2437 uses the check: + valuelen > nframe - 2 * hlen - 1 . */ + if (valuelen > nframe - 2 * hlen - 2 || !nframe) + { + /* Can't encode a VALUELEN value in a NFRAME bytes frame. */ + return GPG_ERR_TOO_SHORT; /* The key is too short. */ + } + + /* Allocate the frame. */ + frame = gcry_calloc_secure (1, nframe); + if (!frame) + return gpg_err_code_from_syserror (); + + /* Step 2a: Compute the hash of the label. We store it in the frame + where later the maskedDB will commence. */ + gcry_md_hash_buffer (algo, frame + 1 + hlen, label, labellen); + + /* Step 2b: Set octet string to zero. */ + /* This has already been done while allocating FRAME. */ + + /* Step 2c: Create DB by concatenating lHash, PS, 0x01 and M. */ + n = nframe - valuelen - 1; + frame[n] = 0x01; + memcpy (frame + n + 1, value, valuelen); + + /* Step 3d: Generate seed. We store it where the maskedSeed will go + later. */ + if (random_override) + { + if (random_override_len != hlen) + { + gcry_free (frame); + return GPG_ERR_INV_ARG; + } + memcpy (frame + 1, random_override, hlen); + } + else + gcry_randomize (frame + 1, hlen, GCRY_STRONG_RANDOM); + + /* Step 2e and 2f: Create maskedDB. */ + { + unsigned char *dmask; + + dmask = gcry_malloc_secure (nframe - hlen - 1); + if (!dmask) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + return rc; + } + rc = mgf1 (dmask, nframe - hlen - 1, frame+1, hlen, algo); + if (rc) + { + gcry_free (dmask); + gcry_free (frame); + return rc; + } + for (n = 1 + hlen, p = dmask; n < nframe; n++) + frame[n] ^= *p++; + gcry_free (dmask); + } + + /* Step 2g and 2h: Create maskedSeed. */ + { + unsigned char *smask; + + smask = gcry_malloc_secure (hlen); + if (!smask) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + return rc; + } + rc = mgf1 (smask, hlen, frame + 1 + hlen, nframe - hlen - 1, algo); + if (rc) + { + gcry_free (smask); + gcry_free (frame); + return rc; + } + for (n = 1, p = smask; n < 1 + hlen; n++) + frame[n] ^= *p++; + gcry_free (smask); + } + + /* Step 2i: Concatenate 0x00, maskedSeed and maskedDB. */ + /* This has already been done by using in-place operations. */ + + /* Convert the stuff into an MPI as expected by the caller. */ + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, frame, nframe, NULL); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("OAEP encoded data", *r_result); + gcry_free (frame); + + return rc; +} + + +/* RFC-3447 (pkcs#1 v2.1) OAEP decoding. NBITS is the length of the + key measured in bits. ALGO is the hash function; it must be a + valid and usable algorithm. VALUE is the raw decrypted message + {LABEL,LABELLEN} is the optional label to be associated with the + message, if LABEL is NULL the default is to use the empty string as + label. On success the plaintext is returned as a newly allocated + buffer at R_RESULT; its valid length is stored at R_RESULTLEN. On + error NULL is stored at R_RESULT. */ +static gcry_err_code_t +oaep_decode (unsigned char **r_result, size_t *r_resultlen, + unsigned int nbits, int algo, + gcry_mpi_t value, const unsigned char *label, size_t labellen) +{ + gcry_err_code_t rc; + unsigned char *frame = NULL; /* Encoded messages (EM). */ + unsigned char *masked_seed; /* Points into FRAME. */ + unsigned char *masked_db; /* Points into FRAME. */ + unsigned char *seed = NULL; /* Allocated space for the seed and DB. */ + unsigned char *db; /* Points into SEED. */ + unsigned char *lhash = NULL; /* Hash of the label. */ + size_t nframe; /* Length of the ciphertext (EM). */ + size_t hlen; /* Length of the hash digest. */ + size_t db_len; /* Length of DB and masked_db. */ + size_t nkey = (nbits+7)/8; /* Length of the key in bytes. */ + int failed = 0; /* Error indicator. */ + size_t n; + + *r_result = NULL; + + /* This code is implemented as described by rfc-3447 7.1.2. */ + + /* Set defaults for LABEL. */ + if (!label || !labellen) + { + label = (const unsigned char*)""; + labellen = 0; + } + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + + /* Hash the label right away. */ + lhash = gcry_malloc (hlen); + if (!lhash) + return gpg_err_code_from_syserror (); + gcry_md_hash_buffer (algo, lhash, label, labellen); + + /* Turn the MPI into an octet string. If the octet string is + shorter than the key we pad it to the left with zeroes. This may + happen due to the leading zero in OAEP frames and due to the + following random octets (seed^mask) which may have leading zero + bytes. This all is needed to cope with our leading zeroes + suppressing MPI implementation. The code implictly implements + Step 1b (bail out if NFRAME != N). */ + rc = octet_string_from_mpi (&frame, NULL, value, nkey); + if (rc) + { + gcry_free (lhash); + return GPG_ERR_ENCODING_PROBLEM; + } + nframe = nkey; + + /* Step 1c: Check that the key is long enough. */ + if ( nframe < 2 * hlen + 2 ) + { + gcry_free (frame); + gcry_free (lhash); + return GPG_ERR_ENCODING_PROBLEM; + } + + /* Step 2 has already been done by the caller and the + gcry_mpi_aprint above. */ + + /* Allocate space for SEED and DB. */ + seed = gcry_malloc_secure (nframe - 1); + if (!seed) + { + rc = gpg_err_code_from_syserror (); + gcry_free (frame); + gcry_free (lhash); + return rc; + } + db = seed + hlen; + + /* To avoid choosen ciphertext attacks from now on we make sure to + run all code even in the error case; this avoids possible timing + attacks as described by Manger. */ + + /* Step 3a: Hash the label. */ + /* This has already been done. */ + + /* Step 3b: Separate the encoded message. */ + masked_seed = frame + 1; + masked_db = frame + 1 + hlen; + db_len = nframe - 1 - hlen; + + /* Step 3c and 3d: seed = maskedSeed ^ mgf(maskedDB, hlen). */ + if (mgf1 (seed, hlen, masked_db, db_len, algo)) + failed = 1; + for (n = 0; n < hlen; n++) + seed[n] ^= masked_seed[n]; + + /* Step 3e and 3f: db = maskedDB ^ mgf(seed, db_len). */ + if (mgf1 (db, db_len, seed, hlen, algo)) + failed = 1; + for (n = 0; n < db_len; n++) + db[n] ^= masked_db[n]; + + /* Step 3g: Check lhash, an possible empty padding string terminated + by 0x01 and the first byte of EM being 0. */ + if (memcmp (lhash, db, hlen)) + failed = 1; + for (n = hlen; n < db_len; n++) + if (db[n] == 0x01) + break; + if (n == db_len) + failed = 1; + if (frame[0]) + failed = 1; + + gcry_free (lhash); + gcry_free (frame); + if (failed) + { + gcry_free (seed); + return GPG_ERR_ENCODING_PROBLEM; + } + + /* Step 4: Output M. */ + /* To avoid an extra allocation we reuse the seed buffer. The only + caller of this function will anyway free the result soon. */ + n++; + memmove (seed, db + n, db_len - n); + *r_result = seed; + *r_resultlen = db_len - n; + seed = NULL; + + if (DBG_CIPHER) + log_printhex ("value extracted from OAEP encoded data:", + *r_result, *r_resultlen); + + return 0; +} + + +/* RFC-3447 (pkcs#1 v2.1) PSS encoding. Encode {VALUE,VALUELEN} for + an NBITS key. Note that VALUE is already the mHash from the + picture below. ALGO is a valid hash algorithm and SALTLEN is the + length of salt to be used. On success the result is stored as a + new MPI at R_RESULT. On error the value at R_RESULT is undefined. + + If {RANDOM_OVERRIDE, RANDOM_OVERRIDE_LEN} is given it is used as + the salt instead of using a random string for the salt. This + feature is only useful for regression tests. + + Here is figure 2 from the RFC (errata 595 applied) depicting the + process: + + +-----------+ + | M | + +-----------+ + | + V + Hash + | + V + +--------+----------+----------+ + M' = |Padding1| mHash | salt | + +--------+----------+----------+ + | + +--------+----------+ V + DB = |Padding2| salt | Hash + +--------+----------+ | + | | + V | +----+ + xor <--- MGF <---| |0xbc| + | | +----+ + | | | + V V V + +-------------------+----------+----+ + EM = | maskedDB | H |0xbc| + +-------------------+----------+----+ + + */ +static gcry_err_code_t +pss_encode (gcry_mpi_t *r_result, unsigned int nbits, int algo, + const unsigned char *value, size_t valuelen, int saltlen, + const void *random_override, size_t random_override_len) +{ + gcry_err_code_t rc = 0; + gcry_error_t err; + size_t hlen; /* Length of the hash digest. */ + unsigned char *em = NULL; /* Encoded message. */ + size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */ + unsigned char *h; /* Points into EM. */ + unsigned char *buf = NULL; /* Help buffer. */ + size_t buflen; /* Length of BUF. */ + unsigned char *mhash; /* Points into BUF. */ + unsigned char *salt; /* Points into BUF. */ + unsigned char *dbmask; /* Points into BUF. */ + unsigned char *p; + size_t n; + + /* This code is implemented as described by rfc-3447 9.1.1. */ + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + + /* Allocate a help buffer and setup some pointers. */ + buflen = 8 + hlen + saltlen + (emlen - hlen - 1); + buf = gcry_malloc (buflen); + if (!buf) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + mhash = buf + 8; + salt = mhash + hlen; + dbmask= salt + saltlen; + + /* Step 2: That would be: mHash = Hash(M) but our input is already + mHash thus we do only a consistency check and copy to MHASH. */ + if (valuelen != hlen) + { + rc = GPG_ERR_INV_LENGTH; + goto leave; + } + memcpy (mhash, value, hlen); + + /* Step 3: Check length constraints. */ + if (emlen < hlen + saltlen + 2) + { + rc = GPG_ERR_TOO_SHORT; + goto leave; + } + + /* Allocate space for EM. */ + em = gcry_malloc (emlen); + if (!em) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + h = em + emlen - 1 - hlen; + + /* Step 4: Create a salt. */ + if (saltlen) + { + if (random_override) + { + if (random_override_len != saltlen) + { + rc = GPG_ERR_INV_ARG; + goto leave; + } + memcpy (salt, random_override, saltlen); + } + else + gcry_randomize (salt, saltlen, GCRY_STRONG_RANDOM); + } + + /* Step 5 and 6: M' = Hash(Padding1 || mHash || salt). */ + memset (buf, 0, 8); /* Padding. */ + gcry_md_hash_buffer (algo, h, buf, 8 + hlen + saltlen); + + /* Step 7 and 8: DB = PS || 0x01 || salt. */ + /* Note that we use EM to store DB and later Xor in-place. */ + p = em + emlen - 1 - hlen - saltlen - 1; + memset (em, 0, p - em); + *p++ = 0x01; + memcpy (p, salt, saltlen); + + /* Step 9: dbmask = MGF(H, emlen - hlen - 1). */ + mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo); + + /* Step 10: maskedDB = DB ^ dbMask */ + for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++) + em[n] ^= *p; + + /* Step 11: Set the leftmost bits to zero. */ + em[0] &= 0xFF >> (8 * emlen - nbits); + + /* Step 12: EM = maskedDB || H || 0xbc. */ + em[emlen-1] = 0xbc; + + /* Convert EM into an MPI. */ + err = gcry_mpi_scan (r_result, GCRYMPI_FMT_USG, em, emlen, NULL); + if (err) + rc = gcry_err_code (err); + else if (DBG_CIPHER) + log_mpidump ("PSS encoded data", *r_result); + + leave: + if (em) + { + wipememory (em, emlen); + gcry_free (em); + } + if (buf) + { + wipememory (buf, buflen); + gcry_free (buf); + } + return rc; +} + + +/* Verify a signature assuming PSS padding. VALUE is the hash of the + message (mHash) encoded as an MPI; its length must match the digest + length of ALGO. ENCODED is the output of the RSA public key + function (EM). NBITS is the size of the public key. ALGO is the + hash algorithm and SALTLEN is the length of the used salt. The + function returns 0 on success or on error code. */ +static gcry_err_code_t +pss_verify (gcry_mpi_t value, gcry_mpi_t encoded, unsigned int nbits, int algo, + size_t saltlen) +{ + gcry_err_code_t rc = 0; + size_t hlen; /* Length of the hash digest. */ + unsigned char *em = NULL; /* Encoded message. */ + size_t emlen = (nbits+7)/8; /* Length in bytes of EM. */ + unsigned char *salt; /* Points into EM. */ + unsigned char *h; /* Points into EM. */ + unsigned char *buf = NULL; /* Help buffer. */ + size_t buflen; /* Length of BUF. */ + unsigned char *dbmask; /* Points into BUF. */ + unsigned char *mhash; /* Points into BUF. */ + unsigned char *p; + size_t n; + + /* This code is implemented as described by rfc-3447 9.1.2. */ + + /* Get the length of the digest. */ + hlen = gcry_md_get_algo_dlen (algo); + gcry_assert (hlen); /* We expect a valid ALGO here. */ + + /* Allocate a help buffer and setup some pointers. + This buffer is used for two purposes: + +------------------------------+-------+ + 1. | dbmask | mHash | + +------------------------------+-------+ + emlen - hlen - 1 hlen + + +----------+-------+---------+-+-------+ + 2. | padding1 | mHash | salt | | mHash | + +----------+-------+---------+-+-------+ + 8 hlen saltlen hlen + */ + buflen = 8 + hlen + saltlen; + if (buflen < emlen - hlen - 1) + buflen = emlen - hlen - 1; + buflen += hlen; + buf = gcry_malloc (buflen); + if (!buf) + { + rc = gpg_err_code_from_syserror (); + goto leave; + } + dbmask = buf; + mhash = buf + buflen - hlen; + + /* Step 2: That would be: mHash = Hash(M) but our input is already + mHash thus we only need to convert VALUE into MHASH. */ + rc = octet_string_from_mpi (NULL, mhash, value, hlen); + if (rc) + goto leave; + + /* Convert the signature into an octet string. */ + rc = octet_string_from_mpi (&em, NULL, encoded, emlen); + if (rc) + goto leave; + + /* Step 3: Check length of EM. Because we internally use MPI + functions we can't do this properly; EMLEN is always the length + of the key because octet_string_from_mpi needs to left pad the + result with zero to cope with the fact that our MPIs suppress all + leading zeroes. Thus what we test here are merely the digest and + salt lengths to the key. */ + if (emlen < hlen + saltlen + 2) + { + rc = GPG_ERR_TOO_SHORT; /* For the hash and saltlen. */ + goto leave; + } + + /* Step 4: Check last octet. */ + if (em[emlen - 1] != 0xbc) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 5: Split EM. */ + h = em + emlen - 1 - hlen; + + /* Step 6: Check the leftmost bits. */ + if ((em[0] & ~(0xFF >> (8 * emlen - nbits)))) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 7: dbmask = MGF(H, emlen - hlen - 1). */ + mgf1 (dbmask, emlen - hlen - 1, h, hlen, algo); + + /* Step 8: maskedDB = DB ^ dbMask. */ + for (n = 0, p = dbmask; n < emlen - hlen - 1; n++, p++) + em[n] ^= *p; + + /* Step 9: Set leftmost bits in DB to zero. */ + em[0] &= 0xFF >> (8 * emlen - nbits); + + /* Step 10: Check the padding of DB. */ + for (n = 0; n < emlen - hlen - saltlen - 2 && !em[n]; n++) + ; + if (n != emlen - hlen - saltlen - 2 || em[n++] != 1) + { + rc = GPG_ERR_BAD_SIGNATURE; + goto leave; + } + + /* Step 11: Extract salt from DB. */ + salt = em + n; + + /* Step 12: M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt */ + memset (buf, 0, 8); + memcpy (buf+8, mhash, hlen); + memcpy (buf+8+hlen, salt, saltlen); + + /* Step 13: H' = Hash(M'). */ + gcry_md_hash_buffer (algo, buf, buf, 8 + hlen + saltlen); + + /* Step 14: Check H == H'. */ + rc = memcmp (h, buf, hlen) ? GPG_ERR_BAD_SIGNATURE : GPG_ERR_NO_ERROR; + + leave: + if (em) + { + wipememory (em, emlen); + gcry_free (em); + } + if (buf) + { + wipememory (buf, buflen); + gcry_free (buf); + } + return rc; +} + + +/* Callback for the pubkey algorithm code to verify PSS signatures. + OPAQUE is the data provided by the actual caller. The meaning of + TMP depends on the actual algorithm (but there is only RSA); now + for RSA it is the output of running the public key function on the + input. */ +static int +pss_verify_cmp (void *opaque, gcry_mpi_t tmp) +{ + struct pk_encoding_ctx *ctx = opaque; + gcry_mpi_t hash = ctx->verify_arg; + + return pss_verify (hash, tmp, ctx->nbits - 1, ctx->hash_algo, ctx->saltlen); +} + + /* Internal function. */ static gcry_err_code_t sexp_elements_extract (gcry_sexp_t key_sexp, const char *element_names, @@ -855,7 +1869,11 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, /* Clear the array for easier error cleanup. */ for (name = element_names, idx = 0; *name; name++, idx++) elements[idx] = NULL; - gcry_assert (idx >= 6); /* We know that ECC has at least 6 elements. */ + gcry_assert (idx >= 5); /* We know that ECC has at least 5 elements + (params only) or 6 (full public key). */ + if (idx == 5) + elements[5] = NULL; /* Extra clear for the params only case. */ + /* Init the array with the available curve parameters. */ for (name = element_names, idx = 0; *name && !err; name++, idx++) @@ -884,23 +1902,23 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, { char *curve; gcry_mpi_t params[6]; - + for (idx = 0; idx < DIM(params); idx++) params[idx] = NULL; - + curve = _gcry_sexp_nth_string (list, 1); gcry_sexp_release (list); if (!curve) { /* No curve name given (or out of core). */ - err = GPG_ERR_INV_OBJ; + err = GPG_ERR_INV_OBJ; goto leave; } err = extraspec->get_param (curve, params); gcry_free (curve); if (err) goto leave; - + for (idx = 0; idx < DIM(params); idx++) { if (!elements[idx]) @@ -924,7 +1942,7 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, err = GPG_ERR_NO_OBJ; goto leave; } - + leave: if (err) { @@ -948,6 +1966,7 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, * openpgp-elg * openpgp-elg-sig * ecdsa + * ecdh * Provide a SE with the first element be either "private-key" or * or "public-key". It is followed by a list with its first element * be one of the above algorithm identifiers and the remaning @@ -955,6 +1974,10 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, * NOTE: we look through the list to find a list beginning with * "private-key" or "public-key" - the first one found is used. * + * If OVERRIDE_ELEMS is not NULL those elems override the parameter + * specification taken from the module. This ise used by + * gcry_pk_get_curve. + * * Returns: A pointer to an allocated array of MPIs if the return value is * zero; the caller has to release this array. * @@ -970,8 +1993,8 @@ sexp_elements_extract_ecc (gcry_sexp_t key_sexp, const char *element_names, * The are expected to be in GCRYMPI_FMT_USG */ static gcry_err_code_t -sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, - gcry_module_t *retalgo) +sexp_to_key (gcry_sexp_t sexp, int want_private, const char *override_elems, + gcry_mpi_t **retarray, gcry_module_t *retalgo) { gcry_err_code_t err = 0; gcry_sexp_t list, l2; @@ -984,7 +2007,7 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, int is_ecc; /* Check that the first element is valid. */ - list = gcry_sexp_find_token (sexp, + list = gcry_sexp_find_token (sexp, want_private? "private-key":"public-key", 0); if (!list) return GPG_ERR_INV_OBJ; /* Does not contain a key object. */ @@ -1002,16 +2025,18 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, ath_mutex_lock (&pubkeys_registered_lock); module = gcry_pk_lookup_name (name); ath_mutex_unlock (&pubkeys_registered_lock); - + /* Fixme: We should make sure that an ECC key is always named "ecc" and not "ecdsa". "ecdsa" should be used for the signature itself. We need a function to test whether an algorithm given with a key is compatible with an application of the key (signing, encryption). For RSA this is easy, but ECC is the first - algorithm which has many flavours. */ - is_ecc = ( !strcmp (name, "ecdsa") || !strcmp (name, "ecc") ); + algorithm which has many flavours. */ + is_ecc = ( !strcmp (name, "ecdsa") + || !strcmp (name, "ecdh") + || !strcmp (name, "ecc") ); gcry_free (name); - + if (!module) { gcry_sexp_release (list); @@ -1023,10 +2048,15 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, extraspec = module->extraspec; } - elems = want_private ? pubkey->elements_skey : pubkey->elements_pkey; + if (override_elems) + elems = override_elems; + else if (want_private) + elems = pubkey->elements_skey; + else + elems = pubkey->elements_pkey; array = gcry_calloc (strlen (elems) + 1, sizeof (*array)); if (!array) - err = gpg_err_code_from_errno (errno); + err = gpg_err_code_from_syserror (); if (!err) { if (is_ecc) @@ -1034,9 +2064,9 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, else err = sexp_elements_extract (list, elems, array, pubkey->name); } - + gcry_sexp_release (list); - + if (err) { gcry_free (array); @@ -1050,7 +2080,7 @@ sexp_to_key (gcry_sexp_t sexp, int want_private, gcry_mpi_t **retarray, *retarray = array; *retalgo = module; } - + return err; } @@ -1066,7 +2096,7 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_mpi_t *array; gcry_module_t module; gcry_pk_spec_t *pubkey; - + /* Check that the first element is valid. */ list = gcry_sexp_find_token( sexp, "sig-val" , 0 ); if (!list) @@ -1085,7 +2115,7 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, gcry_sexp_release (l2); return GPG_ERR_INV_OBJ; /* Invalid structure of object. */ } - else if (!strcmp (name, "flags")) + else if (!strcmp (name, "flags")) { /* Skip flags, since they are not used but here just for the sake of consistent S-expressions. */ @@ -1099,7 +2129,7 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, } name = _gcry_sexp_nth_string (l2, 0); } - + ath_mutex_lock (&pubkeys_registered_lock); module = gcry_pk_lookup_name (name); ath_mutex_unlock (&pubkeys_registered_lock); @@ -1118,7 +2148,7 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, elems = pubkey->elements_sig; array = gcry_calloc (strlen (elems) + 1 , sizeof *array ); if (!array) - err = gpg_err_code_from_errno (errno); + err = gpg_err_code_from_syserror (); if (!err) err = sexp_elements_extract (list, elems, array, NULL); @@ -1131,7 +2161,7 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, ath_mutex_lock (&pubkeys_registered_lock); _gcry_module_release (module); ath_mutex_unlock (&pubkeys_registered_lock); - + gcry_free (array); } else @@ -1139,26 +2169,82 @@ sexp_to_sig (gcry_sexp_t sexp, gcry_mpi_t **retarray, *retarray = array; *retalgo = module; } - + return err; } +static inline int +get_hash_algo (const char *s, size_t n) +{ + static const struct { const char *name; int algo; } hashnames[] = { + { "sha1", GCRY_MD_SHA1 }, + { "md5", GCRY_MD_MD5 }, + { "sha256", GCRY_MD_SHA256 }, + { "ripemd160", GCRY_MD_RMD160 }, + { "rmd160", GCRY_MD_RMD160 }, + { "sha384", GCRY_MD_SHA384 }, + { "sha512", GCRY_MD_SHA512 }, + { "sha224", GCRY_MD_SHA224 }, + { "md2", GCRY_MD_MD2 }, + { "md4", GCRY_MD_MD4 }, + { "tiger", GCRY_MD_TIGER }, + { "haval", GCRY_MD_HAVAL }, + { NULL, 0 } + }; + int algo; + int i; + + for (i=0; hashnames[i].name; i++) + { + if ( strlen (hashnames[i].name) == n + && !memcmp (hashnames[i].name, s, n)) + break; + } + if (hashnames[i].name) + algo = hashnames[i].algo; + else + { + /* In case of not listed or dynamically allocated hash + algorithm we fall back to this somewhat slower + method. Further, it also allows to use OIDs as + algorithm names. */ + char *tmpname; + + tmpname = gcry_malloc (n+1); + if (!tmpname) + algo = 0; /* Out of core - silently give up. */ + else + { + memcpy (tmpname, s, n); + tmpname[n] = 0; + algo = gcry_md_map_name (tmpname); + gcry_free (tmpname); + } + } + return algo; +} + /**************** * Take sexp and return an array of MPI as used for our internal decrypt * function. * s_data = (enc-val - * [(flags [pkcs1])] + * [(flags [raw, pkcs1, oaep, no-blinding])] + * [(hash-algo )] + * [(label