2007-10-31 22:29:20 +00:00
|
|
|
|
/* ata.c - ATA disk access. */
|
|
|
|
|
/*
|
|
|
|
|
* GRUB -- GRand Unified Bootloader
|
2009-02-14 12:57:55 +00:00
|
|
|
|
* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
|
2007-10-31 22:29:20 +00:00
|
|
|
|
*
|
|
|
|
|
* 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/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-02-14 12:57:55 +00:00
|
|
|
|
#include <grub/ata.h>
|
2007-10-31 22:29:20 +00:00
|
|
|
|
#include <grub/dl.h>
|
|
|
|
|
#include <grub/disk.h>
|
|
|
|
|
#include <grub/mm.h>
|
2007-11-05 14:54:00 +00:00
|
|
|
|
#include <grub/time.h>
|
2008-08-05 09:41:10 +00:00
|
|
|
|
#include <grub/pci.h>
|
2008-08-27 15:05:00 +00:00
|
|
|
|
#include <grub/scsi.h>
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
/* At the moment, only two IDE ports are supported. */
|
2009-11-06 21:50:44 +00:00
|
|
|
|
static const grub_port_t grub_ata_ioaddress[] = { 0x1f0, 0x170 };
|
|
|
|
|
static const grub_port_t grub_ata_ioaddress2[] = { 0x3f6, 0x376 };
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
static struct grub_ata_device *grub_ata_devices;
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* Wait for !BSY. */
|
2009-02-14 12:57:55 +00:00
|
|
|
|
grub_err_t
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* 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);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
int i = 1;
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_uint8_t sts;
|
|
|
|
|
while ((sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS))
|
|
|
|
|
& GRUB_ATA_STATUS_BUSY)
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (i >= milliseconds)
|
|
|
|
|
{
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_dprintf ("ata", "timeout: %dms, status=0x%x\n",
|
|
|
|
|
milliseconds, sts);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout");
|
|
|
|
|
}
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
|
|
|
|
grub_millisleep (1);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
i++;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
return GRUB_ERR_NONE;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
grub_ata_wait (void)
|
|
|
|
|
{
|
2007-11-05 14:54:00 +00:00
|
|
|
|
grub_millisleep (50);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* Wait for !BSY, DRQ. */
|
2009-02-14 12:57:55 +00:00
|
|
|
|
grub_err_t
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_wait_drq (struct grub_ata_device *dev, int rw,
|
|
|
|
|
int milliseconds)
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_wait_not_busy (dev, milliseconds))
|
2009-01-16 19:29:41 +00:00
|
|
|
|
return grub_errno;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* !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");
|
|
|
|
|
}
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Byteorder has to be changed before strings can be read. */
|
2009-01-22 20:15:05 +00:00
|
|
|
|
static void
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_ata_strncpy (char *dst, char *src, grub_size_t len)
|
|
|
|
|
{
|
|
|
|
|
grub_uint16_t *src16 = (grub_uint16_t *) src;
|
|
|
|
|
grub_uint16_t *dst16 = (grub_uint16_t *) dst;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < len / 2; i++)
|
2008-08-07 23:37:33 +00:00
|
|
|
|
*(dst16++) = grub_be_to_cpu16 (*(src16++));
|
2007-10-31 22:29:20 +00:00
|
|
|
|
dst[len] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-14 12:57:55 +00:00
|
|
|
|
void
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
static void
|
|
|
|
|
grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
/* Write the data, word by word. */
|
|
|
|
|
for (i = 0; i < size / 2; i++)
|
|
|
|
|
grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
grub_ata_dumpinfo (struct grub_ata_device *dev, char *info)
|
|
|
|
|
{
|
|
|
|
|
char text[41];
|
|
|
|
|
|
|
|
|
|
/* The device information was read, dump it for debugging. */
|
|
|
|
|
grub_ata_strncpy (text, info + 20, 20);
|
2008-07-27 20:57:43 +00:00
|
|
|
|
grub_dprintf ("ata", "Serial: %s\n", text);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_ata_strncpy (text, info + 46, 8);
|
2008-07-27 20:57:43 +00:00
|
|
|
|
grub_dprintf ("ata", "Firmware: %s\n", text);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_ata_strncpy (text, info + 54, 40);
|
2008-07-27 20:57:43 +00:00
|
|
|
|
grub_dprintf ("ata", "Model: %s\n", text);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-07-06 01:24:19 +00:00
|
|
|
|
if (! dev->atapi)
|
|
|
|
|
{
|
2008-07-27 20:57:43 +00:00
|
|
|
|
grub_dprintf ("ata", "Addressing: %d\n", dev->addr);
|
2009-11-16 15:01:15 +00:00
|
|
|
|
grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size);
|
2008-07-06 01:24:19 +00:00
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_atapi_identify (struct grub_ata_device *dev)
|
|
|
|
|
{
|
|
|
|
|
char *info;
|
|
|
|
|
|
2009-01-16 19:29:41 +00:00
|
|
|
|
info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
if (! info)
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_ata_wait ();
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_check_ready (dev))
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
|
|
|
|
grub_free (info);
|
|
|
|
|
return grub_errno;
|
|
|
|
|
}
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
|
2009-01-19 20:39:57 +00:00
|
|
|
|
grub_ata_wait ();
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
|
|
|
|
grub_free (info);
|
|
|
|
|
return grub_errno;
|
|
|
|
|
}
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
dev->atapi = 1;
|
|
|
|
|
|
|
|
|
|
grub_ata_dumpinfo (dev, info);
|
|
|
|
|
|
|
|
|
|
grub_free (info);
|
|
|
|
|
|
2008-08-07 23:37:33 +00:00
|
|
|
|
return GRUB_ERR_NONE;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2009-01-19 20:39:57 +00:00
|
|
|
|
grub_atapi_wait_drq (struct grub_ata_device *dev,
|
|
|
|
|
grub_uint8_t ireason,
|
|
|
|
|
int milliseconds)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* Wait for !BSY, DRQ, ireason */
|
|
|
|
|
if (grub_ata_wait_not_busy (dev, milliseconds))
|
2009-01-19 20:39:57 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
|
|
|
|
grub_uint8_t irs = grub_ata_regget (dev, GRUB_ATAPI_REG_IREASON);
|
|
|
|
|
|
|
|
|
|
/* OK if DRQ is asserted and interrupt reason is as expected. */
|
|
|
|
|
if ((sts & GRUB_ATA_STATUS_DRQ)
|
|
|
|
|
&& (irs & GRUB_ATAPI_IREASON_MASK) == ireason)
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* !DRQ implies error condition. */
|
|
|
|
|
grub_dprintf ("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n",
|
|
|
|
|
sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
|
2009-01-19 20:39:57 +00:00
|
|
|
|
|
|
|
|
|
if (! (sts & GRUB_ATA_STATUS_DRQ)
|
|
|
|
|
&& (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR)
|
|
|
|
|
{
|
|
|
|
|
if (ireason == GRUB_ATAPI_IREASON_CMD_OUT)
|
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "ATA PACKET command error");
|
|
|
|
|
else
|
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "ATAPI read error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_atapi_packet (struct grub_ata_device *dev, char *packet,
|
|
|
|
|
grub_size_t size)
|
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
|
|
|
|
if (grub_ata_check_ready (dev))
|
2009-01-19 20:39:57 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
/* Send ATA PACKET command. */
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0);
|
2009-01-19 20:39:57 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATAPI_REG_CNTHIGH, size >> 8);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATAPI_REG_CNTLOW, size & 0xFF);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET);
|
|
|
|
|
|
2009-01-19 20:39:57 +00:00
|
|
|
|
/* Wait for !BSY, DRQ, !I/O, C/D. */
|
|
|
|
|
if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_CMD_OUT, GRUB_ATA_TOUT_STD))
|
2008-08-07 23:37:33 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-01-19 20:39:57 +00:00
|
|
|
|
/* Write the packet. */
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_pio_write (dev, packet, 12);
|
2009-01-19 20:39:57 +00:00
|
|
|
|
|
2008-08-07 23:37:33 +00:00
|
|
|
|
return GRUB_ERR_NONE;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_identify (struct grub_ata_device *dev)
|
|
|
|
|
{
|
|
|
|
|
char *info;
|
|
|
|
|
grub_uint16_t *info16;
|
|
|
|
|
|
|
|
|
|
info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
|
|
|
|
|
if (! info)
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
info16 = (grub_uint16_t *) info;
|
|
|
|
|
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_ata_wait ();
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_check_ready (dev))
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
|
|
|
|
grub_free (info);
|
|
|
|
|
return grub_errno;
|
|
|
|
|
}
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
|
2007-11-03 15:45:07 +00:00
|
|
|
|
grub_ata_wait ();
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2009-05-25 19:36:50 +00:00
|
|
|
|
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 */))
|
|
|
|
|
/* 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;
|
|
|
|
|
|
2009-01-16 19:29:41 +00:00
|
|
|
|
else
|
2009-05-25 19:36:50 +00:00
|
|
|
|
/* Other Error. */
|
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
|
2009-12-25 00:04:51 +00:00
|
|
|
|
"device cannot be identified");
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
|
|
|
|
|
|
2009-05-25 19:36:50 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Now it is certain that this is not an ATAPI device. */
|
|
|
|
|
dev->atapi = 0;
|
|
|
|
|
|
|
|
|
|
/* CHS is always supported. */
|
|
|
|
|
dev->addr = GRUB_ATA_CHS;
|
|
|
|
|
|
|
|
|
|
/* Check if LBA is supported. */
|
|
|
|
|
if (info16[49] & (1 << 9))
|
|
|
|
|
{
|
|
|
|
|
/* Check if LBA48 is supported. */
|
|
|
|
|
if (info16[83] & (1 << 10))
|
|
|
|
|
dev->addr = GRUB_ATA_LBA48;
|
|
|
|
|
else
|
|
|
|
|
dev->addr = GRUB_ATA_LBA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the amount of sectors. */
|
|
|
|
|
if (dev->addr != GRUB_ATA_LBA48)
|
|
|
|
|
dev->size = grub_le_to_cpu32(*((grub_uint32_t *) &info16[60]));
|
|
|
|
|
else
|
2008-03-20 21:00:15 +00:00
|
|
|
|
dev->size = grub_le_to_cpu64(*((grub_uint64_t *) &info16[100]));
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
/* Read CHS information. */
|
|
|
|
|
dev->cylinders = info16[1];
|
|
|
|
|
dev->heads = info16[3];
|
|
|
|
|
dev->sectors_per_track = info16[6];
|
|
|
|
|
|
|
|
|
|
grub_ata_dumpinfo (dev, info);
|
|
|
|
|
|
|
|
|
|
grub_free(info);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2008-08-05 09:41:10 +00:00
|
|
|
|
grub_ata_device_initialize (int port, int device, int addr, int addr2)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
struct grub_ata_device *dev;
|
|
|
|
|
struct grub_ata_device **devp;
|
|
|
|
|
|
2008-08-05 09:41:10 +00:00
|
|
|
|
grub_dprintf ("ata", "detecting device %d,%d (0x%x, 0x%x)\n",
|
|
|
|
|
port, device, addr, addr2);
|
|
|
|
|
|
|
|
|
|
dev = grub_malloc (sizeof(*dev));
|
|
|
|
|
if (! dev)
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
/* Setup the device information. */
|
|
|
|
|
dev->port = port;
|
|
|
|
|
dev->device = device;
|
2009-11-06 21:50:44 +00:00
|
|
|
|
dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
|
|
|
|
|
dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE;
|
2008-08-05 09:41:10 +00:00
|
|
|
|
dev->next = NULL;
|
|
|
|
|
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
|
|
|
|
grub_ata_wait ();
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
|
|
|
|
/* Try to detect if the port is in use by writing to it,
|
|
|
|
|
waiting for a while and reading it again. If the value
|
2009-05-25 19:36:50 +00:00
|
|
|
|
was preserved, there is a device connected. */
|
2009-06-10 21:04:23 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
|
2008-08-05 09:41:10 +00:00
|
|
|
|
grub_ata_wait ();
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_uint8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_dprintf ("ata", "sectors=0x%x\n", sec);
|
|
|
|
|
if (sec != 0x5A)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-05 09:41:10 +00:00
|
|
|
|
grub_free(dev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-05-25 19:36:50 +00:00
|
|
|
|
/* 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. */
|
|
|
|
|
|
2008-08-05 09:41:10 +00:00
|
|
|
|
/* Use the IDENTIFY DEVICE command to query the device. */
|
|
|
|
|
if (grub_ata_identify (dev))
|
|
|
|
|
{
|
|
|
|
|
grub_free (dev);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Register the device. */
|
|
|
|
|
for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next);
|
|
|
|
|
*devp = dev;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-17 15:36:41 +00:00
|
|
|
|
static int NESTED_FUNC_ATTR
|
2009-10-14 08:11:59 +00:00
|
|
|
|
grub_ata_pciinit (grub_pci_device_t dev,
|
2009-11-06 21:50:44 +00:00
|
|
|
|
grub_pci_id_t pciid)
|
2008-08-05 09:41:10 +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 regb;
|
|
|
|
|
int i;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
static int controller = 0;
|
2009-11-06 21:50:44 +00:00
|
|
|
|
int cs5536 = 0;
|
|
|
|
|
int nports = 2;
|
2008-08-05 09:41:10 +00:00
|
|
|
|
|
|
|
|
|
/* Read class. */
|
2009-10-14 08:11:59 +00:00
|
|
|
|
addr = grub_pci_make_address (dev, 2);
|
2008-08-05 09:41:10 +00:00
|
|
|
|
class = grub_pci_read (addr);
|
|
|
|
|
|
2009-11-06 21:50:44 +00:00
|
|
|
|
/* AMD CS5536 Southbridge. */
|
|
|
|
|
if (pciid == 0x208f1022)
|
|
|
|
|
{
|
|
|
|
|
cs5536 = 1;
|
|
|
|
|
nports = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-05 09:41:10 +00:00
|
|
|
|
/* Check if this class ID matches that of a PCI IDE Controller. */
|
2009-11-06 21:50:44 +00:00
|
|
|
|
if (!cs5536 && (class >> 16 != 0x0101))
|
2008-08-05 09:41:10 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2009-11-06 21:50:44 +00:00
|
|
|
|
for (i = 0; i < nports; i++)
|
2008-08-05 09:41:10 +00:00
|
|
|
|
{
|
|
|
|
|
/* Set to 0 when the channel operated in compatibility mode. */
|
2009-11-06 21:50:44 +00:00
|
|
|
|
int compat;
|
|
|
|
|
|
|
|
|
|
/* We don't support non-compatibility mode for CS5536. */
|
|
|
|
|
if (cs5536)
|
|
|
|
|
compat = 0;
|
|
|
|
|
else
|
|
|
|
|
compat = (class >> (8 + 2 * i)) & 1;
|
2008-08-05 09:41:10 +00:00
|
|
|
|
|
|
|
|
|
rega = 0;
|
|
|
|
|
regb = 0;
|
|
|
|
|
|
|
|
|
|
/* If the channel is in compatibility mode, just assign the
|
|
|
|
|
default registers. */
|
|
|
|
|
if (compat == 0 && !compat_use[i])
|
|
|
|
|
{
|
|
|
|
|
rega = grub_ata_ioaddress[i];
|
|
|
|
|
regb = grub_ata_ioaddress2[i];
|
2009-01-12 20:23:13 +00:00
|
|
|
|
compat_use[i] = 1;
|
2008-08-05 09:41:10 +00:00
|
|
|
|
}
|
|
|
|
|
else if (compat)
|
|
|
|
|
{
|
|
|
|
|
/* Read the BARs, which either contain a mmapped IO address
|
|
|
|
|
or the IO port address. */
|
2009-10-14 08:11:59 +00:00
|
|
|
|
addr = grub_pci_make_address (dev, 4 + 2 * i);
|
2008-08-05 09:41:10 +00:00
|
|
|
|
bar1 = grub_pci_read (addr);
|
2009-10-14 08:11:59 +00:00
|
|
|
|
addr = grub_pci_make_address (dev, 5 + 2 * i);
|
2008-08-05 09:41:10 +00:00
|
|
|
|
bar2 = grub_pci_read (addr);
|
|
|
|
|
|
|
|
|
|
/* Check if the BARs describe an IO region. */
|
2009-06-10 21:04:23 +00:00
|
|
|
|
if ((bar1 & 1) && (bar2 & 1))
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-05 09:41:10 +00:00
|
|
|
|
rega = bar1 & ~3;
|
|
|
|
|
regb = bar2 & ~3;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
2008-08-05 09:41:10 +00:00
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-08-05 09:41:10 +00:00
|
|
|
|
grub_dprintf ("ata",
|
|
|
|
|
"PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n",
|
2009-10-14 08:11:59 +00:00
|
|
|
|
grub_pci_get_bus (dev), grub_pci_get_device (dev),
|
|
|
|
|
grub_pci_get_function (dev), compat, rega, regb);
|
2008-08-05 09:41:10 +00:00
|
|
|
|
|
|
|
|
|
if (rega && regb)
|
|
|
|
|
{
|
2009-01-16 19:29:41 +00:00
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
grub_ata_device_initialize (controller * 2 + i, 0, rega, regb);
|
2008-11-29 21:05:59 +00:00
|
|
|
|
|
2009-05-04 20:06:05 +00:00
|
|
|
|
/* Most errors raised by grub_ata_device_initialize() are harmless.
|
2008-11-29 21:05:59 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-07 23:37:33 +00:00
|
|
|
|
grub_ata_device_initialize (controller * 2 + i, 1, rega, regb);
|
2008-11-29 21:05:59 +00:00
|
|
|
|
|
|
|
|
|
/* Likewise. */
|
|
|
|
|
if (grub_errno)
|
|
|
|
|
{
|
|
|
|
|
grub_print_error ();
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-07 23:37:33 +00:00
|
|
|
|
controller++;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-05 09:41:10 +00:00
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_initialize (void)
|
|
|
|
|
{
|
|
|
|
|
grub_pci_iterate (grub_ata_pciinit);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
static void
|
|
|
|
|
grub_ata_setlba (struct grub_ata_device *dev, grub_disk_addr_t sector,
|
|
|
|
|
grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, size);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_LBALOW, sector & 0xFF);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, (sector >> 8) & 0xFF);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, (sector >> 16) & 0xFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_setaddress (struct grub_ata_device *dev,
|
|
|
|
|
grub_ata_addressing_t addressing,
|
|
|
|
|
grub_disk_addr_t sector,
|
|
|
|
|
grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
switch (addressing)
|
|
|
|
|
{
|
|
|
|
|
case GRUB_ATA_CHS:
|
|
|
|
|
{
|
|
|
|
|
unsigned int cylinder;
|
|
|
|
|
unsigned int head;
|
|
|
|
|
unsigned int sect;
|
|
|
|
|
|
|
|
|
|
/* Calculate the sector, cylinder and head to use. */
|
|
|
|
|
sect = ((grub_uint32_t) sector % dev->sectors_per_track) + 1;
|
|
|
|
|
cylinder = (((grub_uint32_t) sector / dev->sectors_per_track)
|
|
|
|
|
/ dev->heads);
|
|
|
|
|
head = ((grub_uint32_t) sector / dev->sectors_per_track) % dev->heads;
|
|
|
|
|
|
|
|
|
|
if (sect > dev->sectors_per_track
|
|
|
|
|
|| cylinder > dev->cylinders
|
|
|
|
|
|| head > dev->heads)
|
|
|
|
|
return grub_error (GRUB_ERR_OUT_OF_RANGE,
|
2009-12-25 00:04:51 +00:00
|
|
|
|
"sector %d cannot be addressed "
|
2007-10-31 22:29:20 +00:00
|
|
|
|
"using CHS addressing", sector);
|
|
|
|
|
|
2009-01-16 19:29:41 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_check_ready (dev))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_SECTNUM, sect);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CYLLSB, cylinder & 0xFF);
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case GRUB_ATA_LBA:
|
|
|
|
|
if (size == 256)
|
|
|
|
|
size = 0;
|
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK,
|
|
|
|
|
0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_check_ready (dev))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-01-16 19:29:41 +00:00
|
|
|
|
grub_ata_setlba (dev, sector, size);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GRUB_ATA_LBA48:
|
|
|
|
|
if (size == 65536)
|
|
|
|
|
size = 0;
|
|
|
|
|
|
2009-01-16 19:29:41 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_check_ready (dev))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Set "Previous". */
|
|
|
|
|
grub_ata_setlba (dev, sector >> 24, size >> 8);
|
|
|
|
|
/* Set "Current". */
|
|
|
|
|
grub_ata_setlba (dev, sector, size);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
|
|
|
|
|
grub_size_t size, char *buf, int rw)
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
|
|
|
|
|
|
2009-11-16 15:01:15 +00:00
|
|
|
|
grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", (unsigned long long) size, rw);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
|
|
|
|
grub_ata_addressing_t addressing = dev->addr;
|
|
|
|
|
grub_size_t batch;
|
|
|
|
|
int cmd, cmd_write;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
|
|
|
|
|
{
|
|
|
|
|
batch = 65536;
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS_EXT;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (addressing == GRUB_ATA_LBA48)
|
|
|
|
|
addressing = GRUB_ATA_LBA;
|
|
|
|
|
batch = 256;
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_size_t nsectors = 0;
|
|
|
|
|
while (nsectors < size)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (size - nsectors < batch)
|
|
|
|
|
batch = size - nsectors;
|
|
|
|
|
|
2009-11-16 15:01:15 +00:00
|
|
|
|
grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2009-05-04 20:06:05 +00:00
|
|
|
|
/* Send read/write command. */
|
2007-10-31 22:29:20 +00:00
|
|
|
|
if (grub_ata_setaddress (dev, addressing, sector, batch))
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
unsigned sect;
|
|
|
|
|
for (sect = 0; sect < batch; sect++)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* Wait for !BSY, DRQ. */
|
|
|
|
|
if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA))
|
2008-08-07 23:37:33 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
/* 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);
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2007-11-03 12:25:19 +00:00
|
|
|
|
buf += GRUB_DISK_SECTOR_SIZE;
|
|
|
|
|
}
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (rw)
|
|
|
|
|
{
|
|
|
|
|
/* Check for write error. */
|
|
|
|
|
if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
|
|
|
|
return grub_errno;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS)
|
|
|
|
|
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
2007-11-03 13:12:52 +00:00
|
|
|
|
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
2007-11-03 12:25:19 +00:00
|
|
|
|
}
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
|
|
|
|
sector += batch;
|
|
|
|
|
nsectors += batch;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
grub_ata_iterate (int (*hook) (const char *name))
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata_device *dev;
|
|
|
|
|
|
|
|
|
|
for (dev = grub_ata_devices; dev; dev = dev->next)
|
|
|
|
|
{
|
|
|
|
|
char devname[5];
|
|
|
|
|
grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device);
|
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
if (dev->atapi)
|
|
|
|
|
continue;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
if (hook (devname))
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_open (const char *name, grub_disk_t disk)
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata_device *dev;
|
|
|
|
|
|
|
|
|
|
for (dev = grub_ata_devices; dev; dev = dev->next)
|
|
|
|
|
{
|
|
|
|
|
char devname[5];
|
|
|
|
|
grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device);
|
|
|
|
|
if (grub_strcmp (name, devname) == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! dev)
|
2009-12-24 22:53:05 +00:00
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
if (dev->atapi)
|
2008-08-27 15:05:00 +00:00
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
|
|
|
|
|
|
|
|
|
|
disk->total_sectors = dev->size;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-04-02 04:25:41 +00:00
|
|
|
|
disk->id = (unsigned long) dev;
|
2009-06-10 21:04:23 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
disk->has_partitions = 1;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
disk->data = dev;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
grub_ata_close (grub_disk_t disk __attribute__((unused)))
|
|
|
|
|
{
|
2009-06-10 21:04:23 +00:00
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
static grub_err_t
|
|
|
|
|
grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector,
|
|
|
|
|
grub_size_t size, char *buf)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
return grub_ata_readwrite (disk, sector, size, buf, 0);
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_ata_write (grub_disk_t disk,
|
|
|
|
|
grub_disk_addr_t sector,
|
|
|
|
|
grub_size_t size,
|
|
|
|
|
const char *buf)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
return grub_ata_readwrite (disk, sector, size, (char *) buf, 1);
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
static struct grub_disk_dev grub_atadisk_dev =
|
|
|
|
|
{
|
|
|
|
|
.name = "ATA",
|
|
|
|
|
.id = GRUB_DISK_DEVICE_ATA_ID,
|
|
|
|
|
.iterate = grub_ata_iterate,
|
|
|
|
|
.open = grub_ata_open,
|
|
|
|
|
.close = grub_ata_close,
|
|
|
|
|
.read = grub_ata_read,
|
|
|
|
|
.write = grub_ata_write,
|
|
|
|
|
.next = 0
|
|
|
|
|
};
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ATAPI code. */
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
grub_atapi_iterate (int (*hook) (const char *name, int luns))
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata_device *dev;
|
|
|
|
|
|
|
|
|
|
for (dev = grub_ata_devices; dev; dev = dev->next)
|
|
|
|
|
{
|
|
|
|
|
char devname[7];
|
|
|
|
|
grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device);
|
|
|
|
|
|
|
|
|
|
if (! dev->atapi)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (hook (devname, 1))
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_atapi_read (struct grub_scsi *scsi,
|
|
|
|
|
grub_size_t cmdsize __attribute__((unused)),
|
|
|
|
|
char *cmd, grub_size_t size, char *buf)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-11-16 15:01:15 +00:00
|
|
|
|
grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size);
|
2009-01-19 20:39:57 +00:00
|
|
|
|
|
|
|
|
|
if (grub_atapi_packet (dev, cmd, size))
|
2008-08-27 15:05:00 +00:00
|
|
|
|
return grub_errno;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-01-19 20:39:57 +00:00
|
|
|
|
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)))
|
2009-12-24 22:53:05 +00:00
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count");
|
2009-01-19 20:39:57 +00:00
|
|
|
|
|
|
|
|
|
/* Read the data. */
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_ata_pio_read (dev, buf + nread, cnt);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-01-19 20:39:57 +00:00
|
|
|
|
if (cnt & 1)
|
|
|
|
|
buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA));
|
|
|
|
|
|
|
|
|
|
nread += cnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
2008-08-27 15:05:00 +00:00
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
static grub_err_t
|
2009-01-19 20:39:57 +00:00
|
|
|
|
grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_size_t cmdsize __attribute__((unused)),
|
2009-01-19 20:39:57 +00:00
|
|
|
|
char *cmd __attribute__((unused)),
|
|
|
|
|
grub_size_t size __attribute__((unused)),
|
|
|
|
|
char *buf __attribute__((unused)))
|
2008-08-27 15:05:00 +00:00
|
|
|
|
{
|
2009-01-19 20:39:57 +00:00
|
|
|
|
// XXX: scsi.mod does not use write yet.
|
|
|
|
|
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented");
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_atapi_open (const char *name, struct grub_scsi *scsi)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
struct grub_ata_device *dev;
|
2008-09-22 03:49:26 +00:00
|
|
|
|
struct grub_ata_device *devfnd = 0;
|
2008-07-03 20:53:00 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
for (dev = grub_ata_devices; dev; dev = dev->next)
|
|
|
|
|
{
|
|
|
|
|
char devname[7];
|
|
|
|
|
grub_sprintf (devname, "ata%d", dev->port * 2 + dev->device);
|
2008-07-03 20:53:00 +00:00
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
if (!grub_strcmp (devname, name))
|
|
|
|
|
{
|
|
|
|
|
devfnd = dev;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
grub_dprintf ("ata", "opening ATAPI dev `%s'\n", name);
|
|
|
|
|
|
|
|
|
|
if (! devfnd)
|
2009-12-24 22:53:05 +00:00
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
|
|
|
|
scsi->data = devfnd;
|
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
static void
|
|
|
|
|
grub_atapi_close (struct grub_scsi *scsi)
|
|
|
|
|
{
|
|
|
|
|
grub_free (scsi->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct grub_scsi_dev grub_atapi_dev =
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
.name = "ATAPI",
|
|
|
|
|
.iterate = grub_atapi_iterate,
|
|
|
|
|
.open = grub_atapi_open,
|
|
|
|
|
.close = grub_atapi_close,
|
|
|
|
|
.read = grub_atapi_read,
|
|
|
|
|
.write = grub_atapi_write
|
2009-06-10 21:04:23 +00:00
|
|
|
|
};
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GRUB_MOD_INIT(ata)
|
|
|
|
|
{
|
2007-11-05 16:15:27 +00:00
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2009-06-10 21:04:23 +00:00
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* ATA initialization. */
|
|
|
|
|
grub_ata_initialize ();
|
|
|
|
|
|
|
|
|
|
grub_disk_dev_register (&grub_atadisk_dev);
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
|
|
|
|
/* ATAPI devices are handled by scsi.mod. */
|
|
|
|
|
grub_scsi_dev_register (&grub_atapi_dev);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GRUB_MOD_FINI(ata)
|
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
grub_scsi_dev_unregister (&grub_atapi_dev);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_disk_dev_unregister (&grub_atadisk_dev);
|
|
|
|
|
}
|