mirror of
				https://git.proxmox.com/git/grub2
				synced 2025-11-04 14:51:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			551 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			551 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
 | 
						||
#define grub_ata_to_cpu16(x) ((grub_uint16_t) (x))
 | 
						||
#define grub_cpu_to_ata16(x) ((grub_uint16_t) (x))
 | 
						||
#else
 | 
						||
#define grub_ata_to_cpu16 grub_le_to_cpu16
 | 
						||
#define grub_cpu_to_ata16 grub_cpu_to_le16
 | 
						||
#endif
 | 
						||
 | 
						||
static void
 | 
						||
grub_pata_pio_read (struct grub_pata_device *dev, char *buf, grub_size_t size)
 | 
						||
{ 
 | 
						||
  unsigned int i;
 | 
						||
 | 
						||
  /* Read in the data, word by word.  */
 | 
						||
  for (i = 0; i < size / 2; i++)
 | 
						||
    grub_set_unaligned16 (buf + 2 * i,
 | 
						||
			  grub_ata_to_cpu16 (grub_inw(dev->ioaddress
 | 
						||
						     + GRUB_ATA_REG_DATA)));
 | 
						||
  if (size & 1)
 | 
						||
    buf[size - 1] = (char) grub_ata_to_cpu16 (grub_inw (dev->ioaddress
 | 
						||
						       + GRUB_ATA_REG_DATA));
 | 
						||
}
 | 
						||
 | 
						||
static void
 | 
						||
grub_pata_pio_write (struct grub_pata_device *dev, char *buf, grub_size_t size)
 | 
						||
{
 | 
						||
  unsigned int i;
 | 
						||
 | 
						||
  /* Write the data, word by word.  */
 | 
						||
  for (i = 0; i < size / 2; i++)
 | 
						||
    grub_outw(grub_cpu_to_ata16 (grub_get_unaligned16 (buf + 2 * i)), dev->ioaddress + GRUB_ATA_REG_DATA);
 | 
						||
}
 | 
						||
 | 
						||
/* ATA pass through support, used by hdparm.mod.  */
 | 
						||
static grub_err_t
 | 
						||
