2007-10-31 Marco Gerards <marco@gnu.org>
* conf/i386-pc.rmk (pkgdata_MODULES): Add `ata.mod'. (ata_mod_SOURCES): New variable. (ata_mod_CFLAGS): Likewise. (ata_mod_LDFLAGS): Likewise. * disk/ata.c: New file. * include/grub/disk.h (grub_disk_dev_id): Add `GRUB_DISK_DEV_ATA_ID'.
This commit is contained in:
parent
7f66d0e020
commit
e911ecc1c2
6 changed files with 807 additions and 3 deletions
12
ChangeLog
12
ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2007-10-31 Marco Gerards <marco@gnu.org>
|
||||
|
||||
* conf/i386-pc.rmk (pkgdata_MODULES): Add `ata.mod'.
|
||||
(ata_mod_SOURCES): New variable.
|
||||
(ata_mod_CFLAGS): Likewise.
|
||||
(ata_mod_LDFLAGS): Likewise.
|
||||
|
||||
* disk/ata.c: New file.
|
||||
|
||||
* include/grub/disk.h (grub_disk_dev_id): Add
|
||||
`GRUB_DISK_DEV_ATA_ID'.
|
||||
|
||||
2007-10-31 Robert Millan <rmh@aybabtu.com>
|
||||
|
||||
* include/grub/i386/pc/init.h (grub_lower_mem): Moved from here ...
|
||||
|
|
2
NEWS
2
NEWS
|
@ -11,6 +11,8 @@ New in 1.96:
|
|||
|
||||
* Add support for the NTFS filesystems.
|
||||
|
||||
* Add support for ATA/ATAPI.
|
||||
|
||||
New in 1.95 - 2006-10-15:
|
||||
|
||||
* Number partitions from 1 instead of 0. For instance, the first
|
||||
|
|
|
@ -859,7 +859,7 @@ grub-mkrescue: util/i386/pc/grub-mkrescue.in config.status
|
|||
pkgdata_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
|
||||
_multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \
|
||||
vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
|
||||
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod
|
||||
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod ata.mod
|
||||
|
||||
# For biosdisk.mod.
|
||||
biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
|
||||
|
@ -2248,4 +2248,56 @@ fs-cpuid_mod-commands_i386_cpuid.lst: commands/i386/cpuid.c genfslist.sh
|
|||
cpuid_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
cpuid_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
# For ata.mod.
|
||||
ata_mod_SOURCES = disk/ata.c
|
||||
CLEANFILES += ata.mod mod-ata.o mod-ata.c pre-ata.o ata_mod-disk_ata.o und-ata.lst
|
||||
ifneq ($(ata_mod_EXPORTS),no)
|
||||
CLEANFILES += def-ata.lst
|
||||
DEFSYMFILES += def-ata.lst
|
||||
endif
|
||||
MOSTLYCLEANFILES += ata_mod-disk_ata.d
|
||||
UNDSYMFILES += und-ata.lst
|
||||
|
||||
ata.mod: pre-ata.o mod-ata.o
|
||||
-rm -f $@
|
||||
$(TARGET_CC) $(ata_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ $^
|
||||
$(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@
|
||||
|
||||
pre-ata.o: $(ata_mod_DEPENDENCIES) ata_mod-disk_ata.o
|
||||
-rm -f $@
|
||||
$(TARGET_CC) $(ata_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ ata_mod-disk_ata.o
|
||||
|
||||
mod-ata.o: mod-ata.c
|
||||
$(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_mod_CFLAGS) -c -o $@ $<
|
||||
|
||||
mod-ata.c: moddep.lst genmodsrc.sh
|
||||
sh $(srcdir)/genmodsrc.sh 'ata' $< > $@ || (rm -f $@; exit 1)
|
||||
|
||||
ifneq ($(ata_mod_EXPORTS),no)
|
||||
def-ata.lst: pre-ata.o
|
||||
$(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 ata/' > $@
|
||||
endif
|
||||
|
||||
und-ata.lst: pre-ata.o
|
||||
echo 'ata' > $@
|
||||
$(NM) -u -P -p $< | cut -f1 -d' ' >> $@
|
||||
|
||||
ata_mod-disk_ata.o: disk/ata.c
|
||||
$(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_mod_CFLAGS) -MD -c -o $@ $<
|
||||
-include ata_mod-disk_ata.d
|
||||
|
||||
CLEANFILES += cmd-ata_mod-disk_ata.lst fs-ata_mod-disk_ata.lst
|
||||
COMMANDFILES += cmd-ata_mod-disk_ata.lst
|
||||
FSFILES += fs-ata_mod-disk_ata.lst
|
||||
|
||||
cmd-ata_mod-disk_ata.lst: disk/ata.c gencmdlist.sh
|
||||
set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh ata > $@ || (rm -f $@; exit 1)
|
||||
|
||||
fs-ata_mod-disk_ata.lst: disk/ata.c genfslist.sh
|
||||
set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(ata_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh ata > $@ || (rm -f $@; exit 1)
|
||||
|
||||
|
||||
ata_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
ata_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
include $(srcdir)/conf/common.mk
|
||||
|
|
|
@ -129,7 +129,7 @@ grub_mkrescue_SOURCES = util/i386/pc/grub-mkrescue.in
|
|||
pkgdata_MODULES = biosdisk.mod _chain.mod _linux.mod linux.mod normal.mod \
|
||||
_multiboot.mod chain.mod multiboot.mod reboot.mod halt.mod \
|
||||
vbe.mod vbetest.mod vbeinfo.mod video.mod gfxterm.mod \
|
||||
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod
|
||||
videotest.mod play.mod bitmap.mod tga.mod cpuid.mod serial.mod ata.mod
|
||||
|
||||
# For biosdisk.mod.
|
||||
biosdisk_mod_SOURCES = disk/i386/pc/biosdisk.c
|
||||
|
@ -246,4 +246,9 @@ cpuid_mod_SOURCES = commands/i386/cpuid.c
|
|||
cpuid_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
cpuid_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
# For ata.mod.
|
||||
ata_mod_SOURCES = disk/ata.c
|
||||
ata_mod_CFLAGS = $(COMMON_CFLAGS)
|
||||
ata_mod_LDFLAGS = $(COMMON_LDFLAGS)
|
||||
|
||||
include $(srcdir)/conf/common.mk
|
||||
|
|
732
disk/ata.c
Normal file
732
disk/ata.c
Normal file
|
@ -0,0 +1,732 @@
|
|||
/* ata.c - ATA disk access. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2007 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/dl.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/mm.h>
|
||||
/* XXX: For now this only works on i386. */
|
||||
#include <grub/cpu/io.h>
|
||||
#include <grub/machine/time.h>
|
||||
#include <grub/machine/biosdisk.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GRUB_ATA_CHS,
|
||||
GRUB_ATA_LBA,
|
||||
GRUB_ATA_LBA48
|
||||
} grub_ata_addressing_t;
|
||||
|
||||
/* At the moment, only two IDE ports are supported. */
|
||||
static const int grub_ata_ioaddress[] = { 0x1f0, 0x170 };
|
||||
static const int grub_ata_ioaddress2[] = { 0x3f6, 0x376 };
|
||||
|
||||
#define GRUB_CDROM_SECTOR_SIZE 2048
|
||||
|
||||
#define GRUB_ATA_REG_DATA 0
|
||||
#define GRUB_ATA_REG_ERROR 1
|
||||
#define GRUB_ATA_REG_FEATURES 1
|
||||
#define GRUB_ATA_REG_SECTORS 2
|
||||
#define GRUB_ATA_REG_SECTNUM 3
|
||||
#define GRUB_ATA_REG_CYLLSB 4
|
||||
#define GRUB_ATA_REG_CYLMSB 5
|
||||
#define GRUB_ATA_REG_LBALOW 3
|
||||
#define GRUB_ATA_REG_LBAMID 4
|
||||
#define GRUB_ATA_REG_LBAHIGH 5
|
||||
#define GRUB_ATA_REG_DISK 6
|
||||
#define GRUB_ATA_REG_CMD 7
|
||||
#define GRUB_ATA_REG_STATUS 7
|
||||
|
||||
#define GRUB_ATA_REG2_CONTROL 0
|
||||
|
||||
enum grub_ata_commands
|
||||
{
|
||||
GRUB_ATA_CMD_READ_SECTORS = 0x20,
|
||||
GRUB_ATA_CMD_READ_SECTORS_EXT = 0x24,
|
||||
GRUB_ATA_CMD_WRITE_SECTORS = 0x30,
|
||||
GRUB_ATA_CMD_WRITE_SECTORS_EXT = 0x34,
|
||||
GRUB_ATA_CMD_IDENTIFY_DEVICE = 0xEC,
|
||||
GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE = 0xA1,
|
||||
GRUB_ATA_CMD_PACKET = 0xA0
|
||||
};
|
||||
|
||||
struct grub_ata_device
|
||||
{
|
||||
/* IDE port to use. */
|
||||
int port;
|
||||
|
||||
/* IO addresses on which the registers for this device can be
|
||||
found. */
|
||||
int ioaddress;
|
||||
int ioaddress2;
|
||||
|
||||
/* Two devices can be connected to a single cable. Use this field
|
||||
to select device 0 (commonly known as "master") or device 1
|
||||
(commonly known as "slave"). */
|
||||
int device;
|
||||
|
||||
/* Addressing methods available for accessing this device. If CHS
|
||||
is only available, use that. Otherwise use LBA, except for the
|
||||
high sectors. In that case use LBA48. */
|
||||
grub_ata_addressing_t addr;
|
||||
|
||||
/* Sector count. */
|
||||
grub_size_t size;
|
||||
|
||||
/* CHS maximums. */
|
||||
grub_uint16_t cylinders;
|
||||
grub_uint16_t heads;
|
||||
grub_uint16_t sectors_per_track;
|
||||
|
||||
/* Set to 0 for ATA, set to 1 for ATAPI. */
|
||||
int atapi;
|
||||
|
||||
struct grub_ata_device *next;
|
||||
};
|
||||
|
||||
static struct grub_ata_device *grub_ata_devices;
|
||||
|
||||
static inline void
|
||||
grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
|
||||
{
|
||||
grub_outb (val, dev->ioaddress + reg);
|
||||
}
|
||||
|
||||
static inline int
|
||||
grub_ata_regget (struct grub_ata_device *dev, int reg)
|
||||
{
|
||||
return grub_inb (dev->ioaddress + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
|
||||
{
|
||||
grub_outb (val, dev->ioaddress2 + reg);
|
||||
}
|
||||
|
||||
static inline int
|
||||
grub_ata_regget2 (struct grub_ata_device *dev, int reg)
|
||||
{
|
||||
return grub_inb (dev->ioaddress2 + reg);
|
||||
}
|
||||
|
||||
/* Wait until the device DEV has the status set to ready. */
|
||||
static inline void
|
||||
grub_ata_wait_busy (struct grub_ata_device *dev)
|
||||
{
|
||||
while ((grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & 0x80));
|
||||
}
|
||||
|
||||
static inline void
|
||||
grub_ata_wait_drq (struct grub_ata_device *dev)
|
||||
{
|
||||
while (! (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & 0x08));
|
||||
}
|
||||
|
||||
static inline void
|
||||
grub_ata_wait (void)
|
||||
{
|
||||
grub_uint32_t time;
|
||||
time = grub_get_rtc ();
|
||||
|
||||
while (time + 1 > grub_get_rtc ());
|
||||
}
|
||||
|
||||
/* Byteorder has to be changed before strings can be read. */
|
||||
static inline 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';
|
||||
}
|
||||
|
||||
static int
|
||||
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;
|
||||
|
||||
/* Make sure the read command is processed. */
|
||||
grub_ata_wait ();
|
||||
|
||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) & 1)
|
||||
return grub_ata_regget (dev, GRUB_ATA_REG_ERROR);
|
||||
|
||||
/* Wait until the data is available. */
|
||||
grub_ata_wait_drq (dev);
|
||||
|
||||
/* 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));
|
||||
|
||||
/* XXX: Do some error checks. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
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;
|
||||
|
||||
/* Make sure the write command is processed. */
|
||||
grub_ata_wait ();
|
||||
|
||||
/* Wait until the device is ready to write. */
|
||||
grub_ata_wait_drq (dev);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* XXX: Do some error checks. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_printf ("Serial: %s\n", text);
|
||||
grub_ata_strncpy (text, info + 46, 8);
|
||||
grub_printf ("Firmware: %s\n", text);
|
||||
grub_ata_strncpy (text, info + 54, 40);
|
||||
grub_printf ("Model: %s\n", text);
|
||||
|
||||
grub_printf ("Addressing: %d\n", dev->addr);
|
||||
grub_printf ("#sectors: %d\n", dev->size);
|
||||
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_atapi_identify (struct grub_ata_device *dev)
|
||||
{
|
||||
char *info;
|
||||
|
||||
info = grub_malloc (256);
|
||||
if (! info)
|
||||
return grub_errno;
|
||||
|
||||
grub_ata_wait_busy (dev);
|
||||
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD,
|
||||
GRUB_ATA_CMD_IDENTIFY_PACKET_DEVICE);
|
||||
|
||||
grub_ata_pio_read (dev, info, 256);
|
||||
|
||||
dev->atapi = 1;
|
||||
|
||||
grub_ata_dumpinfo (dev, info);
|
||||
|
||||
grub_free (info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_atapi_packet (struct grub_ata_device *dev, char *packet)
|
||||
{
|
||||
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_SECTORS, 0);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_LBAHIGH, 0xFF);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_LBAMID, 0xFF);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, GRUB_ATA_CMD_PACKET);
|
||||
|
||||
grub_ata_pio_write (dev, packet, 12);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ata_identify (struct grub_ata_device *dev)
|
||||
{
|
||||
char *info;
|
||||
grub_uint16_t *info16;
|
||||
int ataerr;
|
||||
|
||||
info = grub_malloc (GRUB_DISK_SECTOR_SIZE);
|
||||
if (! info)
|
||||
return grub_errno;
|
||||
|
||||
info16 = (grub_uint16_t *) info;
|
||||
|
||||
grub_ata_wait_busy (dev);
|
||||
|
||||
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);
|
||||
|
||||
ataerr = grub_ata_pio_read (dev, info, GRUB_DISK_SECTOR_SIZE);
|
||||
if (ataerr & 4)
|
||||
{
|
||||
/* ATAPI device detected. */
|
||||
grub_free(info);
|
||||
return grub_atapi_identify (dev);
|
||||
}
|
||||
else if (ataerr)
|
||||
{
|
||||
/* Error. */
|
||||
grub_free(info);
|
||||
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
|
||||
"device can not be identified");
|
||||
}
|
||||
|
||||
/* 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_uint32_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_initialize (void)
|
||||
{
|
||||
struct grub_ata_device *dev;
|
||||
struct grub_ata_device **devp;
|
||||
int port;
|
||||
int device;
|
||||
|
||||
for (port = 0; port <= 1; port++)
|
||||
{
|
||||
for (device = 0; device <= 1; device++)
|
||||
{
|
||||
dev = grub_malloc (sizeof(*dev));
|
||||
if (! dev)
|
||||
return grub_errno;
|
||||
|
||||
/* Setup the device information. */
|
||||
dev->port = port;
|
||||
dev->device = device;
|
||||
dev->ioaddress = grub_ata_ioaddress[dev->port];
|
||||
dev->ioaddress2 = grub_ata_ioaddress2[dev->port];
|
||||
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_wait ();
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
|
||||
grub_ata_wait ();
|
||||
if (grub_ata_regget (dev, GRUB_ATA_REG_SECTORS) != 0x5A)
|
||||
{
|
||||
grub_free(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Detect if the device is present by issuing a reset. */
|
||||
grub_ata_regset2 (dev, GRUB_ATA_REG2_CONTROL, 6);
|
||||
grub_ata_wait ();
|
||||
grub_ata_regset2 (dev, GRUB_ATA_REG2_CONTROL, 2);
|
||||
grub_ata_wait ();
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
||||
grub_ata_wait ();
|
||||
|
||||
/* XXX: Check some registers to see if the reset worked as
|
||||
expected for this device. */
|
||||
#if 1
|
||||
/* Enable for ATAPI . */
|
||||
if (grub_ata_regget (dev, GRUB_ATA_REG_CYLLSB) != 0x14
|
||||
|| grub_ata_regget (dev, GRUB_ATA_REG_CYLMSB) != 0xeb)
|
||||
#endif
|
||||
if (grub_ata_regget (dev, GRUB_ATA_REG_STATUS) == 0
|
||||
|| (grub_ata_regget (dev, GRUB_ATA_REG_CYLLSB) != 0
|
||||
&& grub_ata_regget (dev, GRUB_ATA_REG_CYLMSB) != 0
|
||||
&& grub_ata_regget (dev, GRUB_ATA_REG_CYLLSB) != 0x3c
|
||||
&& grub_ata_regget (dev, GRUB_ATA_REG_CYLLSB) != 0xc3))
|
||||
{
|
||||
grub_free (dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Use the IDENTIFY DEVICE command to query the device. */
|
||||
if (grub_ata_identify (dev))
|
||||
{
|
||||
grub_free (dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Register the device. */
|
||||
for (devp = &grub_ata_devices; *devp; devp = &(*devp)->next);
|
||||
*devp = dev;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
grub_ata_wait_busy (dev);
|
||||
|
||||
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 can not be addressed "
|
||||
"using CHS addressing", sector);
|
||||
|
||||
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);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, (dev->device << 4) | head);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GRUB_ATA_LBA:
|
||||
if (size == 256)
|
||||
size = 0;
|
||||
grub_ata_setlba (dev, sector, size);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK,
|
||||
0xE0 | (dev->device << 4) | ((sector >> 24) & 0x0F));
|
||||
break;
|
||||
|
||||
case GRUB_ATA_LBA48:
|
||||
if (size == 65536)
|
||||
size = 0;
|
||||
|
||||
/* Set "Previous". */
|
||||
grub_ata_setlba (dev, sector >> 24, size >> 8);
|
||||
/* Set "Current". */
|
||||
grub_ata_setlba (dev, sector, size);
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | (dev->device << 4));
|
||||
|
||||
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_size_t cnt;
|
||||
grub_size_t batch;
|
||||
grub_ata_addressing_t addressing;
|
||||
int cmd;
|
||||
int cmd_write;
|
||||
|
||||
addressing = dev->addr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cnt = size / batch;
|
||||
|
||||
/* Read/write batches of 256/65536 sectors, when more than 256/65536
|
||||
sectors should be read/written. */
|
||||
for (; cnt; cnt--)
|
||||
{
|
||||
if (grub_ata_setaddress (dev, addressing, sector, batch))
|
||||
return grub_errno;
|
||||
|
||||
if (rw == 0)
|
||||
{
|
||||
/* Read 256/65536 sectors. */
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd);
|
||||
if (grub_ata_pio_read (dev, buf,
|
||||
batch * GRUB_DISK_SECTOR_SIZE))
|
||||
return grub_errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Write 256/65536 sectors. */
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd_write);
|
||||
if (grub_ata_pio_write (dev, buf,
|
||||
batch * GRUB_DISK_SECTOR_SIZE))
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
buf += batch * GRUB_DISK_SECTOR_SIZE;
|
||||
sector += batch * GRUB_DISK_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
/* Read/write just a "few" sectors. */
|
||||
if (grub_ata_setaddress (dev, addressing, sector, size % batch))
|
||||
return grub_errno;
|
||||
|
||||
if (rw == 0)
|
||||
{
|
||||
/* Read sectors. */
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd);
|
||||
if (grub_ata_pio_read (dev, buf,
|
||||
(size % batch) * GRUB_DISK_SECTOR_SIZE))
|
||||
return grub_errno;
|
||||
} else {
|
||||
/* Write sectors. */
|
||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, cmd_write);
|
||||
if (grub_ata_pio_write (dev, buf,
|
||||
(size % batch) * GRUB_DISK_SECTOR_SIZE))
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Can't open device");
|
||||
|
||||
if (dev->atapi)
|
||||
disk->total_sectors = 9000000; /* XXX */
|
||||
else
|
||||
disk->total_sectors = dev->size;
|
||||
|
||||
disk->id = (int) dev;
|
||||
|
||||
disk->has_partitions = !dev->atapi;
|
||||
disk->data = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
grub_ata_close (grub_disk_t disk __attribute__((unused)))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct grub_atapi_read
|
||||
{
|
||||
grub_uint8_t code;
|
||||
grub_uint8_t reserved1;
|
||||
grub_uint32_t lba;
|
||||
grub_uint32_t length;
|
||||
grub_uint8_t reserved2[2];
|
||||
} __attribute__((packed));
|
||||
|
||||
static grub_err_t
|
||||
grub_atapi_readsector (struct grub_ata_device *dev,
|
||||
char *buf, grub_disk_addr_t sector)
|
||||
{
|
||||
struct grub_atapi_read readcmd;
|
||||
|
||||
readcmd.code = 0xA8;
|
||||
readcmd.lba = grub_cpu_to_be32 (sector);
|
||||
readcmd.length = grub_cpu_to_be32 (1);
|
||||
|
||||
grub_atapi_packet (dev, (char *) &readcmd);
|
||||
grub_ata_pio_read (dev, buf, GRUB_CDROM_SECTOR_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ata_read (grub_disk_t disk, grub_disk_addr_t sector,
|
||||
grub_size_t size, char *buf)
|
||||
{
|
||||
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
|
||||
int cdsector;
|
||||
char *sbuf;
|
||||
|
||||
if (! dev->atapi)
|
||||
return grub_ata_readwrite (disk, sector, size, buf, 0);
|
||||
|
||||
/* ATAPI is being used, so try to read from CDROM using ATAPI. */
|
||||
|
||||
sbuf = grub_malloc (GRUB_CDROM_SECTOR_SIZE);
|
||||
if (! sbuf)
|
||||
return grub_errno;
|
||||
|
||||
/* CDROMs have sectors of 2048 bytes, so chop them into pieces of
|
||||
512 bytes. */
|
||||
while (size > 0)
|
||||
{
|
||||
int rsize;
|
||||
int offset;
|
||||
int max;
|
||||
|
||||
cdsector = sector >> 2;
|
||||
rsize = ((size * GRUB_DISK_SECTOR_SIZE > GRUB_CDROM_SECTOR_SIZE)
|
||||
? GRUB_CDROM_SECTOR_SIZE : size * GRUB_DISK_SECTOR_SIZE);
|
||||
offset = (sector & 3) * GRUB_DISK_SECTOR_SIZE;
|
||||
max = GRUB_CDROM_SECTOR_SIZE - offset;
|
||||
rsize = (rsize > max) ? max : rsize;
|
||||
|
||||
grub_atapi_readsector (dev, sbuf, cdsector);
|
||||
grub_memcpy (buf + offset, sbuf, rsize);
|
||||
|
||||
buf += rsize;
|
||||
size -= rsize / GRUB_DISK_SECTOR_SIZE;
|
||||
sector += rsize / GRUB_DISK_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
grub_free (sbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_ata_write (grub_disk_t disk,
|
||||
grub_disk_addr_t sector,
|
||||
grub_size_t size,
|
||||
const char *buf)
|
||||
{
|
||||
#if 1
|
||||
return GRUB_ERR_NOT_IMPLEMENTED_YET;
|
||||
#else
|
||||
return grub_ata_readwrite (disk, sector, size, (char *) buf, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
|
||||
GRUB_MOD_INIT(ata)
|
||||
{
|
||||
(void) mod; /* To stop warning. */
|
||||
|
||||
/* XXX: To prevent two drivers operating on the same disks. */
|
||||
grub_biosdisk_fini ();
|
||||
|
||||
/* ATA initialization. */
|
||||
grub_ata_initialize ();
|
||||
|
||||
grub_disk_dev_register (&grub_atadisk_dev);
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(ata)
|
||||
{
|
||||
grub_disk_dev_unregister (&grub_atadisk_dev);
|
||||
}
|
|
@ -34,7 +34,8 @@ enum grub_disk_dev_id
|
|||
GRUB_DISK_DEVICE_EFIDISK_ID,
|
||||
GRUB_DISK_DEVICE_RAID_ID,
|
||||
GRUB_DISK_DEVICE_LVM_ID,
|
||||
GRUB_DISK_DEVICE_HOST_ID
|
||||
GRUB_DISK_DEVICE_HOST_ID,
|
||||
GRUB_DISK_DEVICE_ATA_ID
|
||||
};
|
||||
|
||||
struct grub_disk;
|
||||
|
|
Loading…
Reference in a new issue