]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
merge mainline into lazy
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 7 Jul 2011 10:21:53 +0000 (12:21 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Thu, 7 Jul 2011 10:21:53 +0000 (12:21 +0200)
20 files changed:
1  2 
Makefile.util.def
grub-core/Makefile.core.def
grub-core/disk/ahci.c
grub-core/disk/ata.c
grub-core/disk/efi/efidisk.c
grub-core/disk/i386/pc/biosdisk.c
grub-core/disk/ieee1275/nand.c
grub-core/disk/ieee1275/ofdisk.c
grub-core/disk/lvm.c
grub-core/disk/pata.c
grub-core/disk/raid.c
grub-core/disk/scsi.c
grub-core/disk/usbms.c
grub-core/kern/disk.c
grub-core/kern/emu/getroot.c
grub-core/kern/emu/hostdisk.c
grub-core/kern/emu/raid.c
include/grub/ata.h
include/grub/disk.h
include/grub/scsi.h

Simple merge
index 4146e3841c69d430c3d366901fa2da04b3509549,7bceaa98f5a52391e273f922c3a4cbd7ed4dcbbc..73d4621856854b7c4fbb3e8bc73e70b8b83e5ba2
@@@ -162,10 -193,9 +193,10 @@@ kernel = 
    emu = disk/host.c;
    emu = gnulib/progname.c;
    emu = gnulib/error.c;
-   emu = kern/emu/cache.S;
+   emu = kern/emu/cache_s.S;
    emu = kern/emu/console.c;
    emu = kern/emu/getroot.c;
 +  emu = kern/emu/raid.c;
    emu = kern/emu/hostdisk.c;
    emu = kern/emu/hostfs.c;
    emu = kern/emu/main.c;
index 0000000000000000000000000000000000000000,415004fcfe7cb379058aa0714ea66045d2b7856b..38c4d5e2d5d7c4e24b9377582aa615a262e7eded
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,734 +1,738 @@@
 -grub_ahci_iterate (int (*hook) (int id, int bus))
+ /*
+  *  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>
+ #include <grub/loader.h>
+ GRUB_MOD_LICENSE ("GPLv3+");
+ 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[0x10];
+   grub_uint8_t reserved[0x30];
+   struct grub_ahci_prdt_entry prdt[1];
+ };
+ struct grub_ahci_hba_port
+ {
+   grub_uint64_t command_list_base;
+   grub_uint64_t fis_base;
+   grub_uint32_t intstatus;
+   grub_uint32_t inten;
+   grub_uint32_t command;
+   grub_uint32_t unused1;
+   grub_uint32_t task_file_data;
+   grub_uint32_t sig;
+   grub_uint32_t status;
+   grub_uint32_t unused2;
+   grub_uint32_t sata_error;
+   grub_uint32_t sata_active;
+   grub_uint32_t command_issue;
+   grub_uint32_t unused3;
+   grub_uint32_t fbs;
+   grub_uint32_t unused4[15];
+ };
+ enum grub_ahci_hba_port_command
+   {
+     GRUB_AHCI_HBA_PORT_CMD_ST  = 0x01,
+     GRUB_AHCI_HBA_PORT_CMD_FRE = 0x10,
+     GRUB_AHCI_HBA_PORT_CMD_CR = 0x8000,
+     GRUB_AHCI_HBA_PORT_CMD_FR = 0x4000,
+   };
+ struct grub_ahci_hba
+ {
+   grub_uint32_t cap;
+   grub_uint32_t global_control;
+   grub_uint32_t intr_status;
+   grub_uint32_t ports_implemented;
+   grub_uint32_t unused1[6];
+   grub_uint32_t bios_handoff;
+   grub_uint32_t unused2[53];
+   struct grub_ahci_hba_port ports[32];
+ };
+ struct grub_ahci_received_fis
+ {
+   char raw[4096];
+ };
+ enum
+   {
+     GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
+   };
+ enum
+   {
+     GRUB_AHCI_HBA_GLOBAL_CONTROL_RESET = 0x00000001,
+     GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
+     GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
+   };
+ enum
+   {
+     GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
+     GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
+     GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
+     GRUB_AHCI_BIOS_HANDOFF_RWC = 8
+   };
+ struct grub_ahci_device
+ {
+   struct grub_ahci_device *next;
+   volatile struct grub_ahci_hba *hba;
+   int port;
+   int num;
+   struct grub_pci_dma_chunk *command_list_chunk;
+   volatile struct grub_ahci_cmd_head *command_list;
+   struct grub_pci_dma_chunk *command_table_chunk;
+   volatile struct grub_ahci_cmd_table *command_table;
+   struct grub_pci_dma_chunk *rfis;
+   int present;
+ };
+ static grub_err_t 
+ grub_ahci_readwrite_real (struct grub_ahci_device *dev,
+                         struct grub_disk_ata_pass_through_parms *parms,
+                         int spinup);
+ 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 
+ init_port (struct grub_ahci_device *dev)
+ {
+   struct grub_pci_dma_chunk *command_list;
+   struct grub_pci_dma_chunk *command_table;
+   grub_uint64_t endtime;
+   command_list = grub_memalign_dma32 (1024, sizeof (struct grub_ahci_cmd_head));
+   if (!command_list)
+     return 1;
+   command_table = grub_memalign_dma32 (1024,
+                                      sizeof (struct grub_ahci_cmd_table));
+   if (!command_table)
+     {
+       grub_dma_free (command_list);
+       return 1;
+     }
+   grub_dprintf ("ahci", "found device ahci%d (port %d)\n", dev->num, dev->port);
+   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
+   endtime = grub_get_time_ms () + 1000;
+   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "couldn't stop FR\n");
+       goto out;
+       }
+   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
+   endtime = grub_get_time_ms () + 1000;
+   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "couldn't stop CR\n");
+       goto out;
+       }
+   dev->hba->ports[dev->port].fbs = 2;
+   dev->rfis = grub_memalign_dma32 (4096, 
+                                  sizeof (struct grub_ahci_received_fis));
+   grub_memset ((char *) grub_dma_get_virt (dev->rfis), 0,
+              sizeof (struct grub_ahci_received_fis));
+   dev->hba->ports[dev->port].fis_base = grub_dma_get_phys (dev->rfis);
+   dev->hba->ports[dev->port].command_list_base
+     = grub_dma_get_phys (command_list);
+   dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_FRE;
+   while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "couldn't start FR\n");
+       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
+       goto out;
+       }
+   dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
+   while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "couldn't start CR\n");
+       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_CR;
+       goto out_stop_fr;
+       }
+   dev->hba->ports[dev->port].command
+     = (dev->hba->ports[dev->port].command & 0x0fffffff) | (1 << 28) | 2 | 4;
+   dev->command_list_chunk = command_list;
+   dev->command_list = grub_dma_get_virt (command_list);
+   dev->command_table_chunk = command_table;
+   dev->command_table = grub_dma_get_virt (command_table);
+   dev->command_list->command_table_base
+     = grub_dma_get_phys (command_table);
+   return 0;
+  out_stop_fr:
+   dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
+   endtime = grub_get_time_ms () + 1000;
+   while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "couldn't stop FR\n");
+       break;
+       }
+  out:
+   grub_dma_free (command_list);
+   grub_dma_free (command_table);
+   grub_dma_free (dev->rfis);
+   return 1;
+ }
+ static 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));
+   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");
+   if (~(hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN))
+     grub_dprintf ("ahci", "AHCI is in compat mode. Switching\n");
+   else
+     grub_dprintf ("ahci", "AHCI is in AHCI mode.\n");
+   for (i = 0; i < 5; i++)
+     {
+       hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
+       grub_millisleep (1);
+       if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
+       break;
+     }
+   if (i == 5)
+     {
+       grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
+       return 0;
+     }
+   /*
+   {
+       grub_uint64_t endtime;
+       hba->global_control |= 1;
+       endtime = grub_get_time_ms () + 1000;
+       while (hba->global_control & 1)
+       if (grub_get_time_ms () > endtime)
+         {
+           grub_dprintf ("ahci", "couldn't reset AHCI\n");
+           return 0;
+         }
+   }
+   for (i = 0; i < 5; i++)
+     {
+       hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
+       grub_millisleep (1);
+       if (hba->global_control & GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN)
+       break;
+     }
+   if (i == 5)
+     {
+       grub_dprintf ("ahci", "Couldn't put AHCI in AHCI mode\n");
+       return 0;
+     }
+   */
+   nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
+   grub_dprintf ("ahci", "%d AHCI ports\n", nports);
+   for (i = 0; i < nports; i++)
+     {
+       struct grub_ahci_device *adev;
+       grub_uint32_t st;
+       if (!(hba->ports_implemented & (1 << i)))
+       continue;
+       grub_dprintf ("ahci", "status %d:%x\n", i, hba->ports[i].status);
+       /* FIXME: support hotplugging.  */
+       st = hba->ports[i].status;
+       if ((st & 0xf) != 0x3 && (st & 0xf) != 0x1)
+       continue;
+       adev = grub_malloc (sizeof (*adev));
+       if (!adev)
+       return 1;
+       adev->hba = hba;
+       adev->port = i;
+       adev->present = 1;
+       adev->num = numdevs++;
+       if (init_port (adev))
+       {
+         grub_free (adev);
+         return 1;
+       }
+       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;
+ }
+ static grub_err_t
+ grub_ahci_fini_hw (int noreturn __attribute__ ((unused)))
+ {
+   struct grub_ahci_device *dev;
+   for (dev = grub_ahci_devices; dev; dev = dev->next)
+     {
+       grub_uint64_t endtime;
+       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_FRE;
+       endtime = grub_get_time_ms () + 1000;
+       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_FR))
+       if (grub_get_time_ms () > endtime)
+         {
+           grub_dprintf ("ahci", "couldn't stop FR\n");
+           break;
+         }
+       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
+       endtime = grub_get_time_ms () + 1000;
+       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
+       if (grub_get_time_ms () > endtime)
+         {
+           grub_dprintf ("ahci", "couldn't stop CR\n");
+           break;
+         }
+       grub_dma_free (dev->command_list_chunk);
+       grub_dma_free (dev->command_table_chunk);
+       grub_dma_free (dev->rfis);
+       dev->command_list_chunk = NULL;
+       dev->command_table_chunk = NULL;
+       dev->rfis = NULL;
+     }
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_ahci_restore_hw (void)
+ {
+   struct grub_ahci_device **pdev;
+   for (pdev = &grub_ahci_devices; *pdev; pdev = &((*pdev)->next))
+     if (init_port (*pdev))
+       {
+       struct grub_ahci_device *odev;
+       odev = *pdev;
+       *pdev = (*pdev)->next;
+       grub_free (odev);
+       }
+   return GRUB_ERR_NONE;
+ }
\f
+ static int
++grub_ahci_iterate (int (*hook) (int id, int bus),
++                grub_disk_pull_t pull)
+ {
+   struct grub_ahci_device *dev;
++  if (pull != GRUB_DISK_PULL_NONE)
++    return 0;
++
+   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
+     if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num))
+       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_real (struct grub_ahci_device *dev,
+                         struct grub_disk_ata_pass_through_parms *parms,
+                         int spinup)
+ {
+   struct grub_pci_dma_chunk *bufc;
+   grub_uint64_t endtime;
+   unsigned i;
+   grub_err_t err = GRUB_ERR_NONE;
+   grub_dprintf ("ahci", "AHCI tfd = %x\n",
+               dev->hba->ports[dev->port].task_file_data);
+   if ((dev->hba->ports[dev->port].task_file_data & 0x80))
+     {
+       dev->hba->ports[dev->port].command &= ~GRUB_AHCI_HBA_PORT_CMD_ST;
+       endtime = grub_get_time_ms () + 1000;
+       while ((dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
+       if (grub_get_time_ms () > endtime)
+         {
+           grub_dprintf ("ahci", "couldn't stop CR\n");
+           break;
+         }
+       dev->hba->ports[dev->port].command |= GRUB_AHCI_HBA_PORT_CMD_ST;
+       endtime = grub_get_time_ms () + (spinup ? 10000 : 1000);
+       while (!(dev->hba->ports[dev->port].command & GRUB_AHCI_HBA_PORT_CMD_CR))
+       if (grub_get_time_ms () > endtime)
+         {
+           grub_dprintf ("ahci", "couldn't start CR\n");
+           break;
+         }
+     }
+   dev->hba->ports[dev->port].sata_error = dev->hba->ports[dev->port].sata_error;
+   grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
+              (unsigned long long) parms->size,
+              (unsigned long long) parms->cmdsize);
+   if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
+     return grub_error (GRUB_ERR_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));
+   dev->hba->ports[dev->port].command |= 8;
+   grub_dprintf ("ahci", "AHCI tfd = %x\n",
+               dev->hba->ports[dev->port].task_file_data);
+   /* FIXME: support port multipliers.  */
+   dev->command_list[0].config
+     = (5 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
+     //    | GRUB_AHCI_CONFIG_CLEAR_R_OK
+     | (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
+     | (1 << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
+     | (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
+     | (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ)
+     | (parms->taskfile.cmd == 8 ? (1 << 8) : 0);
+   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;
+   dev->command_table[0].cfis[1] = 0x80;
+   for (i = 0; i < sizeof (parms->taskfile.raw); i++)
+     dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i]; 
+   grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+               dev->command_table[0].cfis[0], dev->command_table[0].cfis[1],
+               dev->command_table[0].cfis[2], dev->command_table[0].cfis[3],
+               dev->command_table[0].cfis[4], dev->command_table[0].cfis[5],
+               dev->command_table[0].cfis[6], dev->command_table[0].cfis[7]);
+   grub_dprintf ("ahci", "cfis: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+               dev->command_table[0].cfis[8], dev->command_table[0].cfis[9],
+               dev->command_table[0].cfis[10], dev->command_table[0].cfis[11],
+               dev->command_table[0].cfis[12], dev->command_table[0].cfis[13],
+               dev->command_table[0].cfis[14], dev->command_table[0].cfis[15]);
+   dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
+   dev->command_table[0].prdt[0].unused = 0;
+   dev->command_table[0].prdt[0].size = (parms->size + (parms->size & 1) - 1)
+     | GRUB_AHCI_INTERRUPT_ON_COMPLETE;
+   grub_dprintf ("ahci", "PRDT = %" PRIxGRUB_UINT64_T ", %x, %x (%"
+               PRIuGRUB_SIZE ")\n",
+               dev->command_table[0].prdt[0].data_base,
+               dev->command_table[0].prdt[0].unused,
+               dev->command_table[0].prdt[0].size,
+               (char *) &dev->command_table[0].prdt[0]
+               - (char *) &dev->command_table[0]);
+   if (parms->write)
+     grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
+   grub_dprintf ("ahci", "AHCI command schedulded\n");
+   grub_dprintf ("ahci", "AHCI tfd = %x\n",
+               dev->hba->ports[dev->port].task_file_data);
+   dev->hba->ports[dev->port].inten = 0xffffffff;//(1 << 2) | (1 << 5);
+   dev->hba->ports[dev->port].intstatus = 0xffffffff;//(1 << 2) | (1 << 5);
+   grub_dprintf ("ahci", "AHCI tfd = %x\n",
+               dev->hba->ports[dev->port].task_file_data);
+   dev->hba->ports[dev->port].command_issue |= 1;
+   grub_dprintf ("ahci", "AHCI sig = %x\n", dev->hba->ports[dev->port].sig);
+   grub_dprintf ("ahci", "AHCI tfd = %x\n",
+               dev->hba->ports[dev->port].task_file_data);
+   endtime = grub_get_time_ms () + (spinup ? 10000 : 1000);
+   while ((dev->hba->ports[dev->port].command_issue & 1))
+     if (grub_get_time_ms () > endtime)
+       {
+       grub_dprintf ("ahci", "AHCI status <%x %x %x>\n",
+                     dev->hba->ports[dev->port].command_issue,
+                     dev->hba->ports[dev->port].intstatus,
+                     dev->hba->ports[dev->port].task_file_data);
+       err = grub_error (GRUB_ERR_IO, "AHCI transfer timeouted");
+       break;
+       }
+   grub_dprintf ("ahci", "AHCI command completed <%x %x %x %x %x, %x %x>\n",
+               dev->hba->ports[dev->port].command_issue,
+               dev->hba->ports[dev->port].intstatus,
+               dev->hba->ports[dev->port].task_file_data,
+               dev->command_list[0].transfered,
+               dev->hba->ports[dev->port].sata_error,
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x00],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x18]);
+   grub_dprintf ("ahci",
+               "last PIO FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x08],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x09],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0a],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0b],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0c],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0d],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0e],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x0f]);
+   grub_dprintf ("ahci",
+               "last REG FIS %08x %08x %08x %08x %08x %08x %08x %08x\n",
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x10],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x11],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x12],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x13],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x14],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x15],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x16],
+               ((grub_uint32_t *) grub_dma_get_virt (dev->rfis))[0x17]);
+   if (!parms->write)
+     grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
+   grub_dma_free (bufc);
+   return err;
+ }
+ static grub_err_t 
+ grub_ahci_readwrite (grub_ata_t disk,
+                    struct grub_disk_ata_pass_through_parms *parms,
+                    int spinup)
+ {
+   return grub_ahci_readwrite_real (disk->data, parms, spinup);
+ }
+ static grub_err_t
+ grub_ahci_open (int id, int devnum, struct grub_ata *ata)
+ {
+   struct grub_ahci_device *dev;
+   if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
+   FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
+     if (dev->num == devnum)
+       break;
+   if (! dev)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
+   grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
+   ata->data = dev;
+   ata->dma = 1;
+   ata->present = &dev->present;
+   return GRUB_ERR_NONE;
+ }
+ static struct grub_ata_dev grub_ahci_dev =
+   {
+     .iterate = grub_ahci_iterate,
+     .open = grub_ahci_open,
+     .readwrite = grub_ahci_readwrite,
+   };
\f
+ static void *fini_hnd;
+ GRUB_MOD_INIT(ahci)
+ {
+   /* 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;
+     }
+   /* AHCI initialization.  */
+   grub_ahci_initialize ();
+   /* AHCI devices are handled by scsi.mod.  */
+   grub_ata_dev_register (&grub_ahci_dev);
+   fini_hnd = grub_loader_register_preboot_hook (grub_ahci_fini_hw,
+                                               grub_ahci_restore_hw,
+                                               GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
+ }
+ GRUB_MOD_FINI(ahci)
+ {
+   grub_ahci_fini_hw (0);
+   grub_loader_unregister_preboot_hook (fini_hnd);
+   grub_ata_dev_unregister (&grub_ahci_dev);
+ }
index 3a43a8caeb126fb2fd01f9cf7f42b06d875323b8,a8ccc2c5949050099e48476f19a967f509de26a7..9a09ee158bd2a26e0b72d3a9a3f8a4339b4acdab
@@@ -685,52 -358,84 +358,86 @@@ grub_ata_real_open (int id, int bus
          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 (dev->atapi)
-       continue;
-       grub_snprintf (devname, sizeof (devname), 
-                    "ata%d", dev->port * 2 + dev->device);
+ static int
 -grub_ata_iterate (int (*hook_in) (const char *name))
++grub_ata_iterate (int (*hook_in) (const char *name),
++                grub_disk_pull_t pull)
+ {
+   auto int hook (int id, int bus);
+   int hook (int id, int bus)
+   {
+     struct grub_ata *ata;
+     int ret;
+     char devname[40];
  
-       if (hook (devname))
-       return 1;
-     }
+     ata = grub_ata_real_open (id, bus);
  
 -    if (p->iterate && p->iterate (hook))
+     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, pull))
+       return 1;
    return 0;
  }
  
  static grub_err_t
 -grub_ata_open (const char *name, grub_disk_t disk)
 +grub_ata_open (const char *name, grub_disk_t disk,
 +             grub_disk_pull_t pull __attribute__ ((unused)))
  {
-   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;
-     }
+   unsigned id, bus;
+   struct grub_ata *ata;
  
