]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
merge mainline into ahci
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 16 May 2011 15:17:04 +0000 (17:17 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 16 May 2011 15:17:04 +0000 (17:17 +0200)
1  2 
grub-core/Makefile.core.def
grub-core/bus/usb/ohci.c
grub-core/commands/hdparm.c
grub-core/disk/ahci.c
grub-core/disk/ata.c
grub-core/disk/pata.c
grub-core/disk/scsi.c
grub-core/disk/usbms.c
include/grub/ata.h
util/grub-install.in

Simple merge
Simple merge
index 800bfa06d372ff3e7e889a7ddd3c8d9f31b16d0f,0c12b8814bf6ea6bd7a828c417ef6a04e92850a6..2401707346683672a0a844b0f6adfe16fcbf303f
@@@ -77,7 -76,7 +79,7 @@@ grub_hdparm_do_ata_cmd (grub_ata_t ata
    apt.buffer = buffer;
    apt.size = size;
  
-   if (ata->dev->readwrite (ata, &apt))
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  if (ata->dev->readwrite (ata, &apt, 0))
      return grub_errno;
  
    return GRUB_ERR_NONE;
@@@ -89,13 -88,12 +91,13 @@@ grub_hdparm_do_check_powermode_cmd (gru
    struct grub_disk_ata_pass_through_parms apt;
    grub_memset (&apt, 0, sizeof (apt));
  
 -  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
 +  apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE;
 +  apt.taskfile.disk = 0xE0;
  
-   if (ata->dev->readwrite (ata, &apt))
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  if (ata->dev->readwrite (ata, &apt, 0))
      return -1;
  
 -  return apt.taskfile[GRUB_ATA_REG_SECTORS];
 +  return apt.taskfile.sectors;
  }
  
  static int
@@@ -104,13 -102,12 +106,13 @@@ grub_hdparm_do_smart_cmd (grub_ata_t at
    struct grub_disk_ata_pass_through_parms apt;
    grub_memset (&apt, 0, sizeof (apt));
  
 -  apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
 -  apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
 -  apt.taskfile[GRUB_ATA_REG_LBAMID]  = 0x4f;
 -  apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
 +  apt.taskfile.cmd = GRUB_ATA_CMD_SMART;
 +  apt.taskfile.features = features;
 +  apt.taskfile.lba_mid  = 0x4f;
 +  apt.taskfile.lba_high = 0xc2;
 +  apt.taskfile.disk = 0xE0;
  
-   if (ata->dev->readwrite (ata, &apt))
 -  if (grub_disk_ata_pass_through (disk, &apt))
++  if (ata->dev->readwrite (ata, &apt, 0))
      return -1;
  
    if (features == GRUB_ATA_FEAT_SMART_STATUS)
index c67dbbe7ef2d47f4045d72739ca87d2bee99b232,0000000000000000000000000000000000000000..699df767cc3c66be77d9d90052b5518626cf3ded
mode 100644,000000..100644
--- /dev/null
@@@ -1,725 -1,0 +1,731 @@@
-                         struct grub_disk_ata_pass_through_parms *parms);
 +/*
 + *  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>
 +
 +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)
++                        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))
 +{
 +  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_real (struct grub_ahci_device *dev,
-       endtime = grub_get_time_ms () + 1000;
++                        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 () + 1000;
++      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 (%x)\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);
 +
-                    struct grub_disk_ata_pass_through_parms *parms)
++  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,
-   return grub_ahci_readwrite_real (disk->data, parms);
++                   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 c7cde316653c07d6a7160304df2b24f9ff48cec0,391ccb9a2ee2ad35e6d8ceaa9cd7938164418051..a8ccc2c5949050099e48476f19a967f509de26a7
  #include <grub/dl.h>
  #include <grub/disk.h>
  #include <grub/mm.h>
 -#include <grub/time.h>
 -#include <grub/pci.h>
  #include <grub/scsi.h>
 -#include <grub/cs5536.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;
 -}
+ GRUB_MOD_LICENSE ("GPLv3+");
 +static grub_ata_dev_t grub_ata_dev_list;
  
  /* Byteorder has to be changed before strings can be read.  */
  static void
@@@ -69,19 -158,28 +71,25 @@@ grub_atapi_identify (struct grub_ata *d
    if (! info)
      return grub_errno;
  
 -  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
 -  grub_ata_wait ();
 -  if ((grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
 -      && grub_ata_wait_not_busy (dev, dev->present ? GRUB_ATA_TOUT_DEV_INIT 
 -                               : GRUB_ATA_TOUT_STD))
 +  grub_memset (&parms, 0, sizeof (parms));
 +  parms.taskfile.disk = 0xE0;
 +  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
 +  parms.size = GRUB_DISK_SECTOR_SIZE;
 +  parms.buffer = info;
 +
-   err = dev->dev->readwrite (dev, &parms);
++  err = dev->dev->readwrite (dev, &parms, *dev->present);
 +  if (err)
-     return err;
+     {
 -      grub_free (info);
 -      dev->present = 0;
 -      return grub_errno;
++      *dev->present = 0;
++      return err;
+     }
  
 -  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
 -  grub_ata_wait ();
 -
 -  if (grub_ata_wait_drq (dev, 0, dev->present ? GRUB_ATA_TOUT_DEV_INIT 
 -                       : GRUB_ATA_TOUT_STD))
 +  if (parms.size != GRUB_DISK_SECTOR_SIZE)
-     return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
-                      "device cannot be identified");
+     {
 -      grub_free (info);
 -      dev->present = 0;
 -      return grub_errno;
++      *dev->present = 0;
++      return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
++                       "device cannot be identified");
+     }
 -  grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
  
    dev->atapi = 1;
  
@@@ -105,36 -260,62 +113,41 @@@ grub_ata_identify (struct grub_ata *dev
      return grub_errno;
  
    info16 = (grub_uint16_t *) info;
 +  grub_memset (&parms, 0, sizeof (parms));
 +  parms.buffer = info;
 +  parms.size = GRUB_DISK_SECTOR_SIZE;
 +  parms.taskfile.disk = 0xE0;
  
 -  grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
 -  grub_ata_wait ();
 -  if ((grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
 -      && grub_ata_wait_not_busy (dev, dev->present ? GRUB_ATA_TOUT_DEV_INIT 
 -                               : GRUB_ATA_TOUT_STD))
 -    {
 -      dev->present = 0;
 -      grub_free (info);
 -      return grub_errno;
 -    }
 +  parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
  
-   err = dev->dev->readwrite (dev, &parms);
 -  grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
 -  grub_ata_wait ();
++  err = dev->dev->readwrite (dev, &parms, *dev->present);
  
 -  if (grub_ata_wait_drq (dev, 0, dev->present ? GRUB_ATA_TOUT_DEV_INIT 
 -                       : GRUB_ATA_TOUT_STD))
 +  if (err || parms.size != GRUB_DISK_SECTOR_SIZE)
      {
 +      grub_uint8_t sts = parms.taskfile.status;
        grub_free (info);
        grub_errno = GRUB_ERR_NONE;
 -      grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
 -
        if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
          | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
 -        && (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04 /* ABRT */))
 +        && (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;
+       {
 -        dev->present = 0;
++        *dev->present = 0;
+         /* 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");
+       {
 -        dev->present = 0;
++        *dev->present = 0;
+         /* Other Error.  */
+         return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                            "device cannot be identified");
+       }
      }
  
 -  grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
 -
 -  /* Re-check status to avoid bogus identify data due to stuck DRQ.  */
 -  grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
 -  if (sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
 -    {
 -      grub_dprintf ("ata", "bad status=0x%x\n", sts);
 -      grub_free (info);
 -      /* No device, return error but don't print message.  */
 -      grub_errno = GRUB_ERR_NONE;
 -      return GRUB_ERR_UNKNOWN_DEVICE;
 -    }
 -
    /* Now it is certain that this is not an ATAPI device.  */
    dev->atapi = 0;
  
@@@ -297,21 -637,40 +310,21 @@@ grub_ata_readwrite (grub_disk_t disk, g
        batch = size - nsectors;
  
        grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
 -
 -      /* Send read/write command.  */
 -      if (grub_ata_setaddress (dev, addressing, sector, batch))
 -      return grub_errno;
 -
 -      grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
 -
 -      unsigned sect;
 -      for (sect = 0; sect < batch; sect++)
 -      {
 -        /* Wait for !BSY, DRQ.  */
 -        if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA))
 -          return grub_errno;
 -
 -        /* Transfer data.  */
 -        if (! rw)
 -          grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE);
 -        else
 -          grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE);
 -
 -        buf += GRUB_DISK_SECTOR_SIZE;
 -      }
 -
 -      if (rw)
 -        {
 -        /* Check for write error.  */
 -        if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 -          return grub_errno;
 -
 -        if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS)
 -            & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
 -          return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
 -      }
 -
 +      grub_memset (&parms, 0, sizeof (parms));
 +      grub_ata_setaddress (ata, &parms, sector, batch, addressing);
 +      parms.taskfile.cmd = (! rw ? cmd : cmd_write);
 +      parms.buffer = buf;
 +      parms.size = batch * GRUB_DISK_SECTOR_SIZE;
 +      parms.write = rw;
 +      if (ata->dma)
 +      parms.dma = 1;
 +  
