merge mainline into ahci
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Dec 2010 15:16:01 +0000 (16:16 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Fri, 24 Dec 2010 15:16:01 +0000 (16:16 +0100)
28 files changed:
1  2 
grub-core/Makefile.core.def
grub-core/commands/crc.c
grub-core/commands/hdparm.c
grub-core/commands/i386/pc/vbeinfo.c
grub-core/commands/i386/pc/vbetest.c
grub-core/disk/ahci.c
grub-core/disk/ata.c
grub-core/disk/pata.c
grub-core/disk/scsi.c
grub-core/disk/usbms.c
grub-core/efiemu/runtime/efiemu.sh
grub-core/gnulib/alloca.h
grub-core/gnulib/argp-version-etc.c
grub-core/gnulib/argp-version-etc.h
grub-core/gnulib/fnmatch.h
grub-core/gnulib/getopt.h
grub-core/kern/disk.c
grub-core/kern/i386/ieee1275/init.c
grub-core/kern/i386/loader.S
grub-core/kern/i386/misc.S
grub-core/lib/crc.c
grub-core/loader/i386/bsd_helper.S
grub-core/loader/i386/bsd_trampoline.S
grub-core/loader/i386/efi/linux.c
grub-core/loader/i386/ieee1275/linux.c
grub-core/loader/i386/linux_trampoline.S
include/grub/disk.h
include/grub/scsi.h

index 0000000,37c0ce9..dde9454
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1517 +1,1523 @@@
 -  name = ata_pthru;
 -  common = disk/ata_pthru.c;
+ AutoGen definitions Makefile.tpl;
+ script = {
+   installdir = noinst;
+   name = gensyminfo.sh;
+   common = gensyminfo.sh.in;
+ };
+ script = {
+   installdir = noinst;
+   name = genmod.sh;
+   common = genmod.sh.in;
+ };
+ kernel = {
+   name = kernel;
+   nostrip = emu;
+   emu_ldflags              = '-Wl,-r,-d';
+   x86_efi_ldflags          = '-Wl,-r,-d';
+   x86_efi_stripflags       = '--strip-unneeded -K start -R .note -R .comment';
+   i386_pc_ldflags          = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags          = '$(TARGET_IMG_BASE_LDOPT),0x8200';
+   i386_qemu_ldflags        = '$(TARGET_IMG_LDFLAGS)';
+   i386_qemu_ldflags        = '$(TARGET_IMG_BASE_LDOPT),0x8200';
+   ldadd = '$(LDADD_KERNEL)';
+   i386_coreboot_ldflags    = '-Wl,-Ttext=0x8200';
+   i386_multiboot_ldflags   = '-Wl,-Ttext=0x8200';
+   i386_ieee1275_ldflags    = '-Wl,-Ttext=0x10000';
+   mips_yeeloong_ldflags    = '-Wl,-Ttext,0x80200000';
+   powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000';
+   sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400';
+   mips_yeeloong_cppflags = '-DUSE_ASCII_FAILBACK';
+   i386_qemu_cppflags     = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   emu_cflags = '$(CFLAGS_GNULIB)';
+   emu_cppflags = '$(CPPFLAGS_GNULIB)';
+   i386_pc_startup = kern/i386/pc/startup.S;
+   i386_efi_startup = kern/i386/efi/startup.S;
+   x86_64_efi_startup = kern/x86_64/efi/startup.S;
+   i386_qemu_startup = kern/i386/qemu/startup.S;
+   i386_ieee1275_startup = kern/i386/ieee1275/startup.S;
+   i386_coreboot_startup = kern/i386/coreboot/startup.S;
+   i386_multiboot_startup = kern/i386/coreboot/startup.S;
+   mips_yeeloong_startup = kern/mips/startup.S;
+   sparc64_ieee1275_startup = kern/sparc64/ieee1275/crt0.S;
+   powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S;
+   common = kern/command.c;
+   common = kern/corecmd.c;
+   common = kern/device.c;
+   common = kern/disk.c;
+   common = kern/dl.c;
+   common = kern/env.c;
+   common = kern/err.c;
+   common = kern/file.c;
+   common = kern/fs.c;
+   common = kern/list.c;
+   common = kern/main.c;
+   common = kern/misc.c;
+   common = kern/parser.c;
+   common = kern/partition.c;
+   common = kern/rescue_parser.c;
+   common = kern/rescue_reader.c;
+   common = kern/term.c;
+   noemu = kern/mm.c;
+   noemu = kern/time.c;
+   noemu = kern/generic/millisleep.c;
+   noemu_nodist = symlist.c;
+   i386_pc = kern/generic/rtc_get_time_ms.c;
+   x86_efi = kern/generic/rtc_get_time_ms.c;
+   i386_qemu = kern/generic/rtc_get_time_ms.c;
+   i386_coreboot = kern/generic/rtc_get_time_ms.c;
+   i386_multiboot = kern/generic/rtc_get_time_ms.c;
+   mips_yeeloong = kern/generic/rtc_get_time_ms.c;
+   ieee1275 = disk/ieee1275/ofdisk.c;
+   ieee1275 = kern/ieee1275/cmain.c;
+   ieee1275 = kern/ieee1275/ieee1275.c;
+   ieee1275 = kern/ieee1275/mmap.c;
+   ieee1275 = kern/ieee1275/openfw.c;
+   ieee1275 = term/ieee1275/ofconsole.c;
+   terminfoinkernel = term/terminfo.c;
+   terminfoinkernel = term/tparm.c;
+   terminfoinkernel = commands/extcmd.c;
+   terminfoinkernel = lib/arg.c;
+   i386 = kern/i386/dl.c;
+   i386_coreboot_multiboot_qemu = kern/i386/coreboot/init.c;
+   i386_coreboot_multiboot_qemu = term/i386/pc/vga_text.c;
+   i386_coreboot_multiboot_qemu = term/i386/vga_common.c;
+   i386_pc = term/i386/vga_common.c;
+   x86 = kern/i386/pit.c;
+   x86_efi = disk/efi/efidisk.c;
+   x86_efi = kern/efi/efi.c;
+   x86_efi = kern/efi/init.c;
+   x86_efi = kern/efi/mm.c;
+   x86_efi = kern/i386/efi/init.c;
+   x86_efi = term/efi/console.c;
+   i386_efi = kern/i386/tsc.c;
+   x86_64_efi = kern/i386/tsc.c;
+   x86_64_efi = kern/x86_64/dl.c;
+   x86_64_efi = kern/x86_64/efi/callwrap.S;
+   i386_pc = kern/i386/pc/init.c;
+   i386_pc = kern/i386/pc/mmap.c;
+   i386_pc = kern/i386/tsc.c;
+   i386_pc = term/i386/pc/console.c;
+   i386_qemu = bus/pci.c;
+   i386_qemu = kern/i386/qemu/init.c;
+   i386_qemu = kern/i386/qemu/mmap.c;
+   i386_qemu = kern/i386/tsc.c;
+   i386_coreboot = kern/i386/coreboot/mmap.c;
+   i386_coreboot = kern/i386/tsc.c;
+   i386_multiboot = kern/i386/multiboot_mmap.c;
+   i386_multiboot = kern/i386/tsc.c;
+   i386_ieee1275 = kern/ieee1275/init.c;
+   mips_yeeloong = term/ns8250.c;
+   mips_yeeloong = bus/bonito.c;
+   mips_yeeloong = bus/cs5536.c;
+   mips_yeeloong = bus/pci.c;
+   mips_yeeloong = kern/mips/cache.S;
+   mips_yeeloong = kern/mips/dl.c;
+   mips_yeeloong = kern/mips/init.c;
+   mips_yeeloong = kern/mips/yeeloong/init.c;
+   mips_yeeloong = term/at_keyboard.c;
+   mips_yeeloong = term/serial.c;
+   mips_yeeloong = video/sm712.c;
+   extra_dist = video/sm712_init.c;
+   mips_yeeloong = commands/keylayouts.c;
+   powerpc_ieee1275 = kern/ieee1275/init.c;
+   powerpc_ieee1275 = kern/powerpc/cache.S;
+   powerpc_ieee1275 = kern/powerpc/dl.c;
+   sparc64_ieee1275 = kern/sparc64/cache.S;
+   sparc64_ieee1275 = kern/sparc64/dl.c;
+   sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c;
+   sparc64_ieee1275 = kern/sparc64/ieee1275/init.c;
+   emu = disk/host.c;
+   emu = gnulib/progname.c;
+   emu = gnulib/error.c;
+   emu = kern/emu/cache.S;
+   emu = kern/emu/console.c;
+   emu = kern/emu/getroot.c;
+   emu = kern/emu/hostdisk.c;
+   emu = kern/emu/hostfs.c;
+   emu = kern/emu/main.c;
+   emu = kern/emu/misc.c;
+   emu = kern/emu/mm.c;
+   emu = kern/emu/time.c;
+   videoinkernel = term/gfxterm.c;
+   videoinkernel = font/font.c;
+   videoinkernel = font/font_cmd.c;
+   videoinkernel = io/bufio.c;
+   videoinkernel = video/bitmap.c;
+   videoinkernel = video/bitmap_scale.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;
+   videoinkernel = commands/boot.c;
+   extra_dist = kern/i386/realmode.S;
+   extra_dist = kern/i386/pc/lzma_decode.S;
+   extra_dist = kern/mips/cache_flush.S;
+ };
+ program = {
+   name = grub-emu;
+   mansection = 1;
+   emu = kern/emu/full.c;
+   emu_nodist = grub_emu_init.c;
+   ldadd = 'kernel.img$(EXEEXT)';
+   ldadd = '$(MODULE_FILES)';
+   ldadd = '$(LIBUTIL) $(LIBCURSES) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR)';
+   enable = emu;
+ };
+ program = {
+   name = grub-emu-lite;
+   emu = kern/emu/lite.c;
+   emu_nodist = symlist.c;
+   ldadd = 'kernel.img$(EXEEXT)';
+   ldadd = '$(LIBUTIL) $(LIBCURSES) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR)';
+   enable = emu;
+ };
+ image = {
+   name = boot;
+   i386_pc = boot/i386/pc/boot.S;
+   i386_qemu = boot/i386/qemu/boot.S;
+   sparc64_ieee1275 = boot/sparc64/ieee1275/boot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   i386_qemu_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_qemu_ldflags = '$(TARGET_IMG_BASE_LDOPT),$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   i386_qemu_ccasflags = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+   sparc64_ieee1275_objcopyflags = '-O a.out-sunos-big';
+   sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x4000';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+   enable = i386_qemu;
+   enable = sparc64_ieee1275;
+ };
+ image = {
+   name = cdboot;
+   i386_pc = boot/i386/pc/cdboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = pxeboot;
+   i386_pc = boot/i386/pc/pxeboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = diskboot;
+   i386_pc = boot/i386/pc/diskboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x8000';
+   sparc64_ieee1275 = boot/sparc64/ieee1275/diskboot.S;
+   sparc64_ieee1275_ldflags = '-Wl,-Ttext=0x4200';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+   enable = sparc64_ieee1275;
+ };
+ image = {
+   name = lnxboot;
+   i386_pc = boot/i386/pc/lnxboot.S;
+   i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+   i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x6000';
+   objcopyflags = '-O binary';
+   enable = i386_pc;
+ };
+ image = {
+   name = xz_decompress;
+   mips = 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;
+   cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed';
+   mips_cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -DGRUB_EMBED_DECOMPRESSOR=1 -DGRUB_MACHINE_LINK_ADDR=0x80200000';
+   objcopyflags = '-O binary';
+   ldflags = '-static-libgcc -Wl,-Ttext,0x80100000';
+   ldadd = '-lgcc';
+   cflags = '-static-libgcc';
+   enable = mips;
+ };
+ image = {
+   name = none_decompress;
+   mips = boot/mips/startup_raw.S;
+   common = boot/decompressor/none.c;
+   mips_cppflags = '-DGRUB_EMBED_DECOMPRESSOR=1 -DGRUB_MACHINE_LINK_ADDR=0x80200000';
+   objcopyflags = '-O binary';
+   ldflags = '-static-libgcc -Wl,-Ttext,0x80100000';
+   ldadd = '-lgcc';
+   cflags = '-static-libgcc';
+   enable = mips;
+ };
+ image = {
+   name = fwstart;
+   mips_yeeloong = boot/mips/yeeloong/fwstart.S;
+   objcopyflags = '-O binary';
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = trig;
+   common_nodist = trigtables.c;
+   extra_dist = gentrigtables.c;
+ };
+ module = {
+   name = cs5536;
+   x86 = bus/cs5536.c;
+   enable = x86;
+ };
+ module = {
+   name = libusb;
+   emu = bus/usb/emu/usb.c;
+   enable = emu;
+   condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = lsspd;
+   mips_yeeloong = commands/mips/yeeloong/lsspd.c;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = usb;
+   common = bus/usb/usb.c;
+   noemu = bus/usb/usbtrans.c;
+   noemu = bus/usb/usbhub.c;
+   enable = emu;
+   enable = usb;
+   emu_condition = COND_GRUB_EMU_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 = uhci;
+   common = bus/usb/uhci.c;
+   enable = x86;
+ };
+ module = {
+   name = ohci;
+   common = bus/usb/ohci.c;
+   enable = pci;
+ };
+ module = {
+   name = pci;
+   noemu = bus/pci.c;
+   emu = bus/emu/pci.c;
+   emu = commands/lspci.c;
+   enable = emu;
+   enable = i386_pc;
+   enable = x86_efi;
+   enable = i386_ieee1275;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+   emu_condition = COND_GRUB_EMU_PCI;
+ };
+ library = {
+   name = libgnulib.a;
+   common = gnulib/regex.c;
+   cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+ };
+ module = {
+   name = cmostest;
+   common = commands/i386/cmostest.c;
+   enable = cmos;
+ };
+ module = {
+   name = iorw;
+   common = commands/iorw.c;
+   enable = x86;
+ };
+ module = {
+   name = regexp;
+   common = commands/regexp.c;
+   common = commands/wildcard.c;
+   ldadd = libgnulib.a;
+   cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+ };
+ module = {
+   name = acpi;
+   common = commands/acpi.c;
+   x86_efi = commands/efi/acpi.c;
+   i386_pc = commands/i386/pc/acpi.c;
+   i386_coreboot = commands/i386/pc/acpi.c;
+   i386_multiboot = commands/i386/pc/acpi.c;
+   enable = x86_efi;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+ };
+ module = {
+   name = lsacpi;
+   common = commands/lsacpi.c;
+   enable = x86_efi;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+ };
+ module = {
+   name = lsefisystab;
+   common = commands/efi/lsefisystab.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = lssal;
+   common = commands/efi/lssal.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = lsefimmap;
+   common = commands/efi/lsefimmap.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = blocklist;
+   common = commands/blocklist.c;
+ };
+ module = {
+   name = boot;
+   common = commands/boot.c;
+   i386_pc = lib/i386/pc/biosnum.c;
+   enable = videomodules;
+ };
+ module = {
+   name = cat;
+   common = commands/cat.c;
+ };
+ module = {
+   name = cmp;
+   common = commands/cmp.c;
+ };
+ module = {
+   name = configfile;
+   common = commands/configfile.c;
+ };
+ module = {
+   name = cpuid;
+   x86 = commands/i386/cpuid.c;
+   enable = x86;
+ };
+ module = {
+   name = date;
+   common = commands/date.c;
+ };
+ module = {
+   name = drivemap;
+   i386_pc = commands/i386/pc/drivemap.c;
+   i386_pc = commands/i386/pc/drivemap_int13h.S;
+   enable = i386_pc;
+ };
+ module = {
+   name = echo;
+   common = commands/echo.c;
+ };
+ module = {
+   name = extcmd;
+   common = commands/extcmd.c;
+   common = lib/arg.c;
+   enable = terminfomodule;
+ };
+ module = {
+   name = fixvideo;
+   x86_efi = commands/efi/fixvideo.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = gptsync;
+   common = commands/gptsync.c;
+ };
+ module = {
+   name = halt;
+   nopc = commands/halt.c;
+   i386_pc = commands/i386/pc/halt.c;
+   i386_pc = commands/acpihalt.c;
+   i386_coreboot = commands/acpihalt.c;
+   i386_multiboot = commands/acpihalt.c;
+   x86_efi = commands/acpihalt.c;
+   i386_multiboot = lib/i386/halt.c;
+   i386_coreboot = lib/i386/halt.c;
+   i386_qemu = lib/i386/halt.c;
+   x86_efi = lib/efi/halt.c;
+   ieee1275 = lib/ieee1275/halt.c;
+   emu = lib/emu/halt.c;
+ };
+ module = {
+   name = hashsum;
+   common = commands/hashsum.c;
+ };
+ module = {
+   name = hdparm;
+   common = commands/hdparm.c;
+   common = lib/hexdump.c;
+   enable = pci;
+ };
+ module = {
+   name = help;
+   common = commands/help.c;
+ };
+ module = {
+   name = hexdump;
+   common = commands/hexdump.c;
+   common = lib/hexdump.c;
+ };
+ module = {
+   name = keystatus;
+   common = commands/keystatus.c;
+ };
+ module = {
+   name = loadbios;
+   x86_efi = commands/efi/loadbios.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = loadenv;
+   common = commands/loadenv.c;
+   common = lib/envblk.c;
+ };
+ module = {
+   name = ls;
+   common = commands/ls.c;
+ };
+ module = {
+   name = lsmmap;
+   common = commands/lsmmap.c;
+ };
+ module = {
+   name = lspci;
+   common = commands/lspci.c;
+   enable = 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 = probe;
+   common = commands/probe.c;
+ };
+ module = {
+   name = pxecmd;
+   i386_pc = commands/i386/pc/pxecmd.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = read;
+   common = commands/read.c;
+ };
+ module = {
+   name = reboot;
+   common = commands/reboot.c;
+ };
+ module = {
+   name = search;
+   common = commands/search_wrap.c;
+   extra_dist = commands/search.c;
+ };
+ module = {
+   name = search_fs_file;
+   common = commands/search_file.c;
+ };
+ module = {
+   name = search_fs_uuid;
+   common = commands/search_uuid.c;
+ };
+ module = {
+   name = search_label;
+   common = commands/search_label.c;
+ };
+ module = {
+   name = setpci;
+   common = commands/setpci.c;
+   enable = x86;
+ };
+ module = {
+   name = sleep;
+   common = commands/sleep.c;
+ };
+ module = {
+   name = suspend;
+   ieee1275 = commands/ieee1275/suspend.c;
+   enable = i386_ieee1275;
+   enable = powerpc_ieee1275;
+ };
+ module = {
+   name = terminal;
+   common = commands/terminal.c;
+ };
+ module = {
+   name = test;
+   common = commands/test.c;
+ };
+ module = {
+   name = true;
+   common = commands/true.c;
+ };
+ module = {
+   name = usbtest;
+   common = commands/usbtest.c;
+   enable = usb;
+   enable = emu;
+   emu_condition = COND_GRUB_EMU_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 = lvm;
+   common = disk/lvm.c;
+ };
+ module = {
+   name = mdraid09;
+   common = disk/mdraid_linux.c;
+ };
+ module = {
+   name = mdraid1x;
+   common = disk/mdraid1x_linux.c;
+ };
+ module = {
+   name = raid;
+   common = disk/raid.c;
+ };
+ module = {
+   name = raid5rec;
+   common = disk/raid5_recover.c;
+ };
+ module = {
+   name = raid6rec;
+   common = disk/raid6_recover.c;
+ };
+ module = {
+   name = scsi;
+   common = disk/scsi.c;
+ };
+ module = {
+   name = memdisk;
+   common = disk/memdisk.c;
+ };
+ module = {
+   name = ata;
+   common = disk/ata.c;
+   enable = pci;
+ };
+ module = {
++  name = ahci;
++  common = disk/ahci.c;
++  enable = pci;
++};
++
++module = {
++  name = pata;
++  common = disk/pata.c;
+   enable = pci;
+ };
+ module = {
+   name = biosdisk;
+   i386_pc = disk/i386/pc/biosdisk.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = usbms;
+   common = disk/usbms.c;
+   enable = usb;
+   enable = emu;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = nand;
+   ieee1275 = disk/ieee1275/nand.c;
+   enable = i386_ieee1275;
+ };
+ module = {
+   name = efiemu;
+   common = efiemu/main.c;
+   common = efiemu/i386/loadcore32.c;
+   common = efiemu/i386/loadcore64.c;
+   i386_pc = efiemu/i386/pc/cfgtables.c;
+   i386_coreboot = efiemu/i386/pc/cfgtables.c;
+   i386_multiboot = efiemu/i386/pc/cfgtables.c;
+   i386_ieee1275 = efiemu/i386/nocfgtables.c;
+   i386_qemu = efiemu/i386/nocfgtables.c;
+   common = efiemu/mm.c;
+   common = efiemu/loadcore_common.c;
+   common = efiemu/symbols.c;
+   common = efiemu/loadcore32.c;
+   common = efiemu/loadcore64.c;
+   common = efiemu/prepare32.c;
+   common = efiemu/prepare64.c;
+   common = efiemu/pnvram.c;
+   common = efiemu/i386/coredetect.c;
+   extra_dist = efiemu/prepare.c;
+   extra_dist = efiemu/loadcore.c;
+   extra_dist = efiemu/runtime/efiemu.S;
+   extra_dist = efiemu/runtime/efiemu.c;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_ieee1275;
+   enable = i386_multiboot;
+   enable = i386_qemu;
+ };
+ module = {
+   name = font;
+   common = font/font.c;
+   common = font/font_cmd.c;
+   enable = videomodules;
+ };
+ module = {
+   name = affs;
+   common = fs/affs.c;
+ };
+ module = {
+   name = afs;
+   common = fs/afs.c;
+ };
+ module = {
+   name = afs_be;
+   common = fs/afs_be.c;
+ };
+ module = {
+   name = befs;
+   common = fs/befs.c;
+ };
+ module = {
+   name = befs_be;
+   common = fs/befs_be.c;
+ };
+ module = {
+   name = btrfs;
+   common = fs/btrfs.c;
+ };
+ module = {
+   name = cpio;
+   common = fs/cpio.c;
+ };
+ module = {
+   name = ext2;
+   common = fs/ext2.c;
+ };
+ module = {
+   name = fat;
+   common = fs/fat.c;
+ };
+ module = {
+   name = fshelp;
+   common = fs/fshelp.c;
+ };
+ module = {
+   name = hfs;
+   common = fs/hfs.c;
+ };
+ module = {
+   name = hfsplus;
+   common = fs/hfsplus.c;
+ };
+ module = {
+   name = iso9660;
+   common = fs/iso9660.c;
+ };
+ module = {
+   name = jfs;
+   common = fs/jfs.c;
+ };
+ module = {
+   name = minix;
+   common = fs/minix.c;
+ };
+ module = {
+   name = minix2;
+   common = fs/minix2.c;
+ };
+ module = {
+   name = nilfs2;
+   common = fs/nilfs2.c;
+ };
+ module = {
+   name = ntfs;
+   common = fs/ntfs.c;
+ };
+ module = {
+   name = ntfscomp;
+   common = fs/ntfscomp.c;
+ };
+ module = {
+   name = reiserfs;
+   common = fs/reiserfs.c;
+ };
+ module = {
+   name = sfs;
+   common = fs/sfs.c;
+ };
+ module = {
+   name = tar;
+   common = fs/tar.c;
+ };
+ module = {
+   name = udf;
+   common = fs/udf.c;
+ };
+ module = {
+   name = ufs1;
+   common = fs/ufs.c;
+ };
+ module = {
+   name = ufs2;
+   common = fs/ufs2.c;
+ };
+ module = {
+   name = xfs;
+   common = fs/xfs.c;
+ };
+ module = {
+   name = zfs;
+   common = fs/zfs/zfs.c;
+   common = fs/zfs/zfs_lzjb.c;
+   common = fs/zfs/zfs_sha256.c;
+   common = fs/zfs/zfs_fletcher.c;
+ };
+ module = {
+   name = zfsinfo;
+   common = fs/zfs/zfsinfo.c;
+ };
+ module = {
+   name = pxe;
+   i386_pc = fs/i386/pc/pxe.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = gettext;
+   common = gettext/gettext.c;
+ };
+ module = {
+   name = gfxmenu;
+   common = gfxmenu/gfxmenu.c;
+   common = gfxmenu/model.c;
+   common = gfxmenu/view.c;
+   common = gfxmenu/font.c;
+   common = gfxmenu/icon_manager.c;
+   common = gfxmenu/theme_loader.c;
+   common = gfxmenu/widget-box.c;
+   common = gfxmenu/gui_canvas.c;
+   common = gfxmenu/gui_circular_progress.c;
+   common = gfxmenu/gui_box.c;
+   common = gfxmenu/gui_label.c;
+   common = gfxmenu/gui_list.c;
+   common = gfxmenu/gui_image.c;
+   common = gfxmenu/gui_progress_bar.c;
+   common = gfxmenu/gui_util.c;
+   common = gfxmenu/gui_string_util.c;
+   common = gfxmenu/named_colors.c;
+ };
+ module = {
+   name = hello;
+   common = hello/hello.c;
+ };
+ module = {
+   name = gzio;
+   common = io/gzio.c;
+ };
+ module = {
+   name = bufio;
+   common = io/bufio.c;
+   enable = videomodules;
+ };
+ module = {
+   name = elf;
+   common = kern/elf.c;
+ };
+ module = {
+   name = crypto;
+   common = lib/crypto.c;
+   extra_dist = lib/libgcrypt-grub/cipher/crypto.lst;
+ };
+ module = {
+   name = pbkdf2;
+   common = lib/pbkdf2.c;
+ };
+ module = {
+   name = relocator;
+   common = lib/relocator.c;
+   x86 = lib/i386/relocator16.S;
+   x86 = lib/i386/relocator32.S;
+   x86 = lib/i386/relocator64.S;
+   i386 = lib/i386/relocator_asm.S;
+   x86_64 = lib/x86_64/relocator_asm.S;
+   x86 = lib/i386/relocator.c;
+   ieee1275 = lib/ieee1275/relocator.c;
+   x86_efi = lib/efi/relocator.c;
+   mips = lib/mips/relocator_asm.S;
+   mips = lib/mips/relocator.c;
+   powerpc = lib/powerpc/relocator_asm.S;
+   powerpc = lib/powerpc/relocator.c;
+   extra_dist = lib/i386/relocator_common.S;
+   extra_dist = kern/powerpc/cache_flush.S;
+   enable = mips;
+   enable = powerpc;
+   enable = x86;
+ };
+ module = {
+   name = datetime;
+   cmos = lib/cmos_datetime.c;
+   x86_efi = lib/efi/datetime.c;
+   sparc64_ieee1275 = lib/ieee1275/datetime.c;
+   powerpc_ieee1275 = lib/ieee1275/datetime.c;
+   enable = noemu;
+ };
+ module = {
+   name = setjmp;
+   common = lib/setjmp.S;
+   extra_dist = lib/i386/setjmp.S;
+   extra_dist = lib/mips/setjmp.S;
+   extra_dist = lib/x86_64/setjmp.S;
+   extra_dist = lib/sparc64/setjmp.S;
+   extra_dist = lib/powerpc/setjmp.S;
+ };
+ module = {
+   name = aout;
+   common = loader/aout.c;
+   enable = x86;
+ };
+ module = {
+   name = bsd;
+   x86 = loader/i386/bsd.c;
+   x86 = loader/i386/bsd32.c;
+   x86 = loader/i386/bsd64.c;
+   extra_dist = loader/i386/bsdXX.c;
+   extra_dist = loader/i386/bsd_pagetable.c;
+   enable = x86;
+ };
+ module = {
+   name = linux16;
+   i386_pc = loader/i386/pc/linux.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = ntldr;
+   i386_pc = loader/i386/pc/ntldr.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = multiboot2;
+   cppflags = "-DGRUB_USE_MULTIBOOT2";
+   common = loader/multiboot.c;
+   common = loader/multiboot_mbi2.c;
+   enable = x86;
+   enable = mips;
+ };
+ module = {
+   name = multiboot;
+   common = loader/multiboot.c;
+   x86 = loader/i386/multiboot_mbi.c;
+   extra_dist = loader/multiboot_elfxx.c;
+   enable = x86;
+ };
+ module = {
+   name = linux;
+   x86 = loader/i386/linux.c;
+   i386_pc = 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;
+   enable = noemu;
+ };
+ module = {
+   name = xnu;
+   x86 = loader/xnu_resume.c;
+   x86 = loader/i386/xnu.c;
+   x86 = loader/macho32.c;
+   x86 = loader/macho64.c;
+   x86 = loader/macho.c;
+   x86 = loader/xnu.c;
+   extra_dist = loader/machoXX.c;
+   enable = x86;
+ };
+ module = {
+   name = appleldr;
+   x86_efi = loader/efi/appleloader.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = chain;
+   x86_efi = loader/efi/chainloader.c;
+   i386_pc = loader/i386/pc/chainloader.c;
+   enable = i386_pc;
+   enable = x86_efi;
+ };
+ module = {
+   name = mmap;
+   common = mmap/mmap.c;
+   x86 = mmap/i386/uppermem.c;
+   x86 = mmap/i386/mmap.c;
+   i386_pc = mmap/i386/pc/mmap.c;
+   i386_pc = mmap/i386/pc/mmap_helper.S;
+   x86_efi = mmap/efi/mmap.c;
+   mips_yeeloong = mmap/mips/yeeloong/uppermem.c;
+   enable = x86;
+   enable = mips_yeeloong;
+ };
+ module = {
+   name = normal;
+   common = normal/main.c;
+   common = normal/cmdline.c;
+   common = normal/dyncmd.c;
+   common = normal/auth.c;
+   common = normal/autofs.c;
+   common = normal/color.c;
+   common = normal/completion.c;
+   common = normal/datetime.c;
+   common = normal/menu.c;
+   common = normal/menu_entry.c;
+   common = normal/menu_text.c;
+   common = normal/misc.c;
+   common = normal/crypto.c;
+   common = normal/term.c;
+   common = normal/context.c;
+   common = normal/charset.c;
+   common = script/main.c;
+   common = script/script.c;
+   common = script/execute.c;
+   common = script/function.c;
+   common = script/lexer.c;
+   common = script/argv.c;
+   common = 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-error';
+   cppflags = '$(CPPFLAGS_POSIX)';
+ };
+ module = {
+   name = part_acorn;
+   common = partmap/acorn.c;
+ };
+ module = {
+   name = part_amiga;
+   common = partmap/amiga.c;
+ };
+ module = {
+   name = part_apple;
+   common = partmap/apple.c;
+ };
+ module = {
+   name = part_gpt;
+   common = partmap/gpt.c;
+ };
+ module = {
+   name = part_msdos;
+   common = partmap/msdos.c;
+ };
+ module = {
+   name = part_sun;
+   common = partmap/sun.c;
+ };
+ module = {
+   name = part_bsd;
+   common = partmap/bsdlabel.c;
+ };
+ module = {
+   name = part_sunpc;
+   common = partmap/sunpc.c;
+ };
+ module = {
+   name = msdospart;
+   common = parttool/msdospart.c;
+ };
+ module = {
+   name = at_keyboard;
+   common = term/at_keyboard.c;
+   enable = x86;
+ };
+ module = {
+   name = gfxterm;
+   common = term/gfxterm.c;
+   enable = videomodules;
+ };
+ module = {
+   name = serial;
+   common = term/serial.c;
+   x86 = term/ns8250.c;
+   enable = emu;
+   enable = i386;
+   enable = x86_64_efi;
+   emu_condition = COND_GRUB_EMU_USB;
+ };
+ module = {
+   name = sendkey;
+   i386_pc = commands/i386/pc/sendkey.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = terminfo;
+   common = term/terminfo.c;
+   common = term/tparm.c;
+   enable = terminfomodule;
+ };
+ module = {
+   name = usb_keyboard;
+   common = term/usb_keyboard.c;
+   enable = usb;
+ };
+ module = {
+   name = vga;
+   i386_pc = video/i386/pc/vga.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = vga_text;
+   common = term/i386/pc/vga_text.c;
+   common = term/i386/vga_common.c;
+   enable = i386_pc;
+   enable = i386_coreboot;
+   enable = i386_multiboot;
+ };
+ module = {
+   name = video_cirrus;
+   x86 = video/cirrus.c;
+   enable = x86;
+ };
+ module = {
+   name = video_bochs;
+   x86 = video/bochs.c;
+   enable = x86;
+ };
+ module = {
+   name = functional_test;
+   common = tests/lib/functional_test.c;
+   common = tests/lib/test.c;
+ };
+ module = {
+   name = example_functional_test;
+   common = tests/example_functional_test.c;
+   cflags = -Wno-format;
+ };
+ module = {
+   name = bitmap;
+   common = video/bitmap.c;
+   enable = videomodules;
+ };
+ module = {
+   name = bitmap_scale;
+   common = video/bitmap_scale.c;
+   enable = videomodules;
+ };
+ module = {
+   name = efi_gop;
+   x86_efi = video/efi_gop.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = efi_uga;
+   x86_efi = video/efi_uga.c;
+   enable = x86_efi;
+ };
+ module = {
+   name = jpeg;
+   common = video/readers/jpeg.c;
+ };
+ module = {
+   name = png;
+   common = video/readers/png.c;
+ };
+ module = {
+   name = tga;
+   common = video/readers/tga.c;
+ };
+ module = {
+   name = vbe;
+   i386_pc = video/i386/pc/vbe.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = video_fb;
+   common = video/fb/video_fb.c;
+   common = video/fb/fbblit.c;
+   common = video/fb/fbfill.c;
+   common = video/fb/fbutil.c;
+   enable = videomodules;
+ };
+ module = {
+   name = video;
+   common = video/video.c;
+   enable = videomodules;
+ };
+ module = {
+   name = ieee1275_fb;
+   ieee1275 = video/ieee1275.c;
+   enable = powerpc;
+   enable = sparc64;
+ };
+ module = {
+   name = sdl;
+   emu = video/emu/sdl.c;
+   enable = emu;
+   condition = COND_GRUB_EMU_SDL;
+ };
+ module = {
+   name = datehook;
+   common = hook/datehook.c;
+ };
+ module = {
+   name = legacycfg;
+   common = commands/legacycfg.c;
+   common = lib/legacy_parse.c;
+   emu = lib/i386/pc/vesa_modes_table.c;
+   enable = i386_pc;
+   enable = emu;
+ };
+ 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';
+ };
+ module = {
+   name = testload;
+   common = commands/testload.c;
+ };
+ module = {
+   name = lsapm;
+   common = commands/i386/pc/lsapm.c;
+   enable = i386_pc;
+ };
+ module = {
+   name = keylayouts;
+   common = commands/keylayouts.c;
+   enable = videomodules;
+ };
index 1c1a690,0000000..1c1a690
mode 100644,000000..100644
--- /dev/null
index 0000000,b6ab787..0154ece
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,420 +1,441 @@@
 -grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
