2009-02-14 12:57:55 +00:00
|
|
|
|
/* 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>
|
2010-12-24 15:07:53 +00:00
|
|
|
|
#include <grub/scsi.h>
|
2009-02-14 12:57:55 +00:00
|
|
|
|
#include <grub/disk.h>
|
|
|
|
|
#include <grub/dl.h>
|
|
|
|
|
#include <grub/mm.h>
|
2011-05-18 10:36:26 +00:00
|
|
|
|
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
|
2010-12-24 15:07:53 +00:00
|
|
|
|
#include <grub/pci.h>
|
|
|
|
|
#include <grub/cs5536.h>
|
2011-05-18 10:36:26 +00:00
|
|
|
|
#else
|
|
|
|
|
#define GRUB_MACHINE_PCI_IO_BASE 0xb4000000
|
|
|
|
|
#endif
|
2010-12-24 15:07:53 +00:00
|
|
|
|
#include <grub/time.h>
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
/* 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 };
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
int present;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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. */
|
2009-02-14 12:57:55 +00:00
|
|
|
|
static grub_err_t
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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
|
2011-05-16 15:17:04 +00:00
|
|
|
|
grub_pata_check_ready (struct grub_pata_device *dev, int spinup)
|
2009-02-14 12:57:55 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
|
2011-05-16 15:17:04 +00:00
|
|
|
|
return grub_pata_wait_not_busy (dev, spinup ? GRUB_ATA_TOUT_SPINUP
|
|
|
|
|
: GRUB_ATA_TOUT_STD);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static inline void
|
|
|
|
|
grub_pata_wait (void)
|
|
|
|
|
{
|
|
|
|
|
grub_millisleep (50);
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 10:38:49 +00:00
|
|
|
|
#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
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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++)
|
2011-12-13 14:55:36 +00:00
|
|
|
|
grub_set_unaligned16 (buf + 2 * i,
|
2012-06-06 10:38:49 +00:00
|
|
|
|
grub_ata_to_cpu16 (grub_inw(dev->ioaddress
|
2011-12-13 14:55:36 +00:00
|
|
|
|
+ GRUB_ATA_REG_DATA)));
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (size & 1)
|
2012-06-06 10:38:49 +00:00
|
|
|
|
buf[size - 1] = (char) grub_ata_to_cpu16 (grub_inw (dev->ioaddress
|
2010-12-24 15:07:53 +00:00
|
|
|
|
+ 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++)
|
2012-06-06 10:38:49 +00:00
|
|
|
|
grub_outw(grub_cpu_to_ata16 (grub_get_unaligned16 (buf + 2 * i)), dev->ioaddress + GRUB_ATA_REG_DATA);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ATA pass through support, used by hdparm.mod. */
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_pata_readwrite (struct grub_ata *disk,
|
2011-05-16 15:17:04 +00:00
|
|
|
|
struct grub_disk_ata_pass_through_parms *parms,
|
|
|
|
|
int spinup)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
|
|
|
|
|
grub_size_t nread = 0;
|
2011-05-16 15:17:04 +00:00
|
|
|
|
int i;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
|
2009-02-14 12:57:55 +00:00
|
|
|
|
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
2010-12-24 15:07:53 +00:00
|
|
|
|
"ATAPI non-12 byte commands not supported");
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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);
|
2011-06-27 06:43:13 +00:00
|
|
|
|
grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%"
|
|
|
|
|
PRIuGRUB_SIZE "\n",
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.lba_high,
|
|
|
|
|
parms->taskfile.lba_mid,
|
|
|
|
|
parms->taskfile.lba_low, parms->size);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
|
|
|
|
/* Set registers. */
|
2010-12-25 02:47:51 +00:00
|
|
|
|
grub_pata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4)
|
|
|
|
|
| (parms->taskfile.disk & 0xef));
|
2011-05-16 15:17:04 +00:00
|
|
|
|
if (grub_pata_check_ready (dev, spinup))
|
2009-02-14 12:57:55 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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)]);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
|
|
|
|
/* Start command. */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2011-07-23 16:18:14 +00:00
|
|
|
|
/* Wait for !BSY. */
|
|
|
|
|
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-02-14 12:57:55 +00:00
|
|
|
|
/* Check status. */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
|
|
|
|
|
grub_dprintf ("pata", "status=0x%x\n", sts);
|
|
|
|
|
|
|
|
|
|
if (parms->cmdsize)
|
|
|
|
|
{
|
2010-12-24 16:11:21 +00:00
|
|
|
|
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);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
/* 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);
|
|
|
|
|
}
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
|
|
|
|
/* Transfer data. */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
while (nread < parms->size
|
2012-02-29 11:45:32 +00:00
|
|
|
|
&& (sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
|
|
|
|
== GRUB_ATA_STATUS_DRQ)
|
2009-02-14 12:57:55 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
unsigned cnt;
|
2010-12-24 16:11:21 +00:00
|
|
|
|
|
|
|
|
|
/* Wait for !BSY. */
|
|
|
|
|
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (parms->cmdsize)
|
|
|
|
|
{
|
2012-02-29 11:45:32 +00:00
|
|
|
|
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");
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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");
|
2009-02-14 12:57:55 +00:00
|
|
|
|
}
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->size = nread;
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 16:11:21 +00:00
|
|
|
|
/* Wait for !BSY. */
|
|
|
|
|
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-02-14 12:57:55 +00:00
|
|
|
|
/* Return registers. */
|
|
|
|
|
for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
|
|
|
|
|
parms->taskfile.status,
|
|
|
|
|
parms->taskfile.error,
|
|
|
|
|
parms->taskfile.sectors);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (parms->taskfile.status
|
2009-02-14 12:57:55 +00:00
|
|
|
|
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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
|
2010-12-25 02:22:12 +00:00
|
|
|
|
grub_pata_device_initialize (int port, int device, int addr)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
struct grub_pata_device *dev;
|
|
|
|
|
struct grub_pata_device **devp;
|
|
|
|
|
grub_err_t err;
|
|
|
|
|
|
2010-12-25 02:22:12 +00:00
|
|
|
|
grub_dprintf ("pata", "detecting device %d,%d (0x%x)\n",
|
|
|
|
|
port, device, addr);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2011-05-16 15:17:04 +00:00
|
|
|
|
dev->present = 1;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-18 10:36:26 +00:00
|
|
|
|
#ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
|
2013-01-13 01:10:41 +00:00
|
|
|
|
static int
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_pata_pciinit (grub_pci_device_t dev,
|
2013-01-13 01:10:41 +00:00
|
|
|
|
grub_pci_id_t pciid,
|
|
|
|
|
void *data __attribute__ ((unused)))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
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. */
|
2013-11-26 13:21:11 +00:00
|
|
|
|
if ((bar1 & 1) && (bar2 & 1) && (bar1 & ~3))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
rega = bar1 & ~3;
|
2013-11-26 13:21:11 +00:00
|
|
|
|
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);
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
grub_dprintf ("pata",
|
2010-12-25 02:22:12 +00:00
|
|
|
|
"PCI dev (%d,%d,%d) compat=%d rega=0x%x\n",
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_pci_get_bus (dev), grub_pci_get_device (dev),
|
2010-12-25 02:22:12 +00:00
|
|
|
|
grub_pci_get_function (dev), compat, rega);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
2010-12-25 02:22:12 +00:00
|
|
|
|
if (rega)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
2010-12-25 02:22:12 +00:00
|
|
|
|
grub_pata_device_initialize (controller * 2 + i, 0, rega);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-25 02:22:12 +00:00
|
|
|
|
grub_pata_device_initialize (controller * 2 + i, 1, rega);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
/* Likewise. */
|
|
|
|
|
if (grub_errno)
|
|
|
|
|
{
|
|
|
|
|
grub_print_error ();
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
controller++;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_pata_initialize (void)
|
|
|
|
|
{
|
2013-01-13 01:10:41 +00:00
|
|
|
|
grub_pci_iterate (grub_pata_pciinit, NULL);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-05-18 10:36:26 +00:00
|
|
|
|
#else
|
|
|
|
|
static grub_err_t
|
2011-06-27 17:26:52 +00:00
|
|
|
|
grub_pata_initialize (void)
|
2011-05-18 10:36:26 +00:00
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
|
{
|
2011-06-27 17:26:52 +00:00
|
|
|
|
grub_pata_device_initialize (i, 0, grub_pata_ioaddress[i]);
|
|
|
|
|
grub_pata_device_initialize (i, 1, grub_pata_ioaddress[i]);
|
2011-05-18 10:36:26 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
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;
|
2010-12-25 01:53:20 +00:00
|
|
|
|
ata->dma = 0;
|
2012-01-29 18:00:30 +00:00
|
|
|
|
ata->maxbuffer = 256 * 512;
|
2011-05-16 15:17:04 +00:00
|
|
|
|
ata->present = &devfnd->present;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2013-01-20 15:52:15 +00:00
|
|
|
|
grub_pata_iterate (grub_ata_dev_iterate_hook_t hook, void *hook_data,
|
2011-07-07 10:21:53 +00:00
|
|
|
|
grub_disk_pull_t pull)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
|
|
|
|
struct grub_pata_device *dev;
|
|
|
|
|
|
2011-07-07 10:21:53 +00:00
|
|
|
|
if (pull != GRUB_DISK_PULL_NONE)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
for (dev = grub_pata_devices; dev; dev = dev->next)
|
2013-01-20 15:52:15 +00:00
|
|
|
|
if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device,
|
|
|
|
|
hook_data))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct grub_ata_dev grub_pata_dev =
|
|
|
|
|
{
|
|
|
|
|
.iterate = grub_pata_iterate,
|
|
|
|
|
.open = grub_pata_open,
|
|
|
|
|
.readwrite = grub_pata_readwrite,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2009-02-14 12:57:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GRUB_MOD_INIT(ata_pthru)
|
|
|
|
|
{
|
2013-04-29 10:16:46 +00:00
|
|
|
|
grub_stop_disk_firmware ();
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
|
|
|
|
/* ATA initialization. */
|
|
|
|
|
grub_pata_initialize ();
|
|
|
|
|
|
|
|
|
|
grub_ata_dev_register (&grub_pata_dev);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GRUB_MOD_FINI(ata_pthru)
|
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_dev_unregister (&grub_pata_dev);
|
2009-02-14 12:57:55 +00:00
|
|
|
|
}
|