-   if (! dev)
-     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
-   if (dev->atapi)
+   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;
  
-   err = check_device (dev);
-   if (err)
-     return err;
+   if (ata->atapi)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
  
-   disk->total_sectors = dev->size;
+   disk->total_sectors = ata->size;
  
-   disk->id = (unsigned long) dev;
+   disk->id = grub_make_scsi_id (id, bus, 0);
  
-   disk->data = dev;
+   disk->data = ata;
  
    return 0;
  }
@@@ -852,36 -523,62 +525,63 @@@ grub_atapi_write (struct grub_scsi *scs
  }
  
  static grub_err_t
- grub_atapi_open (int devnum, struct grub_scsi *scsi)
+ grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
  {
-   struct grub_ata_device *dev;
-   struct grub_ata_device *devfnd = 0;
-   grub_err_t err;
+   struct grub_ata *ata;
  
-   for (dev = grub_ata_devices; dev; dev = dev->next)
-     {
-       if (dev->port * 2 + dev->device == devnum)
-       {
-         devfnd = dev;
-         break;
-       }
-     }
+   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");
  
-   grub_dprintf ("ata", "opening ATAPI dev `ata%d'\n", devnum);
+   scsi->data = ata;
+   scsi->luns = 1;
  
-   if (! devfnd)
-     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
+   return GRUB_ERR_NONE;
+ }
  