+ /* hdparm.c - command to get/set ATA disk parameters.  */
+ /*
+  *  GRUB  --  GRand Unified Bootloader
+  *  Copyright (C) 2009  Free Software Foundation, Inc.
+  *
+  *  GRUB is free software: you can redistribute it and/or modify
+  *  it under the terms of the GNU General Public License as published by
+  *  the Free Software Foundation, either version 3 of the License, or
+  *  (at your option) any later version.
+  *
+  *  GRUB is distributed in the hope that it will be useful,
+  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  *  GNU General Public License for more details.
+  *
+  *  You should have received a copy of the GNU General Public License
+  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/ata.h>
++#include <grub/scsi.h>
+ #include <grub/disk.h>
+ #include <grub/dl.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/lib/hexdump.h>
+ #include <grub/extcmd.h>
+ #include <grub/i18n.h>
+ 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},
+   {"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},
+   {"aam",             'M', 0, N_("Set Automatic Acoustic Management\n"
+                             "(0=off, 128=quiet, ..., 254=fast)."),
+                             0, ARG_TYPE_INT},
+   {"standby-timeout", 'S', 0, N_("Set standby timeout\n"
+                             "(0=off, 1=5s, 2=10s, ..., 240=20m, 241=30m, ...)."),
+                             0, ARG_TYPE_INT},
+   {"standby",         'y', 0, N_("Set drive to standby mode."), 0, ARG_TYPE_NONE},
+   {"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."),
+                              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},
+   {0, 0, 0, 0, 0, 0}
+ };
+ enum grub_ata_smart_commands
+   {
+     GRUB_ATA_FEAT_SMART_ENABLE  = 0xd8,
+     GRUB_ATA_FEAT_SMART_DISABLE = 0xd9,
+     GRUB_ATA_FEAT_SMART_STATUS  = 0xda,
+   };
+ static int quiet = 0;
+ static grub_err_t
 -  apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
 -  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
 -  apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
++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));
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  apt.taskfile.cmd = cmd;
++  apt.taskfile.features = features;
++  apt.taskfile.sectors = sectors;
+   apt.buffer = buffer;
+   apt.size = size;
 -grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
++  if (ata->dev->readwrite (ata, &apt))
+     return grub_errno;
+   return GRUB_ERR_NONE;
+ }
+ static int
 -  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
++grub_hdparm_do_check_powermode_cmd (grub_ata_t ata)
+ {
+   struct grub_disk_ata_pass_through_parms apt;
+   grub_memset (&apt, 0, sizeof (apt));
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE;
 -  return apt.taskfile[GRUB_ATA_REG_SECTORS];
++  if (ata->dev->readwrite (ata, &apt))
+     return -1;
 -grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
++  return apt.taskfile.sectors;
+ }
+ static int
 -  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;
++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));
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  apt.taskfile.cmd = GRUB_ATA_CMD_SMART;
++  apt.taskfile.features = features;
++  apt.taskfile.lba_mid  = 0x4f;
++  apt.taskfile.lba_high = 0xc2;
 -      if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0x4f
 -          && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
++  if (ata->dev->readwrite (ata, &apt))
+     return -1;
+   if (features == GRUB_ATA_FEAT_SMART_STATUS)
+     {
 -      else if (   apt.taskfile[GRUB_ATA_REG_LBAMID]  == 0xf4
 -             && apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
++      if (   apt.taskfile.lba_mid  == 0x4f
++          && apt.taskfile.lba_high == 0xc2)
+       return 0; /* Good SMART status.  */
 -                      grub_disk_t disk, grub_uint8_t cmd)