-       err = ata->dev->readwrite (ata, &parms);
++      err = ata->dev->readwrite (ata, &parms, 0);
 +      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;
      }
@@@ -466,35 -783,70 +479,35 @@@ static struct grub_disk_dev grub_atadis
  \f
  /* ATAPI code.  */
  
 -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;
 -
 -}
 -
  static grub_err_t
 -grub_atapi_read (struct grub_scsi *scsi,
 -               grub_size_t cmdsize __attribute__((unused)),
 -               char *cmd, grub_size_t size, char *buf)
 +grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
 +               grub_size_t size, char *buf)
  {
 -  struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
 +  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);
-   err = dev->dev->readwrite (dev, &parms);
 +  grub_memset (&parms, 0, sizeof (parms));
 +
 +  parms.taskfile.disk = 0;
 +  parms.taskfile.features = 0;
 +  parms.taskfile.atapi_ireason = 0;
 +  parms.taskfile.atapi_cnthigh = size >> 8;
 +  parms.taskfile.atapi_cntlow = size & 0xff;
 +  parms.taskfile.cmd = GRUB_ATA_CMD_PACKET;
 +  parms.cmd = cmd;
 +  parms.cmdsize = cmdsize;
 +
 +  parms.size = size;
 +  parms.buffer = buf;
 +  