-   err = check_device (devfnd);
-   if (err)
-     return err;
+ static int
 -grub_atapi_iterate (int NESTED_FUNC_ATTR (*hook_in) (int id, int bus, int luns))
++grub_atapi_iterate (int NESTED_FUNC_ATTR (*hook_in) (int id, int bus, int luns),
++                  grub_disk_pull_t pull)
+ {
+   auto int hook (int id, int bus);
+   int hook (int id, int bus)
+   {
+     struct grub_ata *ata;
+     int ret;
  
-   if (! devfnd->atapi)
-     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
+     ata = grub_ata_real_open (id, bus);
  
-   scsi->data = devfnd;
+     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))
++    if (p->iterate && p->iterate (hook, pull))
+       return 1;
+   return 0;
+ }
  
-   return GRUB_ERR_NONE;
+ static void
+ grub_atapi_close (grub_scsi_t disk)
+ {
+   struct grub_ata *ata = disk->data;
+   grub_ata_real_close (ata);
  }
  
  
index 08094fa5c9ec06f59e92cbffd2691ee6a3cb62e5,07f0f68593a7c26150ccd07a8c3a591b3fbea818..4ae1d7bb95818937e3f217cee133ddb75ac1a651
@@@ -432,34 -378,34 +378,43 @@@ enumerate_disks (void
  }
  
  static int