++      else if (   apt.taskfile.lba_mid  == 0xf4
++             && apt.taskfile.lba_high == 0x2c)
+       return 1; /* Bad SMART status.  */
+       else
+       return -1;
+     }
+   return 0;
+ }
+ static grub_err_t
+ grub_hdparm_simple_cmd (const char * msg,
 -  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
++                      grub_ata_t ata, grub_uint8_t cmd)
+ {
+   if (! quiet && msg)
+     grub_printf ("%s", msg);
 -                       grub_disk_t disk, grub_uint8_t cmd,
++  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");
+   return err;
+ }
+ static grub_err_t
+ grub_hdparm_set_val_cmd (const char * msg, int val,
 -  grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
++                       grub_ata_t ata, grub_uint8_t cmd,
+                        grub_uint8_t features, grub_uint8_t sectors)
+ {
+   if (! quiet && msg && *msg)
+     {
+       if (val >= 0)
+       grub_printf ("Set %s to %d", msg, val);
+       else
+       grub_printf ("Disable %s", msg);
+     }
 -  if (! grub_disk_ata_pass_through)
 -    return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
 -
++  grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors,
+                                          NULL, 0);
+   if (! quiet && msg)
+     grub_printf ("%s\n", ! err ? "" : ": not supported");
+   return err;
+ }
+ static const char *
+ le16_to_char (char *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;
+ }
+ static void
+ grub_hdparm_print_identify (const char * idbuf)
+ {
+   const grub_uint16_t * idw = (const grub_uint16_t *) idbuf;
+   /* Print identity strings.  */
+   char tmp[40];
+   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));
+   /* Print AAM, APM and SMART settings.  */
+   grub_uint16_t features1 = grub_le_to_cpu16 (idw[82]);
+   grub_uint16_t features2 = grub_le_to_cpu16 (idw[83]);
+   grub_uint16_t enabled1  = grub_le_to_cpu16 (idw[85]);
+   grub_uint16_t enabled2  = grub_le_to_cpu16 (idw[86]);
+   grub_printf ("Automatic Acoustic Management: ");
+   if (features2 & 0x0200)
+     {
+       if (enabled2 & 0x0200)
+       {
+         grub_uint16_t aam = grub_le_to_cpu16 (idw[94]);
+         grub_printf ("%u (128=quiet, ..., 254=fast, recommended=%u)\n",
+                      aam & 0xff, (aam >> 8) & 0xff);
+       }
+       else
+       grub_printf ("disabled\n");
+     }
+   else
+     grub_printf ("not supported\n");
+   grub_printf ("Advanced Power Management: ");
+   if (features2 & 0x0008)
+     {
+       if (enabled2 & 0x0008)
+       grub_printf ("%u (1=low, ..., 254=high)\n",
+                    grub_le_to_cpu16 (idw[91]) & 0xff);
+       else
+       grub_printf ("disabled\n");
+     }
+   else
+     grub_printf ("not supported\n");
+   grub_printf ("SMART Feature Set: ");
+   if (features1 & 0x0001)
+     grub_printf ("%sabled\n", (enabled1 & 0x0001 ? "en" : "dis"));
+   else
+     grub_printf ("not supported\n");
+   /* Print security settings.  */
+   grub_uint16_t security = grub_le_to_cpu16 (idw[128]);
+   grub_printf ("ATA Security: ");
+   if (security & 0x0001)
+     grub_printf ("%s, %s, %s, %s\n",
+                (security & 0x0002 ? "ENABLED" : "disabled"),
+                (security & 0x0004 ? "**LOCKED**"  : "not locked"),
+                (security & 0x0008 ? "frozen" : "NOT FROZEN"),
+                (security & 0x0010 ? "COUNT EXPIRED" : "count not expired"));
+   else
+     grub_printf ("not supported\n");
+ }
+ static void
+ grub_hdparm_print_standby_tout (int timeout)
+ {
+   if (timeout == 0)
+     grub_printf ("off");
+   else if (timeout <= 252 || timeout == 255)
+     {
+       int h = 0, m = 0 , s = 0;
+       if (timeout == 255)
+       {
+         m = 21;
+         s = 15;
+       }
+       else if (timeout == 252)
+       m = 21;
+       else if (timeout <= 240)
+       {
+         s = timeout * 5;
+         m = s / 60;
+         s %= 60;
+       }
+       else
+       {
+         m = (timeout - 240) * 30;
+         h  = m / 60;
+         m %= 60;
+       }
+       grub_printf ("%02d:%02d:%02d", h, m, s);
+     }
+   else
+     grub_printf ("invalid or vendor-specific");
+ }
+ static int get_int_arg (const struct grub_arg_list *state)
+ {
+   return (state->set ? (int)grub_strtoul (state->arg, 0, 0) : -1);
+ }
+ static grub_err_t
+ grub_cmd_hdparm (grub_extcmd_context_t ctxt, int argc, char **args) // state????
+ {
+   struct grub_arg_list *state = ctxt->state;
++  struct grub_ata *ata;
+   /* Check command line.  */
+   if (argc != 1)
+     return grub_error (GRUB_ERR_BAD_ARGUMENT, "missing device name argument");
+   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;
 -      disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
+   int i = 0;
+   int apm          = get_int_arg (&state[i++]);
+   int power        = state[i++].set;
+   int sec_freeze   = state[i++].set;
+   int health       = state[i++].set;
+   int aam          = get_int_arg (&state[i++]);
+   int standby_tout = get_int_arg (&state[i++]);
+   int standby_now  = state[i++].set;
+   int sleep_now    = state[i++].set;
+   int ident        = state[i++].set;
+   int dumpid       = state[i++].set;
+   int enable_smart = get_int_arg (&state[i++]);
+   quiet            = state[i++].set;
+   /* Open disk.  */
+   grub_disk_t disk = grub_disk_open (&args[0][1]);
+   if (! disk)
+     return grub_errno;
+   if (disk->partition)
+     {
+       grub_disk_close (disk);
+       return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
+     }
++  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;
++      }
++    default:
++      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),
 -      (apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
 -      (apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
++                           ata, GRUB_ATA_CMD_SET_FEATURES,
++                           (aam ? 0x42 : 0xc2), aam);
+   if (apm >= 0)
+     grub_hdparm_set_val_cmd ("Advanced Power Management",
 -      grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
++                           (apm != 255 ? apm : -1), ata,
++                           GRUB_ATA_CMD_SET_FEATURES,
++                           (apm != 255 ? 0x05 : 0x85),
++                           (apm != 255 ? apm : 0));
+   if (standby_tout >= 0)
+     {
+       if (! quiet)
+       {
+         grub_printf ("Set standby timeout to %d (", standby_tout);
+         grub_hdparm_print_standby_tout (standby_tout);
+         grub_printf (")");
+       }
+       /* The IDLE cmd sets disk to idle mode and configures standby timer.  */
 -      int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
++      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"));
 -    grub_hdparm_simple_cmd ("Freeze security settings", disk,
++      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)
 -      if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
++    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];
 -      int mode = grub_hdparm_do_check_powermode_cmd (disk);
++      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
+       {
+         if (ident)
+           grub_hdparm_print_identify (buf);
+         if (dumpid)
+           hexdump (0, buf, sizeof (buf));
+       }
+     }
+   /* Check power mode.  */
+   if (power)
+     {
+       grub_printf ("Disk power mode is: ");
 -      int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
++      int mode = grub_hdparm_do_check_powermode_cmd (ata);
+       if (mode < 0)
+         grub_printf ("unknown\n");
+       else
+       grub_printf ("%s (0x%02x)\n",
+                    (mode == 0xff ? "active/idle" :
+                     mode == 0x80 ? "idle" :
+                     mode == 0x00 ? "standby" : "unknown"), mode);
+     }
+   /* Check health.  */
+   int status = 0;
+   if (health)
+     {
+       if (! quiet)
+       grub_printf ("SMART status is: ");
 -    grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
++      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*"));
+       status = (err > 0);
+     }
+   /* Change power mode.  */
+   if (standby_now)
 -    grub_hdparm_simple_cmd ("Set disk to sleep 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", ata,
+                           GRUB_ATA_CMD_SLEEP);
+   grub_disk_close (disk);
+   grub_errno = GRUB_ERR_NONE;
+   return status;
+ }
+ static grub_extcmd_t cmd;
+ GRUB_MOD_INIT(hdparm)
+ {
+   cmd = grub_register_extcmd ("hdparm", grub_cmd_hdparm, 0,
+                             N_("[OPTIONS] DISK"),
+                             N_("Get/set ATA disk parameters."), options);
+ }
+ GRUB_MOD_FINI(hdparm)
+ {
+   grub_unregister_extcmd (cmd);
+ }
index c266bbf,0000000..c266bbf
mode 100644,000000..100644
--- /dev/null
index d2921c0,0000000..d2921c0
mode 100644,000000..100644
--- /dev/null
index 946db82,0000000..946db82
mode 100644,000000..100644
--- /dev/null
index 0000000,fe677e2..04b81cd
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,916 +1,593 @@@
 -#include <grub/time.h>
 -#include <grub/pci.h>
