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>
|
2008-08-27 15:05:00 +00:00
|
|
|
|
#include <grub/scsi.h>
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2011-04-11 21:01:51 +00:00
|
|
|
|
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static grub_ata_dev_t grub_ata_dev_list;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
|
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
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_ata_strncpy (grub_uint16_t *dst16, grub_uint16_t *src16, grub_size_t len)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < len / 2; i++)
|
2012-06-06 10:22:32 +00:00
|
|
|
|
*(dst16++) = grub_swap_bytes16 (*(src16++));
|
2012-04-18 21:21:38 +00:00
|
|
|
|
*dst16 = 0;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
static void
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_ata_dumpinfo (struct grub_ata *dev, grub_uint16_t *info)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_uint16_t text[21];
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
/* The device information was read, dump it for debugging. */
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_ata_strncpy (text, info + 10, 20);
|
|
|
|
|
grub_dprintf ("ata", "Serial: %s\n", (char *) text);
|
|
|
|
|
grub_ata_strncpy (text, info + 23, 8);
|
|
|
|
|
grub_dprintf ("ata", "Firmware: %s\n", (char *) text);
|
|
|
|
|
grub_ata_strncpy (text, info + 27, 40);
|
|
|
|
|
grub_dprintf ("ata", "Model: %s\n", (char *) 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);
|
2011-07-25 06:14:34 +00:00
|
|
|
|
grub_dprintf ("ata", "Sector size: %u\n", 1U << dev->log_sector_size);
|
2008-07-06 01:24:19 +00:00
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_atapi_identify (struct grub_ata *dev)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_disk_ata_pass_through_parms parms;
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_uint16_t *info;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_err_t err;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
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;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_memset (&parms, 0, sizeof (parms));
|
2010-12-25 02:47:51 +00:00
|
|
|
|
parms.taskfile.disk = 0xE0;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE;
|
2010-12-24 15:54:33 +00:00
|
|
|
|
parms.size = GRUB_DISK_SECTOR_SIZE;
|
|
|
|
|
parms.buffer = info;
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
err = dev->dev->readwrite (dev, &parms, *dev->present);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (err)
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
2011-05-16 15:17:04 +00:00
|
|
|
|
*dev->present = 0;
|
|
|
|
|
return err;
|
2008-08-07 23:37:33 +00:00
|
|
|
|
}
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (parms.size != GRUB_DISK_SECTOR_SIZE)
|
2008-08-07 23:37:33 +00:00
|
|
|
|
{
|
2011-05-16 15:17:04 +00:00
|
|
|
|
*dev->present = 0;
|
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
|
|
|
|
|
"device cannot be identified");
|
2008-08-07 23:37:33 +00:00
|
|
|
|
}
|
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
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_identify (struct grub_ata *dev)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_disk_ata_pass_through_parms parms;
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_uint64_t *info64;
|
|
|
|
|
grub_uint32_t *info32;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_uint16_t *info16;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_err_t err;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2013-11-03 12:52:36 +00:00
|
|
|
|
if (dev->atapi)
|
|
|
|
|
return grub_atapi_identify (dev);
|
|
|
|
|
|
2011-12-13 13:57:46 +00:00
|
|
|
|
info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE);
|
|
|
|
|
info32 = (grub_uint32_t *) info64;
|
|
|
|
|
info16 = (grub_uint16_t *) info64;
|
|
|
|
|
if (! info16)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
return grub_errno;
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_memset (&parms, 0, sizeof (parms));
|
2011-12-13 13:57:46 +00:00
|
|
|
|
parms.buffer = info16;
|
2010-12-24 15:54:33 +00:00
|
|
|
|
parms.size = GRUB_DISK_SECTOR_SIZE;
|
2010-12-25 02:47:51 +00:00
|
|
|
|
parms.taskfile.disk = 0xE0;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms.taskfile.cmd = GRUB_ATA_CMD_IDENTIFY_DEVICE;
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
err = dev->dev->readwrite (dev, &parms, *dev->present);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 16:10:41 +00:00
|
|
|
|
if (err || parms.size != GRUB_DISK_SECTOR_SIZE)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_uint8_t sts = parms.taskfile.status;
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_free (info16);
|
2009-05-25 19:36:50 +00:00
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
if ((sts & (GRUB_ATA_STATUS_BUSY | GRUB_ATA_STATUS_DRQ
|
2013-11-03 12:52:36 +00:00
|
|
|
|
| GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_ERR
|
2010-12-24 15:07:53 +00:00
|
|
|
|
&& (parms.taskfile.error & 0x04 /* ABRT */))
|
2009-05-25 19:36:50 +00:00
|
|
|
|
/* Device without ATA IDENTIFY, try ATAPI. */
|
|
|
|
|
return grub_atapi_identify (dev);
|
|
|
|
|
|
|
|
|
|
else if (sts == 0x00)
|
2011-05-13 19:41:18 +00:00
|
|
|
|
{
|
2011-05-16 15:17:04 +00:00
|
|
|
|
*dev->present = 0;
|
2011-05-13 19:41:18 +00:00
|
|
|
|
/* No device, return error but don't print message. */
|
|
|
|
|
return GRUB_ERR_UNKNOWN_DEVICE;
|
|
|
|
|
}
|
2009-01-16 19:29:41 +00:00
|
|
|
|
else
|
2011-05-13 19:41:18 +00:00
|
|
|
|
{
|
2011-05-16 15:17:04 +00:00
|
|
|
|
*dev->present = 0;
|
2011-05-13 19:41:18 +00:00
|
|
|
|
/* Other Error. */
|
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
|
|
|
|
|
"device cannot be identified");
|
|
|
|
|
}
|
2009-05-25 19:36:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
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. */
|
2012-06-06 10:22:32 +00:00
|
|
|
|
if (info16[49] & grub_cpu_to_le16_compile_time ((1 << 9)))
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
/* Check if LBA48 is supported. */
|
2012-06-06 10:22:32 +00:00
|
|
|
|
if (info16[83] & grub_cpu_to_le16_compile_time ((1 << 10)))
|
2007-10-31 22:29:20 +00:00
|
|
|
|
dev->addr = GRUB_ATA_LBA48;
|
|
|
|
|
else
|
|
|
|
|
dev->addr = GRUB_ATA_LBA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the amount of sectors. */
|
|
|
|
|
if (dev->addr != GRUB_ATA_LBA48)
|
2011-12-13 13:57:46 +00:00
|
|
|
|
dev->size = grub_le_to_cpu32 (info32[30]);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
else
|
2011-12-13 13:57:46 +00:00
|
|
|
|
dev->size = grub_le_to_cpu64 (info64[25]);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2012-06-06 10:22:32 +00:00
|
|
|
|
if (info16[106] & grub_cpu_to_le16_compile_time ((1 << 12)))
|
2011-07-25 06:14:34 +00:00
|
|
|
|
{
|
|
|
|
|
grub_uint32_t secsize;
|
2011-12-13 13:57:46 +00:00
|
|
|
|
secsize = grub_le_to_cpu32 (grub_get_unaligned32 (&info16[117]));
|
2011-07-25 06:14:34 +00:00
|
|
|
|
if (secsize & (secsize - 1) || !secsize
|
|
|
|
|
|| secsize > 1048576)
|
|
|
|
|
secsize = 256;
|
|
|
|
|
for (dev->log_sector_size = 0;
|
|
|
|
|
(1U << dev->log_sector_size) < secsize;
|
|
|
|
|
dev->log_sector_size++);
|
|
|
|
|
dev->log_sector_size++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
dev->log_sector_size = 9;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Read CHS information. */
|
2012-06-06 10:22:32 +00:00
|
|
|
|
dev->cylinders = grub_le_to_cpu16 (info16[1]);
|
|
|
|
|
dev->heads = grub_le_to_cpu16 (info16[3]);
|
|
|
|
|
dev->sectors_per_track = grub_le_to_cpu16 (info16[6]);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_ata_dumpinfo (dev, info16);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2011-12-13 13:57:46 +00:00
|
|
|
|
grub_free (info16);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_setaddress (struct grub_ata *dev,
|
|
|
|
|
struct grub_disk_ata_pass_through_parms *parms,
|
2007-10-31 22:29:20 +00:00
|
|
|
|
grub_disk_addr_t sector,
|
2010-12-25 14:47:23 +00:00
|
|
|
|
grub_size_t size,
|
|
|
|
|
grub_ata_addressing_t addressing)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
|
|
|
|
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);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
2010-12-25 02:47:51 +00:00
|
|
|
|
parms->taskfile.disk = 0xE0 | head;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.sectnum = sect;
|
|
|
|
|
parms->taskfile.cyllsb = cylinder & 0xFF;
|
|
|
|
|
parms->taskfile.cylmsb = cylinder >> 8;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case GRUB_ATA_LBA:
|
|
|
|
|
if (size == 256)
|
|
|
|
|
size = 0;
|
2010-12-25 02:47:51 +00:00
|
|
|
|
parms->taskfile.disk = 0xE0 | ((sector >> 24) & 0x0F);
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.sectors = size;
|
|
|
|
|
parms->taskfile.lba_low = sector & 0xFF;
|
|
|
|
|
parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
|
|
|
|
|
parms->taskfile.lba_high = (sector >> 16) & 0xFF;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GRUB_ATA_LBA48:
|
|
|
|
|
if (size == 65536)
|
|
|
|
|
size = 0;
|
|
|
|
|
|
2010-12-25 02:47:51 +00:00
|
|
|
|
parms->taskfile.disk = 0xE0;
|
2009-01-22 20:15:05 +00:00
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Set "Previous". */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.sectors = size & 0xFF;
|
|
|
|
|
parms->taskfile.lba_low = sector & 0xFF;
|
|
|
|
|
parms->taskfile.lba_mid = (sector >> 8) & 0xFF;
|
|
|
|
|
parms->taskfile.lba_high = (sector >> 16) & 0xFF;
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
/* Set "Current". */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms->taskfile.sectors48 = (size >> 8) & 0xFF;
|
|
|
|
|
parms->taskfile.lba48_low = (sector >> 24) & 0xFF;
|
|
|
|
|
parms->taskfile.lba48_mid = (sector >> 32) & 0xFF;
|
|
|
|
|
parms->taskfile.lba48_high = (sector >> 40) & 0xFF;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_ata *ata = disk->data;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_addressing_t addressing = ata->addr;
|
2009-01-22 20:15:05 +00:00
|
|
|
|
grub_size_t batch;
|
|
|
|
|
int cmd, cmd_write;
|
2012-01-29 18:00:30 +00:00
|
|
|
|
grub_size_t nsectors = 0;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n",
|
|
|
|
|
(unsigned long long) size, rw);
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
|
|
|
|
|
{
|
2010-12-25 01:53:20 +00:00
|
|
|
|
if (ata->dma)
|
|
|
|
|
{
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS_DMA_EXT;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA_EXT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS_EXT;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_EXT;
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (addressing == GRUB_ATA_LBA48)
|
|
|
|
|
addressing = GRUB_ATA_LBA;
|
2010-12-25 01:53:20 +00:00
|
|
|
|
if (ata->dma)
|
|
|
|
|
{
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS_DMA;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS_DMA;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cmd = GRUB_ATA_CMD_READ_SECTORS;
|
|
|
|
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
|
|
|
|
|
}
|
2012-01-29 18:00:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-01 22:28:03 +00:00
|
|
|
|
if (addressing != GRUB_ATA_CHS)
|
|
|
|
|
batch = 256;
|
|
|
|
|
else
|
|
|
|
|
batch = 1;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2009-01-22 20:15:05 +00:00
|
|
|
|
while (nsectors < size)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_disk_ata_pass_through_parms parms;
|
|
|
|
|
grub_err_t err;
|
|
|
|
|
|
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);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_memset (&parms, 0, sizeof (parms));
|
2010-12-25 14:47:23 +00:00
|
|
|
|
grub_ata_setaddress (ata, &parms, sector, batch, addressing);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
parms.taskfile.cmd = (! rw ? cmd : cmd_write);
|
|
|
|
|
parms.buffer = buf;
|
2011-07-25 06:14:34 +00:00
|
|
|
|
parms.size = batch << ata->log_sector_size;
|
2010-12-25 16:40:00 +00:00
|
|
|
|
parms.write = rw;
|
2010-12-25 01:53:20 +00:00
|
|
|
|
if (ata->dma)
|
|
|
|
|
parms.dma = 1;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
err = ata->dev->readwrite (ata, &parms, 0);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2011-07-25 06:14:34 +00:00
|
|
|
|
if (parms.size != batch << ata->log_sector_size)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "incomplete read");
|
2011-07-25 06:14:34 +00:00
|
|
|
|
buf += batch << ata->log_sector_size;
|
2009-01-22 20:15:05 +00:00
|
|
|
|
sector += batch;
|
|
|
|
|
nsectors += batch;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static inline void
|
|
|
|
|
grub_ata_real_close (struct grub_ata *ata)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (ata->dev->close)
|
|
|
|
|
ata->dev->close (ata);
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static struct grub_ata *
|
|
|
|
|
grub_ata_real_open (int id, int bus)
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata *ata;
|
|
|
|
|
grub_ata_dev_t p;
|
2011-04-21 22:09:07 +00:00
|
|
|
|
|
2013-11-03 12:52:36 +00:00
|
|
|
|
ata = grub_zalloc (sizeof (*ata));
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (!ata)
|
|
|
|
|
return NULL;
|
|
|
|
|
for (p = grub_ata_dev_list; p; p = p->next)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-03-14 22:18:13 +00:00
|
|
|
|
grub_err_t err;
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (p->open (id, bus, ata))
|
2010-03-14 22:18:13 +00:00
|
|
|
|
{
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-12-24 15:07:53 +00:00
|
|
|
|
ata->dev = p;
|
|
|
|
|
/* Use the IDENTIFY DEVICE command to query the device. */
|
|
|
|
|
err = grub_ata_identify (ata);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2013-04-24 12:47:14 +00:00
|
|
|
|
if (!grub_errno)
|
|
|
|
|
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_free (ata);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return ata;
|
|
|
|
|
}
|
|
|
|
|
grub_free (ata);
|
|
|
|
|
grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATA device");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
/* Context for grub_ata_iterate. */
|
|
|
|
|
struct grub_ata_iterate_ctx
|
|
|
|
|
{
|
|
|
|
|
grub_disk_dev_iterate_hook_t hook;
|
|
|
|
|
void *hook_data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Helper for grub_ata_iterate. */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static int
|
2013-01-20 15:52:15 +00:00
|
|
|
|
grub_ata_iterate_iter (int id, int bus, void *data)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
2013-01-20 15:52:15 +00:00
|
|
|
|
struct grub_ata_iterate_ctx *ctx = data;
|
|
|
|
|
struct grub_ata *ata;
|
|
|
|
|
int ret;
|
|
|
|
|
char devname[40];
|
2009-12-29 09:04:06 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
ata = grub_ata_real_open (id, bus);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
if (!ata)
|
|
|
|
|
{
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (ata->atapi)
|
|
|
|
|
{
|
|
|
|
|
grub_ata_real_close (ata);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
grub_snprintf (devname, sizeof (devname),
|
|
|
|
|
"%s%d", grub_scsi_names[id], bus);
|
|
|
|
|
ret = ctx->hook (devname, ctx->hook_data);
|
|
|
|
|
grub_ata_real_close (ata);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
static int
|
|
|
|
|
grub_ata_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
|
|
|
|
|
grub_disk_pull_t pull)
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata_iterate_ctx ctx = { hook, hook_data };
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_dev_t p;
|
|
|
|
|
|
|
|
|
|
for (p = grub_ata_dev_list; p; p = p->next)
|
2013-01-20 15:52:15 +00:00
|
|
|
|
if (p->iterate && p->iterate (grub_ata_iterate_iter, &ctx, pull))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return 1;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static grub_err_t
|
2011-07-07 19:46:25 +00:00
|
|
|
|
grub_ata_open (const char *name, grub_disk_t disk)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
unsigned id, bus;
|
|
|
|
|
struct grub_ata *ata;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
for (id = 0; id < GRUB_SCSI_NUM_SUBSYSTEMS; id++)
|
|
|
|
|
if (grub_strncmp (grub_scsi_names[id], name,
|
|
|
|
|
grub_strlen (grub_scsi_names[id])) == 0
|
|
|
|
|
&& grub_isdigit (name[grub_strlen (grub_scsi_names[id])]))
|
|
|
|
|
break;
|
|
|
|
|
if (id == GRUB_SCSI_NUM_SUBSYSTEMS)
|
2008-08-27 15:05:00 +00:00
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
|
2010-12-24 15:07:53 +00:00
|
|
|
|
bus = grub_strtoul (name + grub_strlen (grub_scsi_names[id]), 0, 0);
|
|
|
|
|
ata = grub_ata_real_open (id, bus);
|
|
|
|
|
if (!ata)
|
|
|
|
|
return grub_errno;
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (ata->atapi)
|
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
|
2010-03-14 22:18:13 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
disk->total_sectors = ata->size;
|
2013-11-01 22:28:03 +00:00
|
|
|
|
disk->max_agglomerate = (ata->maxbuffer >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
|
|
|
|
|
if (disk->max_agglomerate > (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size)))
|
|
|
|
|
disk->max_agglomerate = (256U >> (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS - ata->log_sector_size));
|
|
|
|
|
|
2011-07-25 06:14:34 +00:00
|
|
|
|
disk->log_sector_size = ata->log_sector_size;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
disk->id = grub_make_scsi_id (id, bus, 0);
|
2009-06-10 21:04:23 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
disk->data = ata;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_close (grub_disk_t disk)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_ata *ata = disk->data;
|
|
|
|
|
grub_ata_real_close (ata);
|
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. */
|
|
|
|
|
|
2007-10-31 22:29:20 +00:00
|
|
|
|
static grub_err_t
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_atapi_read (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
|
|
|
|
|
grub_size_t size, char *buf)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_ata *dev = scsi->data;
|
|
|
|
|
struct grub_disk_ata_pass_through_parms parms;
|
|
|
|
|
grub_err_t err;
|
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);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
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;
|
|
|
|
|
|
2011-05-16 15:17:04 +00:00
|
|
|
|
err = dev->dev->readwrite (dev, &parms, 0);
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2009-01-19 20:39:57 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
if (parms.size != size)
|
|
|
|
|
return grub_error (GRUB_ERR_READ_ERROR, "incomplete ATAPI read");
|
2009-01-19 20:39:57 +00:00
|
|
|
|
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)),
|
2012-01-30 11:26:11 +00:00
|
|
|
|
const 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
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_atapi_open (int id, int bus, struct grub_scsi *scsi)
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2010-12-24 15:07:53 +00:00
|
|
|
|
struct grub_ata *ata;
|
2008-07-03 20:53:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
ata = grub_ata_real_open (id, bus);
|
|
|
|
|
if (!ata)
|
|
|
|
|
return grub_errno;
|
|
|
|
|
|
|
|
|
|
if (! ata->atapi)
|
|
|
|
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
scsi->data = ata;
|
2010-12-24 16:10:41 +00:00
|
|
|
|
scsi->luns = 1;
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return GRUB_ERR_NONE;
|
|
|
|
|
}
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
/* Context for grub_atapi_iterate. */
|
|
|
|
|
struct grub_atapi_iterate_ctx
|
|
|
|
|
{
|
|
|
|
|
grub_scsi_dev_iterate_hook_t hook;
|
|
|
|
|
void *hook_data;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Helper for grub_atapi_iterate. */
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static int
|
2013-01-20 15:52:15 +00:00
|
|
|
|
grub_atapi_iterate_iter (int id, int bus, void *data)
|
2010-12-24 15:07:53 +00:00
|
|
|
|
{
|
2013-01-20 15:52:15 +00:00
|
|
|
|
struct grub_atapi_iterate_ctx *ctx = data;
|
|
|
|
|
struct grub_ata *ata;
|
|
|
|
|
int ret;
|
2010-03-14 22:18:13 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
ata = grub_ata_real_open (id, bus);
|
2010-03-14 22:18:13 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
if (!ata)
|
|
|
|
|
{
|
|
|
|
|
grub_errno = GRUB_ERR_NONE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (!ata->atapi)
|
|
|
|
|
{
|
|
|
|
|
grub_ata_real_close (ata);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
ret = ctx->hook (id, bus, 1, ctx->hook_data);
|
|
|
|
|
grub_ata_real_close (ata);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2010-12-24 15:07:53 +00:00
|
|
|
|
|
2013-01-20 15:52:15 +00:00
|
|
|
|
static int
|
|
|
|
|
grub_atapi_iterate (grub_scsi_dev_iterate_hook_t hook, void *hook_data,
|
|
|
|
|
grub_disk_pull_t pull)
|
|
|
|
|
{
|
|
|
|
|
struct grub_atapi_iterate_ctx ctx = { hook, hook_data };
|
2010-12-24 15:07:53 +00:00
|
|
|
|
grub_ata_dev_t p;
|
|
|
|
|
|
|
|
|
|
for (p = grub_ata_dev_list; p; p = p->next)
|
2013-01-20 15:52:15 +00:00
|
|
|
|
if (p->iterate && p->iterate (grub_atapi_iterate_iter, &ctx, pull))
|
2010-12-24 15:07:53 +00:00
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
static void
|
|
|
|
|
grub_atapi_close (grub_scsi_t disk)
|
|
|
|
|
{
|
|
|
|
|
struct grub_ata *ata = disk->data;
|
|
|
|
|
grub_ata_real_close (ata);
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
void
|
|
|
|
|
grub_ata_dev_register (grub_ata_dev_t dev)
|
|
|
|
|
{
|
|
|
|
|
dev->next = grub_ata_dev_list;
|
|
|
|
|
grub_ata_dev_list = dev;
|
2007-10-31 22:29:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-24 15:07:53 +00:00
|
|
|
|
void
|
|
|
|
|
grub_ata_dev_unregister (grub_ata_dev_t dev)
|
|
|
|
|
{
|
|
|
|
|
grub_ata_dev_t *p, q;
|
|
|
|
|
|
|
|
|
|
for (p = &grub_ata_dev_list, q = *p; q; p = &(q->next), q = q->next)
|
|
|
|
|
if (q == dev)
|
|
|
|
|
{
|
|
|
|
|
*p = q->next;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-08-27 15:05:00 +00:00
|
|
|
|
|
|
|
|
|
static struct grub_scsi_dev grub_atapi_dev =
|
2007-10-31 22:29:20 +00:00
|
|
|
|
{
|
2008-08-27 15:05:00 +00:00
|
|
|
|
.iterate = grub_atapi_iterate,
|
|
|
|
|
.open = grub_atapi_open,
|
2010-12-24 15:07:53 +00:00
|
|
|
|
.close = grub_atapi_close,
|
2008-08-27 15:05:00 +00:00
|
|
|
|
.read = grub_atapi_read,
|
2010-12-24 15:07:53 +00:00
|
|
|
|
.write = grub_atapi_write,
|
|
|
|
|
.next = 0
|
2009-06-10 21:04:23 +00:00
|
|
|
|
};
|
2007-10-31 22:29:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GRUB_MOD_INIT(ata)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|