--grub_efidisk_iterate (int (*hook) (const char *name))
++grub_efidisk_iterate (int (*hook) (const char *name),
++                    grub_disk_pull_t pull)
  {
    struct grub_efidisk_data *d;
    char buf[16];
    int count;
  
--  for (d = fd_devices, count = 0; d; d = d->next, count++)
++  switch (pull)
      {
--      grub_snprintf (buf, sizeof (buf), "fd%d", count);
--      grub_dprintf ("efidisk", "iterating %s\n", buf);
--      if (hook (buf))
--      return 1;
--    }
--
--  for (d = hd_devices, count = 0; d; d = d->next, count++)
--    {
--      grub_snprintf (buf, sizeof (buf), "hd%d", count);
--      grub_dprintf ("efidisk", "iterating %s\n", buf);
--      if (hook (buf))
--      return 1;
--    }
++    case GRUB_DISK_PULL_NONE:
++      for (d = hd_devices, count = 0; d; d = d->next, count++)
++      {
++        grub_snprintf (buf, sizeof (buf), "hd%d", count);
++        grub_dprintf ("efidisk", "iterating %s\n", buf);
++        if (hook (buf))
++          return 1;
++      }
++      break;
++    case GRUB_DISK_PULL_REMOVABLE:
++      for (d = fd_devices, count = 0; d; d = d->next, count++)
++      {
++        grub_snprintf (buf, sizeof (buf), "fd%d", count);
++        grub_dprintf ("efidisk", "iterating %s\n", buf);
++        if (hook (buf))
++          return 1;
++      }
  
--  for (d = cd_devices, count = 0; d; d = d->next, count++)
--    {
--      grub_snprintf (buf, sizeof (buf), "cd%d", count);
--      grub_dprintf ("efidisk", "iterating %s\n", buf);
--      if (hook (buf))
--      return 1;
++      for (d = cd_devices, count = 0; d; d = d->next, count++)
++      {
++        grub_snprintf (buf, sizeof (buf), "cd%d", count);
++        grub_dprintf ("efidisk", "iterating %s\n", buf);
++        if (hook (buf))
++          return 1;
++      }
++      break;
++    default:
++      return 0;
      }
  
    return 0;
@@@ -499,7 -445,7 +454,8 @@@ get_device (struct grub_efidisk_data *d
  }
  
  static grub_err_t
--grub_efidisk_open (const char *name, struct grub_disk *disk)
++grub_efidisk_open (const char *name, struct grub_disk *disk,
++                 grub_disk_pull_t pull __attribute__ ((unused)))
  {
    int num;
    struct grub_efidisk_data *d = 0;
@@@ -785,7 -732,9 +742,11 @@@ grub_efidisk_get_device_name (grub_efi_
        dup_ldp->length[0] = sizeof (*dup_ldp);
        dup_ldp->length[1] = 0;
  
-       grub_efidisk_iterate (find_parent_disk);
+       sdp = dup_dp;
 -      grub_efidisk_iterate (find_parent_disk);
++      grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_NONE);
++      if (!parent)
++      grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_REMOVABLE);
        grub_free (dup_dp);
  
        if (! parent)
    else
      {
        /* This should be an entire disk.  */
-       auto int find_disk (const char *name);
        char *device_name = 0;
  
-       int find_disk (const char *name)
-       {
-         grub_disk_t disk;
+       sdp = dp;
  
-         disk = grub_disk_open (name);
-         if (! disk)
-           return 1;
-         if (disk->dev->id == GRUB_DISK_DEVICE_EFIDISK_ID)
-           {
-             struct grub_efidisk_data *d;
-             d = disk->data;
-             if (compare_device_paths (d->device_path, dp) == 0)
-               {
-                 device_name = grub_strdup (disk->name);
-                 grub_disk_close (disk);
-                 return 1;
-               }
-           }
-         grub_disk_close (disk);
-         return 0;
-       }
-       grub_efidisk_iterate (find_disk);
 -      grub_efidisk_iterate (find_parent_disk);
++      grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_NONE);
++      if (!parent)
++      grub_efidisk_iterate (find_parent_disk, GRUB_DISK_PULL_REMOVABLE);
+       if (!parent)
+       return NULL;
+       device_name = grub_strdup (parent->name);
+       grub_disk_close (parent);
        return device_name;
      }
  