+ /* 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 <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/ata.h>
+ #include <grub/dl.h>
+ #include <grub/disk.h>
+ #include <grub/mm.h>
 -#include <grub/cs5536.h>
+ #include <grub/scsi.h>
 -/* At the moment, only two IDE ports are supported.  */
 -static const grub_port_t grub_ata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
 -                                                GRUB_ATA_CH1_PORT1 };
 -static const grub_port_t grub_ata_ioaddress2[] = { GRUB_ATA_CH0_PORT2, 
 -                                                 GRUB_ATA_CH1_PORT2 };
 -
 -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;
 -}
 -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 grub_ata_dev_t grub_ata_dev_list;
+ /* 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';
+ }
 -grub_ata_dumpinfo (struct grub_ata_device *dev, char *info)
+ static void
 -grub_atapi_identify (struct grub_ata_device *dev)
++grub_ata_dumpinfo (struct grub_ata *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_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_atapi_identify (struct grub_ata *dev)
+ {
++  struct grub_disk_ata_pass_through_parms parms;
+   char *info;
++  grub_err_t err;
+   info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
+   if (! info)
+     return grub_errno;
 -  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
 -  grub_ata_wait ();
++  grub_memset (&parms, 0, sizeof (parms));
++  parms.taskfile.disk = 0;
++  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
 -  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);
++  err = dev->dev->readwrite (dev, &parms);
++  if (err)
++    return err;
 -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)
++  if (parms.size != GRUB_DISK_SECTOR_SIZE)
++    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_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_identify (struct grub_ata *dev)
+ {
++  struct grub_disk_ata_pass_through_parms parms;
+   char *info;
+   grub_uint16_t *info16;
++  grub_err_t err;
+   info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
+   if (! info)
+     return grub_errno;
+   info16 = (grub_uint16_t *) info;
++  grub_memset (&parms, 0, sizeof (parms));
++  parms.buffer = info;
++  parms.taskfile.disk = 0;
 -  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
 -  grub_ata_wait ();
++  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
 -  if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
++  err = dev->dev->readwrite (dev, &parms);
++  if (err)
++    return err;
 -      grub_errno = GRUB_ERR_NONE;
 -      grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
 -
++  if (parms.size != GRUB_DISK_SECTOR_SIZE)
+     {
++      grub_uint8_t sts = parms.taskfile.status;
+       grub_free (info);
 -        && (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04 /* ABRT */))
