123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901 |
- /* ata.c - ATA disk access. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/ata.h>
- #include <grub/dl.h>
- #include <grub/disk.h>
- #include <grub/mm.h>
- #include <grub/time.h>
- #include <grub/pci.h>
- #include <grub/scsi.h>
- GRUB_EXPORT(grub_ata_pio_read);
- GRUB_EXPORT(grub_ata_wait_not_busy);
- GRUB_EXPORT(grub_ata_wait_drq);
- /* At the moment, only two IDE ports are supported. */
- static const grub_port_t grub_ata_ioaddress[] = { 0x1f0, 0x170 };
- static const grub_port_t grub_ata_ioaddress2[] = { 0x3f6, 0x376 };
- static struct grub_ata_device *grub_ata_devices;
- /* Wait for !BSY. */
- grub_err_t
- grub_ata_wait_not_busy (struct grub_ata_device *dev, int milliseconds)
- {
- /* ATA requires 400ns (after a write to CMD register) or
- 1 PIO cycle (after a DRQ block transfer) before
- first check of BSY. */
- grub_millisleep (1);
- int i = 1;
- grub_uint8_t sts;
- while ((sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS))
- & GRUB_ATA_STATUS_BUSY)
- {
- if (i >= milliseconds)
- {
- grub_dprintf ("ata", "timeout: %dms, status=0x%x\n",
- milliseconds, sts);
- return grub_error (GRUB_ERR_TIMEOUT, "ATA timeout");
- }
- grub_millisleep (1);
- i++;
- }
- return GRUB_ERR_NONE;
- }
- static inline void
- grub_ata_wait (void)
- {
- grub_millisleep (50);
- }
- /* Wait for !BSY, DRQ. */
- 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;
- /* !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;
- }
- /* Byteorder has to be changed before strings can be read. */
- static void
- 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++)
- *(dst16++) = grub_be_to_cpu16 (*(src16++));
- dst[len] = '\0';
- }
- void
- grub_ata_pio_read (struct grub_ata_device *dev, char *buf, grub_size_t size)
- {
- 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));
- }
- static void
- grub_ata_pio_write (struct grub_ata_device *dev, char *buf, grub_size_t size)
- {
- 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);
- grub_dprintf ("ata", "Serial: %s\n", text);
- grub_ata_strncpy (text, info + 46, 8);
- grub_dprintf ("ata", "Firmware: %s\n", text);
- grub_ata_strncpy (text, info + 54, 40);
- grub_dprintf ("ata", "Model: %s\n", text);
- if (! dev->atapi)
- {
- grub_dprintf ("ata", "Addressing: %d\n", dev->addr);
- grub_dprintf ("ata", "Sectors: %lld\n", (unsigned long long) dev->size);
- }
- }
- static grub_err_t
- grub_atapi_identify (struct grub_ata_device *dev)
- {
- char *info;
- info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
- if (! info)
- return grub_errno;
- grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
- grub_ata_wait ();
- if (grub_ata_check_ready (dev))
- {
- grub_free (info);
- return grub_errno;
- }
- grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
- grub_ata_wait ();
- if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
- {
- grub_free (info);
- return grub_errno;
- }
- grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
- dev->atapi = 1;
- grub_ata_dumpinfo (dev, info);
- grub_free (info);
- return GRUB_ERR_NONE;
- }
- static grub_err_t
- grub_atapi_wait_drq (struct grub_ata_device *dev,
- grub_uint8_t ireason,
- int milliseconds)
- {
- /* Wait for !BSY, DRQ, ireason */
- if (grub_ata_wait_not_busy (dev, milliseconds))
- 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;
- /* !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));
- 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)
- {
- grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
- if (grub_ata_check_ready (dev))
- return grub_errno;
- /* Send ATA PACKET command. */
- 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_CNTHIGH, size >> 8);
- grub_ata_regset (dev, GRUB_ATAPI_REG_CNTLOW, size & 0xFF);
- grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET);
- /* Wait for !BSY, DRQ, !I/O, C/D. */
- if (grub_atapi_wait_drq (dev, GRUB_ATAPI_IREASON_CMD_OUT, GRUB_ATA_TOUT_STD))
- return grub_errno;
- /* Write the packet. */
- grub_ata_pio_write (dev, packet, 12);
- return GRUB_ERR_NONE;
- }
- 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);
- grub_ata_wait ();
- if (grub_ata_check_ready (dev))
- {
- grub_free (info);
- return grub_errno;
- }
- grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_IDENTIFY_DEVICE);
- grub_ata_wait ();
- if (grub_ata_wait_drq (dev, 0, GRUB_ATA_TOUT_STD))
- {
- 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;
- else
- /* Other Error. */
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
- "device cannot be identified");
- }
- grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
- /* 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;
- }
- /* 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
- dev->size = grub_le_to_cpu64(*((grub_uint64_t *) &info16[100]));
- /* 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
- grub_ata_device_initialize (int port, int device, int addr, int addr2)
- {
- struct grub_ata_device *dev;
- struct grub_ata_device **devp;
- 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;
- dev->ioaddress = addr + GRUB_MACHINE_PCI_IO_BASE;
- dev->ioaddress2 = addr2 + GRUB_MACHINE_PCI_IO_BASE;
- dev->next = NULL;
- grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
- grub_ata_wait ();
- /* Try to detect if the port is in use by writing to it,
- waiting for a while and reading it again. If the value
- was preserved, there is a device connected. */
- grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
- grub_ata_wait ();
- grub_uint8_t sec = grub_ata_regget (dev, GRUB_ATA_REG_SECTORS);
- grub_dprintf ("ata", "sectors=0x%x\n", sec);
- if (sec != 0x5A)
- {
- grub_free(dev);
- return 0;
- }
- /* 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. */
- /* 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;
- }
- static int
- grub_ata_pciinit (grub_pci_device_t dev, grub_pci_id_t pciid,
- void *closure __attribute__((unused)))
- {
- static int compat_use[2] = { 0 };
- grub_pci_address_t addr;
- grub_uint32_t class;
- grub_uint32_t bar1;
- grub_uint32_t bar2;
- int rega;
- int regb;
- int i;
- static int controller = 0;
- int cs5536 = 0;
- int nports = 2;
- /* Read class. */
- addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
- class = grub_pci_read (addr);
- /* AMD CS5536 Southbridge. */
- if (pciid == 0x208f1022)
- {
- cs5536 = 1;
- nports = 1;
- }
- /* Check if this class ID matches that of a PCI IDE Controller. */
- if (!cs5536 && (class >> 16 != 0x0101))
- return 0;
- for (i = 0; i < nports; i++)
- {
- /* Set to 0 when the channel operated in compatibility mode. */
- int compat;
- /* We don't support non-compatibility mode for CS5536. */
- if (cs5536)
- compat = 0;
- else
- compat = (class >> (8 + 2 * i)) & 1;
- rega = 0;
- 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];
- compat_use[i] = 1;
- }
- else if (compat)
- {
- /* Read the BARs, which either contain a mmapped IO address
- or the IO port address. */
- addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
- + sizeof (grub_uint64_t) * i);
- bar1 = grub_pci_read (addr);
- addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESSES
- + sizeof (grub_uint64_t) * i
- + sizeof (grub_uint32_t));
- bar2 = grub_pci_read (addr);
- /* Check if the BARs describe an IO region. */
- if ((bar1 & 1) && (bar2 & 1))
- {
- rega = bar1 & ~3;
- regb = bar2 & ~3;
- }
- }
- grub_dprintf ("ata",
- "PCI dev (%d,%d,%d) compat=%d rega=0x%x regb=0x%x\n",
- grub_pci_get_bus (dev), grub_pci_get_device (dev),
- grub_pci_get_function (dev), compat, rega, regb);
- if (rega && regb)
- {
- grub_errno = GRUB_ERR_NONE;
- grub_ata_device_initialize (controller * 2 + i, 0, rega, regb);
- /* Most errors raised by grub_ata_device_initialize() are harmless.
- They just indicate this particular drive is not responding, most
- likely because it doesn't exist. We might want to ignore specific
- error types here, instead of printing them. */
- if (grub_errno)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- }
- grub_ata_device_initialize (controller * 2 + i, 1, rega, regb);
- /* Likewise. */
- if (grub_errno)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- }
- }
- }
- controller++;
- return 0;
- }
- static grub_err_t
- grub_ata_initialize (void)
- {
- grub_pci_iterate (grub_ata_pciinit, 0);
- return 0;
- }
- 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,
- "sector %d cannot be addressed "
- "using CHS addressing", sector);
- 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_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));
- if (grub_ata_check_ready (dev))
- return grub_errno;
- grub_ata_setlba (dev, sector, size);
- break;
- case GRUB_ATA_LBA48:
- if (size == 65536)
- size = 0;
- grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
- if (grub_ata_check_ready (dev))
- return grub_errno;
- /* 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;
- grub_dprintf("ata", "grub_ata_readwrite (size=%llu, rw=%d)\n", (unsigned long long) 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)
- {
- 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;
- }
- grub_size_t nsectors = 0;
- while (nsectors < size)
- {
- if (size - nsectors < batch)
- batch = size - nsectors;
- grub_dprintf("ata", "rw=%d, sector=%llu, batch=%llu\n", rw, (unsigned long long) sector, (unsigned long long) batch);
- /* Send read/write command. */
- if (grub_ata_setaddress (dev, addressing, sector, batch))
- return grub_errno;
- grub_ata_regset (dev, GRUB_ATA_REG_CMD, (! rw ? cmd : cmd_write));
- unsigned sect;
- for (sect = 0; sect < batch; sect++)
- {
- /* Wait for !BSY, DRQ. */
- if (grub_ata_wait_drq (dev, rw, GRUB_ATA_TOUT_DATA))
- return grub_errno;
- /* 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);
- buf += GRUB_DISK_SECTOR_SIZE;
- }
- if (rw)
- {
- /* Check for write error. */
- if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
- return grub_errno;
- if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS)
- & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
- return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
- }
- sector += batch;
- nsectors += batch;
- }
- return GRUB_ERR_NONE;
- }
- static int
- grub_ata_iterate (int (*hook) (const char *name, void *closure),
- void *closure)
- {
- struct grub_ata_device *dev;
- for (dev = grub_ata_devices; dev; dev = dev->next)
- {
- char devname[10];
- if (dev->atapi)
- continue;
- grub_snprintf (devname, sizeof (devname),
- "ata%d", dev->port * 2 + dev->device);
- if (hook (devname, closure))
- 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[10];
- grub_snprintf (devname, sizeof (devname),
- "ata%d", dev->port * 2 + dev->device);
- if (grub_strcmp (name, devname) == 0)
- break;
- }
- if (! dev)
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device");
- if (dev->atapi)
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an ATA harddisk");
- disk->total_sectors = dev->size;
- disk->id = (unsigned long) dev;
- disk->has_partitions = 1;
- disk->data = dev;
- return 0;
- }
- static void
- grub_ata_close (grub_disk_t disk __attribute__((unused)))
- {
- }
- static grub_err_t
- grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector,
- grub_size_t size, char *buf)
- {
- return grub_ata_readwrite (disk, sector, size, buf, 0);
- }
- static grub_err_t
- grub_ata_write (grub_disk_t disk,
- grub_disk_addr_t sector,
- grub_size_t size,
- const char *buf)
- {
- return grub_ata_readwrite (disk, sector, size, (char *) buf, 1);
- }
- 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
- };
- /* ATAPI code. */
- static int
- grub_atapi_iterate (int (*hook) (const char *name, int luns, void *closure),
- void *closure)
- {
- struct grub_ata_device *dev;
- for (dev = grub_ata_devices; dev; dev = dev->next)
- {
- char devname[10];
- grub_snprintf (devname, sizeof (devname),
- "ata%d", dev->port * 2 + dev->device);
- if (! dev->atapi)
- continue;
- if (hook (devname, 1, closure))
- return 1;
- }
- return 0;
- }
- static grub_err_t
- grub_atapi_read (struct grub_scsi *scsi,
- grub_size_t cmdsize __attribute__((unused)),
- char *cmd, grub_size_t size, char *buf)
- {
- struct grub_ata_device *dev = (struct grub_ata_device *) scsi->data;
- grub_dprintf("ata", "grub_atapi_read (size=%llu)\n", (unsigned long long) size);
- if (grub_atapi_packet (dev, cmd, size))
- return grub_errno;
- 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)))
- return grub_error (GRUB_ERR_READ_ERROR, "invalid ATAPI transfer count");
- /* Read the data. */
- grub_ata_pio_read (dev, buf + nread, cnt);
- 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;
- }
- static grub_err_t
- grub_atapi_write (struct grub_scsi *scsi __attribute__((unused)),
- grub_size_t cmdsize __attribute__((unused)),
- char *cmd __attribute__((unused)),
- grub_size_t size __attribute__((unused)),
- char *buf __attribute__((unused)))
- {
- // XXX: scsi.mod does not use write yet.
- return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ATAPI write not implemented");
- }
- static grub_err_t
- grub_atapi_open (const char *name, struct grub_scsi *scsi)
- {
- struct grub_ata_device *dev;
- struct grub_ata_device *devfnd = 0;
- for (dev = grub_ata_devices; dev; dev = dev->next)
- {
- char devname[10];
- grub_snprintf (devname, sizeof (devname),
- "ata%d", dev->port * 2 + dev->device);
- if (!grub_strcmp (devname, name))
- {
- devfnd = dev;
- break;
- }
- }
- grub_dprintf ("ata", "opening ATAPI dev `%s'\n", name);
- if (! devfnd)
- return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such ATAPI device");
- scsi->data = devfnd;
- return GRUB_ERR_NONE;
- }
- static void
- grub_atapi_close (struct grub_scsi *scsi)
- {
- grub_free (scsi->name);
- }
- static struct grub_scsi_dev grub_atapi_dev =
- {
- .name = "ATAPI",
- .iterate = grub_atapi_iterate,
- .open = grub_atapi_open,
- .close = grub_atapi_close,
- .read = grub_atapi_read,
- .write = grub_atapi_write
- };
- GRUB_MOD_INIT(ata)
- {
- /* 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;
- }
- /* ATA initialization. */
- grub_ata_initialize ();
- grub_disk_dev_register (&grub_atadisk_dev);
- /* ATAPI devices are handled by scsi.mod. */
- grub_scsi_dev_register (&grub_atapi_dev);
- }
- GRUB_MOD_FINI(ata)
- {
- grub_scsi_dev_unregister (&grub_atapi_dev);
- grub_disk_dev_unregister (&grub_atadisk_dev);
- }
|