splitting generic ata from pata.
This commit is contained in:
parent
7e8b77c033
commit
c7336d912c
10 changed files with 1095 additions and 843 deletions
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <grub/ata.h>
|
#include <grub/ata.h>
|
||||||
|
#include <grub/scsi.h>
|
||||||
#include <grub/disk.h>
|
#include <grub/disk.h>
|
||||||
#include <grub/dl.h>
|
#include <grub/dl.h>
|
||||||
#include <grub/misc.h>
|
#include <grub/misc.h>
|
||||||
|
@ -61,60 +62,60 @@ enum grub_ata_smart_commands
|
||||||
static int quiet = 0;
|
static int quiet = 0;
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_hdparm_do_ata_cmd (grub_disk_t disk, grub_uint8_t cmd,
|
grub_hdparm_do_ata_cmd (grub_ata_t ata, grub_uint8_t cmd,
|
||||||
grub_uint8_t features, grub_uint8_t sectors,
|
grub_uint8_t features, grub_uint8_t sectors,
|
||||||
void * buffer, int size)
|
void * buffer, int size)
|
||||||
{
|
{
|
||||||
struct grub_disk_ata_pass_through_parms apt;
|
struct grub_disk_ata_pass_through_parms apt;
|
||||||
grub_memset (&apt, 0, sizeof (apt));
|
grub_memset (&apt, 0, sizeof (apt));
|
||||||
|
|
||||||
apt.taskfile[GRUB_ATA_REG_CMD] = cmd;
|
apt.taskfile.cmd = cmd;
|
||||||
apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
|
apt.taskfile.features = features;
|
||||||
apt.taskfile[GRUB_ATA_REG_SECTORS] = sectors;
|
apt.taskfile.sectors = sectors;
|
||||||
apt.buffer = buffer;
|
apt.buffer = buffer;
|
||||||
apt.size = size;
|
apt.size = size;
|
||||||
|
|
||||||
if (grub_disk_ata_pass_through (disk, &apt))
|
if (ata->dev->readwrite (ata, &apt))
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
grub_hdparm_do_check_powermode_cmd (grub_disk_t disk)
|
grub_hdparm_do_check_powermode_cmd (grub_ata_t ata)
|
||||||
{
|
{
|
||||||
struct grub_disk_ata_pass_through_parms apt;
|
struct grub_disk_ata_pass_through_parms apt;
|
||||||
grub_memset (&apt, 0, sizeof (apt));
|
grub_memset (&apt, 0, sizeof (apt));
|
||||||
|
|
||||||
apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_CHECK_POWER_MODE;
|
apt.taskfile.cmd = GRUB_ATA_CMD_CHECK_POWER_MODE;
|
||||||
|
|
||||||
if (grub_disk_ata_pass_through (disk, &apt))
|
if (ata->dev->readwrite (ata, &apt))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return apt.taskfile[GRUB_ATA_REG_SECTORS];
|
return apt.taskfile.sectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
|
grub_hdparm_do_smart_cmd (grub_ata_t ata, grub_uint8_t features)
|
||||||
{
|
{
|
||||||
struct grub_disk_ata_pass_through_parms apt;
|
struct grub_disk_ata_pass_through_parms apt;
|
||||||
grub_memset (&apt, 0, sizeof (apt));
|
grub_memset (&apt, 0, sizeof (apt));
|
||||||
|
|
||||||
apt.taskfile[GRUB_ATA_REG_CMD] = GRUB_ATA_CMD_SMART;
|
apt.taskfile.cmd = GRUB_ATA_CMD_SMART;
|
||||||
apt.taskfile[GRUB_ATA_REG_FEATURES] = features;
|
apt.taskfile.features = features;
|
||||||
apt.taskfile[GRUB_ATA_REG_LBAMID] = 0x4f;
|
apt.taskfile.lba_mid = 0x4f;
|
||||||
apt.taskfile[GRUB_ATA_REG_LBAHIGH] = 0xc2;
|
apt.taskfile.lba_high = 0xc2;
|
||||||
|
|
||||||
if (grub_disk_ata_pass_through (disk, &apt))
|
if (ata->dev->readwrite (ata, &apt))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (features == GRUB_ATA_FEAT_SMART_STATUS)
|
if (features == GRUB_ATA_FEAT_SMART_STATUS)
|
||||||
{
|
{
|
||||||
if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0x4f
|
if ( apt.taskfile.lba_mid == 0x4f
|
||||||
&& apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0xc2)
|
&& apt.taskfile.lba_high == 0xc2)
|
||||||
return 0; /* Good SMART status. */
|
return 0; /* Good SMART status. */
|
||||||
else if ( apt.taskfile[GRUB_ATA_REG_LBAMID] == 0xf4
|
else if ( apt.taskfile.lba_mid == 0xf4
|
||||||
&& apt.taskfile[GRUB_ATA_REG_LBAHIGH] == 0x2c)
|
&& apt.taskfile.lba_high == 0x2c)
|
||||||
return 1; /* Bad SMART status. */
|
return 1; /* Bad SMART status. */
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -124,12 +125,12 @@ grub_hdparm_do_smart_cmd (grub_disk_t disk, grub_uint8_t features)
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_hdparm_simple_cmd (const char * msg,
|
grub_hdparm_simple_cmd (const char * msg,
|
||||||
grub_disk_t disk, grub_uint8_t cmd)
|
grub_ata_t ata, grub_uint8_t cmd)
|
||||||
{
|
{
|
||||||
if (! quiet && msg)
|
if (! quiet && msg)
|
||||||
grub_printf ("%s", msg);
|
grub_printf ("%s", msg);
|
||||||
|
|
||||||
grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, 0, 0, NULL, 0);
|
grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, 0, 0, NULL, 0);
|
||||||
|
|
||||||
if (! quiet && msg)
|
if (! quiet && msg)
|
||||||
grub_printf ("%s\n", ! err ? "" : ": not supported");
|
grub_printf ("%s\n", ! err ? "" : ": not supported");
|
||||||
|
@ -138,7 +139,7 @@ grub_hdparm_simple_cmd (const char * msg,
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_hdparm_set_val_cmd (const char * msg, int val,
|
grub_hdparm_set_val_cmd (const char * msg, int val,
|
||||||
grub_disk_t disk, grub_uint8_t cmd,
|
grub_ata_t ata, grub_uint8_t cmd,
|
||||||
grub_uint8_t features, grub_uint8_t sectors)
|
grub_uint8_t features, grub_uint8_t sectors)
|
||||||
{
|
{
|
||||||
if (! quiet && msg && *msg)
|
if (! quiet && msg && *msg)
|
||||||
|
@ -149,7 +150,7 @@ grub_hdparm_set_val_cmd (const char * msg, int val,
|
||||||
grub_printf ("Disable %s", msg);
|
grub_printf ("Disable %s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_err_t err = grub_hdparm_do_ata_cmd (disk, cmd, features, sectors,
|
grub_err_t err = grub_hdparm_do_ata_cmd (ata, cmd, features, sectors,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
|
|
||||||
if (! quiet && msg)
|
if (! quiet && msg)
|
||||||
|
@ -273,6 +274,7 @@ static grub_err_t
|
||||||
grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
{
|
{
|
||||||
struct grub_arg_list *state = cmd->state;
|
struct grub_arg_list *state = cmd->state;
|
||||||
|
struct grub_ata *ata;
|
||||||
|
|
||||||
/* Check command line. */
|
/* Check command line. */
|
||||||
if (argc != 1)
|
if (argc != 1)
|
||||||
|
@ -283,9 +285,6 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "argument is not a device name");
|
||||||
args[0][len - 1] = 0;
|
args[0][len - 1] = 0;
|
||||||
|
|
||||||
if (! grub_disk_ata_pass_through)
|
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "ATA pass through not available");
|
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int apm = get_int_arg (&state[i++]);
|
int apm = get_int_arg (&state[i++]);
|
||||||
int power = state[i++].set;
|
int power = state[i++].set;
|
||||||
|
@ -311,15 +310,37 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "partition not allowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (disk->dev->id)
|
||||||
|
{
|
||||||
|
case GRUB_DISK_DEVICE_ATA_ID:
|
||||||
|
ata = disk->data;
|
||||||
|
break;
|
||||||
|
case GRUB_DISK_DEVICE_SCSI_ID:
|
||||||
|
if (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
|
||||||
|
== GRUB_SCSI_SUBSYSTEM_PATA
|
||||||
|
|| (((disk->id >> GRUB_SCSI_ID_SUBSYSTEM_SHIFT) & 0xFF)
|
||||||
|
== GRUB_SCSI_SUBSYSTEM_AHCI))
|
||||||
|
{
|
||||||
|
ata = ((struct grub_scsi *) disk->data)->data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return grub_error (GRUB_ERR_IO, "not an ATA device");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Change settings. */
|
/* Change settings. */
|
||||||
if (aam >= 0)
|
if (aam >= 0)
|
||||||
grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
|
grub_hdparm_set_val_cmd ("Automatic Acoustic Management", (aam ? aam : -1),
|
||||||
disk, GRUB_ATA_CMD_SET_FEATURES, (aam ? 0x42 : 0xc2), aam);
|
ata, GRUB_ATA_CMD_SET_FEATURES,
|
||||||
|
(aam ? 0x42 : 0xc2), aam);
|
||||||
|
|
||||||
if (apm >= 0)
|
if (apm >= 0)
|
||||||
grub_hdparm_set_val_cmd ("Advanced Power Management",
|
grub_hdparm_set_val_cmd ("Advanced Power Management",
|
||||||
(apm != 255 ? apm : -1), disk, GRUB_ATA_CMD_SET_FEATURES,
|
(apm != 255 ? apm : -1), ata,
|
||||||
(apm != 255 ? 0x05 : 0x85), (apm != 255 ? apm : 0));
|
GRUB_ATA_CMD_SET_FEATURES,
|
||||||
|
(apm != 255 ? 0x05 : 0x85),
|
||||||
|
(apm != 255 ? apm : 0));
|
||||||
|
|
||||||
if (standby_tout >= 0)
|
if (standby_tout >= 0)
|
||||||
{
|
{
|
||||||
|
@ -330,28 +351,28 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
grub_printf (")");
|
grub_printf (")");
|
||||||
}
|
}
|
||||||
/* The IDLE cmd sets disk to idle mode and configures standby timer. */
|
/* The IDLE cmd sets disk to idle mode and configures standby timer. */
|
||||||
grub_hdparm_set_val_cmd ("", -1, disk, GRUB_ATA_CMD_IDLE, 0, standby_tout);
|
grub_hdparm_set_val_cmd ("", -1, ata, GRUB_ATA_CMD_IDLE, 0, standby_tout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable_smart >= 0)
|
if (enable_smart >= 0)
|
||||||
{
|
{
|
||||||
if (! quiet)
|
if (! quiet)
|
||||||
grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
|
grub_printf ("%sable SMART operations", (enable_smart ? "En" : "Dis"));
|
||||||
int err = grub_hdparm_do_smart_cmd (disk, (enable_smart ?
|
int err = grub_hdparm_do_smart_cmd (ata, (enable_smart ?
|
||||||
GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
|
GRUB_ATA_FEAT_SMART_ENABLE : GRUB_ATA_FEAT_SMART_DISABLE));
|
||||||
if (! quiet)
|
if (! quiet)
|
||||||
grub_printf ("%s\n", err ? ": not supported" : "");
|
grub_printf ("%s\n", err ? ": not supported" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sec_freeze)
|
if (sec_freeze)
|
||||||
grub_hdparm_simple_cmd ("Freeze security settings", disk,
|
grub_hdparm_simple_cmd ("Freeze security settings", ata,
|
||||||
GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
|
GRUB_ATA_CMD_SECURITY_FREEZE_LOCK);
|
||||||
|
|
||||||
/* Print/dump IDENTIFY. */
|
/* Print/dump IDENTIFY. */
|
||||||
if (ident || dumpid)
|
if (ident || dumpid)
|
||||||
{
|
{
|
||||||
char buf[GRUB_DISK_SECTOR_SIZE];
|
char buf[GRUB_DISK_SECTOR_SIZE];
|
||||||
if (grub_hdparm_do_ata_cmd (disk, GRUB_ATA_CMD_IDENTIFY_DEVICE,
|
if (grub_hdparm_do_ata_cmd (ata, GRUB_ATA_CMD_IDENTIFY_DEVICE,
|
||||||
0, 0, buf, sizeof (buf)))
|
0, 0, buf, sizeof (buf)))
|
||||||
grub_printf ("Cannot read ATA IDENTIFY data\n");
|
grub_printf ("Cannot read ATA IDENTIFY data\n");
|
||||||
else
|
else
|
||||||
|
@ -367,7 +388,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
if (power)
|
if (power)
|
||||||
{
|
{
|
||||||
grub_printf ("Disk power mode is: ");
|
grub_printf ("Disk power mode is: ");
|
||||||
int mode = grub_hdparm_do_check_powermode_cmd (disk);
|
int mode = grub_hdparm_do_check_powermode_cmd (ata);
|
||||||
if (mode < 0)
|
if (mode < 0)
|
||||||
grub_printf ("unknown\n");
|
grub_printf ("unknown\n");
|
||||||
else
|
else
|
||||||
|
@ -383,7 +404,7 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
{
|
{
|
||||||
if (! quiet)
|
if (! quiet)
|
||||||
grub_printf ("SMART status is: ");
|
grub_printf ("SMART status is: ");
|
||||||
int err = grub_hdparm_do_smart_cmd (disk, GRUB_ATA_FEAT_SMART_STATUS);
|
int err = grub_hdparm_do_smart_cmd (ata, GRUB_ATA_FEAT_SMART_STATUS);
|
||||||
if (! quiet)
|
if (! quiet)
|
||||||
grub_printf ("%s\n", (err < 0 ? "unknown" :
|
grub_printf ("%s\n", (err < 0 ? "unknown" :
|
||||||
err == 0 ? "OK" : "*BAD*"));
|
err == 0 ? "OK" : "*BAD*"));
|
||||||
|
@ -392,11 +413,11 @@ grub_cmd_hdparm (grub_extcmd_t cmd, int argc, char **args) // state????
|
||||||
|
|
||||||
/* Change power mode. */
|
/* Change power mode. */
|
||||||
if (standby_now)
|
if (standby_now)
|
||||||
grub_hdparm_simple_cmd ("Set disk to standby mode", disk,
|
grub_hdparm_simple_cmd ("Set disk to standby mode", ata,
|
||||||
GRUB_ATA_CMD_STANDBY_IMMEDIATE);
|
GRUB_ATA_CMD_STANDBY_IMMEDIATE);
|
||||||
|
|
||||||
if (sleep_now)
|
if (sleep_now)
|
||||||
grub_hdparm_simple_cmd ("Set disk to sleep mode", disk,
|
grub_hdparm_simple_cmd ("Set disk to sleep mode", ata,
|
||||||
GRUB_ATA_CMD_SLEEP);
|
GRUB_ATA_CMD_SLEEP);
|
||||||
|
|
||||||
grub_disk_close (disk);
|
grub_disk_close (disk);
|
||||||
|
|
287
disk/ahci.c
287
disk/ahci.c
|
@ -21,39 +21,79 @@
|
||||||
#include <grub/mm.h>
|
#include <grub/mm.h>
|
||||||
#include <grub/time.h>
|
#include <grub/time.h>
|
||||||
#include <grub/pci.h>
|
#include <grub/pci.h>
|
||||||
|
#include <grub/ata.h>
|
||||||
#include <grub/scsi.h>
|
#include <grub/scsi.h>
|
||||||
|
#include <grub/misc.h>
|
||||||
|
#include <grub/list.h>
|
||||||
|
|
||||||
struct grub_ahci_cmd_head
|
struct grub_ahci_cmd_head
|
||||||
{
|
{
|
||||||
grub_uint32_t unused[8];
|
grub_uint32_t config;
|
||||||
|
grub_uint32_t transfered;
|
||||||
|
grub_uint64_t command_table_base;
|
||||||
|
grub_uint32_t unused[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grub_ahci_prdt_entry
|
||||||
|
{
|
||||||
|
grub_uint64_t data_base;
|
||||||
|
grub_uint32_t unused;
|
||||||
|
grub_uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grub_ahci_cmd_table
|
||||||
|
{
|
||||||
|
grub_uint8_t cfis[0x40];
|
||||||
|
grub_uint8_t command[16];
|
||||||
|
grub_uint32_t reserved[0xc];
|
||||||
|
struct grub_ahci_prdt_entry prdt[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_ahci_hba_port
|
struct grub_ahci_hba_port
|
||||||
{
|
{
|
||||||
grub_uint64_t command_list_base;
|
grub_uint64_t command_list_base;
|
||||||
grub_uint32_t unused[12];
|
grub_uint64_t fis_base;
|
||||||
|
grub_uint32_t intstatus;
|
||||||
|
grub_uint32_t inten;
|
||||||
|
grub_uint32_t command;
|
||||||
|
grub_uint32_t unused1[6];
|
||||||
grub_uint32_t sata_active;
|
grub_uint32_t sata_active;
|
||||||
grub_uint32_t command_issue;
|
grub_uint32_t command_issue;
|
||||||
grub_uint32_t unused[16];
|
grub_uint32_t unused2[17];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_ahci_hba
|
struct grub_ahci_hba
|
||||||
{
|
{
|
||||||
grub_uint32_t cap;
|
grub_uint32_t cap;
|
||||||
#define GRUB_AHCI_HBA_CAP_MASK 0x1f
|
|
||||||
grub_uint32_t global_control;
|
grub_uint32_t global_control;
|
||||||
#define GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN 0x80000000
|
grub_uint32_t intr_status;
|
||||||
grub_uint32_t ports_implemented;
|
grub_uint32_t ports_implemented;
|
||||||
grub_uint32_t unused1[7];
|
grub_uint32_t unused1[6];
|
||||||
grub_uint32_t bios_handoff;
|
grub_uint32_t bios_handoff;
|
||||||
#define GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED 1
|
|
||||||
#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNED 2
|
|
||||||
#define GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED 8
|
|
||||||
#define GRUB_AHCI_BIOS_HANDOFF_RWC 8
|
|
||||||
grub_uint32_t unused2[53];
|
grub_uint32_t unused2[53];
|
||||||
struct grub_ahci_hba_port ports[32];
|
struct grub_ahci_hba_port ports[32];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GRUB_AHCI_HBA_CAP_NPORTS_MASK = 0x1f
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GRUB_AHCI_HBA_GLOBAL_CONTROL_INTR_EN = 0x00000002,
|
||||||
|
GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN = 0x80000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GRUB_AHCI_BIOS_HANDOFF_BIOS_OWNED = 1,
|
||||||
|
GRUB_AHCI_BIOS_HANDOFF_OS_OWNED = 2,
|
||||||
|
GRUB_AHCI_BIOS_HANDOFF_OS_OWNERSHIP_CHANGED = 8,
|
||||||
|
GRUB_AHCI_BIOS_HANDOFF_RWC = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct grub_ahci_device
|
struct grub_ahci_device
|
||||||
{
|
{
|
||||||
struct grub_ahci_device *next;
|
struct grub_ahci_device *next;
|
||||||
|
@ -62,19 +102,41 @@ struct grub_ahci_device
|
||||||
int num;
|
int num;
|
||||||
struct grub_pci_dma_chunk *command_list_chunk;
|
struct grub_pci_dma_chunk *command_list_chunk;
|
||||||
volatile struct grub_ahci_cmd_head *command_list;
|
volatile struct grub_ahci_cmd_head *command_list;
|
||||||
|
struct grub_pci_dma_chunk *command_table_chunk;
|
||||||
|
volatile struct grub_ahci_cmd_table *command_table;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GRUB_AHCI_CONFIG_READ = 0,
|
||||||
|
GRUB_AHCI_CONFIG_CFIS_LENGTH_MASK = 0x1f,
|
||||||
|
GRUB_AHCI_CONFIG_ATAPI = 0x20,
|
||||||
|
GRUB_AHCI_CONFIG_WRITE = 0x40,
|
||||||
|
GRUB_AHCI_CONFIG_PREFETCH = 0x80,
|
||||||
|
GRUB_AHCI_CONFIG_RESET = 0x100,
|
||||||
|
GRUB_AHCI_CONFIG_BIST = 0x200,
|
||||||
|
GRUB_AHCI_CONFIG_CLEAR_R_OK = 0x400,
|
||||||
|
GRUB_AHCI_CONFIG_PMP_MASK = 0xf000,
|
||||||
|
GRUB_AHCI_CONFIG_PRDT_LENGTH_MASK = 0xffff0000,
|
||||||
|
};
|
||||||
|
#define GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT 0
|
||||||
|
#define GRUB_AHCI_CONFIG_PMP_SHIFT 12
|
||||||
|
#define GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT 16
|
||||||
|
#define GRUB_AHCI_INTERRUPT_ON_COMPLETE 0x80000000
|
||||||
|
|
||||||
|
#define GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH 0x200000
|
||||||
|
|
||||||
static struct grub_ahci_device *grub_ahci_devices;
|
static struct grub_ahci_device *grub_ahci_devices;
|
||||||
static int numdevs;
|
static int numdevs;
|
||||||
|
|
||||||
static int NESTED_FUNC_ATTR
|
static int NESTED_FUNC_ATTR
|
||||||
grub_ahci_pciinit (grub_pci_device_t dev,
|
grub_ahci_pciinit (grub_pci_device_t dev,
|
||||||
grub_pci_id_t pciid __attribute__ ((unused)))
|
grub_pci_id_t pciid __attribute__ ((unused)))
|
||||||
{
|
{
|
||||||
grub_pci_address_t addr;
|
grub_pci_address_t addr;
|
||||||
grub_uint32_t class;
|
grub_uint32_t class;
|
||||||
grub_uint32_t bar;
|
grub_uint32_t bar;
|
||||||
int nports;
|
unsigned i, nports;
|
||||||
volatile struct grub_ahci_hba *hba;
|
volatile struct grub_ahci_hba *hba;
|
||||||
|
|
||||||
/* Read class. */
|
/* Read class. */
|
||||||
|
@ -88,8 +150,8 @@ grub_ahci_pciinit (grub_pci_device_t dev,
|
||||||
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
|
addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG5);
|
||||||
bar = grub_pci_read (addr);
|
bar = grub_pci_read (addr);
|
||||||
|
|
||||||
if (bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
|
if ((bar & (GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_TYPE_MASK
|
||||||
| GRUB_PCI_ADDR_MEM_PREFETCH)
|
| GRUB_PCI_ADDR_MEM_PREFETCH))
|
||||||
!= (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
|
!= (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_32))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -98,7 +160,7 @@ grub_ahci_pciinit (grub_pci_device_t dev,
|
||||||
|
|
||||||
hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
|
hba->global_control |= GRUB_AHCI_HBA_GLOBAL_CONTROL_AHCI_EN;
|
||||||
|
|
||||||
nports = hba->cap & GRUB_AHCI_HBA_CAP_MASK;
|
nports = (hba->cap & GRUB_AHCI_HBA_CAP_NPORTS_MASK) + 1;
|
||||||
|
|
||||||
if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
|
if (! (hba->bios_handoff & GRUB_AHCI_BIOS_HANDOFF_OS_OWNED))
|
||||||
{
|
{
|
||||||
|
@ -122,11 +184,13 @@ grub_ahci_pciinit (grub_pci_device_t dev,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
grub_dprintf ("ahci", "AHCI is already in OS mode\n");
|
grub_dprintf ("ahci", "AHCI is already in OS mode\n");
|
||||||
|
grub_dprintf ("ahci", "%d AHCI ports\n", nports);
|
||||||
|
|
||||||
for (i = 0; i < nports; i++)
|
for (i = 0; i < nports; i++)
|
||||||
{
|
{
|
||||||
struct grub_ahci_device *adev;
|
struct grub_ahci_device *adev;
|
||||||
struct grub_pci_dma_chunk *command_list;
|
struct grub_pci_dma_chunk *command_list;
|
||||||
|
struct grub_pci_dma_chunk *command_table;
|
||||||
|
|
||||||
if (!(hba->ports_implemented & (1 << i)))
|
if (!(hba->ports_implemented & (1 << i)))
|
||||||
continue;
|
continue;
|
||||||
|
@ -136,18 +200,34 @@ grub_ahci_pciinit (grub_pci_device_t dev,
|
||||||
if (!command_list)
|
if (!command_list)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
adev = grub_malloc (sizeof (*adev));
|
command_table = grub_memalign_dma32 (1024,
|
||||||
if (!adev)
|
sizeof (struct grub_ahci_cmd_table));
|
||||||
|
if (!command_table)
|
||||||
{
|
{
|
||||||
grub_dma_free (command_list);
|
grub_dma_free (command_list);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adev = grub_malloc (sizeof (*adev));
|
||||||
|
if (!adev)
|
||||||
|
{
|
||||||
|
grub_dma_free (command_list);
|
||||||
|
grub_dma_free (command_table);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_dprintf ("ahci", "found device ahci%d (port %d)\n", numdevs, i);
|
||||||
|
|
||||||
adev->hba = hba;
|
adev->hba = hba;
|
||||||
adev->port = i;
|
adev->port = i;
|
||||||
adev->num = numdevs++;
|
adev->num = numdevs++;
|
||||||
adev->command_list_chunk = command_list;
|
adev->command_list_chunk = command_list;
|
||||||
adev->command_list = grub_dma_get_virt (command_list);
|
adev->command_list = grub_dma_get_virt (command_list);
|
||||||
|
adev->command_table_chunk = command_table;
|
||||||
|
adev->command_table = grub_dma_get_virt (command_table);
|
||||||
|
adev->command_list->command_table_base
|
||||||
|
= grub_dma_get_phys (command_table);
|
||||||
|
|
||||||
adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list);
|
adev->hba->ports[i].command_list_base = grub_dma_get_phys (command_list);
|
||||||
grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
|
grub_list_push (GRUB_AS_LIST_P (&grub_ahci_devices),
|
||||||
GRUB_AS_LIST (adev));
|
GRUB_AS_LIST (adev));
|
||||||
|
@ -167,12 +247,12 @@ grub_ahci_initialize (void)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
grub_ahci_iterate (int (*hook) (int bus, int luns))
|
grub_ahci_iterate (int (*hook) (int id, int bus))
|
||||||
{
|
{
|
||||||
struct grub_ahci_device *dev;
|
struct grub_ahci_device *dev;
|
||||||
|
|
||||||
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
|
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
|
||||||
if (hook (dev->num, 1))
|
if (hook (GRUB_SCSI_SUBSYSTEM_AHCI, dev->num))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -195,95 +275,130 @@ find_free_cmd_slot (struct grub_ahci_device *dev)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GRUB_AHCI_FIS_REG_H2D = 0x27
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int register_map[11] = { 3 /* Features */,
|
||||||
|
12 /* Sectors */,
|
||||||
|
4 /* LBA low */,
|
||||||
|
5 /* LBA mid */,
|
||||||
|
6 /* LBA high */,
|
||||||
|
7 /* Device */,
|
||||||
|
2 /* CMD register */,
|
||||||
|
13 /* Sectors 48 */,
|
||||||
|
8 /* LBA48 low */,
|
||||||
|
9 /* LBA48 mid */,
|
||||||
|
10 /* LBA48 high */ };
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_ahci_packet (struct grub_ahci_device *dev, char *packet,
|
grub_ahci_readwrite (grub_ata_t disk,
|
||||||
grub_size_t size)
|
struct grub_disk_ata_pass_through_parms *parms)
|
||||||
{
|
{
|
||||||
|
struct grub_ahci_device *dev = (struct grub_ahci_device *) disk->data;
|
||||||
|
struct grub_pci_dma_chunk *bufc;
|
||||||
|
grub_uint64_t endtime;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
grub_dprintf("ahci", "grub_ahci_read (size=%llu, cmdsize = %llu)\n",
|
||||||
|
(unsigned long long) parms->size,
|
||||||
|
(unsigned long long) parms->cmdsize);
|
||||||
|
|
||||||
|
if (parms->cmdsize != 0 && parms->cmdsize != 12 && parms->cmdsize != 16)
|
||||||
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "incorrect ATAPI command size");
|
||||||
|
|
||||||
|
if (parms->size > GRUB_AHCI_PRDT_MAX_CHUNK_LENGTH)
|
||||||
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "too big data buffer");
|
||||||
|
|
||||||
|
bufc = grub_memalign_dma32 (1024, parms->size + (parms->size & 1));
|
||||||
|
|
||||||
|
/* FIXME: support port multipliers. */
|
||||||
|
dev->command_list[0].config
|
||||||
|
= (4 << GRUB_AHCI_CONFIG_CFIS_LENGTH_SHIFT)
|
||||||
|
| GRUB_AHCI_CONFIG_CLEAR_R_OK
|
||||||
|
| (0 << GRUB_AHCI_CONFIG_PMP_SHIFT)
|
||||||
|
| (1 << GRUB_AHCI_CONFIG_PRDT_LENGTH_SHIFT)
|
||||||
|
| (parms->cmdsize ? GRUB_AHCI_CONFIG_ATAPI : 0)
|
||||||
|
| (parms->write ? GRUB_AHCI_CONFIG_WRITE : GRUB_AHCI_CONFIG_READ);
|
||||||
|
dev->command_list[0].transfered = 0;
|
||||||
|
dev->command_list[0].command_table_base
|
||||||
|
= grub_dma_get_phys (dev->command_table_chunk);
|
||||||
|
grub_memset ((char *) dev->command_list[0].unused, 0,
|
||||||
|
sizeof (dev->command_list[0].unused));
|
||||||
|
grub_memset ((char *) &dev->command_table[0], 0,
|
||||||
|
sizeof (dev->command_table[0]));
|
||||||
|
if (parms->cmdsize)
|
||||||
|
grub_memcpy ((char *) dev->command_table[0].command, parms->cmd,
|
||||||
|
parms->cmdsize);
|
||||||
|
|
||||||
|
dev->command_table[0].cfis[0] = GRUB_AHCI_FIS_REG_H2D;
|
||||||
|
for (i = 0; i < sizeof (parms->taskfile.raw); i++)
|
||||||
|
dev->command_table[0].cfis[register_map[i]] = parms->taskfile.raw[i];
|
||||||
|
|
||||||
|
dev->command_table[0].prdt[0].data_base = grub_dma_get_phys (bufc);
|
||||||
|
dev->command_table[0].prdt[0].unused = 0;
|
||||||
|
dev->command_table[0].prdt[0].size = (parms->size + (parms->size & 1) - 1)
|
||||||
|
| GRUB_AHCI_INTERRUPT_ON_COMPLETE;
|
||||||
|
|
||||||
|
if (parms->write)
|
||||||
|
grub_memcpy ((char *) grub_dma_get_virt (bufc), parms->buffer, parms->size);
|
||||||
|
|
||||||
|
grub_dprintf ("ahci", "AHCI command schedulded\n");
|
||||||
|
dev->hba->ports[dev->port].inten = (1 << 5);
|
||||||
|
dev->hba->ports[dev->port].intstatus = (1 << 5);
|
||||||
|
dev->hba->ports[dev->port].command_issue |= 1;
|
||||||
|
dev->hba->ports[dev->port].command |= 1;
|
||||||
|
|
||||||
|
endtime = grub_get_time_ms () + 1000;
|
||||||
|
while (!(dev->hba->ports[dev->port].intstatus & (1 << 5)))
|
||||||
|
if (grub_get_time_ms () > endtime)
|
||||||
|
{
|
||||||
|
grub_dprintf ("ahci", "AHCI timeout\n");
|
||||||
|
dev->hba->ports[dev->port].command &= ~1;
|
||||||
|
/* FIXME: free resources. */
|
||||||
|
return grub_error (GRUB_ERR_IO, "AHCI transfer timeouted");
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_dprintf ("ahci", "AHCI command completed succesfully\n");
|
||||||
|
dev->hba->ports[dev->port].command &= ~1;
|
||||||
|
|
||||||
|
if (!parms->write)
|
||||||
|
grub_memcpy (parms->buffer, (char *) grub_dma_get_virt (bufc), parms->size);
|
||||||
|
grub_dma_free (bufc);
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_ahci_read (struct grub_scsi *scsi,
|
grub_ahci_open (int id, int devnum, struct grub_ata *ata)
|
||||||
grub_size_t cmdsize __attribute__((unused)),
|
|
||||||
char *cmd, grub_size_t size, char *buf)
|
|
||||||
{
|
|
||||||
struct grub_ahci_device *dev = (struct grub_ahci_device *) scsi->data;
|
|
||||||
|
|
||||||
grub_dprintf("ahci", "grub_ahci_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_ahci_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, "AHCI write not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
static grub_err_t
|
|
||||||
grub_ahci_open (int devnum, struct grub_scsi *scsi)
|
|
||||||
{
|
{
|
||||||
struct grub_ahci_device *dev;
|
struct grub_ahci_device *dev;
|
||||||
|
|
||||||
|
if (id != GRUB_SCSI_SUBSYSTEM_AHCI)
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not an AHCI device");
|
||||||
|
|
||||||
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
|
FOR_LIST_ELEMENTS(dev, grub_ahci_devices)
|
||||||
{
|
{
|
||||||
if (dev->num == devnum)
|
if (dev->num == devnum)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_dprintf ("ata", "opening AHCI dev `ahci%d'\n", devnum);
|
|
||||||
|
|
||||||
if (! dev)
|
if (! dev)
|
||||||
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such AHCI device");
|
||||||
|
|
||||||
scsi->data = dev;
|
grub_dprintf ("ahci", "opening AHCI dev `ahci%d'\n", dev->num);
|
||||||
|
|
||||||
|
ata->data = dev;
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct grub_ata_dev grub_ahci_dev =
|
||||||
static struct grub_scsi_dev grub_ahci_dev =
|
|
||||||
{
|
{
|
||||||
.name = "ahci",
|
|
||||||
.id = GRUB_SCSI_SUBSYSTEM_AHCI,
|
|
||||||
.iterate = grub_ahci_iterate,
|
.iterate = grub_ahci_iterate,
|
||||||
.open = grub_ahci_open,
|
.open = grub_ahci_open,
|
||||||
.read = grub_ahci_read,
|
.readwrite = grub_ahci_readwrite,
|
||||||
.write = grub_ahci_write
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -291,8 +406,8 @@ static struct grub_scsi_dev grub_ahci_dev =
|
||||||
GRUB_MOD_INIT(ahci)
|
GRUB_MOD_INIT(ahci)
|
||||||
{
|
{
|
||||||
/* To prevent two drivers operating on the same disks. */
|
/* To prevent two drivers operating on the same disks. */
|
||||||
grub_disk_firmware_is_tainted = 1;
|
// grub_disk_firmware_is_tainted = 1;
|
||||||
if (grub_disk_firmware_fini)
|
if (0 && grub_disk_firmware_fini)
|
||||||
{
|
{
|
||||||
grub_disk_firmware_fini ();
|
grub_disk_firmware_fini ();
|
||||||
grub_disk_firmware_fini = NULL;
|
grub_disk_firmware_fini = NULL;
|
||||||
|
@ -302,10 +417,10 @@ GRUB_MOD_INIT(ahci)
|
||||||
grub_ahci_initialize ();
|
grub_ahci_initialize ();
|
||||||
|
|
||||||
/* AHCI devices are handled by scsi.mod. */
|
/* AHCI devices are handled by scsi.mod. */
|
||||||
grub_scsi_dev_register (&grub_ahci_dev);
|
grub_ata_dev_register (&grub_ahci_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
GRUB_MOD_FINI(ahci)
|
GRUB_MOD_FINI(ahci)
|
||||||
{
|
{
|
||||||
grub_scsi_dev_unregister (&grub_ahci_dev);
|
grub_ata_dev_unregister (&grub_ahci_dev);
|
||||||
}
|
}
|
||||||
|
|
821
disk/ata.c
821
disk/ata.c
File diff suppressed because it is too large
Load diff
520
disk/ata_pthru.c
520
disk/ata_pthru.c
|
@ -18,90 +18,496 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <grub/ata.h>
|
#include <grub/ata.h>
|
||||||
|
#include <grub/scsi.h>
|
||||||
#include <grub/disk.h>
|
#include <grub/disk.h>
|
||||||
#include <grub/dl.h>
|
#include <grub/dl.h>
|
||||||
#include <grub/mm.h>
|
#include <grub/mm.h>
|
||||||
|
#include <grub/pci.h>
|
||||||
|
#include <grub/cs5536.h>
|
||||||
|
#include <grub/time.h>
|
||||||
|
|
||||||
|
/* At the moment, only two IDE ports are supported. */
|
||||||
|
static const grub_port_t grub_pata_ioaddress[] = { GRUB_ATA_CH0_PORT1,
|
||||||
|
GRUB_ATA_CH1_PORT1 };
|
||||||
|
static const grub_port_t grub_pata_ioaddress2[] = { GRUB_ATA_CH0_PORT2,
|
||||||
|
GRUB_ATA_CH1_PORT2 };
|
||||||
|
|
||||||
/* ATA pass through support, used by hdparm.mod. */
|
struct grub_pata_device
|
||||||
static grub_err_t
|
|
||||||
grub_ata_pass_through (grub_disk_t disk,
|
|
||||||
struct grub_disk_ata_pass_through_parms *parms)
|
|
||||||
{
|
{
|
||||||
if (disk->dev->id != GRUB_DISK_DEVICE_ATA_ID)
|
/* IDE port to use. */
|
||||||
return grub_error (GRUB_ERR_BAD_DEVICE,
|
int port;
|
||||||
"device not accessed via ata.mod");
|
|
||||||
|
|
||||||
struct grub_ata_device *dev = (struct grub_ata_device *) disk->data;
|
/* IO addresses on which the registers for this device can be
|
||||||
|
found. */
|
||||||
|
grub_port_t ioaddress;
|
||||||
|
grub_port_t ioaddress2;
|
||||||
|
|
||||||
if (! (parms->size == 0 || parms->size == GRUB_DISK_SECTOR_SIZE))
|
/* Two devices can be connected to a single cable. Use this field
|
||||||
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
to select device 0 (commonly known as "master") or device 1
|
||||||
"ATA multi-sector read and DATA OUT not implemented");
|
(commonly known as "slave"). */
|
||||||
|
int device;
|
||||||
|
|
||||||
grub_dprintf ("ata", "ata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
|
struct grub_pata_device *next;
|
||||||
parms->taskfile[GRUB_ATA_REG_CMD],
|
};
|
||||||
parms->taskfile[GRUB_ATA_REG_FEATURES],
|
|
||||||
parms->taskfile[GRUB_ATA_REG_SECTORS]);
|
|
||||||
grub_dprintf ("ata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
|
|
||||||
parms->taskfile[GRUB_ATA_REG_LBAHIGH],
|
|
||||||
parms->taskfile[GRUB_ATA_REG_LBAMID],
|
|
||||||
parms->taskfile[GRUB_ATA_REG_LBALOW], parms->size);
|
|
||||||
|
|
||||||
/* Set registers. */
|
static struct grub_pata_device *grub_pata_devices;
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_DISK, 0xE0 | dev->device << 4
|
|
||||||
| (parms->taskfile[GRUB_ATA_REG_DISK] & 0xf));
|
|
||||||
if (grub_ata_check_ready (dev))
|
|
||||||
return grub_errno;
|
|
||||||
|
|
||||||
int i;
|
static inline void
|
||||||
for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
|
grub_pata_regset (struct grub_pata_device *dev, int reg, int val)
|
||||||
grub_ata_regset (dev, i, parms->taskfile[i]);
|
{
|
||||||
|
grub_outb (val, dev->ioaddress + reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Start command. */
|
static inline grub_uint8_t
|
||||||
grub_ata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile[GRUB_ATA_REG_CMD]);
|
grub_pata_regget (struct grub_pata_device *dev, int reg)
|
||||||
|
{
|
||||||
|
return grub_inb (dev->ioaddress + reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Wait for !BSY. */
|
static inline void
|
||||||
if (grub_ata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
grub_pata_regset2 (struct grub_pata_device *dev, int reg, int val)
|
||||||
return grub_errno;
|
{
|
||||||
|
grub_outb (val, dev->ioaddress2 + reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check status. */
|
static inline grub_uint8_t
|
||||||
grub_int8_t sts = grub_ata_regget (dev, GRUB_ATA_REG_STATUS);
|
grub_pata_regget2 (struct grub_pata_device *dev, int reg)
|
||||||
grub_dprintf ("ata", "status=0x%x\n", sts);
|
{
|
||||||
|
return grub_inb (dev->ioaddress2 + reg);
|
||||||
|
}
|
||||||
|
|
||||||
/* Transfer data. */
|
/* Wait for !BSY. */
|
||||||
if ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR)) == GRUB_ATA_STATUS_DRQ)
|
static grub_err_t
|
||||||
|
grub_pata_wait_not_busy (struct grub_pata_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_pata_regget (dev, GRUB_ATA_REG_STATUS))
|
||||||
|
& GRUB_ATA_STATUS_BUSY)
|
||||||
{
|
{
|
||||||
if (parms->size != GRUB_DISK_SECTOR_SIZE)
|
if (i >= milliseconds)
|
||||||
return grub_error (GRUB_ERR_READ_ERROR, "DRQ unexpected");
|
{
|
||||||
grub_ata_pio_read (dev, parms->buffer, GRUB_DISK_SECTOR_SIZE);
|
grub_dprintf ("pata", "timeout: %dms, status=0x%x\n",
|
||||||
|
milliseconds, sts);
|
||||||
|
return grub_error (GRUB_ERR_TIMEOUT, "PATA timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_millisleep (1);
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return registers. */
|
|
||||||
for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
|
|
||||||
parms->taskfile[i] = grub_ata_regget (dev, i);
|
|
||||||
|
|
||||||
grub_dprintf ("ata", "status=0x%x, error=0x%x, sectors=0x%x\n",
|
|
||||||
parms->taskfile[GRUB_ATA_REG_STATUS],
|
|
||||||
parms->taskfile[GRUB_ATA_REG_ERROR],
|
|
||||||
parms->taskfile[GRUB_ATA_REG_SECTORS]);
|
|
||||||
|
|
||||||
if (parms->taskfile[GRUB_ATA_REG_STATUS]
|
|
||||||
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
|
||||||
return grub_error (GRUB_ERR_READ_ERROR, "ATA passthrough failed");
|
|
||||||
|
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline grub_err_t
|
||||||
|
grub_pata_check_ready (struct grub_pata_device *dev)
|
||||||
|
{
|
||||||
|
if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS) & GRUB_ATA_STATUS_BUSY)
|
||||||
|
return grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_STD);
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
grub_pata_wait (void)
|
||||||
|
{
|
||||||
|
grub_millisleep (50);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grub_pata_pio_read (struct grub_pata_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));
|
||||||
|
if (size & 1)
|
||||||
|
buf[size - 1] = (char) grub_le_to_cpu16 (grub_inw (dev->ioaddress
|
||||||
|
+ GRUB_ATA_REG_DATA));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
grub_pata_pio_write (struct grub_pata_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATA pass through support, used by hdparm.mod. */
|
||||||
|
static grub_err_t
|
||||||
|
grub_pata_readwrite (struct grub_ata *disk,
|
||||||
|
struct grub_disk_ata_pass_through_parms *parms)
|
||||||
|
{
|
||||||
|
struct grub_pata_device *dev = (struct grub_pata_device *) disk->data;
|
||||||
|
grub_size_t nread = 0;
|
||||||
|
|
||||||
|
if (! (parms->cmdsize == 0 || parms->cmdsize == 12))
|
||||||
|
return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
|
||||||
|
"ATAPI non-12 byte commands not supported");
|
||||||
|
|
||||||
|
grub_dprintf ("pata", "pata_pass_through: cmd=0x%x, features=0x%x, sectors=0x%x\n",
|
||||||
|
parms->taskfile.cmd,
|
||||||
|
parms->taskfile.features,
|
||||||
|
parms->taskfile.sectors);
|
||||||
|
grub_dprintf ("pata", "lba_high=0x%x, lba_mid=0x%x, lba_low=0x%x, size=%d\n",
|
||||||
|
parms->taskfile.lba_high,
|
||||||
|
parms->taskfile.lba_mid,
|
||||||
|
parms->taskfile.lba_low, parms->size);
|
||||||
|
|
||||||
|
/* Set registers. */
|
||||||
|
grub_pata_regset (dev, GRUB_ATA_REG_DISK, (parms->cmdsize ? 0 : 0xE0)
|
||||||
|
| dev->device << 4
|
||||||
|
| (parms->taskfile.disk & 0xf));
|
||||||
|
if (grub_pata_check_ready (dev))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = GRUB_ATA_REG_SECTORS; i <= GRUB_ATA_REG_LBAHIGH; i++)
|
||||||
|
grub_pata_regset (dev, i,
|
||||||
|
parms->taskfile.raw[7 + (i - GRUB_ATA_REG_SECTORS)]);
|
||||||
|
for (i = GRUB_ATA_REG_FEATURES; i <= GRUB_ATA_REG_LBAHIGH; i++)
|
||||||
|
grub_pata_regset (dev, i, parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES]);
|
||||||
|
|
||||||
|
/* Start command. */
|
||||||
|
grub_pata_regset (dev, GRUB_ATA_REG_CMD, parms->taskfile.cmd);
|
||||||
|
|
||||||
|
/* Wait for !BSY. */
|
||||||
|
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
/* Check status. */
|
||||||
|
grub_int8_t sts = grub_pata_regget (dev, GRUB_ATA_REG_STATUS);
|
||||||
|
grub_dprintf ("pata", "status=0x%x\n", sts);
|
||||||
|
|
||||||
|
if (parms->cmdsize)
|
||||||
|
{
|
||||||
|
grub_uint8_t irs = grub_pata_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) == GRUB_ATAPI_IREASON_CMD_OUT))
|
||||||
|
return grub_error (GRUB_ERR_READ_ERROR, "ATAPI protocol error");
|
||||||
|
/* Write the packet. */
|
||||||
|
grub_pata_pio_write (dev, parms->cmd, parms->cmdsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Transfer data. */
|
||||||
|
while (nread < parms->size
|
||||||
|
&& ((sts & (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
||||||
|
== GRUB_ATA_STATUS_DRQ)
|
||||||
|
&& (!parms->cmdsize
|
||||||
|
|| ((grub_pata_regget (dev, GRUB_ATAPI_REG_IREASON)
|
||||||
|
& GRUB_ATAPI_IREASON_MASK) == GRUB_ATAPI_IREASON_DATA_IN)))
|
||||||
|
{
|
||||||
|
unsigned cnt;
|
||||||
|
if (parms->cmdsize)
|
||||||
|
{
|
||||||
|
cnt = grub_pata_regget (dev, GRUB_ATAPI_REG_CNTHIGH) << 8
|
||||||
|
| grub_pata_regget (dev, GRUB_ATAPI_REG_CNTLOW);
|
||||||
|
grub_dprintf("pata", "DRQ count=%u\n", cnt);
|
||||||
|
|
||||||
|
/* Count of last transfer may be uneven. */
|
||||||
|
if (! (0 < cnt && cnt <= parms->size - nread
|
||||||
|
&& (! (cnt & 1) || cnt == parms->size - nread)))
|
||||||
|
return grub_error (GRUB_ERR_READ_ERROR,
|
||||||
|
"invalid ATAPI transfer count");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cnt = GRUB_DISK_SECTOR_SIZE;
|
||||||
|
if (cnt > parms->size - nread)
|
||||||
|
cnt = parms->size - nread;
|
||||||
|
|
||||||
|
if (parms->write)
|
||||||
|
grub_pata_pio_write (dev, (char *) parms->buffer + nread, cnt);
|
||||||
|
else
|
||||||
|
grub_pata_pio_read (dev, (char *) parms->buffer + nread, cnt);
|
||||||
|
|
||||||
|
nread += cnt;
|
||||||
|
}
|
||||||
|
if (parms->write)
|
||||||
|
{
|
||||||
|
/* Check for write error. */
|
||||||
|
if (grub_pata_wait_not_busy (dev, GRUB_ATA_TOUT_DATA))
|
||||||
|
return grub_errno;
|
||||||
|
|
||||||
|
if (grub_pata_regget (dev, GRUB_ATA_REG_STATUS)
|
||||||
|
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
||||||
|
return grub_error (GRUB_ERR_WRITE_ERROR, "ATA write error");
|
||||||
|
}
|
||||||
|
parms->size = nread;
|
||||||
|
|
||||||
|
/* Return registers. */
|
||||||
|
for (i = GRUB_ATA_REG_ERROR; i <= GRUB_ATA_REG_STATUS; i++)
|
||||||
|
parms->taskfile.raw[i - GRUB_ATA_REG_FEATURES] = grub_pata_regget (dev, i);
|
||||||
|
|
||||||
|
grub_dprintf ("pata", "status=0x%x, error=0x%x, sectors=0x%x\n",
|
||||||
|
parms->taskfile.status,
|
||||||
|
parms->taskfile.error,
|
||||||
|
parms->taskfile.sectors);
|
||||||
|
|
||||||
|
if (parms->taskfile.status
|
||||||
|
& (GRUB_ATA_STATUS_DRQ | GRUB_ATA_STATUS_ERR))
|
||||||
|
return grub_error (GRUB_ERR_READ_ERROR, "PATA passthrough failed");
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static grub_err_t
|
||||||
|
check_device (struct grub_pata_device *dev)
|
||||||
|
{
|
||||||
|
grub_pata_regset (dev, GRUB_ATA_REG_DISK, dev->device << 4);
|
||||||
|
grub_pata_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_pata_regset (dev, GRUB_ATA_REG_SECTORS, 0x5A);
|
||||||
|
grub_pata_wait ();
|
||||||
|
grub_uint8_t sec = grub_pata_regget (dev, GRUB_ATA_REG_SECTORS);
|
||||||
|
grub_dprintf ("ata", "sectors=0x%x\n", sec);
|
||||||
|
if (sec != 0x5A)
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no device connected");
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static grub_err_t
|
||||||
|
grub_pata_device_initialize (int port, int device, int addr, int addr2)
|
||||||
|
{
|
||||||
|
struct grub_pata_device *dev;
|
||||||
|
struct grub_pata_device **devp;
|
||||||
|
grub_err_t err;
|
||||||
|
|
||||||
|
grub_dprintf ("pata", "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;
|
||||||
|
|
||||||
|
/* Register the device. */
|
||||||
|
for (devp = &grub_pata_devices; *devp; devp = &(*devp)->next);
|
||||||
|
*devp = dev;
|
||||||
|
|
||||||
|
err = check_device (dev);
|
||||||
|
if (err)
|
||||||
|
grub_print_error ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NESTED_FUNC_ATTR
|
||||||
|
grub_pata_pciinit (grub_pci_device_t dev,
|
||||||
|
grub_pci_id_t pciid)
|
||||||
|
{
|
||||||
|
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 == GRUB_CS5536_PCIID)
|
||||||
|
{
|
||||||
|
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_pata_ioaddress[i];
|
||||||
|
regb = grub_pata_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 ("pata",
|
||||||
|
"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_pata_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_pata_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_pata_initialize (void)
|
||||||
|
{
|
||||||
|
grub_pci_iterate (grub_pata_pciinit);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static grub_err_t
|
||||||
|
grub_pata_open (int id, int devnum, struct grub_ata *ata)
|
||||||
|
{
|
||||||
|
struct grub_pata_device *dev;
|
||||||
|
struct grub_pata_device *devfnd = 0;
|
||||||
|
grub_err_t err;
|
||||||
|
|
||||||
|
if (id != GRUB_SCSI_SUBSYSTEM_PATA)
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a PATA device");
|
||||||
|
|
||||||
|
for (dev = grub_pata_devices; dev; dev = dev->next)
|
||||||
|
{
|
||||||
|
if (dev->port * 2 + dev->device == devnum)
|
||||||
|
{
|
||||||
|
devfnd = dev;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grub_dprintf ("pata", "opening PATA dev `ata%d'\n", devnum);
|
||||||
|
|
||||||
|
if (! devfnd)
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such PATA device");
|
||||||
|
|
||||||
|
err = check_device (devfnd);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
ata->data = devfnd;
|
||||||
|
|
||||||
|
return GRUB_ERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
grub_pata_iterate (int (*hook) (int id, int bus))
|
||||||
|
{
|
||||||
|
struct grub_pata_device *dev;
|
||||||
|
|
||||||
|
for (dev = grub_pata_devices; dev; dev = dev->next)
|
||||||
|
if (hook (GRUB_SCSI_SUBSYSTEM_PATA, dev->port * 2 + dev->device))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct grub_ata_dev grub_pata_dev =
|
||||||
|
{
|
||||||
|
.iterate = grub_pata_iterate,
|
||||||
|
.open = grub_pata_open,
|
||||||
|
.readwrite = grub_pata_readwrite,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GRUB_MOD_INIT(ata_pthru)
|
GRUB_MOD_INIT(ata_pthru)
|
||||||
{
|
{
|
||||||
/* Register ATA pass through function. */
|
/* To prevent two drivers operating on the same disks. */
|
||||||
grub_disk_ata_pass_through = grub_ata_pass_through;
|
grub_disk_firmware_is_tainted = 1;
|
||||||
|
if (grub_disk_firmware_fini)
|
||||||
|
{
|
||||||
|
grub_disk_firmware_fini ();
|
||||||
|
grub_disk_firmware_fini = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATA initialization. */
|
||||||
|
grub_pata_initialize ();
|
||||||
|
|
||||||
|
grub_ata_dev_register (&grub_pata_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
GRUB_MOD_FINI(ata_pthru)
|
GRUB_MOD_FINI(ata_pthru)
|
||||||
{
|
{
|
||||||
if (grub_disk_ata_pass_through == grub_ata_pass_through)
|
grub_ata_dev_unregister (&grub_pata_dev);
|
||||||
grub_disk_ata_pass_through = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
38
disk/scsi.c
38
disk/scsi.c
|
@ -30,6 +30,12 @@
|
||||||
|
|
||||||
static grub_scsi_dev_t grub_scsi_dev_list;
|
static grub_scsi_dev_t grub_scsi_dev_list;
|
||||||
|
|
||||||
|
char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5] = {
|
||||||
|
[GRUB_SCSI_SUBSYSTEM_USBMS] = "usb",
|
||||||
|
[GRUB_SCSI_SUBSYSTEM_PATA] = "ata",
|
||||||
|
[GRUB_SCSI_SUBSYSTEM_AHCI] = "ahci"
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
grub_scsi_dev_register (grub_scsi_dev_t dev)
|
grub_scsi_dev_register (grub_scsi_dev_t dev)
|
||||||
{
|
{
|
||||||
|
@ -318,9 +324,9 @@ grub_scsi_iterate (int (*hook) (const char *name))
|
||||||
{
|
{
|
||||||
grub_scsi_dev_t p;
|
grub_scsi_dev_t p;
|
||||||
|
|
||||||
auto int scsi_iterate (int bus, int luns);
|
auto int scsi_iterate (int id, int bus, int luns);
|
||||||
|
|
||||||
int scsi_iterate (int bus, int luns)
|
int scsi_iterate (int id, int bus, int luns)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -329,7 +335,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
|
||||||
{
|
{
|
||||||
char *sname;
|
char *sname;
|
||||||
int ret;
|
int ret;
|
||||||
sname = grub_xasprintf ("%s%d", p->name, bus);
|
sname = grub_xasprintf ("%s%d", grub_scsi_names[id], bus);
|
||||||
if (!sname)
|
if (!sname)
|
||||||
return 1;
|
return 1;
|
||||||
ret = hook (sname);
|
ret = hook (sname);
|
||||||
|
@ -343,7 +349,7 @@ grub_scsi_iterate (int (*hook) (const char *name))
|
||||||
{
|
{
|
||||||
char *sname;
|
char *sname;
|
||||||
int ret;
|
int ret;
|
||||||
sname = grub_xasprintf ("%s%d%c", p->name, bus, 'a' + i);
|
sname = grub_xasprintf ("%s%d%c", grub_scsi_names[id], bus, 'a' + i);
|
||||||
if (!sname)
|
if (!sname)
|
||||||
return 1;
|
return 1;
|
||||||
ret = hook (sname);
|
ret = hook (sname);
|
||||||
|
@ -370,6 +376,7 @@ grub_scsi_open (const char *name, grub_disk_t disk)
|
||||||
int lun, bus;
|
int lun, bus;
|
||||||
grub_uint64_t maxtime;
|
grub_uint64_t maxtime;
|
||||||
const char *nameend;
|
const char *nameend;
|
||||||
|
unsigned id;
|
||||||
|
|
||||||
nameend = name + grub_strlen (name) - 1;
|
nameend = name + grub_strlen (name) - 1;
|
||||||
/* Try to detect a LUN ('a'-'z'), otherwise just use the first
|
/* Try to detect a LUN ('a'-'z'), otherwise just use the first
|
||||||
|
@ -394,15 +401,25 @@ grub_scsi_open (const char *name, grub_disk_t disk)
|
||||||
if (! scsi)
|
if (! scsi)
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
|
for (id = 0; id < ARRAY_SIZE (grub_scsi_names); id++)
|
||||||
|
if (grub_strncmp (grub_scsi_names[id], name, nameend - name) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (id == ARRAY_SIZE (grub_scsi_names))
|
||||||
|
{
|
||||||
|
grub_free (scsi);
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
|
||||||
|
}
|
||||||
|
|
||||||
for (p = grub_scsi_dev_list; p; p = p->next)
|
for (p = grub_scsi_dev_list; p; p = p->next)
|
||||||
{
|
{
|
||||||
if (grub_strncmp (p->name, name, nameend - name) != 0)
|
if (p->open (id, bus, scsi))
|
||||||
continue;
|
{
|
||||||
|
grub_errno = GRUB_ERR_NONE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (p->open (bus, scsi))
|
disk->id = grub_make_scsi_id (id, bus, lun);
|
||||||
continue;
|
|
||||||
|
|
||||||
disk->id = grub_make_scsi_id (p->id, bus, lun);
|
|
||||||
disk->data = scsi;
|
disk->data = scsi;
|
||||||
scsi->dev = p;
|
scsi->dev = p;
|
||||||
scsi->lun = lun;
|
scsi->lun = lun;
|
||||||
|
@ -484,7 +501,6 @@ grub_scsi_open (const char *name, grub_disk_t disk)
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_free (scsi);
|
grub_free (scsi);
|
||||||
|
|
||||||
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a SCSI disk");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
disk/usbms.c
12
disk/usbms.c
|
@ -205,7 +205,7 @@ grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
grub_usbms_iterate (int (*hook) (int bus, int luns))
|
grub_usbms_iterate (int (*hook) (int id, int bus, int luns))
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ grub_usbms_iterate (int (*hook) (int bus, int luns))
|
||||||
for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
|
for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
|
||||||
if (grub_usbms_devices[i])
|
if (grub_usbms_devices[i])
|
||||||
{
|
{
|
||||||
if (hook (i, grub_usbms_devices[i]->luns))
|
if (hook (GRUB_SCSI_SUBSYSTEM_USBMS, i, grub_usbms_devices[i]->luns))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,8 +384,12 @@ grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_err_t
|
static grub_err_t
|
||||||
grub_usbms_open (int devnum, struct grub_scsi *scsi)
|
grub_usbms_open (int id, int devnum, struct grub_scsi *scsi)
|
||||||
{
|
{
|
||||||
|
if (id != GRUB_SCSI_SUBSYSTEM_USBMS)
|
||||||
|
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
|
||||||
|
"not USB Mass Storage device");
|
||||||
|
|
||||||
grub_usb_poll_devices ();
|
grub_usb_poll_devices ();
|
||||||
|
|
||||||
if (!grub_usbms_devices[devnum])
|
if (!grub_usbms_devices[devnum])
|
||||||
|
@ -400,8 +404,6 @@ grub_usbms_open (int devnum, struct grub_scsi *scsi)
|
||||||
|
|
||||||
static struct grub_scsi_dev grub_usbms_dev =
|
static struct grub_scsi_dev grub_usbms_dev =
|
||||||
{
|
{
|
||||||
.name = "usb",
|
|
||||||
.id = GRUB_SCSI_SUBSYSTEM_USBMS,
|
|
||||||
.iterate = grub_usbms_iterate,
|
.iterate = grub_usbms_iterate,
|
||||||
.open = grub_usbms_open,
|
.open = grub_usbms_open,
|
||||||
.read = grub_usbms_read,
|
.read = grub_usbms_read,
|
||||||
|
|
|
@ -97,21 +97,64 @@ enum grub_ata_timeout_milliseconds
|
||||||
GRUB_ATA_TOUT_DATA = 10000 /* 10s DATA I/O timeout. */
|
GRUB_ATA_TOUT_DATA = 10000 /* 10s DATA I/O timeout. */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_ata_device
|
typedef union
|
||||||
{
|
{
|
||||||
/* IDE port to use. */
|
grub_uint8_t raw[11];
|
||||||
int port;
|
struct
|
||||||
|
{
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t features;
|
||||||
|
grub_uint8_t error;
|
||||||
|
};
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t sectors;
|
||||||
|
grub_uint8_t atapi_ireason;
|
||||||
|
};
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t lba_low;
|
||||||
|
grub_uint8_t sectnum;
|
||||||
|
};
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t lba_mid;
|
||||||
|
grub_uint8_t cyllsb;
|
||||||
|
grub_uint8_t atapi_cntlow;
|
||||||
|
};
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t lba_high;
|
||||||
|
grub_uint8_t cylmsb;
|
||||||
|
grub_uint8_t atapi_cnthigh;
|
||||||
|
};
|
||||||
|
grub_uint8_t disk;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
grub_uint8_t cmd;
|
||||||
|
grub_uint8_t status;
|
||||||
|
};
|
||||||
|
grub_uint8_t sectors48;
|
||||||
|
grub_uint8_t lba48_low;
|
||||||
|
grub_uint8_t lba48_mid;
|
||||||
|
grub_uint8_t lba48_high;
|
||||||
|
};
|
||||||
|
} grub_ata_regs_t;
|
||||||
|
|
||||||
/* IO addresses on which the registers for this device can be
|
/* ATA pass through parameters and function. */
|
||||||
found. */
|
struct grub_disk_ata_pass_through_parms
|
||||||
grub_port_t ioaddress;
|
{
|
||||||
grub_port_t ioaddress2;
|
grub_ata_regs_t taskfile;
|
||||||
|
void * buffer;
|
||||||
/* Two devices can be connected to a single cable. Use this field
|
grub_size_t size;
|
||||||
to select device 0 (commonly known as "master") or device 1
|
int write;
|
||||||
(commonly known as "slave"). */
|
void *cmd;
|
||||||
int device;
|
int cmdsize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grub_ata
|
||||||
|
{
|
||||||
/* Addressing methods available for accessing this device. If CHS
|
/* Addressing methods available for accessing this device. If CHS
|
||||||
is only available, use that. Otherwise use LBA, except for the
|
is only available, use that. Otherwise use LBA, except for the
|
||||||
high sectors. In that case use LBA48. */
|
high sectors. In that case use LBA48. */
|
||||||
|
@ -128,47 +171,36 @@ struct grub_ata_device
|
||||||
/* Set to 0 for ATA, set to 1 for ATAPI. */
|
/* Set to 0 for ATA, set to 1 for ATAPI. */
|
||||||
int atapi;
|
int atapi;
|
||||||
|
|
||||||
struct grub_ata_device *next;
|
void *data;
|
||||||
|
|
||||||
|
struct grub_ata_dev *dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
grub_err_t EXPORT_FUNC(grub_ata_wait_not_busy) (struct grub_ata_device *dev,
|
typedef struct grub_ata *grub_ata_t;
|
||||||
int milliseconds);
|
|
||||||
grub_err_t EXPORT_FUNC(grub_ata_wait_drq) (struct grub_ata_device *dev,
|
|
||||||
int rw, int milliseconds);
|
|
||||||
void EXPORT_FUNC(grub_ata_pio_read) (struct grub_ata_device *dev,
|
|
||||||
char *buf, grub_size_t size);
|
|
||||||
|
|
||||||
static inline void
|
struct grub_ata_dev
|
||||||
grub_ata_regset (struct grub_ata_device *dev, int reg, int val)
|
|
||||||
{
|
{
|
||||||
grub_outb (val, dev->ioaddress + reg);
|
/* Call HOOK with each device name, until HOOK returns non-zero. */
|
||||||
}
|
int (*iterate) (int (*hook) (int id, int bus));
|
||||||
|
|
||||||
static inline grub_uint8_t
|
/* Open the device named NAME, and set up SCSI. */
|
||||||
grub_ata_regget (struct grub_ata_device *dev, int reg)
|
grub_err_t (*open) (int id, int bus, struct grub_ata *scsi);
|
||||||
{
|
|
||||||
return grub_inb (dev->ioaddress + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
/* Close the scsi device SCSI. */
|
||||||
grub_ata_regset2 (struct grub_ata_device *dev, int reg, int val)
|
void (*close) (struct grub_ata *ata);
|
||||||
{
|
|
||||||
grub_outb (val, dev->ioaddress2 + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline grub_uint8_t
|
/* Read SIZE bytes from the device SCSI into BUF after sending the
|
||||||
grub_ata_regget2 (struct grub_ata_device *dev, int reg)
|
command CMD of size CMDSIZE. */
|
||||||
{
|
grub_err_t (*readwrite) (struct grub_ata *ata,
|
||||||
return grub_inb (dev->ioaddress2 + reg);
|
struct grub_disk_ata_pass_through_parms *parms);
|
||||||
}
|
|
||||||
|
|
||||||
static inline grub_err_t
|
/* The next scsi device. */
|
||||||
grub_ata_check_ready (struct grub_ata_device *dev)
|
struct grub_ata_dev *next;
|
||||||
{
|
};
|
||||||
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;
|
typedef struct grub_ata_dev *grub_ata_dev_t;
|
||||||
}
|
|
||||||
|
void grub_ata_dev_register (grub_ata_dev_t dev);
|
||||||
|
void grub_ata_dev_unregister (grub_ata_dev_t dev);
|
||||||
|
|
||||||
#endif /* ! GRUB_ATA_HEADER */
|
#endif /* ! GRUB_ATA_HEADER */
|
||||||
|
|
|
@ -166,15 +166,4 @@ grub_uint64_t EXPORT_FUNC(grub_disk_get_size) (grub_disk_t disk);
|
||||||
extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
|
extern void (* EXPORT_VAR(grub_disk_firmware_fini)) (void);
|
||||||
extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
|
extern int EXPORT_VAR(grub_disk_firmware_is_tainted);
|
||||||
|
|
||||||
/* ATA pass through parameters and function. */
|
|
||||||
struct grub_disk_ata_pass_through_parms
|
|
||||||
{
|
|
||||||
grub_uint8_t taskfile[8];
|
|
||||||
void * buffer;
|
|
||||||
int size;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern grub_err_t (* EXPORT_VAR(grub_disk_ata_pass_through)) (grub_disk_t,
|
|
||||||
struct grub_disk_ata_pass_through_parms *);
|
|
||||||
|
|
||||||
#endif /* ! GRUB_DISK_HEADER */
|
#endif /* ! GRUB_DISK_HEADER */
|
||||||
|
|
|
@ -29,10 +29,13 @@ struct grub_scsi;
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
GRUB_SCSI_SUBSYSTEM_USBMS,
|
GRUB_SCSI_SUBSYSTEM_USBMS,
|
||||||
GRUB_SCSI_SUBSYSTEM_ATAPI,
|
GRUB_SCSI_SUBSYSTEM_PATA,
|
||||||
GRUB_SCSI_SUBSYSTEM_AHCI
|
GRUB_SCSI_SUBSYSTEM_AHCI,
|
||||||
|
GRUB_SCSI_NUM_SUBSYSTEMS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern char grub_scsi_names[GRUB_SCSI_NUM_SUBSYSTEMS][5];
|
||||||
|
|
||||||
#define GRUB_SCSI_ID_SUBSYSTEM_SHIFT 24
|
#define GRUB_SCSI_ID_SUBSYSTEM_SHIFT 24
|
||||||
#define GRUB_SCSI_ID_BUS_SHIFT 8
|
#define GRUB_SCSI_ID_BUS_SHIFT 8
|
||||||
#define GRUB_SCSI_ID_LUN_SHIFT 0
|
#define GRUB_SCSI_ID_LUN_SHIFT 0
|
||||||
|
@ -46,16 +49,11 @@ grub_make_scsi_id (int subsystem, int bus, int lun)
|
||||||
|
|
||||||
struct grub_scsi_dev
|
struct grub_scsi_dev
|
||||||
{
|
{
|
||||||
/* The device name. */
|
|
||||||
const char *name;
|
|
||||||
|
|
||||||
grub_uint8_t id;
|
|
||||||
|
|
||||||
/* Call HOOK with each device name, until HOOK returns non-zero. */
|
/* Call HOOK with each device name, until HOOK returns non-zero. */
|
||||||
int (*iterate) (int (*hook) (int bus, int luns));
|
int (*iterate) (int (*hook) (int id, int bus, int luns));
|
||||||
|
|
||||||
/* Open the device named NAME, and set up SCSI. */
|
/* Open the device named NAME, and set up SCSI. */
|
||||||
grub_err_t (*open) (int bus, struct grub_scsi *scsi);
|
grub_err_t (*open) (int id, int bus, struct grub_scsi *scsi);
|
||||||
|
|
||||||
/* Close the scsi device SCSI. */
|
/* Close the scsi device SCSI. */
|
||||||
void (*close) (struct grub_scsi *scsi);
|
void (*close) (struct grub_scsi *scsi);
|
||||||
|
|
|
@ -46,10 +46,6 @@ static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
|
||||||
void (*grub_disk_firmware_fini) (void);
|
void (*grub_disk_firmware_fini) (void);
|
||||||
int grub_disk_firmware_is_tainted;
|
int grub_disk_firmware_is_tainted;
|
||||||
|
|
||||||
grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
|
|
||||||
struct grub_disk_ata_pass_through_parms *);
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static unsigned long grub_disk_cache_hits;
|
static unsigned long grub_disk_cache_hits;
|
||||||
static unsigned long grub_disk_cache_misses;
|
static unsigned long grub_disk_cache_misses;
|
||||||
|
|
Loading…
Reference in a new issue