Simple merge
index e9450167e9fd220de8ee05de96531728f84d3902,e9450167e9fd220de8ee05de96531728f84d3902..4be5b564140538db2ee8fb3b60c099cea47f5286
@@@ -32,7 -32,7 +32,8 @@@ struct grub_nand_dat
  };
  
  static int
--grub_nand_iterate (int (*hook) (const char *name))
++grub_nand_iterate (int (*hook) (const char *name),
++                 grub_disk_pull_t pull)
  {
    auto int dev_iterate (struct grub_ieee1275_devalias *alias);
    int dev_iterate (struct grub_ieee1275_devalias *alias)
@@@ -46,6 -46,6 +47,9 @@@
        return 0;
      }
  
++  if (pull != GRUB_DISK_PULL_NONE)
++    return 0;
++
    return grub_devalias_iterate (dev_iterate);
  }
  
@@@ -54,7 -54,7 +58,8 @@@ grub_nand_read (grub_disk_t disk, grub_
                  grub_size_t size, char *buf);
  
  static grub_err_t
--grub_nand_open (const char *name, grub_disk_t disk)
++grub_nand_open (const char *name, grub_disk_t disk,
++              grub_disk_pull_t pull __attribute__ ((unused)))
  {
    grub_ieee1275_ihandle_t dev_ihandle = 0;
    struct grub_nand_data *data = 0;
index 0a935d5c2b8e5db3d955e28988b63c2a38cab3d3,7868b14b10c403a45bed2d2e6b2bbb420edb0c24..7b7db3a086c390fb340736e1393a1376aabc36e9
@@@ -152,9 -152,9 +152,14 @@@ scan (void
  }
  
  static int
--grub_ofdisk_iterate (int (*hook) (const char *name))
++grub_ofdisk_iterate (int (*hook) (const char *name),
++                   grub_disk_pull_t pull)
  {
    unsigned i;
++
++  if (pull != GRUB_DISK_PULL_NONE)
++    return 0;
++
    scan ();
    
    for (i = 0; i < ARRAY_SIZE (ofdisk_hash); i++)
@@@ -228,7 -228,7 +233,8 @@@ compute_dev_path (const char *name
  }
  
  static grub_err_t
--grub_ofdisk_open (const char *name, grub_disk_t disk)
++grub_ofdisk_open (const char *name, grub_disk_t disk,
++                grub_disk_pull_t pull __attribute__ ((unused)))
  {
    grub_ieee1275_phandle_t dev;
    char *devpath;
Simple merge
index 0000000000000000000000000000000000000000,95828e8f63c2b8c4b58f18b05bd04d88e174a521..ff6f773669aa620e4635f82667ebe528d6467ef9
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,530 +1,534 @@@
 -grub_pata_iterate (int (*hook) (int id, int bus))
+ /* 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>
+ #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
+ #include <grub/pci.h>
+ #include <grub/cs5536.h>
+ #else
+ #define GRUB_MACHINE_PCI_IO_BASE  0xb4000000
+ #endif
+ #include <grub/time.h>
+ GRUB_MOD_LICENSE ("GPLv3+");
+ /* At the moment, only two IDE ports are supported.  */
+ static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
+                                                  GRUB_ATA_CH1_PORT1 };
+ struct grub_pata_device
+ {
+   /* IDE port to use.  */
+   int port;
+   /* IO addresses on which the registers for this device can be
+      found.  */
+   grub_port_t ioaddress;
+   /* Two devices can be connected to a single cable.  Use this field
+      to select device 0 (commonly known as "master") or device 1
+      (commonly known as "slave").  */
+   int device;
+   int present;
+   struct grub_pata_device *next;
+ };
+ static struct grub_pata_device *grub_pata_devices;
+ static inline void
+ grub_pata_regset (struct grub_pata_device *dev, int reg, int val)
+ {
+   grub_outb (val, dev->ioaddress + reg);
+ }
+ static inline grub_uint8_t
+ grub_pata_regget (struct grub_pata_device *dev, int reg)
+ {
+   return grub_inb (dev->ioaddress + reg);
+ }
+ /* Wait for !BSY.  */
+ static grub_err_t
+ grub_pata_wait_not_busy (struct grub_pata_device *dev, int milliseconds)
+ {
+   /* ATA requires 400ns (after a write to CMD register) or
+      1 PIO cycle (after a DRQ block transfer) before
+      first check of BSY.  */
+   grub_millisleep (1);
+   int i = 1;
+   grub_uint8_t sts;
+   while ((sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS))
+        & GRUB_ATA_STATUS_BUSY)
+     {
+       if (i >= milliseconds)
+         {
+         grub_dprintf ("pata", "timeout: %dms, status=0x%x\n",
+                       milliseconds, sts);
+         return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout");
+       }
+       grub_millisleep (1);
+       i++;
+     }
+   return GRUB_ERR_NONE;
+ }
+ static inline grub_err_t
+ grub_pata_check_ready (struct grub_pata_device *dev, int spinup)
+ {
+   if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
+     return grub_pata_wait_not_busy (dev, spinup ? GRUB_ATA_TOUT_SPINUP
+                                   : GRUB_ATA_TOUT_STD);
+   return GRUB_ERR_NONE;
+ }
+ static inline void
+ grub_pata_wait (void)
+ {
+   grub_millisleep (50);
+ }
+ 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,
+                    int spinup)
+ {
+   struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
+   grub_size_t nread = 0;
+   int i;
+   if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
+     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                      "ATAPI non-12 byte commands not supported");
+   grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
+               parms->taskfile.cmd,
+               parms->taskfile.features,
+               parms->taskfile.sectors);
+   grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%"
+               PRIuGRUB_SIZE "\n",
+               parms->taskfile.lba_high,
+               parms->taskfile.lba_mid,
+               parms->taskfile.lba_low, parms->size);
+   /* Set registers.  */
+   grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4)
+                   | (parms->taskfile.disk & 0xef));
+   if (grub_pata_check_ready (dev, spinup))
+     return grub_errno;
+   for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
+     grub_pata_regset (dev, i,
+                    parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
+   for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
+     grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
+   /* Start command. */
+   grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
+   /* Check status.  */
+   grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
+   grub_dprintf ("pata", "status=0x%x\n", sts);
+   if (parms->cmdsize)
+     {
+       grub_uint8_t irs;
+       /* Wait for !BSY.  */
+       if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+       return grub_errno;
+       irs = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON);
+       /* OK if DRQ is asserted and interrupt reason is as expected.  */
+       if (!((sts & GRUB_ATA_STATUS_DRQ)
+           && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_CMD_OUT))
+       return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
+       /* Write the packet.  */
+       grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
+     }
+   /* Transfer data.  */
+   while (nread < parms->size
+        && ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+            == GRUB_ATA_STATUS_DRQ)
+        && (!parms->cmdsize 
+            || ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
+                 & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_DATA_IN)))
+     {
+       unsigned cnt;
+       /* Wait for !BSY.  */
+       if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+       return grub_errno;
+       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;
+   /* Wait for !BSY.  */
+   if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
+     return grub_errno;
+   /* Return registers.  */
+   for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
+     parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
+   grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
+               parms->taskfile.status,
+               parms->taskfile.error,
+               parms->taskfile.sectors);
+   if (parms->taskfile.status
+       & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
+     return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ check_device (struct grub_pata_device *dev)
+ {
+   grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
+   grub_pata_wait ();
+   /* Try to detect if the port is in use by writing to it,
+      waiting for a while and reading it again.  If the value
+      was preserved, there is a device connected.  */
+   grub_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
+   grub_pata_wait ();
+   grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
+   grub_dprintf ("ata", "sectors=0x%x\n", sec);
+   if (sec != 0x5A)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
+   /* The above test may detect a second (slave) device
+      connected to a SATA controller which supports only one
+      (master) device.  It is not safe to use the status register
+      READY bit to check for controller channel existence.  Some
+      ATAPI commands (RESET, DIAGNOSTIC) may clear this bit.  */
+   return GRUB_ERR_NONE;
+ }
+ static grub_err_t
+ grub_pata_device_initialize (int port, int device, int addr)
+ {
+   struct grub_pata_device *dev;
+   struct grub_pata_device **devp;
+   grub_err_t err;
+   grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n",
+               port, device, addr);
+   dev = grub_malloc (sizeof(*dev));
+   if (! dev)
+     return grub_errno;
+   /* Setup the device information.  */
+   dev->port = port;
+   dev->device = device;
+   dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
+   dev->present = 1;
+   dev->next = NULL;
+   /* Register the device.  */
+   for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next);
+   *devp = dev;
+   err = check_device (dev);
+   if (err)
+     grub_print_error ();
+   return 0;
+ }
+ #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
+ 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 i;
+   static int controller = 0;
+   int cs5536 = 0;
+   int nports = 2;
+   /* Read class.  */
+   addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
+   class = grub_pci_read (addr);
+   /* AMD CS5536 Southbridge.  */
+   if (pciid == GRUB_CS5536_PCIID)
+     {
+       cs5536 = 1;
+       nports = 1;
+     }
+   /* Check if this class ID matches that of a PCI IDE Controller.  */
+   if (!cs5536 && (class >> 16 != 0x0101))
+     return 0;
+   for (i = 0; i < nports; i++)
+     {
+       /* Set to 0 when the channel operated in compatibility mode.  */
+       int compat;
+       /* We don't support non-compatibility mode for CS5536.  */
+       if (cs5536)
+       compat = 0;
+       else
+       compat = (class >> (8 + 2 * i)) & 1;
+       rega = 0;
+       /* If the channel is in compatibility mode, just assign the
+        default registers.  */
+       if (compat == 0 && !compat_use[i])
+       {
+         rega = grub_pata_ioaddress[i];
+         compat_use[i] = 1;
+       }
+       else if (compat)
+       {
+         /* Read the BARs, which either contain a mmapped IO address
+            or the IO port address.  */
+         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+                                       + sizeof (grub_uint64_t) * i);
+         bar1 = grub_pci_read (addr);
+         addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
+                                       + sizeof (grub_uint64_t) * i
+                                       + sizeof (grub_uint32_t));
+         bar2 = grub_pci_read (addr);
+         /* Check if the BARs describe an IO region.  */
+         if ((bar1 & 1) && (bar2 & 1))
+           {
+             rega = bar1 & ~3;
+           }
+       }
+       grub_dprintf ("pata",
+                   "PCI dev (%d,%d,%d) compat=%d rega=0x%x\n",
+                   grub_pci_get_bus (dev), grub_pci_get_device (dev),
+                   grub_pci_get_function (dev), compat, rega);
+       if (rega)
+       {
+         grub_errno = GRUB_ERR_NONE;
+         grub_pata_device_initialize (controller * 2 + i, 0, rega);
+         /* Most errors raised by grub_ata_device_initialize() are harmless.
+            They just indicate this particular drive is not responding, most
+            likely because it doesn't exist.  We might want to ignore specific
+            error types here, instead of printing them.  */
+         if (grub_errno)
+           {
+             grub_print_error ();
+             grub_errno = GRUB_ERR_NONE;
+           }
+         grub_pata_device_initialize (controller * 2 + i, 1, rega);
+         /* Likewise.  */
+         if (grub_errno)
+           {
+             grub_print_error ();
+             grub_errno = GRUB_ERR_NONE;
+           }
+       }
+     }
+   controller++;
+   return 0;
+ }
+ static grub_err_t
+ grub_pata_initialize (void)
+ {
+   grub_pci_iterate (grub_pata_pciinit);
+   return 0;
+ }
+ #else
+ static grub_err_t
+ grub_pata_initialize (void)
+ {
+   int i;
+   for (i = 0; i < 2; i++)
+     {
+       grub_pata_device_initialize (i, 0, grub_pata_ioaddress[i]);
+       grub_pata_device_initialize (i, 1, grub_pata_ioaddress[i]);
+     }
+   return 0;
+ }
+ #endif
+ static grub_err_t
+ grub_pata_open (int id, int devnum, struct grub_ata *ata)
+ {
+   struct grub_pata_device *dev;
+   struct grub_pata_device *devfnd = 0;
+   grub_err_t err;
+   if (id != GRUB_SCSI_SUBSYSTEM_PATA)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
+   for (dev = grub_pata_devices; dev; dev = dev->next)
+     {
+       if (dev->port * 2 + dev->device == devnum)
+       {
+         devfnd = dev;
+         break;
+       }
+     }
+   grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
+   if (! devfnd)
+     return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
+   err = check_device (devfnd);
+   if (err)
+     return err;
+   ata->data = devfnd;
+   ata->dma = 0;
+   ata->present = &devfnd->present;
+   return GRUB_ERR_NONE;
+ }
+ static int
++grub_pata_iterate (int (*hook) (int id, int bus),
++                 grub_disk_pull_t pull)
+ {
+   struct grub_pata_device *dev;
++  if (pull != GRUB_DISK_PULL_NONE)
++    return 0;
++
+   for (dev = grub_pata_devices; dev; dev = dev->next)
+     if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device))
+       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);
+ }
Simple merge
index e85036860b8208b77f320a916905e0ee74f3079f,90256416401acfbecc3b88d3823830636530b7c4..5ab5177e97fcf575a93a592aa65c9da63fef89cd
@@@ -357,11 -362,8 +363,8 @@@ grub_scsi_iterate (int (*hook) (const c
        return 0;
      }
  
-   if (pull != GRUB_DISK_PULL_NONE)
-     return 0;
    for (p = grub_scsi_dev_list; p; p = p->next)
--    if (p->iterate && (p->iterate) (scsi_iterate))
++    if (p->iterate && (p->iterate) (scsi_iterate, pull))
        return 1;
  
    return 0;
index 2f1dbd487da3336d1c25c6d7a7f38c04cd788f5e,1666197b181fd009ba45ed1fa67281a25a15961e..a4c2addf100d7d0e392dea79be2c2967b3cf32ec
@@@ -211,10 -211,10 +211,14 @@@ grub_usbms_attach (grub_usb_device_t us
  \f
  
  static int
- grub_usbms_iterate (int (*hook) (int bus, int luns))
 -grub_usbms_iterate (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns))
++grub_usbms_iterate (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns),
++                  grub_disk_pull_t pull)
  {
    unsigned i;
  
++  if (pull != GRUB_DISK_PULL_NONE)
++    return 0;
++
    grub_usb_poll_devices ();
  
    for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
Simple merge
Simple merge
Simple merge
Simple merge
index 9e3aaf0e69407eec652328ec1d781c69fbad0088,84d5e2d10ffbc626eccb9747c0919fd188e833d1..6938b6a42f7c1be005ce211c40f5443622eb3a92
@@@ -22,6 -22,6 +22,7 @@@
  
  #include <grub/misc.h>
  #include <grub/symbol.h>
++#include <grub/disk.h>
  /* XXX: For now this only works on i386.  */
  #include <grub/cpu/io.h>
  
@@@ -128,47 -178,41 +179,42 @@@ struct grub_at
    /* Set to 0 for ATA, set to 1 for ATAPI.  */
    int atapi;
  
-   struct grub_ata_device *next;
+   int dma;
+   int *present;
+   void *data;
+   struct grub_ata_dev *dev;
  };
  