+       if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
+         | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
 -  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;
 -    }
 -
++        && (parms.taskfile.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");
+     }
 -check_device (struct grub_ata_device *dev)
 -{
 -  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)
 -    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.  */
 -
 -  /* Use the IDENTIFY DEVICE command to query the device.  */
 -  return grub_ata_identify (dev);
 -}
 -
 -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_err_t err;
 -
 -  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;
 -
 -  /* Register the device.  */
 -  for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next);
 -  *devp = dev;
 -
 -  err = check_device (dev);
 -  if (err)
 -    grub_print_error ();
 -
 -  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 == 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;
 -      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,
+   /* 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
 -  switch (addressing)
++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_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);
++  switch (dev->addr)
+     {
+     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,
 -                     0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
 -      if (grub_ata_check_ready (dev))
 -      return grub_errno;
++      
++      parms->taskfile.disk = 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;
 -      grub_ata_setlba (dev, sector, size);
++      parms->taskfile.disk = ((sector >> 24) & 0x0F);
 -      grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
 -      if (grub_ata_check_ready (dev))
 -      return grub_errno;
++      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;
 -      grub_ata_setlba (dev, sector >> 24, size >> 8);
++      parms->taskfile.disk = 0;
+       /* Set "Previous".  */
 -      grub_ata_setlba (dev, sector, size);