grub_pata_readwrite (struct grub_ata *disk,
 | 
						||
		     struct grub_disk_ata_pass_through_parms *parms,
 | 
						||
		     int spinup)
 | 
						||
{
 | 
						||
  struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
 | 
						||
  grub_size_t nread = 0;
 | 
						||
  int i;
 | 
						||
 | 
						||
  if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
 | 
						||
    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
 | 
						||
		       "ATAPI non-12 byte commands not supported");
 | 
						||
 | 
						||
  grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
 | 
						||
		parms->taskfile.cmd,
 | 
						||
		parms->taskfile.features,
 | 
						||
		parms->taskfile.sectors);
 | 
						||
  grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%"
 | 
						||
		PRIuGRUB_SIZE "\n",
 | 
						||
	        parms->taskfile.lba_high,
 | 
						||
	        parms->taskfile.lba_mid,
 | 
						||
	        parms->taskfile.lba_low, parms->size);
 | 
						||
 | 
						||
  /* Set registers.  */
 | 
						||
  grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4)
 | 
						||
		    | (parms->taskfile.disk & 0xef));
 | 
						||
  if (grub_pata_check_ready (dev, spinup))
 | 
						||
    return grub_errno;
 | 
						||
 | 
						||
  for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
 | 
						||
    grub_pata_regset (dev, i,
 | 
						||
		     parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
 | 
						||
  for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
 | 
						||
    grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
 | 
						||
 | 
						||
  /* Start command. */
 | 
						||
  grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
 | 
						||
 | 
						||
  /* Wait for !BSY.  */
 | 
						||
  if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 | 
						||
    return grub_errno;
 | 
						||
 | 
						||
  /* Check status.  */
 | 
						||
  grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
 | 
						||
  grub_dprintf ("pata", "status=0x%x\n", sts);
 | 
						||
 | 
						||
  if (parms->cmdsize)
 | 
						||
    {
 | 
						||
      grub_uint8_t irs;
 | 
						||
      /* Wait for !BSY.  */
 | 
						||
      if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 | 
						||
	return grub_errno;
 | 
						||
 | 
						||
      irs = grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON);
 | 
						||
      /* OK if DRQ is asserted and interrupt reason is as expected.  */
 | 
						||
      if (!((sts & GRUB_ATA_STATUS_DRQ)
 | 
						||
	    && (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_CMD_OUT))
 | 
						||
	return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
 | 
						||
      /* Write the packet.  */
 | 
						||
      grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
 | 
						||
    }
 | 
						||
 | 
						||
  /* Transfer data.  */
 | 
						||
  while (nread < parms->size
 | 
						||
	 && (sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
 | 
						||
	 == GRUB_ATA_STATUS_DRQ)
 | 
						||
    {
 | 
						||
      unsigned cnt;
 | 
						||
 | 
						||
      /* Wait for !BSY.  */
 | 
						||
      if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 | 
						||
	return grub_errno;
 | 
						||
 | 
						||
      if (parms->cmdsize)
 | 
						||
	{
 | 
						||
	  if ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
 | 
						||
	       & GRUB_ATAPI_IREASON_MASK) != GRUB_ATAPI_IREASON_DATA_IN)
 | 
						||
	    return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
 | 
						||
 | 
						||
	  cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
 | 
						||
	    | grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
 | 
						||
	  grub_dprintf("pata", "DRQ count=%u\n", cnt);
 | 
						||
 | 
						||
	  /* Count of last transfer may be uneven.  */
 | 
						||
	  if (! (0 < cnt && cnt <= parms->size - nread
 | 
						||
		 && (! (cnt & 1) || cnt == parms->size - nread)))
 | 
						||
	    return grub_error (GRUB_ERR_READ_ERROR,
 | 
						||
			       "invalid ATAPI transfer count");
 | 
						||
	}
 | 
						||
      else
 | 
						||
	cnt = GRUB_DISK_SECTOR_SIZE;
 | 
						||
      if (cnt > parms->size - nread)
 | 
						||
	cnt = parms->size - nread;
 | 
						||
 | 
						||
      if (parms->write)
 | 
						||
	grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt);
 | 
						||
      else
 | 
						||
	grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt);
 | 
						||
 | 
						||
      nread += cnt;
 | 
						||
    }
 | 
						||
  if (parms->write)
 | 
						||
    {
 | 
						||
      /* Check for write error.  */
 | 
						||
      if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 | 
						||
	return grub_errno;
 | 
						||
 | 
						||
      if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS)
 | 
						||
	  & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
 | 
						||
	return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
 | 
						||
    }
 | 
						||
  parms->size = nread;
 | 
						||
 | 
						||
  /* Wait for !BSY.  */
 | 
						||
  if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
 | 
						||
    return grub_errno;
 | 
						||
 | 
						||
  /* Return registers.  */
 | 
						||
  for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
 | 
						||
    parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
 | 
						||
 | 
						||
  grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
 | 
						||
	        parms->taskfile.status,
 | 
						||
	        parms->taskfile.error,
 | 
						||
		parms->taskfile.sectors);
 | 
						||
 | 
						||
  if (parms->taskfile.status
 | 
						||
      & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
 | 
						||
    return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
 | 
						||
 | 
						||
  return GRUB_ERR_NONE;
 | 
						||
}
 | 
						||
 | 
						||
static grub_err_t
 | 
						||
check_device (struct grub_pata_device *dev)
 | 
						||
{
 | 
						||
  grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
 | 
						||
  grub_pata_wait ();
 | 
						||
 | 
						||
  /* Try to detect if the port is in use by writing to it,
 | 
						||
     waiting for a while and reading it again.  If the value
 | 
						||
     was preserved, there is a device connected.  */
 | 
						||
  grub_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
 | 
						||
  grub_pata_wait ();
 | 
						||
  grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
 | 
						||
  grub_dprintf ("ata", "sectors=0x%x\n", sec);
 | 
						||
  if (sec != 0x5A)
 | 
						||
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
 | 
						||
 | 
						||
  /* The above test may detect a second (slave) device
 | 
						||
     connected to a SATA controller which supports only one
 | 
						||
     (master) device.  It is not safe to use the status register
 | 
						||
     READY bit to check for controller channel existence.  Some
 | 
						||
     ATAPI commands (RESET, DIAGNOSTIC) may clear this bit.  */
 | 
						||
 | 
						||
  return GRUB_ERR_NONE;
 | 
						||
}
 | 
						||
 | 
						||
static grub_err_t
 | 
						||
grub_pata_device_initialize (int port, int device, int addr)
 | 
						||
{
 | 
						||
  struct grub_pata_device *dev;
 | 
						||
  struct grub_pata_device **devp;
 | 
						||
  grub_err_t err;
 | 
						||
 | 
						||
  grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n",
 | 
						||
		port, device, addr);
 | 
						||
 | 
						||
  dev = grub_malloc (sizeof(*dev));
 | 
						||
  if (! dev)
 | 
						||
    return grub_errno;
 | 
						||
 | 
						||
  /* Setup the device information.  */
 | 
						||
  dev->port = port;
 | 
						||
  dev->device = device;
 | 
						||
  dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
 | 
						||
  dev->present = 1;
 | 
						||
  dev->next = NULL;
 | 
						||
 | 
						||
  /* Register the device.  */
 | 
						||
  for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next);
 | 
						||
  *devp = dev;
 | 
						||
 | 
						||
  err = check_device (dev);
 | 
						||
  if (err)
 | 
						||
    grub_print_error ();
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
 | 
						||