- grub_err_t EXPORT_FUNC(grub_ata_wait_not_busy) (struct grub_ata_device *dev,
-                                                 int milliseconds);
- grub_err_t EXPORT_FUNC(grub_ata_wait_drq) (struct grub_ata_device *dev,
-                                          int rw, int milliseconds);
- void EXPORT_FUNC(grub_ata_pio_read) (struct grub_ata_device *dev,
-                                    char *buf, grub_size_t size);
+ typedef struct grub_ata *grub_ata_t;
  
- static inline void
- grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
+ struct grub_ata_dev
  {
-   grub_outb (val, dev->ioaddress + reg);
- }
+   /* Call HOOK with each device name, until HOOK returns non-zero.  */
 -  int (*iterate) (int (*hook) (int id, int bus));
++  int (*iterate) (int (*hook) (int id, int bus),
++                grub_disk_pull_t pull);
  
- static inline grub_uint8_t
- grub_ata_regget (struct grub_ata_device *dev, int reg)
- {
-   return grub_inb (dev->ioaddress + reg);
- }
+   /* Open the device named NAME, and set up SCSI.  */
+   grub_err_t (*open) (int id, int bus, struct grub_ata *scsi);
  
- static inline void
- grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
- {
-   grub_outb (val, dev->ioaddress2 + reg);
- }
+   /* Close the scsi device SCSI.  */
+   void (*close) (struct grub_ata *ata);
  