++      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".  */
 -  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);
++      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)
+ {
 -  grub_ata_addressing_t addressing = dev->addr;
++  struct grub_ata *ata = disk->data;
 -      batch = 256;
++  grub_ata_addressing_t addressing = ata->addr;
+   grub_size_t batch;
+   int cmd, cmd_write;
++  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)
+     {
+       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;
 -
 -      /* 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");
 -      }
 -
++      if (addressing != GRUB_ATA_CHS)
++      batch = 256;
++      else
++      batch = 1;
+       cmd = GRUB_ATA_CMD_READ_SECTORS;
+       cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
+     }
+   grub_size_t nsectors = 0;
+   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);
 -static int
 -grub_ata_iterate (int (*hook) (const char *name))
++      grub_memset (&parms, 0, sizeof (parms));
++      grub_ata_setaddress (ata, &parms, sector, batch);
++      parms.taskfile.cmd = (! rw ? cmd : cmd_write);
++      parms.buffer = buf;
++      parms.size = batch * GRUB_DISK_SECTOR_SIZE;
++  
++      err = ata->dev->readwrite (ata, &parms);
++      if (err)
++      return err;
++      if (parms.size != batch * GRUB_DISK_SECTOR_SIZE)
++      return grub_error (GRUB_ERR_READ_ERROR, "incomplete read");
++      buf += GRUB_DISK_SECTOR_SIZE * batch;
+       sector += batch;
+       nsectors += batch;
+     }
+   return GRUB_ERR_NONE;
+ }
\f
 -  struct grub_ata_device *dev;
++static inline void
++grub_ata_real_close (struct grub_ata *ata)
+ {
 -  for (dev = grub_ata_devices; dev; dev = dev->next)
++  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;
 -      char devname[10];
++  ata = grub_malloc (sizeof (*ata));
++  if (!ata)
++    return NULL;
++  for (p = grub_ata_dev_list; p; p = p->next)
+     {
 -
 -      err = check_device (dev);
 -      if (err)
+       grub_err_t err;
 -      if (dev->atapi)
 -      continue;
 -
 -      grub_snprintf (devname, sizeof (devname), 
 -                   "ata%d", dev->port * 2 + dev->device);
++      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)
++      {
++        grub_free (ata);
++        return NULL;
++      }
++      return ata;
++    }
++  grub_free (ata);
++  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
++  return NULL;
++}
 -      if (hook (devname))
 -      return 1;
 -    }
++static int
++grub_ata_iterate (int (*hook_in) (const char *name))
++{
++  auto int hook (int id, int bus);
++  int hook (int id, int bus)
++  {
++    struct grub_ata *ata;
++    int ret;
++    char devname[40];
 -  struct grub_ata_device *dev;
 -  grub_err_t err;
 -
 -  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");
++    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 = hook_in (devname);
++    grub_ata_real_close (ata);
++    return ret;
++  }
++
++  grub_ata_dev_t p;
++  
++  for (p = grub_ata_dev_list; p; p = p->next)
++    if (p->iterate && p->iterate (hook))
++      return 1;
+   return 0;
+ }
+ static grub_err_t
+ grub_ata_open (const char *name, grub_disk_t disk)
+ {
 -  if (dev->atapi)
++  unsigned id, bus;
++  struct grub_ata *ata;
 -  err = check_device (dev);
 -
 -  if (err)
 -    return err;
++  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;
 -  disk->total_sectors = dev->size;
++  if (ata->atapi)
++    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
 -  disk->id = (unsigned long) dev;
++  disk->total_sectors = ata->size;
 -  disk->data = dev;
++  disk->id = grub_make_scsi_id (id, bus, 0);
 -grub_ata_close (grub_disk_t disk __attribute__((unused)))
++  disk->data = ata;
+   return 0;
+ }
+ static void
 -
++grub_ata_close (grub_disk_t disk)
+ {
 -static int
 -grub_atapi_iterate (int (*hook) (int bus, int luns))
 -{
 -  struct grub_ata_device *dev;
 -
 -  for (dev = grub_ata_devices; dev; dev = dev->next)
 -    {
 -      grub_err_t err;
 -
 -      err = check_device (dev);
 -      if (err)
 -      {
 -        grub_errno = GRUB_ERR_NONE;
 -        continue;
 -      }
 -
 -      if (! dev->atapi)
 -      continue;
 -
 -      if (hook (dev->port * 2 + dev->device, 1))
 -      return 1;
 -    }
 -
 -  return 0;
 -
 -}
 -
++  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,
+     .iterate = grub_ata_iterate,
+     .open = grub_ata_open,
+     .close = grub_ata_close,
+     .read = grub_ata_read,
+     .write = grub_ata_write,
+     .next = 0
+   };
\f
+ /* ATAPI code.  */
 -grub_atapi_read (struct grub_scsi *scsi,
 -               grub_size_t cmdsize __attribute__((unused)),
 -               char *cmd, grub_size_t size, char *buf)
+ static grub_err_t
 -  struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
++grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
++               grub_size_t size, char *buf)
+ {
 -  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;
 -    }
 -
