/* * Copyright (C) 2006 Michael Brown . * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * You can also choose to distribute this program under the terms of * the Unmodified Binary Distribution Licence (as given in the file * COPYING.UBDL), provided that you have satisfied its requirements. */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include #include #include #include #include #include #include #include #include /** @file * * ATA block device * */ /****************************************************************************** * * Interface methods * ****************************************************************************** */ /** * Issue ATA command * * @v control ATA control interface * @v data ATA data interface * @v command ATA command * @ret tag Command tag, or negative error */ int ata_command ( struct interface *control, struct interface *data, struct ata_cmd *command ) { struct interface *dest; ata_command_TYPE ( void * ) *op = intf_get_dest_op ( control, ata_command, &dest ); void *object = intf_object ( dest ); int tag; if ( op ) { tag = op ( object, data, command ); } else { /* Default is to fail to issue the command */ tag = -EOPNOTSUPP; } intf_put ( dest ); return tag; } /****************************************************************************** * * ATA devices and commands * ****************************************************************************** */ /** List of all ATA commands */ static LIST_HEAD ( ata_commands ); /** An ATA device */ struct ata_device { /** Reference count */ struct refcnt refcnt; /** Block control interface */ struct interface block; /** ATA control interface */ struct interface ata; /** Device number * * Must be ATA_DEV_MASTER or ATA_DEV_SLAVE. */ unsigned int device; /** Maximum number of blocks per single transfer */ unsigned int max_count; /** Device uses LBA48 extended addressing */ int lba48; }; /** An ATA command */ struct ata_command { /** Reference count */ struct refcnt refcnt; /** ATA device */ struct ata_device *atadev; /** List of ATA commands */ struct list_head list; /** Block data interface */ struct interface block; /** ATA data interface */ struct interface ata; /** Command type */ struct ata_command_type *type; /** Command tag */ uint32_t tag; /** Private data */ uint8_t priv[0]; }; /** An ATA command type */ struct ata_command_type { /** Name */ const char *name; /** Additional working space */ size_t priv_len; /** Command for non-LBA48-capable devices */ uint8_t cmd_lba; /** Command for LBA48-capable devices */ uint8_t cmd_lba48; /** * Calculate data-in buffer * * @v atacmd ATA command * @v buffer Available buffer * @v len Available buffer length * @ret data_in Data-in buffer * @ret data_in_len Data-in buffer length */ void ( * data_in ) ( struct ata_command *atacmd, userptr_t buffer, size_t len, userptr_t *data_in, size_t *data_in_len ); /** * Calculate data-out buffer * * * @v atacmd ATA command * @v buffer Available buffer * @v len Available buffer length * @ret data_out Data-out buffer * @ret data_out_len Data-out buffer length */ void ( * data_out ) ( struct ata_command *atacmd, userptr_t buffer, size_t len, userptr_t *data_out, size_t *data_out_len ); /** * Handle ATA command completion * * @v atacmd ATA command * @v rc Reason for completion */ void ( * done ) ( struct ata_command *atacmd, int rc ); }; /** * Get reference to ATA device * * @v atadev ATA device * @ret atadev ATA device */ static inline __attribute__ (( always_inline )) struct ata_device * atadev_get ( struct ata_device *atadev ) { ref_get ( &atadev->refcnt ); return atadev; } /** * Drop reference to ATA device * * @v atadev ATA device */ static inline __attribute__ (( always_inline )) void atadev_put ( struct ata_device *atadev ) { ref_put ( &atadev->refcnt ); } /** * Get reference to ATA command * * @v atacmd ATA command * @ret atacmd ATA command */ static inline __attribute__ (( always_inline )) struct ata_command * atacmd_get ( struct ata_command *atacmd ) { ref_get ( &atacmd->refcnt ); return atacmd; } /** * Drop reference to ATA command * * @v atacmd ATA command */ static inline __attribute__ (( always_inline )) void atacmd_put ( struct ata_command *atacmd ) { ref_put ( &atacmd->refcnt ); } /** * Get ATA command private data * * @v atacmd ATA command * @ret priv Private data */ static inline __attribute__ (( always_inline )) void * atacmd_priv ( struct ata_command *atacmd ) { return atacmd->priv; } /** * Free ATA command * * @v refcnt Reference count */ static void atacmd_free ( struct refcnt *refcnt ) { struct ata_command *atacmd = container_of ( refcnt, struct ata_command, refcnt ); /* Remove from list of commands */ list_del ( &atacmd->list ); atadev_put ( atacmd->atadev ); /* Free command */ free ( atacmd ); } /** * Close ATA command * * @v atacmd ATA command * @v rc Reason for close */ static void atacmd_close ( struct ata_command *atacmd, int rc ) { struct ata_device *atadev = atacmd->atadev; if ( rc != 0 ) { DBGC ( atadev, "ATA %p tag %08x closed: %s\n", atadev, atacmd->tag, strerror ( rc ) ); } /* Shut down interfaces */ intf_shutdown ( &atacmd->ata, rc ); intf_shutdown ( &atacmd->block, rc ); } /** * Handle ATA command completion * * @v atacmd ATA command * @v rc Reason for close */ static void atacmd_done ( struct ata_command *atacmd, int rc ) { /* Hand over to the command completion handler */ atacmd->type->done ( atacmd, rc ); } /** * Use provided data buffer for ATA command * * @v atacmd ATA command * @v buffer Available buffer * @v len Available buffer length * @ret data Data buffer * @ret data_len Data buffer length */ static void atacmd_data_buffer ( struct ata_command *atacmd __unused, userptr_t buffer, size_t len, userptr_t *data, size_t *data_len ) { *data = buffer; *data_len = len; } /** * Use no data buffer for ATA command * * @v atacmd ATA command * @v buffer Available buffer * @v len Available buffer length * @ret data Data buffer * @ret data_len Data buffer length */ static void atacmd_data_none ( struct ata_command *atacmd __unused, userptr_t buffer __unused, size_t len __unused, userptr_t *data __unused, size_t *data_len __unused ) { /* Nothing to do */ } /** * Use private data buffer for ATA command * * @v atacmd ATA command * @v buffer Available buffer * @v len Available buffer length * @ret data Data buffer * @ret data_len Data buffer length */ static void atacmd_data_priv ( struct ata_command *atacmd, userptr_t buffer __unused, size_t len __unused, userptr_t *data, size_t *data_len ) { *data = virt_to_user ( atacmd_priv ( atacmd ) ); *data_len = atacmd->type->priv_len; } /** ATA READ command type */ static struct ata_command_type atacmd_read = { .name = "READ", .cmd_lba = ATA_CMD_READ, .cmd_lba48 = ATA_CMD_READ_EXT, .data_in = atacmd_data_buffer, .data_out = atacmd_data_none, .done = atacmd_close, }; /** ATA WRITE command type */ static struct ata_command_type atacmd_write = { .name = "WRITE", .cmd_lba = ATA_CMD_WRITE, .cmd_lba48 = ATA_CMD_WRITE_EXT, .data_in = atacmd_data_none, .data_out = atacmd_data_buffer, .done = atacmd_close, }; /** ATA IDENTIFY private data */ struct ata_identify_private { /** Identity data */ struct ata_identity identity; }; /** * Return ATA model string (for debugging) * * @v identify ATA identity data * @ret model Model string */ static const char * ata_model ( struct ata_identity *identity ) { static union { uint16_t words[ sizeof ( identity->model ) / 2 ]; char text[ sizeof ( identity->model ) + 1 /* NUL */ ]; } buf; unsigned int i; for ( i = 0 ; i < ( sizeof ( identity->model ) / 2 ) ; i++ ) buf.words[i] = bswap_16 ( identity->model[i] ); return buf.text; } /** * Handle ATA IDENTIFY command completion * * @v atacmd ATA command * @v rc Reason for completion */ static void atacmd_identify_done ( struct ata_command *atacmd, int rc ) { struct ata_device *atadev = atacmd->atadev; struct ata_identify_private *priv = atacmd_priv ( atacmd ); struct ata_identity *identity = &priv->identity; struct block_device_capacity capacity; /* Close if command failed */ if ( rc != 0 ) { atacmd_close ( atacmd, rc ); return; } /* Extract capacity */ if ( identity->supports_lba48 & cpu_to_le16 ( ATA_SUPPORTS_LBA48 ) ) { atadev->lba48 = 1; capacity.blocks = le64_to_cpu ( identity->lba48_sectors ); } else { capacity.blocks = le32_to_cpu ( identity->lba_sectors ); } capacity.blksize = ATA_SECTOR_SIZE; capacity.max_count = atadev->max_count; DBGC ( atadev, "ATA %p is a %s\n", atadev, ata_model ( identity ) ); DBGC ( atadev, "ATA %p has %#llx blocks (%ld MB) and uses %s\n", atadev, capacity.blocks, ( ( signed long ) ( capacity.blocks >> 11 ) ), ( atadev->lba48 ? "LBA48" : "LBA" ) ); /* Return capacity to caller */ block_capacity ( &atacmd->block, &capacity ); /* Close command */ atacmd_close ( atacmd, 0 ); } /** ATA IDENTITY command type */ static struct ata_command_type atacmd_identify = { .name = "IDENTIFY", .priv_len = sizeof ( struct ata_identify_private ), .cmd_lba = ATA_CMD_IDENTIFY, .cmd_lba48 = ATA_CMD_IDENTIFY, .data_in = atacmd_data_priv, .data_out = atacmd_data_none, .done = atacmd_identify_done, }; /** ATA command block interface operations */ static struct interface_operation atacmd_block_op[] = { INTF_OP ( intf_close, struct ata_command *, atacmd_close ), }; /** ATA command block interface descriptor */ static struct interface_descriptor atacmd_block_desc = INTF_DESC_PASSTHRU ( struct ata_command, block, atacmd_block_op, ata ); /** ATA command ATA interface operations */ static struct interface_operation atacmd_ata_op[] = { INTF_OP ( intf_close, struct ata_command *, atacmd_done ), }; /** ATA command ATA interface descriptor */ static struct interface_descriptor atacmd_ata_desc = INTF_DESC_PASSTHRU ( struct ata_command, ata, atacmd_ata_op, block ); /** * Create ATA command * * @v atadev ATA device * @v block Block data interface * @v type ATA command type * @v lba Starting logical block address * @v count Number of blocks to transfer * @v buffer Data buffer * @v len Length of data buffer * @ret rc Return status code */ static int atadev_command ( struct ata_device *atadev, struct interface *block, struct ata_command_type *type, uint64_t lba, unsigned int count, userptr_t buffer, size_t len ) { struct ata_command *atacmd; struct ata_cmd command; int tag; int rc; /* Allocate and initialise structure */ atacmd = zalloc ( sizeof ( *atacmd ) + type->priv_len ); if ( ! atacmd ) { rc = -ENOMEM; goto err_zalloc; } ref_init ( &atacmd->refcnt, atacmd_free ); intf_init ( &atacmd->block, &atacmd_block_desc, &atacmd->refcnt ); intf_init ( &atacmd->ata, &atacmd_ata_desc, &atacmd->refcnt ); atacmd->atadev = atadev_get ( atadev ); list_add ( &atacmd->list, &ata_commands ); atacmd->type = type; /* Sanity check */ if ( len != ( count * ATA_SECTOR_SIZE ) ) { DBGC ( atadev, "ATA %p tag %08x buffer length mismatch (count " "%d len %zd)\n", atadev, atacmd->tag, count, len ); rc = -EINVAL; goto err_len; } /* Construct command */ memset ( &command, 0, sizeof ( command ) ); command.cb.lba.native = lba; command.cb.count.native = count; command.cb.device = ( atadev->device | ATA_DEV_OBSOLETE | ATA_DEV_LBA ); command.cb.lba48 = atadev->lba48; if ( ! atadev->lba48 ) command.cb.device |= command.cb.lba.bytes.low_prev; command.cb.cmd_stat = ( atadev->lba48 ? type->cmd_lba48 : type->cmd_lba ); type->data_in ( atacmd, buffer, len, &command.data_in, &command.data_in_len ); type->data_out ( atacmd, buffer, len, &command.data_out, &command.data_out_len ); /* Issue command */ if ( ( tag = ata_command ( &atadev->ata, &atacmd->ata, &command ) ) < 0 ) { rc = tag; DBGC ( atadev, "ATA %p tag %08x could not issue command: %s\n", atadev, atacmd->tag, strerror ( rc ) ); goto err_command; } atacmd->tag = tag; DBGC2 ( atadev, "ATA %p tag %08x %s cmd %02x dev %02x LBA%s %08llx " "count %04x\n", atadev, atacmd->tag, atacmd->type->name, command.cb.cmd_stat, command.cb.device, ( command.cb.lba48 ? "48" : "" ), ( unsigned long long ) command.cb.lba.native, command.cb.count.native ); /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &atacmd->block, block ); ref_put ( &atacmd->refcnt ); return 0; err_command: err_len: atacmd_close ( atacmd, rc ); ref_put ( &atacmd->refcnt ); err_zalloc: return rc; } /** * Issue ATA block read * * @v atadev ATA device * @v block Block data interface * @v lba Starting logical block address * @v count Number of blocks to transfer * @v buffer Data buffer * @v len Length of data buffer * @ret rc Return status code */ static int atadev_read ( struct ata_device *atadev, struct interface *block, uint64_t lba, unsigned int count, userptr_t buffer, size_t len ) { return atadev_command ( atadev, block, &atacmd_read, lba, count, buffer, len ); } /** * Issue ATA block write * * @v atadev ATA device * @v block Block data interface * @v lba Starting logical block address * @v count Number of blocks to transfer * @v buffer Data buffer * @v len Length of data buffer * @ret rc Return status code */ static int atadev_write ( struct ata_device *atadev, struct interface *block, uint64_t lba, unsigned int count, userptr_t buffer, size_t len ) { return atadev_command ( atadev, block, &atacmd_write, lba, count, buffer, len ); } /** * Read ATA device capacity * * @v atadev ATA device * @v block Block data interface * @ret rc Return status code */ static int atadev_read_capacity ( struct ata_device *atadev, struct interface *block ) { struct ata_identity *identity; assert ( atacmd_identify.priv_len == sizeof ( *identity ) ); assert ( atacmd_identify.priv_len == ATA_SECTOR_SIZE ); return atadev_command ( atadev, block, &atacmd_identify, 0, 1, UNULL, ATA_SECTOR_SIZE ); } /** * Close ATA device * * @v atadev ATA device * @v rc Reason for close */ static void atadev_close ( struct ata_device *atadev, int rc ) { struct ata_command *atacmd; struct ata_command *tmp; /* Shut down interfaces */ intf_shutdown ( &atadev->block, rc ); intf_shutdown ( &atadev->ata, rc ); /* Shut down any remaining commands */ list_for_each_entry_safe ( atacmd, tmp, &ata_commands, list ) { if ( atacmd->atadev != atadev ) continue; atacmd_get ( atacmd ); atacmd_close ( atacmd, rc ); atacmd_put ( atacmd ); } } /** * Describe ATA device using EDD * * @v atadev ATA device * @v type EDD interface type * @v path EDD device path * @ret rc Return status code */ static int atadev_edd_describe ( struct ata_device *atadev, struct edd_interface_type *type, union edd_device_path *path ) { type->type = cpu_to_le64 ( EDD_INTF_TYPE_ATA ); path->ata.slave = ( ( atadev->device == ATA_DEV_SLAVE ) ? 0x01 : 0x00 ); return 0; } /** ATA device block interface operations */ static struct interface_operation atadev_block_op[] = { INTF_OP ( block_read, struct ata_device *, atadev_read ), INTF_OP ( block_write, struct ata_device *, atadev_write ), INTF_OP ( block_read_capacity, struct ata_device *, atadev_read_capacity ), INTF_OP ( intf_close, struct ata_device *, atadev_close ), INTF_OP ( edd_describe, struct ata_device *, atadev_edd_describe ), }; /** ATA device block interface descriptor */ static struct interface_descriptor atadev_block_desc = INTF_DESC_PASSTHRU ( struct ata_device, block, atadev_block_op, ata ); /** ATA device ATA interface operations */ static struct interface_operation atadev_ata_op[] = { INTF_OP ( intf_close, struct ata_device *, atadev_close ), }; /** ATA device ATA interface descriptor */ static struct interface_descriptor atadev_ata_desc = INTF_DESC_PASSTHRU ( struct ata_device, ata, atadev_ata_op, block ); /** * Open ATA device * * @v block Block control interface * @v ata ATA control interface * @v device ATA device number * @v max_count Maximum number of blocks per single transfer * @ret rc Return status code */ int ata_open ( struct interface *block, struct interface *ata, unsigned int device, unsigned int max_count ) { struct ata_device *atadev; /* Allocate and initialise structure */ atadev = zalloc ( sizeof ( *atadev ) ); if ( ! atadev ) return -ENOMEM; ref_init ( &atadev->refcnt, NULL ); intf_init ( &atadev->block, &atadev_block_desc, &atadev->refcnt ); intf_init ( &atadev->ata, &atadev_ata_desc, &atadev->refcnt ); atadev->device = device; atadev->max_count = max_count; /* Attach to ATA and parent and interfaces, mortalise self, * and return */ intf_plug_plug ( &atadev->ata, ata ); intf_plug_plug ( &atadev->block, block ); ref_put ( &atadev->refcnt ); return 0; }