2009-01-22 Christian Franke <franke@computer.org>
* disk/ata.c (grub_ata_wait_status): Replace by ... (grub_ata_wait_not_busy): ... this function. Checks only BSY bit, other status bits may be invalid while BSY is asserted. (grub_ata_check_ready): New function. (grub_ata_cmd): Removed. (grub_ata_wait_drq): New function. (grub_ata_strncpy): Remove inline. (grub_ata_pio_read): Reduce to actual block transfer. BSY wait and error check now done by grub_ata_wait_drq (). (grub_ata_pio_write): Likewise. (grub_atapi_identify): Set DEV before check for !BSY. Use grub_ata_wait_drq () to wait for data. (grub_ata_device_initialize): Add status register check to detect missing SATA slave devices. Add debug messages. (grub_atapi_wait_drq): Use grub_ata_wait_not_busy (). (grub_atapi_packet): Set DEV before check for !BSY. Replace transfer loop by grub_ata_pio_write (). (grub_ata_identify): Set DEV before check for !BSY. Use grub_ata_wait_drq () to wait for data. (grub_ata_setaddress): Set DEV before check for !BSY. (grub_ata_readwrite): Remove duplicate code, handle batch/rest and read/write in one loop. Fix invalid command on write. Fix incomplete command on (size % batch) == 0. Add missing error check after write of last block. Add debug messages. (grub_atapi_read): Replace transfer loop by grub_ata_pio_read ().
This commit is contained in:
parent
59a64ef6da
commit
3138b44c90
2 changed files with 160 additions and 163 deletions
28
ChangeLog
28
ChangeLog
|
@ -1,3 +1,31 @@
|
||||||
|
2009-01-22 Christian Franke <franke@computer.org>
|
||||||
|
|
||||||
|
* disk/ata.c (grub_ata_wait_status): Replace by ...
|
||||||
|
(grub_ata_wait_not_busy): ... this function. Checks only BSY bit,
|
||||||
|
other status bits may be invalid while BSY is asserted.
|
||||||
|
(grub_ata_check_ready): New function.
|
||||||
|
(grub_ata_cmd): Removed.
|
||||||
|
(grub_ata_wait_drq): New function.
|
||||||
|
(grub_ata_strncpy): Remove inline.
|
||||||
|
(grub_ata_pio_read): Reduce to actual block transfer. BSY wait
|
||||||
|
and error check now done by grub_ata_wait_drq ().
|
||||||
|
(grub_ata_pio_write): Likewise.
|
||||||
|
(grub_atapi_identify): Set DEV before check for !BSY. Use
|
||||||
|
grub_ata_wait_drq () to wait for data.
|
||||||
|
(grub_ata_device_initialize): Add status register check to
|
||||||
|
detect missing SATA slave devices. Add debug messages.
|
||||||
|
(grub_atapi_wait_drq): Use grub_ata_wait_not_busy ().
|
||||||
|
(grub_atapi_packet): Set DEV before check for !BSY. Replace
|
||||||
|
transfer loop by grub_ata_pio_write ().
|
||||||
|
(grub_ata_identify): Set DEV before check for !BSY. Use
|
||||||
|
grub_ata_wait_drq () to wait for data.
|
||||||
|
(grub_ata_setaddress): Set DEV before check for !BSY.
|
||||||
|
(grub_ata_readwrite): Remove duplicate code, handle batch/rest and
|
||||||
|
read/write in one loop. Fix invalid command on write. Fix incomplete
|
||||||
|
command on (size % batch) == 0. Add missing error check after write of
|
||||||
|
last block. Add debug messages.
|
||||||
|
(grub_atapi_read): Replace transfer loop by grub_ata_pio_read ().
|
||||||
|
|
||||||
2009-01-19 Christian Franke <franke@computer.org>
|
2009-01-19 Christian Franke <franke@computer.org>
|
||||||
|
|
||||||
* disk/ata.c (GRUB_ATAPI_REG_*): New defines.
|
* disk/ata.c (GRUB_ATAPI_REG_*): New defines.
|
||||||
|
|
295
disk/ata.c
295
disk/ata.c
|
@ -152,25 +152,29 @@ grub_ata_regget2 (struct grub_ata_device *dev, int reg)
|
||||||
return grub_inb (dev->ioaddress2 + reg);
|
return grub_inb (dev->ioaddress2 + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline grub_err_t
|
/* Wait for !BSY. */
|
||||||
grub_ata_wait_status (struct grub_ata_device *dev,
|
static grub_err_t
|
||||||
grub_uint8_t maskset, grub_uint8_t maskclear,
|
grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
|
||||||
int milliseconds)
|
|
||||||
{
|
{
|
||||||
int i;
|
/* 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);
|
||||||
|
|
||||||
for (i = 0; i < milliseconds; i++)
|
int i = 1;
|
||||||
|
while (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
|
||||||
{
|
{
|
||||||
grub_uint8_t reg;
|
if (i >= milliseconds)
|
||||||
|
{
|
||||||
reg = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
grub_dprintf ("ata", "timeout: %dms\n", milliseconds);
|
||||||
if ((reg & maskset) == maskset && (reg & maskclear) == 0)
|
return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout");
|
||||||
return GRUB_ERR_NONE;
|
}
|
||||||
|
|
||||||
grub_millisleep (1);
|
grub_millisleep (1);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return grub_error (GRUB_ERR_TIMEOUT, "ata timeout");
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
|
@ -179,19 +183,42 @@ grub_ata_wait (void)
|
||||||
grub_millisleep (50);
|
grub_millisleep (50);
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_err_t
|
/* Check for !BSY before issuing a new command. */
|
||||||
grub_ata_cmd (struct grub_ata_device *dev, int cmd)
|
static inline grub_err_t
|
||||||
|
grub_ata_check_ready (struct grub_ata_device *dev)
|
||||||
{
|
{
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
|
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
|
||||||
|
return grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for !BSY, DRQ. */
|
||||||
|
static grub_err_t
|
||||||
|
grub_ata_wait_drq (struct grub_ata_device *dev, int rw,
|
||||||
|
int milliseconds)
|
||||||
|
{
|
||||||
|
if (grub_ata_wait_not_busy (dev, milliseconds))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd);
|
/* !DRQ implies error condition. */
|
||||||
|
grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
||||||
|
if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
||||||
|
!= GRUB_ATA_STATUS_DRQ)
|
||||||
|
{
|
||||||
|
grub_dprintf ("ata", "ata error: status=0x%x, error=0x%x\n",
|
||||||
|
sts, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
|
||||||
|
if (! rw)
|
||||||
|
return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
|
||||||
|
else
|
||||||
|
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
||||||
|
}
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Byteorder has to be changed before strings can be read. */
|
/* Byteorder has to be changed before strings can be read. */
|
||||||
static inline void
|
static void
|
||||||
grub_ata_strncpy (char *dst, char *src, grub_size_t len)
|
grub_ata_strncpy (char *dst, char *src, grub_size_t len)
|
||||||
{
|
{
|
||||||
grub_uint16_t *src16 = (grub_uint16_t *) src;
|
grub_uint16_t *src16 = (grub_uint16_t *) src;
|
||||||
|
@ -203,52 +230,26 @@ grub_ata_strncpy (char *dst, char *src, grub_size_t len)
|
||||||
dst[len] = '\0';
|
dst[len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_err_t
|
static void
|
||||||
grub_ata_pio_read (struct grub_ata_device *dev, char *buf,
|
grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size)
|
||||||
grub_size_t size, int milliseconds)
|
|
||||||
{
|
{
|
||||||
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
|
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
|
|
||||||
return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
|
|
||||||
|
|
||||||
/* Wait until the data is available. */
|
|
||||||
if (grub_ata_wait_status (dev, GRUB_ATA_STATUS_DRQ, 0, milliseconds))
|
|
||||||
return grub_errno;;
|
|
||||||
|
|
||||||
/* Read in the data, word by word. */
|
/* Read in the data, word by word. */
|
||||||
for (i = 0; i < size / 2; i++)
|
for (i = 0; i < size / 2; i++)
|
||||||
buf16[i] = grub_le_to_cpu16 (grub_inw(dev->ioaddress + GRUB_ATA_REG_DATA));
|
buf16[i] = grub_le_to_cpu16 (grub_inw(dev->ioaddress + GRUB_ATA_REG_DATA));
|
||||||
|
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
|
|
||||||
return grub_error (GRUB_ERR_READ_ERROR, "ATA read error");
|
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_err_t
|
static void
|
||||||
grub_ata_pio_write (struct grub_ata_device *dev, char *buf,
|
grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size)
|
||||||
grub_size_t size, int milliseconds)
|
|
||||||
{
|
{
|
||||||
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
|
grub_uint16_t *buf16 = (grub_uint16_t *) buf;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
|
|
||||||
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
|
||||||
|
|
||||||
/* Wait until drive is ready to read data. */
|
|
||||||
if (grub_ata_wait_status (dev, GRUB_ATA_STATUS_DRQ, 0, milliseconds))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
/* Write the data, word by word. */
|
/* Write the data, word by word. */
|
||||||
for (i = 0; i < size / 2; i++)
|
for (i = 0; i < size / 2; i++)
|
||||||
grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA);
|
grub_outw(grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA);
|
||||||
|
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_ERR)
|
|
||||||
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -280,26 +281,22 @@ grub_atapi_identify (struct grub_ata_device *dev)
|
||||||
if (! info)
|
if (! info)
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
|
|
||||||
{
|
|
||||||
grub_free (info);
|
|
||||||
return grub_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
if (grub_ata_cmd (dev, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE))
|
|
||||||
{
|
{
|
||||||
grub_free (info);
|
grub_free (info);
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
|
||||||
grub_ata_wait ();
|
grub_ata_wait ();
|
||||||
|
|
||||||
if (grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_STD))
|
if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
|
||||||
{
|
{
|
||||||
grub_free (info);
|
grub_free (info);
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
|
||||||
|
|
||||||
dev->atapi = 1;
|
dev->atapi = 1;
|
||||||
|
|
||||||
|
@ -315,9 +312,8 @@ grub_atapi_wait_drq (struct grub_ata_device *dev,
|
||||||
grub_uint8_t ireason,
|
grub_uint8_t ireason,
|
||||||
int milliseconds)
|
int milliseconds)
|
||||||
{
|
{
|
||||||
grub_millisleep (1); /* ATA allows 400ns to assert BSY. */
|
/* Wait for !BSY, DRQ, ireason */
|
||||||
|
if (grub_ata_wait_not_busy (dev, milliseconds))
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, milliseconds))
|
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
grub_uint8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
||||||
|
@ -328,9 +324,9 @@ grub_atapi_wait_drq (struct grub_ata_device *dev,
|
||||||
&& (irs & GRUB_ATAPI_IREASON_MASK) == ireason)
|
&& (irs & GRUB_ATAPI_IREASON_MASK) == ireason)
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
|
|
||||||
/* No DRQ implies error condition. */
|
/* !DRQ implies error condition. */
|
||||||
grub_dprintf("ata", "atapi error: status=0x%x, ireason=0x%x, error=0x%x\n",
|
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));
|
sts, irs, grub_ata_regget (dev, GRUB_ATA_REG_ERROR));
|
||||||
|
|
||||||
if (! (sts & GRUB_ATA_STATUS_DRQ)
|
if (! (sts & GRUB_ATA_STATUS_DRQ)
|
||||||
&& (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR)
|
&& (irs & GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_ERROR)
|
||||||
|
@ -348,11 +344,11 @@ static grub_err_t
|
||||||
grub_atapi_packet (struct grub_ata_device *dev, char *packet,
|
grub_atapi_packet (struct grub_ata_device *dev, char *packet,
|
||||||
grub_size_t size)
|
grub_size_t size)
|
||||||
{
|
{
|
||||||
if (grub_ata_wait_status(dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
/* Send ATA PACKET command. */
|
/* Send ATA PACKET command. */
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0);
|
grub_ata_regset (dev, GRUB_ATA_REG_FEATURES, 0);
|
||||||
grub_ata_regset (dev, GRUB_ATAPI_REG_IREASON, 0);
|
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_CNTHIGH, size >> 8);
|
||||||
|
@ -365,10 +361,7 @@ grub_atapi_packet (struct grub_ata_device *dev, char *packet,
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
/* Write the packet. */
|
/* Write the packet. */
|
||||||
grub_uint16_t *buf16 = (grub_uint16_t *) packet;
|
grub_ata_pio_write (dev, packet, 12);
|
||||||
unsigned i;
|
|
||||||
for (i = 0; i < 12 / 2; i++)
|
|
||||||
grub_outw (grub_cpu_to_le16 (buf16[i]), dev->ioaddress + GRUB_ATA_REG_DATA);
|
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
@ -385,21 +378,17 @@ grub_ata_identify (struct grub_ata_device *dev)
|
||||||
|
|
||||||
info16 = (grub_uint16_t *) info;
|
info16 = (grub_uint16_t *) info;
|
||||||
|
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
{
|
{
|
||||||
grub_free (info);
|
grub_free (info);
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
|
||||||
if (grub_ata_cmd (dev, GRUB_ATA_CMD_IDENTIFY_DEVICE))
|
|
||||||
{
|
|
||||||
grub_free (info);
|
|
||||||
return grub_errno;
|
|
||||||
}
|
|
||||||
grub_ata_wait ();
|
grub_ata_wait ();
|
||||||
|
|
||||||
if (grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_STD))
|
if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
|
||||||
{
|
{
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04) /* ABRT */
|
if (grub_ata_regget (dev, GRUB_ATA_REG_ERROR) & 0x04) /* ABRT */
|
||||||
{
|
{
|
||||||
|
@ -417,6 +406,8 @@ grub_ata_identify (struct grub_ata_device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
|
||||||
|
|
||||||
/* Now it is certain that this is not an ATAPI device. */
|
/* Now it is certain that this is not an ATAPI device. */
|
||||||
dev->atapi = 0;
|
dev->atapi = 0;
|
||||||
|
|
||||||
|
@ -471,14 +462,31 @@ grub_ata_device_initialize (int port, int device, int addr, int addr2)
|
||||||
dev->ioaddress2 = addr2;
|
dev->ioaddress2 = addr2;
|
||||||
dev->next = NULL;
|
dev->next = NULL;
|
||||||
|
|
||||||
/* 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_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
||||||
grub_ata_wait ();
|
grub_ata_wait ();
|
||||||
|
|
||||||
|
/* If status is 0x00, it is safe to assume that there
|
||||||
|
is no device (or only a !READY) device connected. */
|
||||||
|
grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
||||||
|
grub_dprintf ("ata", "status=0x%x\n", sts);
|
||||||
|
if (sts == 0x00)
|
||||||
|
{
|
||||||
|
grub_free(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
But this tests often detects a second (slave) device
|
||||||
|
connected to a SATA controller which supports only one
|
||||||
|
(master) device. In this case, the status register
|
||||||
|
check above usually works. */
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
|
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
|
||||||
grub_ata_wait ();
|
grub_ata_wait ();
|
||||||
if (grub_ata_regget (dev, GRUB_ATA_REG_SECTORS) != 0x5A)
|
grub_int8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS);
|
||||||
|
grub_dprintf ("ata", "sectors=0x%x\n", sec);
|
||||||
|
if (sec != 0x5A)
|
||||||
{
|
{
|
||||||
grub_free(dev);
|
grub_free(dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -612,9 +620,6 @@ grub_ata_setaddress (struct grub_ata_device *dev,
|
||||||
grub_disk_addr_t sector,
|
grub_disk_addr_t sector,
|
||||||
grub_size_t size)
|
grub_size_t size)
|
||||||
{
|
{
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_STD))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
switch (addressing)
|
switch (addressing)
|
||||||
{
|
{
|
||||||
case GRUB_ATA_CHS:
|
case GRUB_ATA_CHS:
|
||||||
|
@ -637,6 +642,9 @@ grub_ata_setaddress (struct grub_ata_device *dev,
|
||||||
"using CHS addressing", sector);
|
"using CHS addressing", sector);
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head);
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head);
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_SECTNUM, sect);
|
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_CYLLSB, cylinder & 0xFF);
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8);
|
grub_ata_regset (dev, GRUB_ATA_REG_CYLMSB, cylinder >> 8);
|
||||||
|
@ -649,6 +657,9 @@ grub_ata_setaddress (struct grub_ata_device *dev,
|
||||||
size = 0;
|
size = 0;
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK,
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK,
|
||||||
0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
|
0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
grub_ata_setlba (dev, sector, size);
|
grub_ata_setlba (dev, sector, size);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -657,6 +668,9 @@ grub_ata_setaddress (struct grub_ata_device *dev,
|
||||||
size = 0;
|
size = 0;
|
||||||
|
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
|
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
|
||||||
|
if (grub_ata_check_ready (dev))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
/* Set "Previous". */
|
/* Set "Previous". */
|
||||||
grub_ata_setlba (dev, sector >> 24, size >> 8);
|
grub_ata_setlba (dev, sector >> 24, size >> 8);
|
||||||
/* Set "Current". */
|
/* Set "Current". */
|
||||||
|
@ -673,14 +687,12 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
|
||||||
grub_size_t size, char *buf, int rw)
|
grub_size_t size, char *buf, int rw)
|
||||||
{
|
{
|
||||||
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
|
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
|
||||||
grub_size_t cnt;
|
|
||||||
grub_size_t batch;
|
|
||||||
grub_ata_addressing_t addressing;
|
|
||||||
int cmd;
|
|
||||||
int cmd_write;
|
|
||||||
unsigned int sect;
|
|
||||||
|
|
||||||
addressing = dev->addr;
|
grub_dprintf("ata", "grub_ata_readwrite (size=%u, rw=%d)\n", size, rw);
|
||||||
|
|
||||||
|
grub_ata_addressing_t addressing = dev->addr;
|
||||||
|
grub_size_t batch;
|
||||||
|
int cmd, cmd_write;
|
||||||
|
|
||||||
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
|
if (addressing == GRUB_ATA_LBA48 && ((sector + size) >> 28) != 0)
|
||||||
{
|
{
|
||||||
|
@ -697,89 +709,49 @@ grub_ata_readwrite (grub_disk_t disk, grub_disk_addr_t sector,
|
||||||
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
|
cmd_write = GRUB_ATA_CMD_WRITE_SECTORS;
|
||||||
}
|
}
|
||||||
|
|
||||||
cnt = size / batch;
|
grub_size_t nsectors = 0;
|
||||||
|
while (nsectors < size)
|
||||||
/* Read/write batches of 256/65536 sectors, when more than 256/65536
|
|
||||||
sectors should be read/written. */
|
|
||||||
for (; cnt; cnt--)
|
|
||||||
{
|
{
|
||||||
|
if (size - nsectors < batch)
|
||||||
|
batch = size - nsectors;
|
||||||
|
|
||||||
|
grub_dprintf("ata", "rw=%d, sector=%llu, batch=%u\n", rw, sector, batch);
|
||||||
|
|
||||||
|
/* Send read/write commmand. */
|
||||||
if (grub_ata_setaddress (dev, addressing, sector, batch))
|
if (grub_ata_setaddress (dev, addressing, sector, batch))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
if (rw == 0)
|
grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
|
||||||
|
|
||||||
|
unsigned sect;
|
||||||
|
for (sect = 0; sect < batch; sect++)
|
||||||
{
|
{
|
||||||
/* Read 256/65536 sectors. */
|
/* Wait for !BSY, DRQ. */
|
||||||
if (grub_ata_cmd (dev, cmd))
|
if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
/* Wait for the command to complete. */
|
/* Transfer data. */
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_DATA))
|
if (! rw)
|
||||||
return grub_errno;
|
grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE);
|
||||||
|
else
|
||||||
|
grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE);
|
||||||
|
|
||||||
for (sect = 0; sect < batch; sect++)
|
|
||||||
{
|
|
||||||
if (grub_ata_pio_read (dev, buf,
|
|
||||||
GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_errno;
|
|
||||||
buf += GRUB_DISK_SECTOR_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Write 256/65536 sectors. */
|
|
||||||
if (grub_ata_cmd (dev, cmd))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
/* Wait for the command to complete. */
|
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
for (sect = 0; sect < batch; sect++)
|
|
||||||
{
|
|
||||||
if (grub_ata_pio_write (dev, buf,
|
|
||||||
GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
|
||||||
buf += GRUB_DISK_SECTOR_SIZE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sector += batch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read/write just a "few" sectors. */
|
|
||||||
if (grub_ata_setaddress (dev, addressing, sector, size % batch))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
if (rw == 0)
|
|
||||||
{
|
|
||||||
/* Read sectors. */
|
|
||||||
if (grub_ata_cmd (dev, cmd))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
/* Wait for the command to complete. */
|
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
for (sect = 0; sect < (size % batch); sect++)
|
|
||||||
{
|
|
||||||
if (grub_ata_pio_read (dev, buf, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_errno;
|
|
||||||
buf += GRUB_DISK_SECTOR_SIZE;
|
buf += GRUB_DISK_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* Write sectors. */
|
|
||||||
if (grub_ata_cmd (dev, cmd))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
/* Wait for the command to complete. */
|
if (rw)
|
||||||
if (grub_ata_wait_status (dev, 0, GRUB_ATA_STATUS_BUSY, GRUB_ATA_TOUT_DATA))
|
{
|
||||||
return grub_errno;
|
/* Check for write error. */
|
||||||
|
if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
for (sect = 0; sect < (size % batch); sect++)
|
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS)
|
||||||
{
|
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
||||||
if (grub_ata_pio_write (dev, buf, GRUB_DISK_SECTOR_SIZE, GRUB_ATA_TOUT_DATA))
|
|
||||||
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
||||||
buf += GRUB_DISK_SECTOR_SIZE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sector += batch;
|
||||||
|
nsectors += batch;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
|
@ -924,10 +896,7 @@ grub_atapi_read (struct grub_scsi *scsi,
|
||||||
return grub_error (GRUB_ERR_READ_ERROR, "Invalid ATAPI transfer count");
|
return grub_error (GRUB_ERR_READ_ERROR, "Invalid ATAPI transfer count");
|
||||||
|
|
||||||
/* Read the data. */
|
/* Read the data. */
|
||||||
grub_uint16_t *buf16 = (grub_uint16_t *) (buf + nread);
|
grub_ata_pio_read (dev, buf + nread, cnt);
|
||||||
unsigned i;
|
|
||||||
for (i = 0; i < cnt / 2; i++)
|
|
||||||
buf16[i] = grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA));
|
|
||||||
|
|
||||||
if (cnt & 1)
|
if (cnt & 1)
|
||||||
buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA));
|
buf[nread + cnt - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress + GRUB_ATA_REG_DATA));
|
||||||
|
|
Loading…
Reference in a new issue