++  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);
++  if (err)
++    return err;
 -grub_atapi_open (int devnum, struct grub_scsi *scsi)
++  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)),
+                 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
 -  struct grub_ata_device *dev;
 -  struct grub_ata_device *devfnd = 0;
 -  grub_err_t err;
++grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
+ {
 -  for (dev = grub_ata_devices; dev; dev = dev->next)
 -    {
 -      if (dev->port * 2 + dev->device == devnum)
 -      {
 -        devfnd = dev;
 -        break;
 -      }
 -    }
++  struct grub_ata *ata;
 -  grub_dprintf ("ata", "opening ATAPI dev `ata%d'\n", devnum);
++  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");
 -  if (! devfnd)
 -    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
++  scsi->data = ata;
 -  err = check_device (devfnd);
 -  if (err)
 -    return err;
++  return GRUB_ERR_NONE;
++}
 -  if (! devfnd->atapi)
 -    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
++static int
++grub_atapi_iterate (int (*hook_in) (int id, int bus, int luns))
++{
++  auto int hook (int id, int bus);
++  int hook (int id, int bus)
++  {
++    struct grub_ata *ata;
++    int ret;
 -  scsi->data = devfnd;
++    ata = grub_ata_real_open (id, bus);
 -  return GRUB_ERR_NONE;
++    if (!ata)
++      {
++      grub_errno = GRUB_ERR_NONE;
++      return 0;
++      }
++    if (!ata->atapi)
++      {
++      grub_ata_real_close (ata);
++      return 0;
++      }
++    ret = hook_in (id, bus, 1);
++    grub_ata_real_close (ata);
++    return ret;
++  }
++
++  grub_ata_dev_t p;
++  
++  for (p = grub_ata_dev_list; p; p = p->next)
++    if (p->iterate && p->iterate (hook))
++      return 1;
++  return 0;
++}
 -    .name = "ata",
 -    .id = GRUB_SCSI_SUBSYSTEM_ATAPI,
++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 =
+   {
 -    .write = grub_atapi_write
+     .iterate = grub_atapi_iterate,
+     .open = grub_atapi_open,
++    .close = grub_atapi_close,
+     .read = grub_atapi_read,
 -  /* 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 ();
 -
++    .write = grub_atapi_write,
++    .next = 0
+   };
\f
+ 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);
+ }
index b6edf78,0000000..b6edf78
mode 100644,000000..100644
--- /dev/null
index 0000000,902ab12..b989a98
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,604 +1,620 @@@
 -  auto int scsi_iterate (int bus, int luns);
+ /* 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 <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/disk.h>
+ #include <grub/dl.h>
+ #include <grub/kernel.h>
+ #include <grub/misc.h>
+ #include <grub/mm.h>
+ #include <grub/types.h>
+ #include <grub/scsi.h>
+ #include <grub/scsicmd.h>
+ #include <grub/time.h>
\f
+ static grub_scsi_dev_t grub_scsi_dev_list;
++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;
+       }
+ }
\f
+ /* 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_capacity (grub_scsi_t scsi)
+ {
+   struct grub_scsi_read_capacity rc;
+   struct grub_scsi_read_capacity_data rcd;
+   grub_err_t err;
+   grub_err_t err_sense;
+   rc.opcode = grub_scsi_cmd_read_capacity;
+   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->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;
+   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;
+ }
+ #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;
+   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;
+ }
+ /* 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
\f
+ static int
+ grub_scsi_iterate (int (*hook) (const char *name))
+ {
+   grub_scsi_dev_t p;
 -  int scsi_iterate (int bus, int luns)
++  auto int scsi_iterate (int id, int bus, int luns);
 -        sname = grub_xasprintf ("%s%d", p->name, bus);
++  int scsi_iterate (int id, int bus, int luns)
+     {
+       int i;
+       /* In case of a single LUN, just return `usbX'.  */
+       if (luns == 1)
+       {
+         char *sname;
+         int ret;
 -        sname = grub_xasprintf ("%s%d%c", p->name, bus, 'a' + i);
++        sname = grub_xasprintf ("%s%d", grub_scsi_names[id], bus);
+         if (!sname)
+           return 1;
+         ret = hook (sname);
+         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;
 -  for (p = grub_scsi_dev_list; p; p = p->next)
++        sname = grub_xasprintf ("%s%d%c", grub_scsi_names[id], bus, '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 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;
 -      if (grub_strncmp (p->name, name, nameend - name) != 0)
 -      continue;
++  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))
+     {
 -      if (p->open (bus, scsi))
 -      continue;
++      grub_free (scsi);
++      return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
++    }
 -      disk->id = grub_make_scsi_id (p->id, bus, lun);
++  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_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 = ((grub_uint64_t)scsi->size
+                              * (grub_uint64_t)scsi->blocksize)
+                           >> GRUB_DISK_SECTOR_BITS;
+       grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+                   scsi->size, scsi->blocksize);
+       grub_dprintf ("scsi", "Disk total 512 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;
+   /* 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, &sector_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;
+ #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 __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);
+ }
index 0000000,e63105c..4e1af7b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,435 +1,437 @@@
 -grub_usbms_iterate (int (*hook) (int bus, int luns))
+ /* 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 <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/dl.h>
+ #include <grub/mm.h>
+ #include <grub/usb.h>
+ #include <grub/scsi.h>
+ #include <grub/scsicmd.h>
+ #include <grub/misc.h>
+ #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 config;
+   int interface;
+   struct grub_usb_desc_endp *in;
+   struct grub_usb_desc_endp *out;
+   int in_maxsz;
+   int out_maxsz;
+ };
+ 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_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_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;
+   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)
+     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_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;
+         /* Clear Halt is not possible yet! */
+         /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
+         grub_usbms_devices[curnum]->in_maxsz = endp->maxpacket;
+       }
+       else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
+       {
+         /* Bulk OUT endpoint.  */
+         grub_usbms_devices[curnum]->out = endp;
+         /* Clear Halt is not possible yet! */
+         /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
+         grub_usbms_devices[curnum]->out_maxsz = endp->maxpacket;
+       }
+     }
+   if (!grub_usbms_devices[curnum]->in || !grub_usbms_devices[curnum]->out)
+     {
+       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.  */
+   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;
+   grub_dprintf ("usbms", "alive\n");
+   usbdev->config[configno].interf[interfno].detach_hook = grub_usbms_detach;
+ #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;
+ }
\f
+ static int
 -      if (hook (i, grub_usbms_devices[i]->luns))
++grub_usbms_iterate (int (*hook) (int id, int bus, int luns))
+ {
+   unsigned i;
+   grub_usb_poll_devices ();
+   for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
+     if (grub_usbms_devices[i])
+       {
 -grub_usbms_open (int devnum, struct grub_scsi *scsi)
++      if (hook (GRUB_SCSI_SUBSYSTEM_USBMS, i, grub_usbms_devices[i]->luns))
+         return 1;
+       }
+   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;
+   grub_usb_err_t errCSW = 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; /* 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->endp_addr,
+                            sizeof (cbw), (char *) &cbw);
+   if (err)
+     {
+       if (err == GRUB_USB_ERR_STALL)
+       {
+         grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+         goto CheckCSW;
+       }
+       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
+     }
+   /* Read/write the data, (maybe) according to specification.  */
+   if (size && (read_write == 0))
+     {
+       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, 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->endp_addr, 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; 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");
+     }
+   /* Read the status - (maybe) according to specification.  */
+ CheckCSW:
+   errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+                   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->endp_addr,
+                               sizeof (status), (char *) &status);
+       if (errCSW)
+         { /* Bulk-only reset device. */
+           grub_dprintf ("usb", "Bulk-only reset device - errCSW\n");
+           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;
+         }
+     }
+   /* 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(0x53425355)))
+     { /* Bulk-only reset device. */
+       grub_dprintf ("usb", "Bulk-only reset device - bad status\n");
+       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 "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_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
 -    .name = "usb",
 -    .id = GRUB_SCSI_SUBSYSTEM_USBMS,
++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");
++
+   grub_usb_poll_devices ();
+   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
+   };
+ 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);
+ }
index 5a492dc,0000000..5a492dc
mode 100644,000000..100644
--- /dev/null
index 107534e,0000000..107534e
mode 100644,000000..100644
--- /dev/null
index f500a8f,0000000..f500a8f
mode 100644,000000..100644
--- /dev/null
index 7c12c01,0000000..7c12c01
mode 100644,000000..100644
--- /dev/null
index b086b45,0000000..b086b45
mode 100644,000000..100644
--- /dev/null
index d2d3e6e,0000000..d2d3e6e
mode 100644,000000..100644
--- /dev/null
index 0000000,807ee42..600b7f1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,604 +1,600 @@@
 -grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
 -          struct grub_disk_ata_pass_through_parms *);
 -
 -