++  err = dev->dev->readwrite (dev, &parms, 0);
 +  if (err)
 +    return err;
  
 -  if (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;
 -    }
 -
 +  if (parms.size != size)
 +    return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read");
    return GRUB_ERR_NONE;
  }
  
index f9111bc5f78a1210fe563612a2115c969966bc45,0000000000000000000000000000000000000000..a415ec30980041f749129b1431b34dd8d272b85c
mode 100644,000000..100644
--- /dev/null
@@@ -1,503 -1,0 +1,511 @@@
- grub_pata_check_ready (struct grub_pata_device *dev)
 +/* 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>
 +
++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
-     return grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
++grub_pata_check_ready (struct grub_pata_device *dev, int spinup)
 +{
 +  if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
-                    struct grub_disk_ata_pass_through_parms *parms)
++    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,
-   if (grub_pata_check_ready (dev))
++                   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=%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, (dev->device << 4)
 +                  | (parms->taskfile.disk & 0xef));
-   int i;
++  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;
 +}
 +
 +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;
 +}
 +
 +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))
 +{
 +  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);
 +}
Simple merge
Simple merge
index bea74c180b1f831be2196ec49461db5f44909064,c8f4e5e1b9bb285532744446e71c80d90a2ec2b1..84d5e2d10ffbc626eccb9747c0919fd188e833d1
@@@ -99,68 -94,25 +99,69 @@@ enum grub_ata_command
  enum grub_ata_timeout_milliseconds
    {
      GRUB_ATA_TOUT_STD  =  1000,  /* 1s standard timeout.  */
