--- /dev/null
- 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;
+ };
--- /dev/null
--- /dev/null
++/* crc.c - command to calculate the crc32 checksum of a file */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2008,2010 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/disk.h>
++#include <grub/file.h>
++#include <grub/misc.h>
++#include <grub/lib/crc.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++
++static grub_err_t
++grub_cmd_crc (grub_command_t cmd __attribute__ ((unused)),
++ int argc, char **args)
++
++{
++ grub_file_t file;
++ char buf[GRUB_DISK_SECTOR_SIZE];
++ grub_ssize_t size;
++ grub_uint32_t crc;
++
++ if (argc != 1)
++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
++
++ file = grub_file_open (args[0]);
++ if (! file)
++ return 0;
++
++ crc = 0;
++ while ((size = grub_file_read (file, buf, sizeof (buf))) > 0)
++ crc = grub_getcrc32 (crc, buf, size);
++
++ if (grub_errno)
++ goto fail;
++
++ grub_printf ("%08x\n", crc);
++
++ fail:
++ grub_file_close (file);
++ return 0;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(crc)
++{
++ cmd = grub_register_command ("crc", grub_cmd_crc,
++ N_("FILE"),
++ N_("Calculate the crc32 checksum of a file."));
++}
++
++GRUB_MOD_FINI(crc)
++{
++ grub_unregister_command (cmd);
++}
--- /dev/null
-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);
+ }
--- /dev/null
--- /dev/null
++/* vbeinfo.c - command to list compatible VBE video modes. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/env.h>
++#include <grub/misc.h>
++#include <grub/machine/init.h>
++#include <grub/machine/vbe.h>
++#include <grub/mm.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++
++static void *
++real2pm (grub_vbe_farptr_t ptr)
++{
++ return (void *) ((((unsigned long) ptr & 0xFFFF0000) >> 12UL)
++ + ((unsigned long) ptr & 0x0000FFFF));
++}
++
++static grub_err_t
++grub_cmd_vbeinfo (grub_command_t cmd __attribute__ ((unused)),
++ int argc __attribute__ ((unused)),
++ char **args __attribute__ ((unused)))
++{
++ struct grub_vbe_info_block controller_info;
++ struct grub_vbe_mode_info_block mode_info_tmp;
++ grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
++ grub_uint16_t *video_mode_list;
++ grub_uint16_t *p;
++ grub_uint16_t *saved_video_mode_list;
++ grub_size_t video_mode_list_size;
++ grub_err_t err;
++ char *modevar;
++
++ err = grub_vbe_probe (&controller_info);
++ if (err != GRUB_ERR_NONE)
++ return err;
++
++ grub_printf ("VBE info: version: %d.%d OEM software rev: %d.%d\n",
++ controller_info.version >> 8,
++ controller_info.version & 0xFF,
++ controller_info.oem_software_rev >> 8,
++ controller_info.oem_software_rev & 0xFF);
++
++ /* The total_memory field is in 64 KiB units. */
++ grub_printf (" total memory: %d KiB\n",
++ (controller_info.total_memory << 16) / 1024);
++
++ /* Because the information on video modes is stored in a temporary place,
++ it is better to copy it to somewhere safe. */
++ p = video_mode_list = real2pm (controller_info.video_mode_ptr);
++ while (*p++ != 0xFFFF)
++ ;
++
++ video_mode_list_size = (grub_addr_t) p - (grub_addr_t) video_mode_list;
++ saved_video_mode_list = grub_malloc (video_mode_list_size);
++ if (! saved_video_mode_list)
++ return grub_errno;
++
++ grub_memcpy (saved_video_mode_list, video_mode_list, video_mode_list_size);
++
++ grub_printf ("List of compatible video modes:\n");
++ grub_printf ("Legend: P=Packed pixel, D=Direct color, "
++ "mask/pos=R/G/B/reserved\n");
++
++ /* Walk through all video modes listed. */
++ for (p = saved_video_mode_list; *p != 0xFFFF; p++)
++ {
++ const char *memory_model = 0;
++ grub_uint32_t mode = (grub_uint32_t) *p;
++
++ err = grub_vbe_get_video_mode_info (mode, &mode_info_tmp);
++ if (err != GRUB_ERR_NONE)
++ {
++ grub_errno = GRUB_ERR_NONE;
++ continue;
++ }
++
++ if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_SUPPORTED) == 0)
++ /* If not available, skip it. */
++ continue;
++
++ if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_RESERVED_1) == 0)
++ /* Not enough information. */
++ continue;
++
++ if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_COLOR) == 0)
++ /* Monochrome is unusable. */
++ continue;
++
++ if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_LFB_AVAIL) == 0)
++ /* We support only linear frame buffer modes. */
++ continue;
++
++ if ((mode_info_tmp.mode_attributes & GRUB_VBE_MODEATTR_GRAPHICS) == 0)
++ /* We allow only graphical modes. */
++ continue;
++
++ switch (mode_info_tmp.memory_model)
++ {
++ case GRUB_VBE_MEMORY_MODEL_PACKED_PIXEL:
++ memory_model = "Packed";
++ break;
++ case GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR:
++ memory_model = "Direct";
++ break;
++
++ default:
++ break;
++ }
++
++ if (! memory_model)
++ continue;
++
++ grub_printf ("0x%03x: %4d x %4d x %2d %s",
++ mode,
++ mode_info_tmp.x_resolution,
++ mode_info_tmp.y_resolution,
++ mode_info_tmp.bits_per_pixel,
++ memory_model);
++
++ /* Show mask and position details for direct color modes. */
++ if (mode_info_tmp.memory_model == GRUB_VBE_MEMORY_MODEL_DIRECT_COLOR)
++ grub_printf (", mask: %d/%d/%d/%d pos: %d/%d/%d/%d",
++ mode_info_tmp.red_mask_size,
++ mode_info_tmp.green_mask_size,
++ mode_info_tmp.blue_mask_size,
++ mode_info_tmp.rsvd_mask_size,
++ mode_info_tmp.red_field_position,
++ mode_info_tmp.green_field_position,
++ mode_info_tmp.blue_field_position,
++ mode_info_tmp.rsvd_field_position);
++ grub_printf ("\n");
++ }
++
++ grub_free (saved_video_mode_list);
++
++ /* Check existence of vbe_mode environment variable. */
++ modevar = grub_env_get ("vbe_mode");
++
++ if (modevar != 0)
++ {
++ unsigned long value;
++
++ value = grub_strtoul (modevar, 0, 0);
++ if (grub_errno == GRUB_ERR_NONE)
++ use_mode = value;
++ else
++ grub_errno = GRUB_ERR_NONE;
++ }
++
++ grub_printf ("Configured VBE mode (vbe_mode) = 0x%03x\n", use_mode);
++
++ return 0;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(vbeinfo)
++{
++ cmd =
++ grub_register_command ("vbeinfo", grub_cmd_vbeinfo, 0,
++ N_("List compatible VESA BIOS extension video modes."));
++}
++
++GRUB_MOD_FINI(vbeinfo)
++{
++ grub_unregister_command (cmd);
++}
--- /dev/null
--- /dev/null
++/* vbetest.c - command to test VESA BIOS Extension 2.0+ support. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2005,2007 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/normal.h>
++#include <grub/dl.h>
++#include <grub/env.h>
++#include <grub/misc.h>
++#include <grub/term.h>
++#include <grub/machine/init.h>
++#include <grub/machine/vbe.h>
++#include <grub/video.h>
++#include <grub/err.h>
++#include <grub/i18n.h>
++
++static grub_err_t
++grub_cmd_vbetest (grub_command_t cmd __attribute__ ((unused)),
++ int argc __attribute__ ((unused)),
++ char **args __attribute__ ((unused)))
++{
++ grub_err_t err;
++ char *modevar;
++ struct grub_vbe_mode_info_block mode_info;
++ struct grub_vbe_info_block controller_info;
++ grub_uint32_t use_mode = GRUB_VBE_DEFAULT_VIDEO_MODE;
++ grub_uint32_t old_mode;
++ grub_uint8_t *framebuffer = 0;
++ grub_uint32_t bytes_per_scan_line = 0;
++ unsigned char *ptr;
++ int i;
++
++ grub_printf ("Probing for VESA BIOS Extension ... ");
++
++ /* Check if VESA BIOS exists. */
++ err = grub_vbe_probe (&controller_info);
++ if (err != GRUB_ERR_NONE)
++ return err;
++
++ grub_printf ("found!\n");
++
++ /* Dump out controller information. */
++ grub_printf ("VBE signature = %c%c%c%c\n",
++ controller_info.signature[0],
++ controller_info.signature[1],
++ controller_info.signature[2],
++ controller_info.signature[3]);
++
++ grub_printf ("VBE version = %d.%d\n",
++ controller_info.version >> 8,
++ controller_info.version & 0xFF);
++ grub_printf ("OEM string ptr = %08x\n",
++ controller_info.oem_string_ptr);
++ grub_printf ("Total memory = %d\n",
++ controller_info.total_memory);
++
++ err = grub_vbe_get_video_mode (&old_mode);
++ grub_printf ("Get video mode err = %04x\n", err);
++
++ if (err == GRUB_ERR_NONE)
++ grub_printf ("Old video mode = %04x\n", old_mode);
++ else
++ grub_errno = GRUB_ERR_NONE;
++
++ /* Check existence of vbe_mode environment variable. */
++ modevar = grub_env_get ("vbe_mode");
++ if (modevar != 0)
++ {
++ unsigned long value;
++
++ value = grub_strtoul (modevar, 0, 0);
++ if (grub_errno == GRUB_ERR_NONE)
++ use_mode = value;
++ else
++ grub_errno = GRUB_ERR_NONE;
++ }
++
++ err = grub_vbe_get_video_mode_info (use_mode, &mode_info);
++ if (err != GRUB_ERR_NONE)
++ return err;
++
++ /* Dump out details about the mode being tested. */
++ grub_printf ("mode: 0x%03x\n",
++ use_mode);
++ grub_printf ("width : %d\n",
++ mode_info.x_resolution);
++ grub_printf ("height: %d\n",
++ mode_info.y_resolution);
++ grub_printf ("memory model: %02x\n",
++ mode_info.memory_model);
++ grub_printf ("bytes/scanline: %d\n",
++ mode_info.bytes_per_scan_line);
++ grub_printf ("bytes/scanline (lin): %d\n",
++ mode_info.lin_bytes_per_scan_line);
++ grub_printf ("base address: %08x\n",
++ mode_info.phys_base_addr);
++ grub_printf ("red mask/pos: %d/%d\n",
++ mode_info.red_mask_size,
++ mode_info.red_field_position);
++ grub_printf ("green mask/pos: %d/%d\n",
++ mode_info.green_mask_size,
++ mode_info.green_field_position);
++ grub_printf ("blue mask/pos: %d/%d\n",
++ mode_info.blue_mask_size,
++ mode_info.blue_field_position);
++
++ grub_printf ("Press any key to continue.\n");
++
++ grub_getkey ();
++
++ /* Setup GFX mode. */
++ err = grub_vbe_set_video_mode (use_mode, &mode_info);
++ if (err != GRUB_ERR_NONE)
++ return err;
++
++ /* Determine framebuffer address and how many bytes are in scan line. */
++ framebuffer = (grub_uint8_t *) mode_info.phys_base_addr;
++ ptr = framebuffer;
++
++ if (controller_info.version >= 0x300)
++ {
++ bytes_per_scan_line = mode_info.lin_bytes_per_scan_line;
++ }
++ else
++ {
++ bytes_per_scan_line = mode_info.bytes_per_scan_line;
++ }
++
++ /* Draw some random data to screen. */
++ for (i = 0; i < 256 * 256; i++)
++ {
++ ptr[i] = i & 0x0F;
++ }
++
++ /* Draw white line to screen. */
++ for (i = 0; i < 100; i++)
++ {
++ ptr[mode_info.bytes_per_scan_line * 50 + i] = 0x0F;
++ }
++
++ /* Draw another white line to screen. */
++ grub_memset (ptr + bytes_per_scan_line * 51, 0x0f, bytes_per_scan_line);
++
++ grub_getkey ();
++
++ grub_video_restore ();
++
++ /* Restore old video mode. */
++ grub_vbe_set_video_mode (old_mode, 0);
++
++ return grub_errno;
++}
++
++static grub_command_t cmd;
++
++GRUB_MOD_INIT(vbetest)
++{
++ cmd = grub_register_command ("vbetest", grub_cmd_vbetest,
++ 0, N_("Test VESA BIOS Extension 2.0+ support."));
++}
++
++GRUB_MOD_FINI(vbetest)
++{
++ grub_unregister_command (cmd);
++}
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/dl.h>
++#include <grub/disk.h>
++#include <grub/mm.h>
++#include <grub/time.h>
++#include <grub/pci.h>
++#include <grub/ata.h>
++#include <grub/scsi.h>
++#include <grub/misc.h>
++#include <grub/list.h>
++
++struct grub_ahci_cmd_head
++{
++ grub_uint32_t config;
++ grub_uint32_t transfered;
++ grub_uint64_t command_table_base;
++ grub_uint32_t unused[4];
++};
++
++struct grub_ahci_prdt_entry
++{
++ grub_uint64_t data_base;
++ grub_uint32_t unused;
++ grub_uint32_t size;
++};
++
++struct grub_ahci_cmd_table
++{
++ grub_uint8_t cfis[0x40];
++ grub_uint8_t command[16];
++ grub_uint32_t reserved[0xc];
++ struct grub_ahci_prdt_entry prdt[1];
++};
++
++struct grub_ahci_hba_port
++{
++ grub_uint64_t command_list_base;
++ grub_uint64_t fis_base;
++ grub_uint32_t intstatus;
++ grub_uint32_t inten;
++ grub_uint32_t command;
++ grub_uint32_t unused1[6];
++ grub_uint32_t sata_active;
++ grub_uint32_t command_issue;
++ grub_uint32_t unused2[17];
++};
++
++struct grub_ahci_hba
++{
++ grub_uint32_t cap;
++ grub_uint32_t global_control;
++ grub_uint32_t intr_status;
++ grub_uint32_t ports_implemented;
++ grub_uint32_t unused1[6];
++ grub_uint32_t bios_handoff;
++ grub_uint32_t unused2[53];
++ struct grub_ahci_hba_port ports[32];
++};
++
++enum
++ {
++ GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
++ };
++
++enum
++ {
++ GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
++ GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
++ };
++
++enum
++ {
++ GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
++ GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
++ GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
++ GRUB_AHCI_BIOS_HANDOFF_RWC = 8
++ };
++
++
++struct grub_ahci_device
++{
++ struct grub_ahci_device *next;
++ volatile struct grub_ahci_hba *hba;
++ int port;
++ int num;
++ struct grub_pci_dma_chunk *command_list_chunk;
++ volatile struct grub_ahci_cmd_head *command_list;
++ struct grub_pci_dma_chunk *command_table_chunk;
++ volatile struct grub_ahci_cmd_table *command_table;
++};
++
++enum
++ {
++ GRUB_AHCI_CONFIG_READ = 0,
++ GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
++ GRUB_AHCI_CONFIG_ATAPI = 0x20,
++ GRUB_AHCI_CONFIG_WRITE = 0x40,
++ GRUB_AHCI_CONFIG_PREFETCH = 0x80,
++ GRUB_AHCI_CONFIG_RESET = 0x100,
++ GRUB_AHCI_CONFIG_BIST = 0x200,
++ GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
++ GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
++ GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
++ };
++#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
++#define GRUB_AHCI_CONFIG_PMP_SHIFT 12
++#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
++#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000
++
++#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000
++
++static struct grub_ahci_device *grub_ahci_devices;
++static int numdevs;
++
++static int NESTED_FUNC_ATTR
++grub_ahci_pciinit (grub_pci_device_t dev,
++ grub_pci_id_t pciid __attribute__ ((unused)))
++{
++ grub_pci_address_t addr;
++ grub_uint32_t class;
++ grub_uint32_t bar;
++ unsigned i, nports;
++ volatile struct grub_ahci_hba *hba;
++
++ /* Read class. */
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
++ class = grub_pci_read (addr);
++
++ /* Check if this class ID matches that of a PCI IDE Controller. */
++ if (class >> 8 != 0x010601)
++ return 0;
++
++ addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
++ bar = grub_pci_read (addr);
++
++ if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
++ | GRUB_PCI_ADDR_MEM_PREFETCH))
++ != (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
++ return 0;
++
++ hba = grub_pci_device_map_range (dev, bar & GRUB_PCI_ADDR_MEM_MASK,
++ sizeof (hba));
++
++ hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
++
++ nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
++
++ if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
++ {
++ grub_uint64_t endtime;
++
++ grub_dprintf ("ahci", "Requesting AHCI ownership\n");
++ hba->bios_handoff = (hba->bios_handoff & ~GRUB_AHCI_BIOS_HANDOFF_RWC)
++ | GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
++ grub_dprintf ("ahci", "Waiting for BIOS to give up ownership\n");
++ endtime = grub_get_time_ms () + 1000;
++ while ((hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
++ && grub_get_time_ms () < endtime);
++ if (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED)
++ {
++ grub_dprintf ("ahci", "Forcibly taking ownership\n");
++ hba->bios_handoff = GRUB_AHCI_BIOS_HANDOFF_OS_OWNED;
++ hba->bios_handoff |= GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED;
++ }
++ else
++ grub_dprintf ("ahci", "AHCI ownership obtained\n");
++ }
++ else
++ grub_dprintf ("ahci", "AHCI is already in OS mode\n");
++ grub_dprintf ("ahci", "%d AHCI ports\n", nports);
++
++ for (i = 0; i < nports; i++)
++ {
++ struct grub_ahci_device *adev;
++ struct grub_pci_dma_chunk *command_list;
++ struct grub_pci_dma_chunk *command_table;
++
++ if (!(hba->ports_implemented & (1 << i)))
++ continue;
++
++ command_list = grub_memalign_dma32 (1024,
++ sizeof (struct grub_ahci_cmd_head));
++ if (!command_list)
++ return 1;
++
++ command_table = grub_memalign_dma32 (1024,
++ sizeof (struct grub_ahci_cmd_table));
++ if (!command_table)
++ {
++ grub_dma_free (command_list);
++ return 1;
++ }
++
++ adev = grub_malloc (sizeof (*adev));
++ if (!adev)
++ {
++ grub_dma_free (command_list);
++ grub_dma_free (command_table);
++ return 1;
++ }
++
++ grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i);
++
++ adev->hba = hba;
++ adev->port = i;
++ adev->num = numdevs++;
++ adev->command_list_chunk = command_list;
++ adev->command_list = grub_dma_get_virt (command_list);
++ adev->command_table_chunk = command_table;
++ adev->command_table = grub_dma_get_virt (command_table);
++ adev->command_list->command_table_base
++ = grub_dma_get_phys (command_table);
++
++ adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list);
++ grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
++ GRUB_AS_LIST (adev));
++ }
++
++ return 0;
++}
++
++static grub_err_t
++grub_ahci_initialize (void)
++{
++ grub_pci_iterate (grub_ahci_pciinit);
++ return grub_errno;
++}
++
++
++\f
++
++static int
++grub_ahci_iterate (int (*hook) (int id, int bus))
++{
++ struct grub_ahci_device *dev;
++
++ FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
++ if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num))
++ return 1;
++
++ return 0;
++}
++
++#if 0
++static int
++find_free_cmd_slot (struct grub_ahci_device *dev)
++{
++ int i;
++ for (i = 0; i < 32; i++)
++ {
++ if (dev->hda->ports[dev->port].command_issue & (1 << i))
++ continue;
++ if (dev->hda->ports[dev->port].sata_active & (1 << i))
++ continue;
++ return i;
++ }
++ return -1;
++}
++#endif
++
++enum
++ {
++ GRUB_AHCI_FIS_REG_H2D = 0x27
++ };
++
++static const int register_map[11] = { 3 /* Features */,
++ 12 /* Sectors */,
++ 4 /* LBA low */,
++ 5 /* LBA mid */,
++ 6 /* LBA high */,
++ 7 /* Device */,
++ 2 /* CMD register */,
++ 13 /* Sectors 48 */,
++ 8 /* LBA48 low */,
++ 9 /* LBA48 mid */,
++ 10 /* LBA48 high */ };
++
++static grub_err_t
++grub_ahci_readwrite (grub_ata_t disk,
++ struct grub_disk_ata_pass_through_parms *parms)
++{
++ struct grub_ahci_device *dev = (struct grub_ahci_device *) disk->data;
++ struct grub_pci_dma_chunk *bufc;
++ grub_uint64_t endtime;
++ unsigned i;
++
++ grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
++ (unsigned long long) parms->size,
++ (unsigned long long) parms->cmdsize);
++
++ if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "incorrect ATAPI command size");
++
++ if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH)
++ return grub_error (GRUB_ERR_BAD_ARGUMENT, "too big data buffer");
++
++ bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
++
++ /* FIXME: support port multipliers. */
++ dev->command_list[0].config
++ = (4 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
++ | GRUB_AHCI_CONFIG_CLEAR_R_OK
++ | (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
++ | (1 << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
++ | (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
++ | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ);
++ dev->command_list[0].transfered = 0;
++ dev->command_list[0].command_table_base
++ = grub_dma_get_phys (dev->command_table_chunk);
++ grub_memset ((char *) dev->command_list[0].unused, 0,
++ sizeof (dev->command_list[0].unused));
++ grub_memset ((char *) &dev->command_table[0], 0,
++ sizeof (dev->command_table[0]));
++ if (parms->cmdsize)
++ grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
++ parms->cmdsize);
++
++ dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D;
++ for (i = 0; i < sizeof (parms->taskfile.raw); i++)
++ dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i];
++
++ dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
++ dev->command_table[0].prdt[0].unused = 0;
++ dev->command_table[0].prdt[0].size = (parms->size + (parms->size & 1) - 1)
++ | GRUB_AHCI_INTERRUPT_ON_COMPLETE;
++
++ if (parms->write)
++ grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
++
++ grub_dprintf ("ahci", "AHCI command schedulded\n");
++ dev->hba->ports[dev->port].inten = (1 << 5);
++ dev->hba->ports[dev->port].intstatus = (1 << 5);
++ dev->hba->ports[dev->port].command_issue |= 1;
++ dev->hba->ports[dev->port].command |= 1;
++
++ endtime = grub_get_time_ms () + 1000;
++ while (!(dev->hba->ports[dev->port].intstatus & (1 << 5)))
++ if (grub_get_time_ms () > endtime)
++ {
++ grub_dprintf ("ahci", "AHCI timeout\n");
++ dev->hba->ports[dev->port].command &= ~1;
++ /* FIXME: free resources. */
++ return grub_error (GRUB_ERR_IO, "AHCI transfer timeouted");
++ }
++
++ grub_dprintf ("ahci", "AHCI command completed succesfully\n");
++ dev->hba->ports[dev->port].command &= ~1;
++
++ if (!parms->write)
++ grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
++ grub_dma_free (bufc);
++
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_ahci_open (int id, int devnum, struct grub_ata *ata)
++{
++ struct grub_ahci_device *dev;
++
++ if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
++
++ FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
++ {
++ if (dev->num == devnum)
++ break;
++ }
++
++ if (! dev)
++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
++
++ grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
++
++ ata->data = dev;
++
++ return GRUB_ERR_NONE;
++}
++
++static struct grub_ata_dev grub_ahci_dev =
++ {
++ .iterate = grub_ahci_iterate,
++ .open = grub_ahci_open,
++ .readwrite = grub_ahci_readwrite,
++ };
++
++\f
++
++GRUB_MOD_INIT(ahci)
++{
++ /* To prevent two drivers operating on the same disks. */
++ // grub_disk_firmware_is_tainted = 1;
++ if (0 && grub_disk_firmware_fini)
++ {
++ grub_disk_firmware_fini ();
++ grub_disk_firmware_fini = NULL;
++ }
++
++ /* AHCI initialization. */
++ grub_ahci_initialize ();
++
++ /* AHCI devices are handled by scsi.mod. */
++ grub_ata_dev_register (&grub_ahci_dev);
++}
++
++GRUB_MOD_FINI(ahci)
++{
++ grub_ata_dev_unregister (&grub_ahci_dev);
++}
--- /dev/null
-#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);
+ }
--- /dev/null
--- /dev/null
++/* ata_pthru.c - ATA pass through for ata.mod. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2009 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/ata.h>
++#include <grub/scsi.h>
++#include <grub/disk.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/pci.h>
++#include <grub/cs5536.h>
++#include <grub/time.h>
++
++/* At the moment, only two IDE ports are supported. */
++static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
++ GRUB_ATA_CH1_PORT1 };
++static const grub_port_t grub_pata_ioaddress2[] = { GRUB_ATA_CH0_PORT2,
++ GRUB_ATA_CH1_PORT2 };
++
++struct grub_pata_device
++{
++ /* IDE port to use. */
++ int port;
++
++ /* IO addresses on which the registers for this device can be
++ found. */
++ grub_port_t ioaddress;
++ grub_port_t ioaddress2;
++
++ /* Two devices can be connected to a single cable. Use this field
++ to select device 0 (commonly known as "master") or device 1
++ (commonly known as "slave"). */
++ int device;
++
++ struct grub_pata_device *next;
++};
++
++static struct grub_pata_device *grub_pata_devices;
++
++static inline void
++grub_pata_regset (struct grub_pata_device *dev, int reg, int val)
++{
++ grub_outb (val, dev->ioaddress + reg);
++}
++
++static inline grub_uint8_t
++grub_pata_regget (struct grub_pata_device *dev, int reg)
++{
++ return grub_inb (dev->ioaddress + reg);
++}
++
++static inline void
++grub_pata_regset2 (struct grub_pata_device *dev, int reg, int val)
++{
++ grub_outb (val, dev->ioaddress2 + reg);
++}
++
++static inline grub_uint8_t
++grub_pata_regget2 (struct grub_pata_device *dev, int reg)
++{
++ return grub_inb (dev->ioaddress2 + reg);
++}
++
++/* Wait for !BSY. */
++static grub_err_t
++grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds)
++{
++ /* ATA requires 400ns (after a write to CMD register) or
++ 1 PIO cycle (after a DRQ block transfer) before
++ first check of BSY. */
++ grub_millisleep (1);
++
++ int i = 1;
++ grub_uint8_t sts;
++ while ((sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS))
++ & GRUB_ATA_STATUS_BUSY)
++ {
++ if (i >= milliseconds)
++ {
++ grub_dprintf ("pata", "timeout: %dms, status=0x%x\n",
++ milliseconds, sts);
++ return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout");
++ }
++
++ grub_millisleep (1);
++ i++;
++ }
++
++ return GRUB_ERR_NONE;
++}
++
++static inline grub_err_t
++grub_pata_check_ready (struct grub_pata_device *dev)
++{
++ if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
++ return grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
++
++ return GRUB_ERR_NONE;
++}
++
++static inline void
++grub_pata_wait (void)
++{
++ grub_millisleep (50);
++}
++
++static void
++grub_pata_pio_read (struct grub_pata_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));
++ if (size & 1)
++ buf[size - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress
++ + GRUB_ATA_REG_DATA));
++}
++
++static void
++grub_pata_pio_write (struct grub_pata_device *dev, char *buf, grub_size_t size)
++{
++ 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);
++}
++
++/* ATA pass through support, used by hdparm.mod. */
++static grub_err_t
++grub_pata_readwrite (struct grub_ata *disk,
++ struct grub_disk_ata_pass_through_parms *parms)
++{
++ struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
++ grub_size_t nread = 0;
++
++ if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
++ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
++ "ATAPI non-12 byte commands not supported");
++
++ grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
++ parms->taskfile.cmd,
++ parms->taskfile.features,
++ parms->taskfile.sectors);
++ grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
++ parms->taskfile.lba_high,
++ parms->taskfile.lba_mid,
++ parms->taskfile.lba_low, parms->size);
++
++ /* Set registers. */
++ grub_pata_regset (dev, GRUB_ATA_REG_DISK, (parms->cmdsize ? 0 : 0xE0)
++ | dev->device << 4
++ | (parms->taskfile.disk & 0xf));
++ if (grub_pata_check_ready (dev))
++ return grub_errno;
++
++ int i;
++ for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
++ grub_pata_regset (dev, i,
++ parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
++ for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
++ grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
++
++ /* Start command. */
++ grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
++
++ /* Wait for !BSY. */
++ if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
++ return grub_errno;
++
++ /* Check status. */
++ grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
++ grub_dprintf ("pata", "status=0x%x\n", sts);
++
++ if (parms->cmdsize)
++ {
++ grub_uint8_t irs = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON);
++ /* OK if DRQ is asserted and interrupt reason is as expected. */
++ if (!((sts & GRUB_ATA_STATUS_DRQ)
++ && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_CMD_OUT))
++ return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
++ /* Write the packet. */
++ grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
++ }
++
++ /* Transfer data. */
++ while (nread < parms->size
++ && ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
++ == GRUB_ATA_STATUS_DRQ)
++ && (!parms->cmdsize
++ || ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
++ & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_DATA_IN)))
++ {
++ unsigned cnt;
++ if (parms->cmdsize)
++ {
++ cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
++ | grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
++ grub_dprintf("pata", "DRQ count=%u\n", cnt);
++
++ /* Count of last transfer may be uneven. */
++ if (! (0 < cnt && cnt <= parms->size - nread
++ && (! (cnt & 1) || cnt == parms->size - nread)))
++ return grub_error (GRUB_ERR_READ_ERROR,
++ "invalid ATAPI transfer count");
++ }
++ else
++ cnt = GRUB_DISK_SECTOR_SIZE;
++ if (cnt > parms->size - nread)
++ cnt = parms->size - nread;
++
++ if (parms->write)
++ grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt);
++ else
++ grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt);
++
++ nread += cnt;
++ }
++ if (parms->write)
++ {
++ /* Check for write error. */
++ if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
++ return grub_errno;
++
++ if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS)
++ & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
++ return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
++ }
++ parms->size = nread;
++
++ /* Return registers. */
++ for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
++ parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
++
++ grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
++ parms->taskfile.status,
++ parms->taskfile.error,
++ parms->taskfile.sectors);
++
++ if (parms->taskfile.status
++ & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
++ return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
++
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++check_device (struct grub_pata_device *dev)
++{
++ grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
++ grub_pata_wait ();
++
++ /* Try to detect if the port is in use by writing to it,
++ waiting for a while and reading it again. If the value
++ was preserved, there is a device connected. */
++ grub_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
++ grub_pata_wait ();
++ grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
++ grub_dprintf ("ata", "sectors=0x%x\n", sec);
++ if (sec != 0x5A)
++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
++
++ /* The above test may detect a second (slave) device
++ connected to a SATA controller which supports only one
++ (master) device. It is not safe to use the status register
++ READY bit to check for controller channel existence. Some
++ ATAPI commands (RESET, DIAGNOSTIC) may clear this bit. */
++
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_pata_device_initialize (int port, int device, int addr, int addr2)
++{
++ struct grub_pata_device *dev;
++ struct grub_pata_device **devp;
++ grub_err_t err;
++
++ grub_dprintf ("pata", "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_pata_devices; *devp; devp = &(*devp)->next);
++ *devp = dev;
++
++ err = check_device (dev);
++ if (err)
++ grub_print_error ();
++
++ return 0;
++}
++
++static int NESTED_FUNC_ATTR
++grub_pata_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_pata_ioaddress[i];
++ regb = grub_pata_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 ("pata",
++ "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_pata_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_pata_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_pata_initialize (void)
++{
++ grub_pci_iterate (grub_pata_pciinit);
++ return 0;
++}
++
++static grub_err_t
++grub_pata_open (int id, int devnum, struct grub_ata *ata)
++{
++ struct grub_pata_device *dev;
++ struct grub_pata_device *devfnd = 0;
++ grub_err_t err;
++
++ if (id != GRUB_SCSI_SUBSYSTEM_PATA)
++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
++
++ for (dev = grub_pata_devices; dev; dev = dev->next)
++ {
++ if (dev->port * 2 + dev->device == devnum)
++ {
++ devfnd = dev;
++ break;
++ }
++ }
++
++ grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
++
++ if (! devfnd)
++ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
++
++ err = check_device (devfnd);
++ if (err)
++ return err;
++
++ ata->data = devfnd;
++
++ return GRUB_ERR_NONE;
++}
++
++static int
++grub_pata_iterate (int (*hook) (int id, int bus))
++{
++ struct grub_pata_device *dev;
++
++ for (dev = grub_pata_devices; dev; dev = dev->next)
++ if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device))
++ return 1;
++
++ return 0;
++}
++
++
++static struct grub_ata_dev grub_pata_dev =
++ {
++ .iterate = grub_pata_iterate,
++ .open = grub_pata_open,
++ .readwrite = grub_pata_readwrite,
++ };
++
++
++\f
++
++GRUB_MOD_INIT(ata_pthru)
++{
++ /* 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_pata_initialize ();
++
++ grub_ata_dev_register (&grub_pata_dev);
++}
++
++GRUB_MOD_FINI(ata_pthru)
++{
++ grub_ata_dev_unregister (&grub_pata_dev);
++}
--- /dev/null
- 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, §or_mod);
+
+ if (! (sector_mod == 0 && size % spb == 0))
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ "unaligned SCSI read not supported");
+
+ size /= spb;
+ }
+
+ /* Depending on the type, select a read function. */
+ switch (scsi->devtype)
+ {
+ case grub_scsi_devtype_direct:
+ return grub_scsi_read10 (disk, sector, size, buf);
+
+ case grub_scsi_devtype_cdrom:
+ return grub_scsi_read12 (disk, sector, size, buf);
+ }
+
+ /* XXX: Never reached. */
+ return GRUB_ERR_NONE;
+
+ #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);
+ }
--- /dev/null
-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);
+ }
--- /dev/null
--- /dev/null
++gcc -c -m32 -DELF32 -o efiemu32.o ./efiemu.c -Wall -Werror -nostdlib -O2 -I. -I../../include
++gcc -c -m64 -DELF64 -o efiemu64_c.o ./efiemu.c -Wall -Werror -mcmodel=large -O2 -I. -I../../include
++gcc -c -m64 -DELF64 -o efiemu64_s.o ./efiemu.S -Wall -Werror -mcmodel=large -O2 -I. -I../../include
++ld -o efiemu64.o -r efiemu64_s.o efiemu64_c.o -nostdlib
--- /dev/null
--- /dev/null
++/* Memory allocation on the stack.
++
++ Copyright (C) 1995, 1999, 2001-2004, 2006-2010 Free Software Foundation,
++ Inc.
++
++ This program is free software; you can redistribute it and/or modify it
++ under the terms of the GNU General Public License as published
++ by the Free Software Foundation; either version 2, or (at your option)
++ any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ General Public License for more details.
++
++ You should have received a copy of the GNU General Public
++ License along with this program; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
++ USA. */
++
++/* Avoid using the symbol _ALLOCA_H here, as Bison assumes _ALLOCA_H
++ means there is a real alloca function. */
++#ifndef _GL_ALLOCA_H
++#define _GL_ALLOCA_H
++
++/* alloca (N) returns a pointer to N bytes of memory
++ allocated on the stack, which will last until the function returns.
++ Use of alloca should be avoided:
++ - inside arguments of function calls - undefined behaviour,
++ - in inline functions - the allocation may actually last until the
++ calling function returns,
++ - for huge N (say, N >= 65536) - you never know how large (or small)
++ the stack is, and when the stack cannot fulfill the memory allocation
++ request, the program just crashes.
++ */
++
++#ifndef alloca
++# ifdef __GNUC__
++# define alloca __builtin_alloca
++# elif defined _AIX
++# define alloca __alloca
++# elif defined _MSC_VER
++# include <malloc.h>
++# define alloca _alloca
++# elif defined __DECC && defined __VMS
++# define alloca __ALLOCA
++# else
++# include <stddef.h>
++# ifdef __cplusplus
++extern "C"
++# endif
++void *alloca (size_t);
++# endif
++#endif
++
++#endif /* _GL_ALLOCA_H */
--- /dev/null
--- /dev/null
++/* Version hook for Argp.
++ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++#include <config.h>
++#include <version-etc.h>
++#include <argp.h>
++#include <argp-version-etc.h>
++
++static const char *program_canonical_name;
++static const char * const *program_authors;
++
++static void
++version_etc_hook (FILE *stream, struct argp_state *state)
++{
++ version_etc_ar (stream, program_canonical_name, PACKAGE_NAME, VERSION,
++ program_authors);
++}
++
++void
++argp_version_setup (const char *name, const char * const *authors)
++{
++ argp_program_version_hook = version_etc_hook;
++ program_canonical_name = name;
++ program_authors = authors;
++}
--- /dev/null
--- /dev/null
++/* Version hook for Argp.
++ Copyright (C) 2009, 2010 Free Software Foundation, Inc.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _ARGP_VERSION_ETC_H
++#define _ARGP_VERSION_ETC_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* Setup standard display of the version information for the `--version'
++ option. NAME is the canonical program name, and AUTHORS is a NULL-
++ terminated array of author names. At least one author name must be
++ given.
++
++ If NAME is NULL, the package name (as given by the PACKAGE macro)
++ is asumed to be the name of the program.
++
++ This function is intended to be called before argp_parse().
++*/
++extern void argp_version_setup (const char *name, const char * const *authors);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* _ARGP_VERSION_ETC_H */
--- /dev/null
--- /dev/null
++/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003,
++ 2005, 2007 Free Software Foundation, Inc.
++
++ This file is part of the GNU C Library.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2, or (at your option)
++ any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software Foundation,
++ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
++
++#ifndef _FNMATCH_H
++#define _FNMATCH_H 1
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* We #undef these before defining them because some losing systems
++ (HP-UX A.08.07 for example) define these in <unistd.h>. */
++#undef FNM_PATHNAME
++#undef FNM_NOESCAPE
++#undef FNM_PERIOD
++
++/* Bits set in the FLAGS argument to `fnmatch'. */
++#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */
++#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */
++#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */
++
++#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
++# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */
++# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
++# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */
++# define FNM_EXTMATCH (1 << 5) /* Use ksh-like extended matching. */
++#endif
++
++/* Value returned by `fnmatch' if STRING does not match PATTERN. */
++#define FNM_NOMATCH 1
++
++/* This value is returned if the implementation does not support
++ `fnmatch'. Since this is not the case here it will never be
++ returned but the conformance test suites still require the symbol
++ to be defined. */
++#ifdef _XOPEN_SOURCE
++# define FNM_NOSYS (-1)
++#endif
++
++/* Match NAME against the file name pattern PATTERN,
++ returning zero if it matches, FNM_NOMATCH if not. */
++extern int fnmatch (const char *__pattern, const char *__name,
++ int __flags);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* fnmatch.h */
--- /dev/null
--- /dev/null
++/* Declarations for getopt.
++ Copyright (C) 1989-1994,1996-1999,2001,2003,2004,2005,2006,2007
++ Free Software Foundation, Inc.
++ This file is part of the GNU C Library.
++
++ This program is free software: you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++#ifndef _GETOPT_H
++
++#ifndef __need_getopt
++# define _GETOPT_H 1
++#endif
++
++/* Standalone applications should #define __GETOPT_PREFIX to an
++ identifier that prefixes the external functions and variables
++ defined in this header. When this happens, include the
++ headers that might declare getopt so that they will not cause
++ confusion if included after this file. Then systematically rename
++ identifiers so that they do not collide with the system functions
++ and variables. Renaming avoids problems with some compilers and
++ linkers. */
++#if defined __GETOPT_PREFIX && !defined __need_getopt
++# include <stdlib.h>
++# include <stdio.h>
++# include <unistd.h>
++# undef __need_getopt
++# undef getopt
++# undef getopt_long
++# undef getopt_long_only
++# undef optarg
++# undef opterr
++# undef optind
++# undef optopt
++# define __GETOPT_CONCAT(x, y) x ## y
++# define __GETOPT_XCONCAT(x, y) __GETOPT_CONCAT (x, y)
++# define __GETOPT_ID(y) __GETOPT_XCONCAT (__GETOPT_PREFIX, y)
++# define getopt __GETOPT_ID (getopt)
++# define getopt_long __GETOPT_ID (getopt_long)
++# define getopt_long_only __GETOPT_ID (getopt_long_only)
++# define optarg __GETOPT_ID (optarg)
++# define opterr __GETOPT_ID (opterr)
++# define optind __GETOPT_ID (optind)
++# define optopt __GETOPT_ID (optopt)
++#endif
++
++/* Standalone applications get correct prototypes for getopt_long and
++ getopt_long_only; they declare "char **argv". libc uses prototypes
++ with "char *const *argv" that are incorrect because getopt_long and
++ getopt_long_only can permute argv; this is required for backward
++ compatibility (e.g., for LSB 2.0.1).
++
++ This used to be `#if defined __GETOPT_PREFIX && !defined __need_getopt',
++ but it caused redefinition warnings if both unistd.h and getopt.h were
++ included, since unistd.h includes getopt.h having previously defined
++ __need_getopt.
++
++ The only place where __getopt_argv_const is used is in definitions
++ of getopt_long and getopt_long_only below, but these are visible
++ only if __need_getopt is not defined, so it is quite safe to rewrite
++ the conditional as follows:
++*/
++#if !defined __need_getopt
++# if defined __GETOPT_PREFIX
++# define __getopt_argv_const /* empty */
++# else
++# define __getopt_argv_const const
++# endif
++#endif
++
++/* If __GNU_LIBRARY__ is not already defined, either we are being used
++ standalone, or this is the first header included in the source file.
++ If we are being used with glibc, we need to include <features.h>, but
++ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
++ not defined, include <ctype.h>, which will pull in <features.h> for us
++ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
++ doesn't flood the namespace with stuff the way some other headers do.) */
++#if !defined __GNU_LIBRARY__
++# include <ctype.h>
++#endif
++
++#ifndef __THROW
++# ifndef __GNUC_PREREQ
++# define __GNUC_PREREQ(maj, min) (0)
++# endif
++# if defined __cplusplus && __GNUC_PREREQ (2,8)
++# define __THROW throw ()
++# else
++# define __THROW
++# endif
++#endif
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++/* For communication from `getopt' to the caller.
++ When `getopt' finds an option that takes an argument,
++ the argument value is returned here.
++ Also, when `ordering' is RETURN_IN_ORDER,
++ each non-option ARGV-element is returned here. */
++
++extern char *optarg;
++
++/* Index in ARGV of the next element to be scanned.
++ This is used for communication to and from the caller
++ and for communication between successive calls to `getopt'.
++
++ On entry to `getopt', zero means this is the first call; initialize.
++
++ When `getopt' returns -1, this is the index of the first of the
++ non-option elements that the caller should itself scan.
++
++ Otherwise, `optind' communicates from one call to the next
++ how much of ARGV has been scanned so far. */
++
++extern int optind;
++
++/* Callers store zero here to inhibit the error message `getopt' prints
++ for unrecognized options. */
++
++extern int opterr;
++
++/* Set to an option character which was unrecognized. */
++
++extern int optopt;
++
++#ifndef __need_getopt
++/* Describe the long-named options requested by the application.
++ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
++ of `struct option' terminated by an element containing a name which is
++ zero.
++
++ The field `has_arg' is:
++ no_argument (or 0) if the option does not take an argument,
++ required_argument (or 1) if the option requires an argument,
++ optional_argument (or 2) if the option takes an optional argument.
++
++ If the field `flag' is not NULL, it points to a variable that is set
++ to the value given in the field `val' when the option is found, but
++ left unchanged if the option is not found.
++
++ To have a long-named option do something other than set an `int' to
++ a compiled-in constant, such as set a value from `optarg', set the
++ option's `flag' field to zero and its `val' field to a nonzero
++ value (the equivalent single-letter option character, if there is
++ one). For long options that have a zero `flag' field, `getopt'
++ returns the contents of the `val' field. */
++
++struct option
++{
++ const char *name;
++ /* has_arg can't be an enum because some compilers complain about
++ type mismatches in all the code that assumes it is an int. */
++ int has_arg;
++ int *flag;
++ int val;
++};
++
++/* Names for the values of the `has_arg' field of `struct option'. */
++
++# define no_argument 0
++# define required_argument 1
++# define optional_argument 2
++#endif /* need getopt */
++
++
++/* Get definitions and prototypes for functions to process the
++ arguments in ARGV (ARGC of them, minus the program name) for
++ options given in OPTS.
++
++ Return the option character from OPTS just read. Return -1 when
++ there are no more options. For unrecognized options, or options
++ missing arguments, `optopt' is set to the option letter, and '?' is
++ returned.
++
++ The OPTS string is a list of characters which are recognized option
++ letters, optionally followed by colons, specifying that that letter
++ takes an argument, to be placed in `optarg'.
++
++ If a letter in OPTS is followed by two colons, its argument is
++ optional. This behavior is specific to the GNU `getopt'.
++
++ The argument `--' causes premature termination of argument
++ scanning, explicitly telling `getopt' that there are no more
++ options.
++
++ If OPTS begins with `-', then non-option arguments are treated as
++ arguments to the option '\1'. This behavior is specific to the GNU
++ `getopt'. If OPTS begins with `+', or POSIXLY_CORRECT is set in
++ the environment, then do not permute arguments. */
++
++extern int getopt (int ___argc, char *const *___argv, const char *__shortopts)
++ __THROW;
++
++#ifndef __need_getopt
++extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv,
++ const char *__shortopts,
++ const struct option *__longopts, int *__longind)
++ __THROW;
++extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv,
++ const char *__shortopts,
++ const struct option *__longopts, int *__longind)
++ __THROW;
++
++#endif
++
++#ifdef __cplusplus
++}
++#endif
++
++/* Make sure we later can get all the definitions and declarations. */
++#undef __need_getopt
++
++#endif /* getopt.h */
--- /dev/null
-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, §or, &offset, size) != GRUB_ERR_NONE)
+ {
+ grub_error_push ();
+ grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
+ (unsigned long long) sector, grub_errmsg);
+ grub_error_pop ();
+ return grub_errno;
+ }
+
+ 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, §or, &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;
+ }
--- /dev/null
--- /dev/null
++/* init.c -- Initialize GRUB on Open Firmware. */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2003,2004,2005,2007,2008 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/types.h>
++#include <grub/cache.h>
++
++void grub_stop_floppy (void);
++
++void
++grub_stop_floppy (void)
++{
++}
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++
++/*
++ * Note: These functions defined in this file may be called from C.
++ * Be careful of that you must not modify some registers. Quote
++ * from gcc-2.95.2/gcc/config/i386/i386.h:
++
++ 1 for registers not available across function calls.
++ These must include the FIXED_REGISTERS and also any
++ registers that can be used without being saved.
++ The latter must include the registers where values are returned
++ and the register where structure-value addresses are passed.
++ Aside from that, you can include as many other registers as you like.
++
++ ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg
++{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
++ */
++
++/*
++ * Note: GRUB is compiled with the options -mrtd and -mregparm=3.
++ * So the first three arguments are passed in %eax, %edx, and %ecx,
++ * respectively, and if a function has a fixed number of arguments
++ * and the number if greater than three, the function must return
++ * with "ret $N" where N is ((the number of arguments) - 3) * 4.
++ */
++
++/*
++ * This is the area for all of the special variables.
++ */
++
++ .p2align 2 /* force 4-byte alignment */
++
++/*
++ * void grub_linux_boot_zimage (void)
++ */
++VARIABLE(grub_linux_prot_size)
++ .long 0
++VARIABLE(grub_linux_tmp_addr)
++ .long 0
++VARIABLE(grub_linux_real_addr)
++ .long 0
++VARIABLE(grub_linux_is_bzimage)
++ .long 0
++
++FUNCTION(grub_linux16_real_boot)
++ /* Must be done before zImage copy. */
++ call EXT_C(grub_dl_unload_all)
++
++ movl EXT_C(grub_linux_is_bzimage), %ebx
++ test %ebx, %ebx
++ jne bzimage
++
++ /* copy the kernel */
++ movl EXT_C(grub_linux_prot_size), %ecx
++ addl $3, %ecx
++ shrl $2, %ecx
++ movl $GRUB_LINUX_BZIMAGE_ADDR, %esi
++ movl $GRUB_LINUX_ZIMAGE_ADDR, %edi
++ cld
++ rep
++ movsl
++
++bzimage:
++ movl EXT_C(grub_linux_real_addr), %ebx
++
++ /* copy the real mode code */
++ movl EXT_C(grub_linux_tmp_addr), %esi
++ movl %ebx, %edi
++ movl $GRUB_LINUX_SETUP_MOVE_SIZE, %ecx
++ cld
++ rep
++ movsb
++
++ /* change %ebx to the segment address */
++ shrl $4, %ebx
++ movl %ebx, %eax
++ addl $0x20, %eax
++ movw %ax, linux_setup_seg
++
++ /* XXX new stack pointer in safe area for calling functions */
++ movl $0x4000, %esp
++ call EXT_C(grub_stop_floppy)
++
++ /* final setup for linux boot */
++ call prot_to_real
++ .code16
++
++ cli
++ movw %bx, %ss
++ movw $GRUB_LINUX_SETUP_STACK, %sp
++
++ movw %bx, %ds
++ movw %bx, %es
++ movw %bx, %fs
++ movw %bx, %gs
++
++ /* ljmp */
++ .byte 0xea
++ .word 0
++linux_setup_seg:
++ .word 0
++ .code32
++
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/symbol.h>
++
++ .text
++/*
++ * This call is special... it never returns... in fact it should simply
++ * hang at this point!
++ */
++FUNCTION(grub_stop)
++ cli
++1: hlt
++ jmp 1b
--- /dev/null
--- /dev/null
++/* crc.c - crc function */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2008 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/types.h>
++#include <grub/lib/crc.h>
++
++static grub_uint32_t crc32_table [256];
++
++static void
++init_crc32_table (void)
++{
++ auto grub_uint32_t reflect (grub_uint32_t ref, int len);
++ grub_uint32_t reflect (grub_uint32_t ref, int len)
++ {
++ grub_uint32_t result = 0;
++ int i;
++
++ for (i = 1; i <= len; i++)
++ {
++ if (ref & 1)
++ result |= 1 << (len - i);
++ ref >>= 1;
++ }
++
++ return result;
++ }
++
++ grub_uint32_t polynomial = 0x04c11db7;
++ int i, j;
++
++ for(i = 0; i < 256; i++)
++ {
++ crc32_table[i] = reflect(i, 8) << 24;
++ for (j = 0; j < 8; j++)
++ crc32_table[i] = (crc32_table[i] << 1) ^
++ (crc32_table[i] & (1 << 31) ? polynomial : 0);
++ crc32_table[i] = reflect(crc32_table[i], 32);
++ }
++}
++
++grub_uint32_t
++grub_getcrc32 (grub_uint32_t crc, void *buf, int size)
++{
++ int i;
++ grub_uint8_t *data = buf;
++
++ if (! crc32_table[1])
++ init_crc32_table ();
++
++ crc^= 0xffffffff;
++
++ for (i = 0; i < size; i++)
++ {
++ crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data];
++ data++;
++ }
++
++ return crc ^ 0xffffffff;
++}
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008, 2009 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/symbol.h>
++
++ .p2align 2
++
++
++ .code32
++
++/*
++ * Use cdecl calling convention for *BSD kernels.
++ */
++
++FUNCTION(grub_unix_real_boot)
++
++ /* Interrupts should be disabled. */
++ cli
++
++ /* Discard `grub_unix_real_boot' return address. */
++ popl %eax
++
++ /* Fetch `entry' address ... */
++ popl %eax
++
++ /*
++ * ... and put our return address in its place. The kernel will
++ * ignore it, but it expects %esp to point to it.
++ */
++ call *%eax
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
++ * Copyright (C) 2009 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/* Based on the code from FreeBSD originally distributed under the
++ following terms: */
++
++/*-
++ * Copyright (c) 2003 Peter Wemm <peter@FreeBSD.org>
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * $FreeBSD$
++ */
++
++
++#define MSR_EFER 0xc0000080
++#define EFER_LME 0x00000100
++#define CR4_PAE 0x00000020
++#define CR4_PSE 0x00000010
++#define CR0_PG 0x80000000
++
++#include <grub/symbol.h>
++
++ .p2align 2
++
++ .code32
++
++
++VARIABLE(grub_bsd64_trampoline_start)
++
++ /* Discard `grub_unix_real_boot' return address. */
++ popl %eax
++
++ /* entry */
++ popl %edi
++
++ /* entry_hi */
++ popl %esi
++
++ cli
++
++ /* Turn on EFER.LME. */
++ movl $MSR_EFER, %ecx
++ rdmsr
++ orl $EFER_LME, %eax
++ wrmsr
++
++ /* Turn on PAE. */
++ movl %cr4, %eax
++ orl $(CR4_PAE | CR4_PSE), %eax
++ movl %eax, %cr4
++
++ /* Set %cr3 for PT4. */
++ popl %eax
++ movl %eax, %cr3
++
++ /* Push a dummy return address. */
++ pushl %eax
++
++ /* Turn on paging (implicitly sets EFER.LMA). */
++ movl %cr0, %eax
++ orl $CR0_PG, %eax
++ movl %eax, %cr0
++
++ /* Now we're in compatibility mode. set %cs for long mode. */
++ /* lgdt */
++ .byte 0x0f
++ .byte 0x01
++ .byte 0x15
++VARIABLE (grub_bsd64_trampoline_gdt)
++ .long 0x0
++
++ /* ljmp */
++ .byte 0xea
++VARIABLE (grub_bsd64_trampoline_selfjump)
++ .long 0x0
++ .word 0x08
++
++ .code64
++
++bsd64_longmode:
++ /* We're still running V=P, jump to entry point. */
++ movl %esi, %eax
++ salq $32, %rax
++ orq %rdi, %rax
++ pushq %rax
++ ret
++VARIABLE(grub_bsd64_trampoline_end)
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2006,2007,2008,2009,2010 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/machine/loader.h>
++#include <grub/file.h>
++#include <grub/disk.h>
++#include <grub/err.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include <grub/dl.h>
++#include <grub/mm.h>
++#include <grub/term.h>
++#include <grub/cpu/linux.h>
++#include <grub/efi/api.h>
++#include <grub/efi/efi.h>
++#include <grub/command.h>
++#include <grub/memory.h>
++#include <grub/env.h>
++#include <grub/video.h>
++#include <grub/time.h>
++#include <grub/i18n.h>
++
++#define GRUB_LINUX_CL_OFFSET 0x1000
++#define GRUB_LINUX_CL_END_OFFSET 0x2000
++
++#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
++ ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
++
++static grub_dl_t my_mod;
++
++static grub_size_t linux_mem_size;
++static int loaded;
++static void *real_mode_mem;
++static void *prot_mode_mem;
++static void *initrd_mem;
++static grub_efi_uintn_t real_mode_pages;
++static grub_efi_uintn_t prot_mode_pages;
++static grub_efi_uintn_t initrd_pages;
++static void *mmap_buf;
++
++static grub_uint8_t gdt[] __attribute__ ((aligned(16))) =
++ {
++ /* NULL. */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ /* Reserved. */
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ /* Code segment. */
++ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
++ /* Data segment. */
++ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
++ };
++
++struct gdt_descriptor
++{
++ grub_uint16_t limit;
++ void *base;
++} __attribute__ ((packed));
++
++static struct gdt_descriptor gdt_desc =
++ {
++ sizeof (gdt) - 1,
++ gdt
++ };
++
++struct idt_descriptor
++{
++ grub_uint16_t limit;
++ void *base;
++} __attribute__ ((packed));
++
++static struct idt_descriptor idt_desc =
++ {
++ 0,
++ 0
++ };
++
++static inline grub_size_t
++page_align (grub_size_t size)
++{
++ return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
++}
++
++/* Find the optimal number of pages for the memory map. Is it better to
++ move this code to efi/mm.c? */
++static grub_efi_uintn_t
++find_mmap_size (void)
++{
++ static grub_efi_uintn_t mmap_size = 0;
++
++ if (mmap_size != 0)
++ return mmap_size;
++
++ mmap_size = (1 << 12);
++ while (1)
++ {
++ int ret;
++ grub_efi_memory_descriptor_t *mmap;
++ grub_efi_uintn_t desc_size;
++
++ mmap = grub_malloc (mmap_size);
++ if (! mmap)
++ return 0;
++
++ ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
++ grub_free (mmap);
++
++ if (ret < 0)
++ grub_fatal ("cannot get memory map");
++ else if (ret > 0)
++ break;
++
++ mmap_size += (1 << 12);
++ }
++
++ /* Increase the size a bit for safety, because GRUB allocates more on
++ later, and EFI itself may allocate more. */
++ mmap_size += (1 << 12);
++
++ return page_align (mmap_size);
++}
++
++static void
++free_pages (void)
++{
++ if (real_mode_mem)
++ {
++ grub_efi_free_pages ((grub_addr_t) real_mode_mem, real_mode_pages);
++ real_mode_mem = 0;
++ }
++
++ if (prot_mode_mem)
++ {
++ grub_efi_free_pages ((grub_addr_t) prot_mode_mem, prot_mode_pages);
++ prot_mode_mem = 0;
++ }
++
++ if (initrd_mem)
++ {
++ grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages);
++ initrd_mem = 0;
++ }
++}
++
++/* Allocate pages for the real mode code and the protected mode code
++ for linux as well as a memory map buffer. */
++static int
++allocate_pages (grub_size_t prot_size)
++{
++ grub_efi_uintn_t desc_size;
++ grub_efi_memory_descriptor_t *mmap, *mmap_end;
++ grub_efi_uintn_t mmap_size, tmp_mmap_size;
++ grub_efi_memory_descriptor_t *desc;
++ grub_size_t real_size;
++
++ /* Make sure that each size is aligned to a page boundary. */
++ real_size = GRUB_LINUX_CL_END_OFFSET;
++ prot_size = page_align (prot_size);
++ mmap_size = find_mmap_size ();
++
++ grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n",
++ (unsigned) real_size, (unsigned) prot_size, (unsigned) mmap_size);
++
++ /* Calculate the number of pages; Combine the real mode code with
++ the memory map buffer for simplicity. */
++ real_mode_pages = ((real_size + mmap_size) >> 12);
++ prot_mode_pages = (prot_size >> 12);
++
++ /* Initialize the memory pointers with NULL for convenience. */
++ real_mode_mem = 0;
++ prot_mode_mem = 0;
++
++ /* Read the memory map temporarily, to find free space. */
++ mmap = grub_malloc (mmap_size);
++ if (! mmap)
++ return 0;
++
++ tmp_mmap_size = mmap_size;
++ if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
++ grub_fatal ("cannot get memory map");
++
++ mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
++
++ /* First, find free pages for the real mode code
++ and the memory map buffer. */
++ for (desc = mmap;
++ desc < mmap_end;
++ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
++ {
++ /* Probably it is better to put the real mode code in the traditional
++ space for safety. */
++ if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
++ && desc->physical_start <= 0x90000
++ && desc->num_pages >= real_mode_pages)
++ {
++ grub_efi_physical_address_t physical_end;
++ grub_efi_physical_address_t addr;
++
++ physical_end = desc->physical_start + (desc->num_pages << 12);
++ if (physical_end > 0x90000)
++ physical_end = 0x90000;
++
++ grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n",
++ (unsigned) desc->physical_start,
++ (unsigned) physical_end);
++ addr = physical_end - real_size - mmap_size;
++ if (addr < 0x10000)
++ continue;
++
++ grub_dprintf ("linux", "trying to allocate %u pages at %lx\n",
++ (unsigned) real_mode_pages, (unsigned long) addr);
++ real_mode_mem = grub_efi_allocate_pages (addr, real_mode_pages);
++ if (! real_mode_mem)
++ grub_fatal ("cannot allocate pages");
++
++ desc->num_pages -= real_mode_pages;
++ break;
++ }
++ }
++
++ if (! real_mode_mem)
++ {
++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
++ goto fail;
++ }
++
++ mmap_buf = (void *) ((char *) real_mode_mem + real_size);
++
++ /* Next, find free pages for the protected mode code. */
++ /* XXX what happens if anything is using this address? */
++ prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages + 1);
++ if (! prot_mode_mem)
++ {
++ grub_error (GRUB_ERR_OUT_OF_MEMORY,
++ "cannot allocate protected mode pages");
++ goto fail;
++ }
++
++ grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
++ "prot_mode_mem = %lx, prot_mode_pages = %x\n",
++ (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
++ (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
++
++ grub_free (mmap);
++ return 1;
++
++ fail:
++ grub_free (mmap);
++ free_pages ();
++ return 0;
++}
++
++static void
++grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num,
++ grub_uint64_t start, grub_uint64_t size,
++ grub_uint32_t type)
++{
++ int n = *e820_num;
++
++ if (n >= GRUB_E820_MAX_ENTRY)
++ grub_fatal ("Too many e820 memory map entries");
++
++ if ((n > 0) && (e820_map[n - 1].addr + e820_map[n - 1].size == start) &&
++ (e820_map[n - 1].type == type))
++ e820_map[n - 1].size += size;
++ else
++ {
++ e820_map[n].addr = start;
++ e820_map[n].size = size;
++ e820_map[n].type = type;
++ (*e820_num)++;
++ }
++}
++
++static grub_err_t
++grub_linux_setup_video (struct linux_kernel_params *params)
++{
++ struct grub_video_mode_info mode_info;
++ void *framebuffer;
++ grub_err_t err;
++
++ err = grub_video_get_info_and_fini (&mode_info, &framebuffer);
++
++ if (err)
++ return err;
++
++ params->lfb_width = mode_info.width;
++ params->lfb_height = mode_info.height;
++ params->lfb_depth = mode_info.bpp;
++ params->lfb_line_len = mode_info.pitch;
++
++ params->lfb_base = (grub_size_t) framebuffer;
++ params->lfb_size = ALIGN_UP (params->lfb_line_len * params->lfb_height,
++ 65536);
++
++ params->red_mask_size = mode_info.red_mask_size;
++ params->red_field_pos = mode_info.red_field_pos;
++ params->green_mask_size = mode_info.green_mask_size;
++ params->green_field_pos = mode_info.green_field_pos;
++ params->blue_mask_size = mode_info.blue_mask_size;
++ params->blue_field_pos = mode_info.blue_field_pos;
++ params->reserved_mask_size = mode_info.reserved_mask_size;
++ params->reserved_field_pos = mode_info.reserved_field_pos;
++
++ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
++
++#ifdef GRUB_MACHINE_PCBIOS
++ /* VESA packed modes may come with zeroed mask sizes, which need
++ to be set here according to DAC Palette width. If we don't,
++ this results in Linux displaying a black screen. */
++ if (mode_info.bpp <= 8)
++ {
++ struct grub_vbe_info_block controller_info;
++ int status;
++ int width = 8;
++
++ status = grub_vbe_bios_get_controller_info (&controller_info);
++
++ if (status == GRUB_VBE_STATUS_OK &&
++ (controller_info.capabilities & GRUB_VBE_CAPABILITY_DACWIDTH))
++ status = grub_vbe_bios_set_dac_palette_width (&width);
++
++ if (status != GRUB_VBE_STATUS_OK)
++ /* 6 is default after mode reset. */
++ width = 6;
++
++ params->red_mask_size = params->green_mask_size
++ = params->blue_mask_size = width;
++ params->reserved_mask_size = 0;
++ }
++#endif
++
++ return 0;
++}
++
++#ifdef __x86_64__
++extern grub_uint8_t grub_linux_trampoline_start[];
++extern grub_uint8_t grub_linux_trampoline_end[];
++#endif
++
++static grub_err_t
++grub_linux_boot (void)
++{
++ struct linux_kernel_params *params;
++ grub_efi_uintn_t mmap_size;
++ grub_efi_uintn_t map_key;
++ grub_efi_uintn_t desc_size;
++ grub_efi_uint32_t desc_version;
++ int e820_num;
++ const char *modevar;
++ char *tmp;
++ grub_err_t err;
++
++ params = real_mode_mem;
++
++ grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
++ (unsigned) params->code32_start,
++ (unsigned long) &(idt_desc.limit),
++ (unsigned long) &(gdt_desc.limit));
++ grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
++ (unsigned) idt_desc.limit, (unsigned long) idt_desc.base,
++ (unsigned) gdt_desc.limit, (unsigned long) gdt_desc.base);
++
++ auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
++ int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
++ {
++ switch (type)
++ {
++ case GRUB_MACHINE_MEMORY_AVAILABLE:
++ grub_e820_add_region (params->e820_map, &e820_num,
++ addr, size, GRUB_E820_RAM);
++ break;
++
++#ifdef GRUB_MACHINE_MEMORY_ACPI
++ case GRUB_MACHINE_MEMORY_ACPI:
++ grub_e820_add_region (params->e820_map, &e820_num,
++ addr, size, GRUB_E820_ACPI);
++ break;
++#endif
++
++#ifdef GRUB_MACHINE_MEMORY_NVS
++ case GRUB_MACHINE_MEMORY_NVS:
++ grub_e820_add_region (params->e820_map, &e820_num,
++ addr, size, GRUB_E820_NVS);
++ break;
++#endif
++
++#ifdef GRUB_MACHINE_MEMORY_CODE
++ case GRUB_MACHINE_MEMORY_CODE:
++ grub_e820_add_region (params->e820_map, &e820_num,
++ addr, size, GRUB_E820_EXEC_CODE);
++ break;
++#endif
++
++ default:
++ grub_e820_add_region (params->e820_map, &e820_num,
++ addr, size, GRUB_E820_RESERVED);
++ }
++ return 0;
++ }
++
++ e820_num = 0;
++ grub_mmap_iterate (hook);
++ params->mmap_size = e820_num;
++
++ grub_dprintf ("linux", "Trampoline at %p. code32=%x, real_mode_mem=%p\n",
++ ((char *) prot_mode_mem + (prot_mode_pages << 12)),
++ (unsigned) params->code32_start, real_mode_mem);
++
++ modevar = grub_env_get ("gfxpayload");
++
++ /* Now all graphical modes are acceptable.
++ May change in future if we have modes without framebuffer. */
++ if (modevar && *modevar != 0)
++ {
++ tmp = grub_xasprintf ("%s;auto", modevar);
++ if (! tmp)
++ return grub_errno;
++ err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
++ grub_free (tmp);
++ }
++ else
++ err = grub_video_set_mode ("auto", GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
++
++ if (!err)
++ err = grub_linux_setup_video (params);
++
++ if (err)
++ {
++ grub_print_error ();
++ grub_printf ("Booting however\n");
++ grub_errno = GRUB_ERR_NONE;
++ }
++
++ mmap_size = find_mmap_size ();
++ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key,
++ &desc_size, &desc_version) <= 0)
++ grub_fatal ("cannot get memory map");
++
++ if (! grub_efi_exit_boot_services (map_key))
++ grub_fatal ("cannot exit boot services");
++
++ /* Note that no boot services are available from here. */
++
++ /* Pass EFI parameters. */
++ if (grub_le_to_cpu16 (params->version) >= 0x0206)
++ {
++ params->v0206.efi_mem_desc_size = desc_size;
++ params->v0206.efi_mem_desc_version = desc_version;
++ params->v0206.efi_mmap = (grub_uint32_t) (unsigned long) mmap_buf;
++ params->v0206.efi_mmap_size = mmap_size;
++#ifdef __x86_64__
++ params->v0206.efi_mmap_hi = (grub_uint32_t) ((grub_uint64_t) mmap_buf >> 32);
++#endif
++ }
++ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
++ {
++ params->v0204.efi_mem_desc_size = desc_size;
++ params->v0204.efi_mem_desc_version = desc_version;
++ params->v0204.efi_mmap = (grub_uint32_t) (unsigned long) mmap_buf;
++ params->v0204.efi_mmap_size = mmap_size;
++ }
++
++#ifdef __x86_64__
++
++ grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12),
++ grub_linux_trampoline_start,
++ grub_linux_trampoline_end - grub_linux_trampoline_start);
++
++ ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem
++ + (prot_mode_pages << 12)))
++ (params->code32_start, real_mode_mem);
++
++#else
++
++ /* Hardware interrupts are not safe any longer. */
++ asm volatile ("cli" : : );
++
++ /* Load the IDT and the GDT for the bootstrap. */
++ asm volatile ("lidt %0" : : "m" (idt_desc));
++ asm volatile ("lgdt %0" : : "m" (gdt_desc));
++
++ /* Pass parameters. */
++ asm volatile ("movl %0, %%ecx" : : "m" (params->code32_start));
++ asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem));
++
++ asm volatile ("xorl %%ebx, %%ebx" : : );
++
++ /* Enter Linux. */
++ asm volatile ("jmp *%%ecx" : : );
++
++#endif
++
++ /* Never reach here. */
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_linux_unload (void)
++{
++ free_pages ();
++ grub_dl_unref (my_mod);
++ loaded = 0;
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
++ int argc, char *argv[])
++{
++ grub_file_t file = 0;
++ struct linux_kernel_header lh;
++ struct linux_kernel_params *params;
++ grub_uint8_t setup_sects;
++ grub_size_t real_size, prot_size;
++ grub_ssize_t len;
++ int i;
++ char *dest;
++
++ grub_dl_ref (my_mod);
++
++ if (argc == 0)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
++ goto fail;
++ }
++
++ file = grub_file_open (argv[0]);
++ if (! file)
++ goto fail;
++
++ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++ {
++ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
++ goto fail;
++ }
++
++ if (lh.boot_flag != grub_cpu_to_le16 (0xaa55))
++ {
++ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
++ goto fail;
++ }
++
++ if (lh.setup_sects > GRUB_LINUX_MAX_SETUP_SECTS)
++ {
++ grub_error (GRUB_ERR_BAD_OS, "too many setup sectors");
++ goto fail;
++ }
++
++ /* EFI support is quite new, so reject old versions. */
++ if (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)
++ || grub_le_to_cpu16 (lh.version) < 0x0203)
++ {
++ grub_error (GRUB_ERR_BAD_OS, "too old version");
++ goto fail;
++ }
++
++ /* I'm not sure how to support zImage on EFI. */
++ if (! (lh.loadflags & GRUB_LINUX_FLAG_BIG_KERNEL))
++ {
++ grub_error (GRUB_ERR_BAD_OS, "zImage is not supported");
++ goto fail;
++ }
++
++ setup_sects = lh.setup_sects;
++
++ /* If SETUP_SECTS is not set, set it to the default (4). */
++ if (! setup_sects)
++ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
++
++ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
++ prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
++
++ if (! allocate_pages (prot_size))
++ goto fail;
++
++ params = (struct linux_kernel_params *) real_mode_mem;
++ grub_memset (params, 0, GRUB_LINUX_CL_END_OFFSET);
++ grub_memcpy (¶ms->setup_sects, &lh.setup_sects, sizeof (lh) - 0x1F1);
++
++ params->ps_mouse = params->padding10 = 0;
++
++ len = 0x400 - sizeof (lh);
++ if (grub_file_read (file, (char *) real_mode_mem + sizeof (lh), len) != len)
++ {
++ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++ goto fail;
++ }
++
++ params->type_of_loader = (LINUX_LOADER_ID_GRUB << 4);
++
++ params->cl_magic = GRUB_LINUX_CL_MAGIC;
++ params->cl_offset = 0x1000;
++ params->cmd_line_ptr = (unsigned long) real_mode_mem + 0x1000;
++ params->ramdisk_image = 0;
++ params->ramdisk_size = 0;
++
++ params->heap_end_ptr = GRUB_LINUX_HEAP_END_OFFSET;
++ params->loadflags |= GRUB_LINUX_FLAG_CAN_USE_HEAP;
++
++ /* These are not needed to be precise, because Linux uses these values
++ only to raise an error when the decompression code cannot find good
++ space. */
++ params->ext_mem = ((32 * 0x100000) >> 10);
++ params->alt_mem = ((32 * 0x100000) >> 10);
++
++ {
++ grub_term_output_t term;
++ int found = 0;
++ FOR_ACTIVE_TERM_OUTPUTS(term)
++ if (grub_strcmp (term->name, "vga_text") == 0
++ || grub_strcmp (term->name, "console") == 0)
++ {
++ grub_uint16_t pos = grub_term_getxy (term);
++ params->video_cursor_x = pos >> 8;
++ params->video_cursor_y = pos & 0xff;
++ params->video_width = grub_term_width (term);
++ params->video_height = grub_term_height (term);
++ found = 1;
++ break;
++ }
++ if (!found)
++ {
++ params->video_cursor_x = 0;
++ params->video_cursor_y = 0;
++ params->video_width = 80;
++ params->video_height = 25;
++ }
++ }
++ params->video_page = 0; /* ??? */
++ params->video_mode = grub_efi_system_table->con_out->mode->mode;
++ params->video_ega_bx = 0;
++ params->have_vga = 0;
++ params->font_size = 16; /* XXX */
++
++ if (grub_le_to_cpu16 (params->version) >= 0x0206)
++ {
++ params->v0206.efi_signature = GRUB_LINUX_EFI_SIGNATURE;
++ params->v0206.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
++#ifdef __x86_64__
++ params->v0206.efi_system_table_hi = (grub_uint32_t) ((grub_uint64_t) grub_efi_system_table >> 32);
++#endif
++ }
++ else if (grub_le_to_cpu16 (params->version) >= 0x0204)
++ {
++ params->v0204.efi_signature = GRUB_LINUX_EFI_SIGNATURE_0204;
++ params->v0204.efi_system_table = (grub_uint32_t) (unsigned long) grub_efi_system_table;
++ }
++
++#if 0
++ /* The structure is zeroed already. */
++
++ /* No VBE on EFI. */
++ params->lfb_width = 0;
++ params->lfb_height = 0;
++ params->lfb_depth = 0;
++ params->lfb_base = 0;
++ params->lfb_size = 0;
++ params->lfb_line_len = 0;
++ params->red_mask_size = 0;
++ params->red_field_pos = 0;
++ params->green_mask_size = 0;
++ params->green_field_pos = 0;
++ params->blue_mask_size = 0;
++ params->blue_field_pos = 0;
++ params->reserved_mask_size = 0;
++ params->reserved_field_pos = 0;
++ params->vesapm_segment = 0;
++ params->vesapm_offset = 0;
++ params->lfb_pages = 0;
++ params->vesa_attrib = 0;
++
++ /* No APM on EFI. */
++ params->apm_version = 0;
++ params->apm_code_segment = 0;
++ params->apm_entry = 0;
++ params->apm_16bit_code_segment = 0;
++ params->apm_data_segment = 0;
++ params->apm_flags = 0;
++ params->apm_code_len = 0;
++ params->apm_data_len = 0;
++
++ /* XXX is there any way to use SpeedStep on EFI? */
++ params->ist_signature = 0;
++ params->ist_command = 0;
++ params->ist_event = 0;
++ params->ist_perf_level = 0;
++
++ /* Let the kernel probe the information. */
++ grub_memset (params->hd0_drive_info, 0, sizeof (params->hd0_drive_info));
++ grub_memset (params->hd1_drive_info, 0, sizeof (params->hd1_drive_info));
++
++ /* No MCA on EFI. */
++ params->rom_config_len = 0;
++
++ /* No need to fake the BIOS's memory map. */
++ params->mmap_size = 0;
++
++ /* Let the kernel probe the information. */
++ params->ps_mouse = 0;
++
++ /* Clear padding for future compatibility. */
++ grub_memset (params->padding1, 0, sizeof (params->padding1));
++ grub_memset (params->padding2, 0, sizeof (params->padding2));
++ grub_memset (params->padding3, 0, sizeof (params->padding3));
++ grub_memset (params->padding4, 0, sizeof (params->padding4));
++ grub_memset (params->padding5, 0, sizeof (params->padding5));
++ grub_memset (params->padding6, 0, sizeof (params->padding6));
++ grub_memset (params->padding7, 0, sizeof (params->padding7));
++ grub_memset (params->padding8, 0, sizeof (params->padding8));
++ grub_memset (params->padding9, 0, sizeof (params->padding9));
++
++#endif
++
++ /* The other EFI parameters are filled when booting. */
++
++ grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
++
++ /* XXX there is no way to know if the kernel really supports EFI. */
++ grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n",
++ (unsigned) real_size, (unsigned) prot_size);
++
++ /* Detect explicitly specified memory size, if any. */
++ linux_mem_size = 0;
++ for (i = 1; i < argc; i++)
++ if (grub_memcmp (argv[i], "mem=", 4) == 0)
++ {
++ char *val = argv[i] + 4;
++
++ linux_mem_size = grub_strtoul (val, &val, 0);
++
++ if (grub_errno)
++ {
++ grub_errno = GRUB_ERR_NONE;
++ linux_mem_size = 0;
++ }
++ else
++ {
++ int shift = 0;
++
++ switch (grub_tolower (val[0]))
++ {
++ case 'g':
++ shift += 10;
++ case 'm':
++ shift += 10;
++ case 'k':
++ shift += 10;
++ default:
++ break;
++ }
++
++ /* Check an overflow. */
++ if (linux_mem_size > (~0UL >> shift))
++ linux_mem_size = 0;
++ else
++ linux_mem_size <<= shift;
++ }
++ }
++ else if (grub_memcmp (argv[i], "video=efifb", 11) == 0)
++ {
++ if (params->have_vga)
++ params->have_vga = GRUB_VIDEO_LINUX_TYPE_SIMPLE;
++ }
++
++ /* Specify the boot file. */
++ dest = grub_stpcpy ((char *) real_mode_mem + GRUB_LINUX_CL_OFFSET,
++ "BOOT_IMAGE=");
++ dest = grub_stpcpy (dest, argv[0]);
++
++ /* Copy kernel parameters. */
++ for (i = 1;
++ i < argc
++ && dest + grub_strlen (argv[i]) + 1 < ((char *) real_mode_mem
++ + GRUB_LINUX_CL_END_OFFSET);
++ i++)
++ {
++ *dest++ = ' ';
++ dest = grub_stpcpy (dest, argv[i]);
++ }
++
++ len = prot_size;
++ if (grub_file_read (file, (void *) GRUB_LINUX_BZIMAGE_ADDR, len) != len)
++ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++
++ if (grub_errno == GRUB_ERR_NONE)
++ {
++ grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
++ loaded = 1;
++ }
++
++ fail:
++
++ if (file)
++ grub_file_close (file);
++
++ if (grub_errno != GRUB_ERR_NONE)
++ {
++ grub_dl_unref (my_mod);
++ loaded = 0;
++ }
++
++ return grub_errno;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
++ int argc, char *argv[])
++{
++ grub_file_t file = 0;
++ grub_ssize_t size;
++ grub_addr_t addr_min, addr_max;
++ grub_addr_t addr;
++ grub_efi_uintn_t mmap_size;
++ grub_efi_memory_descriptor_t *desc;
++ grub_efi_uintn_t desc_size;
++ struct linux_kernel_header *lh;
++
++ if (argc == 0)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
++ goto fail;
++ }
++
++ if (! loaded)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
++ goto fail;
++ }
++
++ file = grub_file_open (argv[0]);
++ if (! file)
++ goto fail;
++
++ size = grub_file_size (file);
++ initrd_pages = (page_align (size) >> 12);
++
++ lh = (struct linux_kernel_header *) real_mode_mem;
++
++ addr_max = (grub_cpu_to_le32 (lh->initrd_addr_max) << 10);
++ if (linux_mem_size != 0 && linux_mem_size < addr_max)
++ addr_max = linux_mem_size;
++
++ /* Linux 2.3.xx has a bug in the memory range check, so avoid
++ the last page.
++ Linux 2.2.xx has a bug in the memory range check, which is
++ worse than that of Linux 2.3.xx, so avoid the last 64kb. */
++ addr_max -= 0x10000;
++
++ /* Usually, the compression ratio is about 50%. */
++ addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
++ + page_align (size);
++
++ /* Find the highest address to put the initrd. */
++ mmap_size = find_mmap_size ();
++ if (grub_efi_get_memory_map (&mmap_size, mmap_buf, 0, &desc_size, 0) <= 0)
++ grub_fatal ("cannot get memory map");
++
++ addr = 0;
++ for (desc = mmap_buf;
++ desc < NEXT_MEMORY_DESCRIPTOR (mmap_buf, mmap_size);
++ desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
++ {
++ if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
++ && desc->num_pages >= initrd_pages)
++ {
++ grub_efi_physical_address_t physical_end;
++
++ physical_end = desc->physical_start + (desc->num_pages << 12);
++ if (physical_end > addr_max)
++ physical_end = addr_max;
++
++ if (physical_end < page_align (size))
++ continue;
++
++ physical_end -= page_align (size);
++
++ if ((physical_end >= addr_min) &&
++ (physical_end >= desc->physical_start) &&
++ (physical_end > addr))
++ addr = physical_end;
++ }
++ }
++
++ if (addr == 0)
++ {
++ grub_error (GRUB_ERR_OUT_OF_MEMORY, "no free pages available");
++ goto fail;
++ }
++
++ initrd_mem = grub_efi_allocate_pages (addr, initrd_pages);
++ if (! initrd_mem)
++ grub_fatal ("cannot allocate pages");
++
++ if (grub_file_read (file, initrd_mem, size) != size)
++ {
++ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++ goto fail;
++ }
++
++ grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
++ (unsigned) addr, (unsigned) size);
++
++ lh->ramdisk_image = addr;
++ lh->ramdisk_size = size;
++ lh->root_dev = 0x0100; /* XXX */
++
++ fail:
++ if (file)
++ grub_file_close (file);
++
++ return grub_errno;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++
++GRUB_MOD_INIT(linux)
++{
++ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
++ 0, N_("Load Linux."));
++ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
++ 0, N_("Load initrd."));
++ my_mod = mod;
++}
++
++GRUB_MOD_FINI(linux)
++{
++ grub_unregister_command (cmd_linux);
++ grub_unregister_command (cmd_initrd);
++}
--- /dev/null
--- /dev/null
++/* linux.c - boot Linux zImage or bzImage */
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/loader.h>
++#include <grub/machine/loader.h>
++#include <grub/machine/memory.h>
++#include <grub/file.h>
++#include <grub/err.h>
++#include <grub/disk.h>
++#include <grub/misc.h>
++#include <grub/types.h>
++#include <grub/mm.h>
++#include <grub/dl.h>
++#include <grub/env.h>
++#include <grub/term.h>
++#include <grub/cpu/linux.h>
++#include <grub/ieee1275/ieee1275.h>
++#include <grub/command.h>
++#include <grub/i18n.h>
++
++#define GRUB_OFW_LINUX_PARAMS_ADDR 0x90000
++#define GRUB_OFW_LINUX_KERNEL_ADDR 0x100000
++#define GRUB_OFW_LINUX_INITRD_ADDR 0x800000
++
++#define GRUB_OFW_LINUX_CL_OFFSET 0x1e00
++#define GRUB_OFW_LINUX_CL_LENGTH 0x100
++
++static grub_dl_t my_mod;
++
++static grub_size_t kernel_size;
++static char *kernel_addr, *kernel_cmdline;
++static grub_size_t initrd_size;
++
++static grub_err_t
++grub_linux_unload (void)
++{
++ grub_free (kernel_cmdline);
++ grub_free (kernel_addr);
++ kernel_cmdline = 0;
++ kernel_addr = 0;
++ initrd_size = 0;
++
++ grub_dl_unref (my_mod);
++
++ return GRUB_ERR_NONE;
++}
++
++/*
++static int
++grub_ieee1275_debug (void)
++{
++ struct enter_args
++ {
++ struct grub_ieee1275_common_hdr common;
++ }
++ args;
++
++ INIT_IEEE1275_COMMON (&args.common, "enter", 0, 0);
++
++ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
++ return -1;
++
++ return 0;
++}
++*/
++
++static grub_err_t
++grub_linux_boot (void)
++{
++ struct linux_kernel_params *params;
++ struct linux_kernel_header *lh;
++ char *prot_code;
++ char *bootpath;
++ grub_ssize_t len;
++
++ bootpath = grub_env_get ("root");
++ if (bootpath)
++ grub_ieee1275_set_property (grub_ieee1275_chosen,
++ "bootpath", bootpath,
++ grub_strlen (bootpath) + 1,
++ &len);
++
++ params = (struct linux_kernel_params *) GRUB_OFW_LINUX_PARAMS_ADDR;
++ lh = (struct linux_kernel_header *) params;
++
++ grub_memset ((char *) params, 0, GRUB_OFW_LINUX_CL_OFFSET);
++
++ params->alt_mem = grub_mmap_get_upper () >> 10;
++ params->ext_mem = params->alt_mem;
++
++ lh->cmd_line_ptr = (char *)
++ (GRUB_OFW_LINUX_PARAMS_ADDR + GRUB_OFW_LINUX_CL_OFFSET);
++
++ params->cl_magic = GRUB_LINUX_CL_MAGIC;
++ params->cl_offset = GRUB_OFW_LINUX_CL_OFFSET;
++
++ {
++ grub_term_output_t term;
++ int found = 0;
++ FOR_ACTIVE_TERM_OUTPUTS(term)
++ if (grub_strcmp (term->name, "ofconsole") == 0)
++ {
++ grub_uint16_t pos = grub_term_getxy (term);
++ params->video_cursor_x = pos >> 8;
++ params->video_cursor_y = pos & 0xff;
++ params->video_width = grub_term_width (term);
++ params->video_height = grub_term_height (term);
++ found = 1;
++ break;
++ }
++ if (!found)
++ {
++ params->video_cursor_x = 0;
++ params->video_cursor_y = 0;
++ params->video_width = 80;
++ params->video_height = 25;
++ }
++ }
++
++ params->font_size = 16;
++
++ params->ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
++ params->ofw_num_items = 1;
++ params->ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
++ params->ofw_idt = 0;
++
++ if (initrd_size)
++ {
++ lh->type_of_loader = 1;
++ lh->ramdisk_image = GRUB_OFW_LINUX_INITRD_ADDR;
++ lh->ramdisk_size = initrd_size;
++ }
++
++ if (kernel_cmdline)
++ grub_strcpy (lh->cmd_line_ptr, kernel_cmdline);
++
++ prot_code = (char *) GRUB_OFW_LINUX_KERNEL_ADDR;
++ grub_memcpy (prot_code, kernel_addr, kernel_size);
++
++ asm volatile ("movl %0, %%esi" : : "m" (params));
++ asm volatile ("movl %%esi, %%esp" : : );
++ asm volatile ("movl %0, %%ecx" : : "m" (prot_code));
++ asm volatile ("xorl %%ebx, %%ebx" : : );
++ asm volatile ("jmp *%%ecx" : : );
++
++ return GRUB_ERR_NONE;
++}
++
++static grub_err_t
++grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
++ int argc, char *argv[])
++{
++ grub_file_t file = 0;
++ struct linux_kernel_header lh;
++ grub_uint8_t setup_sects;
++ grub_size_t real_size, prot_size;
++ int i;
++ char *dest;
++
++ grub_dl_ref (my_mod);
++
++ if (argc == 0)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified");
++ goto fail;
++ }
++
++ file = grub_file_open (argv[0]);
++ if (! file)
++ goto fail;
++
++ if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh))
++ {
++ grub_error (GRUB_ERR_READ_ERROR, "cannot read the Linux header");
++ goto fail;
++ }
++
++ if ((lh.boot_flag != grub_cpu_to_le16 (0xaa55)) ||
++ (lh.header != grub_cpu_to_le32 (GRUB_LINUX_MAGIC_SIGNATURE)))
++ {
++ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
++ goto fail;
++ }
++
++ setup_sects = lh.setup_sects;
++ if (! setup_sects)
++ setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
++
++ real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
++ prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
++
++ grub_dprintf ("linux", "Linux-%s, setup=0x%x, size=0x%x\n",
++ "bzImage", real_size, prot_size);
++
++ grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE);
++ if (grub_errno)
++ goto fail;
++
++ kernel_cmdline = grub_malloc (GRUB_OFW_LINUX_CL_LENGTH);
++ if (! kernel_cmdline)
++ goto fail;
++
++ dest = kernel_cmdline;
++ for (i = 1;
++ i < argc
++ && dest + grub_strlen (argv[i]) + 1 < (kernel_cmdline
++ + GRUB_OFW_LINUX_CL_LENGTH);
++ i++)
++ {
++ *dest++ = ' ';
++ dest = grub_stpcpy (dest, argv[i]);
++ }
++
++ kernel_addr = grub_malloc (prot_size);
++ if (! kernel_addr)
++ goto fail;
++
++ kernel_size = prot_size;
++ if (grub_file_read (file, kernel_addr, prot_size) != (int) prot_size)
++ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++
++ if (grub_errno == GRUB_ERR_NONE)
++ grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
++
++fail:
++
++ if (file)
++ grub_file_close (file);
++
++ if (grub_errno != GRUB_ERR_NONE)
++ {
++ grub_free (kernel_cmdline);
++ grub_free (kernel_addr);
++ kernel_cmdline = 0;
++ kernel_addr = 0;
++
++ grub_dl_unref (my_mod);
++ }
++
++ return grub_errno;
++}
++
++static grub_err_t
++grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
++ int argc, char *argv[])
++{
++ grub_file_t file = 0;
++
++ if (argc == 0)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
++ goto fail;
++ }
++
++ if (! kernel_addr)
++ {
++ grub_error (GRUB_ERR_BAD_ARGUMENT, "you need to load the kernel first");
++ goto fail;
++ }
++
++ file = grub_file_open (argv[0]);
++ if (! file)
++ goto fail;
++
++ initrd_size = grub_file_size (file);
++ if (grub_file_read (file, (void *) GRUB_OFW_LINUX_INITRD_ADDR,
++ initrd_size) != (int) initrd_size)
++ {
++ grub_error (GRUB_ERR_FILE_READ_ERROR, "couldn't read file");
++ goto fail;
++ }
++
++fail:
++ if (file)
++ grub_file_close (file);
++
++ return grub_errno;
++}
++
++static grub_command_t cmd_linux, cmd_initrd;
++
++GRUB_MOD_INIT(linux)
++{
++ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
++ 0, N_("Load Linux."));
++ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
++ 0, N_("Load initrd."));
++ my_mod = mod;
++}
++
++GRUB_MOD_FINI(linux)
++{
++ grub_unregister_command (cmd_linux);
++ grub_unregister_command (cmd_initrd);
++}
--- /dev/null
--- /dev/null
++/*
++ * GRUB -- GRand Unified Bootloader
++ * Copyright (C) 2009 Free Software Foundation, Inc.
++ *
++ * GRUB is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, either version 3 of the License, or
++ * (at your option) any later version.
++ *
++ * GRUB is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++#include <grub/symbol.h>
++
++
++ .p2align 4 /* force 16-byte alignment */
++VARIABLE(grub_linux_trampoline_start)
++ cli
++ /* %rdi contains protected memory start and %rsi
++ contains real memory start. */
++
++ mov %rsi, %rbx
++
++ call base
++base:
++ pop %rsi
++
++#ifdef APPLE_CC
++ lea (cont1 - base) (%esi, 1), %rax
++ mov %eax, (jump_vector - base) (%esi, 1)
++
++ lea (gdt - base) (%esi, 1), %rax
++ mov %rax, (gdtaddr - base) (%esi, 1)
++
++ /* Switch to compatibility mode. */
++
++ lidt (idtdesc - base) (%esi, 1)
++ lgdt (gdtdesc - base) (%esi, 1)
++
++ /* Update %cs. Thanks to David Miller for pointing this mistake out. */
++ ljmp *(jump_vector - base) (%esi, 1)
++#else
++ lea (cont1 - base) (%rsi, 1), %rax
++ mov %eax, (jump_vector - base) (%rsi, 1)
++
++ lea (gdt - base) (%rsi, 1), %rax
++ mov %rax, (gdtaddr - base) (%rsi, 1)
++
++ /* Switch to compatibility mode. */
++
++ lidt (idtdesc - base) (%rsi, 1)
++ lgdt (gdtdesc - base) (%rsi, 1)
++
++ /* Update %cs. Thanks to David Miller for pointing this mistake out. */
++ ljmp *(jump_vector - base) (%rsi, 1)
++#endif
++
++cont1:
++ .code32
++
++ /* Update other registers. */
++ mov $0x18, %eax
++ mov %eax, %ds
++ mov %eax, %es
++ mov %eax, %fs
++ mov %eax, %gs
++ mov %eax, %ss
++
++ /* Disable paging. */
++ mov %cr0, %eax
++ and $0x7fffffff, %eax
++ mov %eax, %cr0
++
++ /* Disable amd64. */
++ mov $0xc0000080, %ecx
++ rdmsr
++ and $0xfffffeff, %eax
++ wrmsr
++
++ /* Turn off PAE. */
++ movl %cr4, %eax
++ and $0xffffffcf, %eax
++ mov %eax, %cr4
++
++ jmp cont2
++cont2:
++ .code32
++
++ mov %ebx, %esi
++
++ jmp *%edi
++
++ /* GDT. */
++ .p2align 4
++gdt:
++ /* NULL. */
++ .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++
++ /* Reserved. */
++ .byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++
++ /* Code segment. */
++ .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00
++
++ /* Data segment. */
++ .byte 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
++
++gdtdesc:
++ .word 31
++gdtaddr:
++ .quad gdt
++
++idtdesc:
++ .word 0
++idtaddr:
++ .quad 0
++
++ .p2align 4
++jump_vector:
++ /* Jump location. Is filled by the code */
++ .long 0
++ .long 0x10
++VARIABLE(grub_linux_trampoline_end)
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 */