+ /*
+  *  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 <http://www.gnu.org/licenses/>.
+  */
+ #include <grub/disk.h>
+ #include <grub/err.h>
+ #include <grub/mm.h>
+ #include <grub/types.h>
+ #include <grub/partition.h>
+ #include <grub/misc.h>
+ #include <grub/time.h>
+ #include <grub/file.h>
+ #define       GRUB_CACHE_TIMEOUT      2
+ /* The last time the disk was used.  */
+ static grub_uint64_t grub_last_time = 0;
+ /* Disk cache.  */
+ struct grub_disk_cache
+ {
+   enum grub_disk_dev_id dev_id;
+   unsigned long disk_id;
+   grub_disk_addr_t sector;
+   char *data;
+   int lock;
+ };
+ static 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 0
+ 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
+ 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);
+ }
+ static void
+ grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id,
+                           grub_disk_addr_t sector)
+ {
+   unsigned index;
+   struct grub_disk_cache *cache;
+   sector &= ~(GRUB_DISK_CACHE_SIZE - 1);
+   index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+   cache = grub_disk_cache_table + 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;
+     }
+ }
+ 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 index;
+   index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+   cache = grub_disk_cache_table + index;
+   if (cache->dev_id == dev_id && cache->disk_id == disk_id
+       && cache->sector == sector)
+     {
+       cache->lock = 1;
+ #if 0
+       grub_disk_cache_hits++;
+ #endif
+       return cache->data;
+     }
+ #if 0
+   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 index;
+   index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+   cache = grub_disk_cache_table + 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 index;
+   struct grub_disk_cache *cache;
+   index = grub_disk_cache_get_index (dev_id, disk_id, sector);
+   cache = grub_disk_cache_table + 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;
+ }
\f
+ static 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;
+       }
+ }
+ int
+ grub_disk_dev_iterate (int (*hook) (const char *name))
+ {
+   grub_disk_dev_t p;
+   for (p = grub_disk_dev_list; p; p = p->next)
+     if (p->iterate && (p->iterate) (hook))
+       return 1;
+   return 0;
+ }
+ /* 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;
+   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->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, "no such disk");
+       goto fail;
+     }
+   disk->dev = dev;
+   if (p)
+     {
+       disk->partition = grub_partition_probe (disk, p + 1);
+       if (! disk->partition)
+       {
+         grub_error (GRUB_ERR_UNKNOWN_DEVICE, "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;
+   grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
+   if (disk->dev && disk->dev->close)
+     (disk->dev->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);
+ }
+ /* 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;
+   *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, "out of partition");
+       *sector += start;
+     }
+   if (disk->total_sectors <= *sector
+       || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
+         >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
+     return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk");
+   return GRUB_ERR_NONE;
+ }
+ /* 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)
+ {
+   char *tmp_buf;
+   unsigned real_offset;
+   /* First of all, check if the region is within the disk.  */
+   if (grub_disk_adjust_range (disk, &sector, &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;
+     }
+   real_offset = offset;
+   /* Allocate a temporary buffer.  */
+   tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
+   if (! tmp_buf)
+     return grub_errno;
+   /* Until SIZE is zero...  */
+   while (size)
+     {
+       char *data;
+       grub_disk_addr_t start_sector;
+       grub_size_t len;
+       grub_size_t pos;
+       /* For reading bulk data.  */
+       start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
+       pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
+       len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
+            - pos - real_offset);
+       if (len > size)
+       len = size;
+       /* Fetch the cache.  */
+       data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
+       if (data)
+       {
+         /* Just copy it!  */
+         grub_memcpy (buf, data + pos + real_offset, len);
+         grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector);
+       }
+       else
+       {
+         /* Otherwise read data from the disk actually.  */
+         if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors
+             || (disk->dev->read) (disk, start_sector,
+                                   GRUB_DISK_CACHE_SIZE, tmp_buf)
+             != GRUB_ERR_NONE)
+           {
+             /* Uggh... Failed. Instead, just read necessary data.  */
+             unsigned num;
+             char *p;
+             grub_errno = GRUB_ERR_NONE;
+             num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1)
+                    >> GRUB_DISK_SECTOR_BITS);
+             p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS);
+             if (!p)
+               goto finish;
+             tmp_buf = p;
+             if ((disk->dev->read) (disk, sector, num, tmp_buf))
+               {
+                 grub_error_push ();
+                 grub_dprintf ("disk", "%s read failed\n", disk->name);
+                 grub_error_pop ();
+                 goto finish;
+               }
+             grub_memcpy (buf, tmp_buf + real_offset, size);
+             /* Call the read hook, if any.  */
+             if (disk->read_hook)
+               while (size)
+                 {
+                   grub_size_t to_read = (size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size;
+                   (disk->read_hook) (sector, real_offset,
+                                      to_read);
+                   if (grub_errno != GRUB_ERR_NONE)
+                     goto finish;
+                   sector++;
+                   size -= to_read - real_offset;
+                   real_offset = 0;
+                 }
+             /* This must be the end.  */
+             goto finish;
+           }
+         /* Copy it and store it in the disk cache.  */
+         grub_memcpy (buf, tmp_buf + pos + real_offset, len);
+         grub_disk_cache_store (disk->dev->id, disk->id,
+                                start_sector, tmp_buf);
+       }
+       /* Call the read hook, if any.  */
+       if (disk->read_hook)
+       {
+         grub_disk_addr_t s = sector;
+         grub_size_t l = len;
+         while (l)
+           {
+             (disk->read_hook) (s, real_offset,
+                                ((l > GRUB_DISK_SECTOR_SIZE)
+                                 ? GRUB_DISK_SECTOR_SIZE
+                                 : l));
+             if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
+               break;
+             s++;
+             l -= GRUB_DISK_SECTOR_SIZE - real_offset;
+             real_offset = 0;
+           }
+       }
+       sector = start_sector + GRUB_DISK_CACHE_SIZE;
+       buf = (char *) buf + len;
+       size -= len;
+       real_offset = 0;
+     }
+  finish:
+   grub_free (tmp_buf);
+   return grub_errno;
+ }
+ 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_dprintf ("disk", "Writing `%s'...\n", disk->name);
+   if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
+     return -1;
+   real_offset = offset;
+   while (size)
+     {
+       if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0))
+       {
+         char tmp_buf[GRUB_DISK_SECTOR_SIZE];
+         grub_size_t len;
+         grub_partition_t part;
+         part = disk->partition;
+         disk->partition = 0;
+         if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf)
+             != GRUB_ERR_NONE)
+           {
+             disk->partition = part;
+             goto finish;
+           }
+         disk->partition = part;
+         len = GRUB_DISK_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->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
+           goto finish;
+         sector++;
+         buf = (char *) buf + len;
+         size -= len;
+         real_offset = 0;
+       }
+       else
+       {
+         grub_size_t len;
+         grub_size_t n;
+         len = size & ~(GRUB_DISK_SECTOR_SIZE - 1);
+         n = size >> GRUB_DISK_SECTOR_BITS;
+         if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE)
+           goto finish;
+         while (n--)
+           grub_disk_cache_invalidate (disk->dev->id, disk->id, sector++);
+         buf = (char *) buf + len;
+         size -= len;
+       }
+     }
+  finish:
+   return grub_errno;
+ }
+ grub_uint64_t
+ grub_disk_get_size (grub_disk_t disk)
+ {
+   if (disk->partition)
+     return grub_partition_get_len (disk->partition);
+   else
+     return disk->total_sectors;
+ }
index 9fb9873,0000000..9fb9873
mode 100644,000000..100644
--- /dev/null
index ed57c43,0000000..ed57c43
mode 100644,000000..100644
--- /dev/null
index 7d57df9,0000000..7d57df9
mode 100644,000000..100644
--- /dev/null
index bc0d8aa,0000000..bc0d8aa
mode 100644,000000..100644
--- /dev/null
index 25aee3a,0000000..25aee3a
mode 100644,000000..100644
--- /dev/null
index a568fff,0000000..a568fff
mode 100644,000000..100644
--- /dev/null
index d37feb9,0000000..d37feb9
mode 100644,000000..100644
--- /dev/null
index 9f7556f,0000000..9f7556f
mode 100644,000000..100644
--- /dev/null
@@@ -165,5 -163,27 +163,16 @@@ grub_uint64_t EXPORT_FUNC(grub_disk_get
  
  extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
  extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
-  
 -/* ATA pass through parameters and function.  */
 -struct grub_disk_ata_pass_through_parms
 -{
 -  grub_uint8_t taskfile[8];
 -  void * buffer;
 -  int size;
 -};
 -
 -extern grub_err_t (* EXPORT_VAR(grub_disk_ata_pass_through)) (grub_disk_t,
 -                 struct grub_disk_ata_pass_through_parms *);
 -
+ #if defined (GRUB_UTIL) || defined (GRUB_MACHINE_EMU)
+ void grub_lvm_init (void);
+ void grub_mdraid09_init (void);
+ void grub_mdraid1x_init (void);
+ void grub_raid_init (void);
+ void grub_lvm_fini (void);
+ void grub_mdraid09_fini (void);
+ void grub_mdraid1x_fini (void);
+ void grub_raid_fini (void);
+ #endif
  #endif /* ! GRUB_DISK_HEADER */
Simple merge