- static inline grub_uint8_t
- grub_ata_regget2 (struct grub_ata_device *dev, int reg)
- {
-   return grub_inb (dev->ioaddress2 + reg);
- }
+   /* Read SIZE bytes from the device SCSI into BUF after sending the
+      command CMD of size CMDSIZE.  */
+   grub_err_t (*readwrite) (struct grub_ata *ata,
+                          struct grub_disk_ata_pass_through_parms *parms,
+                          int spinup);
  
- static inline grub_err_t
- grub_ata_check_ready (struct grub_ata_device *dev)
- {
-   if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
    return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
+   /* The next scsi device.  */
+   struct grub_ata_dev *next;
+ };
typedef struct grub_ata_dev *grub_ata_dev_t;
  
  return GRUB_ERR_NONE;
- }
void grub_ata_dev_register (grub_ata_dev_t dev);
+ void grub_ata_dev_unregister (grub_ata_dev_t dev);
  
  #endif /* ! GRUB_ATA_HEADER */
Simple merge
index b30d317c71c3b60868143276fa548ce5bcb9b0e0,f71ef099dc32f25f2c46415ac7c1873b779aa889..dfee69c625c11f54dfe204a88e2d35e39210d301
@@@ -19,6 -19,6 +19,8 @@@
  #ifndef       GRUB_SCSI_H
  #define       GRUB_SCSI_H     1
  
++#include <grub/disk.h>
++
  typedef struct grub_scsi_dev *grub_scsi_dev_t;
  
  void grub_scsi_dev_register (grub_scsi_dev_t dev);
@@@ -45,16 -49,11 +51,12 @@@ grub_make_scsi_id (int subsystem, int b
  
  struct grub_scsi_dev
  {
-   /* The device name.  */
-   const char *name;
-   grub_uint8_t id;
    /* Call HOOK with each device name, until HOOK returns non-zero.  */
-   int (*iterate) (int (*hook) (int bus, int luns));
 -  int (*iterate) (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns));
++  int (*iterate) (int NESTED_FUNC_ATTR (*hook) (int id, int bus, int luns),
++                grub_disk_pull_t pull);
  
    /* Open the device named NAME, and set up SCSI.  */
-   grub_err_t (*open) (int bus, struct grub_scsi *scsi);
+   grub_err_t (*open) (int id, int bus, struct grub_scsi *scsi);
  
    /* Close the scsi device SCSI.  */
    void (*close) (struct grub_scsi *scsi);