static int
 | 
						||
grub_pata_pciinit (grub_pci_device_t dev,
 | 
						||
		   grub_pci_id_t pciid,
 | 
						||
		   void *data __attribute__ ((unused)))
 | 
						||
{
 | 
						||
  static int compat_use[2] = { 0 };
 | 
						||
  grub_pci_address_t addr;
 | 
						||
  grub_uint32_t class;
 | 
						||
  grub_uint32_t bar1;
 | 
						||
  grub_uint32_t bar2;
 | 
						||
  int rega;
 | 
						||
  int i;
 | 
						||
  static int controller = 0;
 | 
						||
  int cs5536 = 0;
 | 
						||
  int nports = 2;
 | 
						||
 | 
						||
  /* Read class.  */
 | 
						||
  addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
 | 
						||
  class = grub_pci_read (addr);
 | 
						||
 | 
						||
  /* AMD CS5536 Southbridge.  */
 | 
						||
  if (pciid == GRUB_CS5536_PCIID)
 | 
						||
    {
 | 
						||
      cs5536 = 1;
 | 
						||
      nports = 1;
 | 
						||
    }
 | 
						||
 | 
						||
  /* Check if this class ID matches that of a PCI IDE Controller.  */
 | 
						||
  if (!cs5536 && (class >> 16 != 0x0101))
 | 
						||
    return 0;
 | 
						||
 | 
						||
  for (i = 0; i < nports; i++)
 | 
						||
    {
 | 
						||
      /* Set to 0 when the channel operated in compatibility mode.  */
 | 
						||
      int compat;
 | 
						||
 | 
						||
      /* We don't support non-compatibility mode for CS5536.  */
 | 
						||
      if (cs5536)
 | 
						||
	compat = 0;
 | 
						||
      else
 | 
						||
	compat = (class >> (8 + 2 * i)) & 1;
 | 
						||
 | 
						||
      rega = 0;
 | 
						||
 | 
						||
      /* If the channel is in compatibility mode, just assign the
 | 
						||
	 default registers.  */
 | 
						||
      if (compat == 0 && !compat_use[i])
 | 
						||
	{
 | 
						||
	  rega = grub_pata_ioaddress[i];
 | 
						||
	  compat_use[i] = 1;
 | 
						||
	}
 | 
						||
      else if (compat)
 | 
						||
	{
 | 
						||
	  /* Read the BARs, which either contain a mmapped IO address
 | 
						||
	     or the IO port address.  */
 | 
						||
	  addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
 | 
						||
					+ sizeof (grub_uint64_t) * i);
 | 
						||
	  bar1 = grub_pci_read (addr);
 | 
						||
	  addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
 | 
						||
					+ sizeof (grub_uint64_t) * i
 | 
						||
					+ sizeof (grub_uint32_t));
 | 
						||
	  bar2 = grub_pci_read (addr);
 | 
						||
 | 
						||
	  /* Check if the BARs describe an IO region.  */
 | 
						||
	  if ((bar1 & 1) && (bar2 & 1) && (bar1 & ~3))
 | 
						||
	    {
 | 
						||
	      rega = bar1 & ~3;
 | 
						||
	      addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
 | 
						||
	      grub_pci_write_word (addr, grub_pci_read_word (addr)
 | 
						||
				   | GRUB_PCI_COMMAND_IO_ENABLED
 | 
						||
				   | GRUB_PCI_COMMAND_MEM_ENABLED
 | 
						||
				   | GRUB_PCI_COMMAND_BUS_MASTER);
 | 
						||
 | 
						||
	    }
 | 
						||
	}
 | 
						||
 | 
						||
      grub_dprintf ("pata",
 | 
						||
		    "PCI dev (%d,%d,%d) compat=%d rega=0x%x\n",
 | 
						||
		    grub_pci_get_bus (dev), grub_pci_get_device (dev),
 | 
						||
		    grub_pci_get_function (dev), compat, rega);
 | 
						||
 | 
						||
      if (rega)
 | 
						||
	{
 | 
						||
	  grub_errno = GRUB_ERR_NONE;
 | 
						||
	  grub_pata_device_initialize (controller * 2 + i, 0, rega);
 | 
						||
 | 
						||
	  /* Most errors raised by grub_ata_device_initialize() are harmless.
 | 
						||
	     They just indicate this particular drive is not responding, most
 | 
						||
	     likely because it doesn't exist.  We might want to ignore specific
 | 
						||
	     error types here, instead of printing them.  */
 | 
						||
	  if (grub_errno)
 | 
						||
	    {
 | 
						||
	      grub_print_error ();
 | 
						||
	      grub_errno = GRUB_ERR_NONE;
 | 
						||
	    }
 | 
						||
 | 
						||
	  grub_pata_device_initialize (controller * 2 + i, 1, rega);
 | 
						||
 | 
						||
	  /* Likewise.  */
 | 
						||
	  if (grub_errno)
 | 
						||
	    {
 | 
						||
	      grub_print_error ();
 | 
						||
	      grub_errno = GRUB_ERR_NONE;
 | 
						||
	    }
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  controller++;
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
static grub_err_t
 | 
						||
grub_pata_initialize (void)
 | 
						||
{
 | 
						||
  grub_pci_iterate (grub_pata_pciinit, NULL);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
#else
 | 
						||
static grub_err_t
 | 
						||
grub_pata_initialize (void)
 | 
						||
{
 | 
						||
  int i;
 | 
						||
  for (i = 0; i < 2; i++)
 | 
						||
    {
 | 
						||
      grub_pata_device_initialize (i, 0, grub_pata_ioaddress[i]);
 | 
						||
      grub_pata_device_initialize (i, 1, grub_pata_ioaddress[i]);
 | 
						||
    }
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
#endif
 | 
						||
 | 
						||
static grub_err_t
 | 
						||
grub_pata_open (int id, int devnum, struct grub_ata *ata)
 | 
						||
{
 | 
						||
  struct grub_pata_device *dev;
 | 
						||
  struct grub_pata_device *devfnd = 0;
 | 
						||
  grub_err_t err;
 | 
						||
 | 
						||
  if (id != GRUB_SCSI_SUBSYSTEM_PATA)
 | 
						||
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
 | 
						||
 | 
						||
  for (dev = grub_pata_devices; dev; dev = dev->next)
 | 
						||
    {
 | 
						||
      if (dev->port * 2 + dev->device == devnum)
 | 
						||
	{
 | 
						||
	  devfnd = dev;
 | 
						||
	  break;
 | 
						||
	}
 | 
						||
    }
 | 
						||
 | 
						||
  grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
 | 
						||
 | 
						||
  if (! devfnd)
 | 
						||
    return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
 | 
						||
 | 
						||
  err = check_device (devfnd);
 | 
						||
  if (err)
 | 
						||
    return err;
 | 
						||
 | 
						||
  ata->data = devfnd;
 | 
						||
  ata->dma = 0;
 | 
						||
  ata->maxbuffer = 256 * 512;
 | 
						||
  ata->present = &devfnd->present;
 | 
						||
 | 
						||
  return GRUB_ERR_NONE;
 | 
						||
}
 | 
						||
 | 
						||
static int
 | 
						||
grub_pata_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data,
 | 
						||
		   grub_disk_pull_t pull)
 | 
						||
{
 | 
						||
  struct grub_pata_device *dev;
 | 
						||
 | 
						||
  if (pull != GRUB_DISK_PULL_NONE)
 | 
						||
    return 0;
 | 
						||
 | 
						||
  for (dev = grub_pata_devices; dev; dev = dev->next)
 | 
						||
    if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device,
 | 
						||
	      hook_data))
 | 
						||
      return 1;
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
static struct grub_ata_dev grub_pata_dev =
 | 
						||
  {
 | 
						||
    .iterate = grub_pata_iterate,
 | 
						||
    .open = grub_pata_open,
 | 
						||
    .readwrite = grub_pata_readwrite,
 | 
						||
  };
 | 
						||
 | 
						||
 | 
						||
 | 
						||
 | 
						||
GRUB_MOD_INIT(ata_pthru)
 | 
						||
{
 | 
						||
  grub_stop_disk_firmware ();
 | 
						||
 | 
						||
  /* ATA initialization.  */
 | 
						||
  grub_pata_initialize ();
 | 
						||
 | 
						||
  grub_ata_dev_register (&grub_pata_dev);
 | 
						||
}
 | 
						||
 | 
						||
GRUB_MOD_FINI(ata_pthru)
 | 
						||
{
 | 
						||
  grub_ata_dev_unregister (&grub_pata_dev);
 | 
						||
}
 |