-     GRUB_ATA_TOUT_DATA = 10000   /* 10s DATA I/O timeout.  */
+     GRUB_ATA_TOUT_DATA = 10000,   /* 10s DATA I/O timeout.  */
 -    GRUB_ATA_TOUT_DEV_INIT  =  10000,  /* Give the device 10s on first try to spinon.  */
++    GRUB_ATA_TOUT_SPINUP  =  10000,  /* Give the device 10s on first try to spinon.  */
    };
  
 -struct grub_ata_device
 +typedef union
  {
 -  /* 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;
 +  grub_uint8_t raw[11];
 +  struct
 +  {
 +    union
 +    {
 +      grub_uint8_t features;
 +      grub_uint8_t error;
 +    };
 +    union
 +    {
 +      grub_uint8_t sectors;
 +      grub_uint8_t atapi_ireason;
 +    };
 +    union
 +    {
 +      grub_uint8_t lba_low;
 +      grub_uint8_t sectnum;
 +    };
 +    union
 +    {
 +      grub_uint8_t lba_mid;
 +      grub_uint8_t cyllsb;
 +      grub_uint8_t atapi_cntlow;
 +    };
 +    union
 +    {
 +      grub_uint8_t lba_high;
 +      grub_uint8_t cylmsb;
 +      grub_uint8_t atapi_cnthigh;
 +    };
 +    grub_uint8_t disk;
 +    union
 +    {
 +      grub_uint8_t cmd;
 +      grub_uint8_t status;
 +    };
 +    grub_uint8_t sectors48;
 +    grub_uint8_t lba48_low;
 +    grub_uint8_t lba48_mid;
 +    grub_uint8_t lba48_high;
 +  };
 +} grub_ata_regs_t;
  
 -  /* 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;
 +/* ATA pass through parameters and function.  */
 +struct grub_disk_ata_pass_through_parms
 +{
 +  grub_ata_regs_t taskfile;
 +  void * buffer;
 +  grub_size_t size;
 +  int write;
 +  void *cmd;
 +  int cmdsize;
 +  int dma;
 +};
  
 +struct grub_ata
 +{
    /* Addressing methods available for accessing this device.  If CHS
       is only available, use that.  Otherwise use LBA, except for the
       high sectors.  In that case use LBA48.  */
    /* Set to 0 for ATA, set to 1 for ATAPI.  */
    int atapi;
  
 -  int present;
 +  int dma;
 +
++  int *present;
 -  struct grub_ata_device *next;
 +  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));
  
 -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);
++                         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 */
index 6873d434ae157fc0022dbb2a03fea66258637a6e,b8ad6a6c7dc69678b0c72726082f58b72676fb6b..08daadcd10fe6c275a9e975df6a5d798ed70e545
@@@ -527,7 -522,7 +530,7 @@@ if [ "x${devabstraction_module}" = "x" 
      # Strip partition number
      grub_partition="`echo "${grub_drive}" | sed -e 's/^[^,]*[,)]//; s/)$//'`"
      grub_drive="`echo "${grub_drive}" | sed -e s/,[a-z0-9,]*//g`"
-     if [ "x$disk_module" != x ] && [ "x$disk_module" != xbiosdisk ]; then
 -    if [ "$disk_module" = ata ] || [ "x${grub_drive}" != "x${install_drive}" ] || ([ "x$platform" != xefi ] && [ "x$platform" != xpc ] && [ x"${target_cpu}-${platform}" != x"sparc64-ieee1275" ]) ; then
++    if ([ "x$disk_module" != x ] && [ "x$disk_module" != xbiosdisk ]) || [ "x${grub_drive}" != "x${install_drive}" ] || ([ "x$platform" != xefi ] && [ "x$platform" != xpc ] && [ x"${target_cpu}-${platform}" != x"sparc64-ieee1275" ]); then
          # generic method (used on coreboot and ata mod)
          uuid="`"$grub_probe" --device-map="${device_map}" --target=fs_uuid --device "${grub_device}"`"
          if [ "x${uuid}" = "x" ] ; then