splitting generic ata from pata.

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-12-24 16:07:53 +01:00
parent 7e8b77c033
commit c7336d912c
10 changed files with 1095 additions and 843 deletions

View file

@ -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);

View file

@ -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
static grub_err_t enum
grub_ahci_packet (struct grub_ahci_device *dev, char *packet, {
grub_size_t size) 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
grub_ahci_readwrite (grub_ata_t disk,
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);
} }

File diff suppressed because it is too large Load diff

View file

@ -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;
} }

View file

@ -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");
} }

View file

@ -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,

View file

@ -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 */

View file

@ -165,16 +165,5 @@ 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 */

View file

@ -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);

View file

@ -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;