#ifndef _AHCI_H_ #define _AHCI_H_ #include #include #include #include struct driver; struct device; struct iovec; #define SATA_SIG_ATA 0x00000101 // SATA drive #define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive #define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge #define SATA_SIG_PM 0x96690101 // Port multiplier #define AHCI_DEV_NULL 0 #define AHCI_DEV_SATA 1 #define AHCI_DEV_SEMB 2 #define AHCI_DEV_PM 3 #define AHCI_DEV_SATAPI 4 #define HBA_PORT_IPM_ACTIVE 1 #define HBA_PORT_DET_PRESENT 3 #define AHCI_BASE 0x400000 // 4M #define HBA_PxCMD_ST 0x0001 #define HBA_PxCMD_FRE 0x0010 #define HBA_PxCMD_FR 0x4000 #define HBA_PxCMD_CR 0x8000 #define HBA_PxIS_TFES 0x40000000 #define HBA_PxIS_DPS 0x20u #define ATA_CMD_READ_DMA_EX 0x25u #define ATA_CMD_IDENTIFY_DEVICE 0xECu #define ATA_CMD_PACKET 0xA0u #define ATA_CMD_IDENTIFY_PACKET_DEVICE 0xA1u #define SCSI_CMD_READ 0xA8u #define SCSI_CMD_READ_CAPACITY 0x25u #define ATA_DEV_BUSY 0x80 #define ATA_DEV_DRQ 0x08 enum fis_type { FIS_TYPE_REG_H2D = 0x27u, FIS_TYPE_REG_D2H = 0x34u, FIS_TYPE_DMA_ACT = 0x39u, FIS_TYPE_DMA_SETUP = 0x41u, FIS_TYPE_DATA = 0x46u, FIS_TYPE_BIST = 0x58u, FIS_TYPE_PIO_SETUP = 0x5F, FIS_TYPE_DEV_BITS = 0xA1, }; struct fis_reg_h2d { uint8_t fis_type; uint8_t pmport : 4; uint8_t rsv0 : 3; uint8_t c : 1; uint8_t command; uint8_t feature_l; uint8_t lba0; uint8_t lba1; uint8_t lba2; uint8_t device; uint8_t lba3; uint8_t lba4; uint8_t lba5; uint8_t feature_h; uint8_t count_l; uint8_t count_h; uint8_t icc; uint8_t control; uint8_t rsv1[4]; } __packed; struct fis_reg_d2h { uint8_t fis_type; uint8_t pmport : 4; uint8_t rsv0 : 2; uint8_t i : 1; uint8_t rsv1 : 1; uint8_t status; uint8_t error; uint8_t lba0; uint8_t lba1; uint8_t lba2; uint8_t device; uint8_t lba3; uint8_t lba4; uint8_t lba5; uint8_t rsv2; uint8_t count_l; uint8_t count_h; uint8_t rsv3[2]; uint8_t rsv4[4]; } __packed; struct fis_data { uint8_t fis_type; uint8_t pmport : 4; uint8_t rsv0 : 4; uint8_t rsv1[2]; // data } __packed; struct fis_pio_setup { uint8_t fis_type; uint8_t pmport : 4; uint8_t rsv0 : 1; uint8_t d : 1; uint8_t i : 1; uint8_t rsv1 : 1; uint8_t status; uint8_t error; uint8_t lba0; uint8_t lba1; uint8_t lba2; uint8_t device; uint8_t lba3; uint8_t lba4; uint8_t lba5; uint8_t rsv2; uint8_t count_l; uint8_t count_h; uint8_t rsv3; uint8_t e_status; uint16_t rc; uint8_t rsv4[2]; } __packed; struct fis_dma_setup { uint8_t fis_type; uint8_t pmport : 4; uint8_t rsv0 : 1; uint8_t d : 1; uint8_t i : 1; uint8_t a : 1; uint8_t rsv1[2]; uint64_t dma_buffer_id; uint32_t rsv2; uint32_t dba_buffer_offset; uint32_t dma_transfer_size; uint32_t rsv3; } __packed; struct hba_cmd_header { uint8_t cfl : 5; uint8_t a : 1; uint8_t w : 1; uint8_t p : 1; uint8_t r : 1; uint8_t b : 1; uint8_t c : 1; uint8_t rsv0 : 1; uint8_t pmp : 4; uint16_t prdtl; volatile uint32_t prdbc; uint32_t ctba; uint32_t ctbau; uint32_t rsv1[4]; } __packed; struct hba_prdt_entry { uint32_t dba; uint32_t dbau; uint32_t rsv0; uint32_t dbc : 22; uint32_t rsv1 : 9; uint32_t i : 1; } __packed; struct scsi_command { uint8_t cmd; union { struct { uint8_t rsvd[15]; } read_capacity; struct { uint8_t rdprotect : 3; uint8_t dpo : 1; uint8_t fua : 1; uint8_t rarc : 1; uint8_t obsolete : 2; uint32_t lba; uint32_t count; } read; } __packed; } __packed; struct hba_cmd_table { uint8_t cfis[64]; struct scsi_command acmd; uint8_t rsv[48]; struct hba_prdt_entry prdt_entry[]; } __packed; struct hba_port { uint32_t clb; uint32_t clbu; uint32_t fb; uint32_t fbu; uint32_t is; uint32_t ie; uint32_t cmd; uint32_t rsv0; uint32_t tfd; uint32_t sig; uint32_t ssts; uint32_t sctl; uint32_t serr; uint32_t sact; uint32_t ci; uint32_t sntf; uint32_t fbs; uint32_t rsv1[11]; uint32_t vendor[4]; } __packed; struct hba_config { uint32_t cap; uint32_t ghc; uint32_t is; uint32_t pi; uint32_t vs; uint32_t ccc_ctl; uint32_t ccc_ptl; uint32_t em_loc; uint32_t em_ctl; uint32_t cap2; uint32_t bohc; uint8_t rsv[116]; uint8_t vendor[96]; struct hba_port ports[32]; } __packed; struct identify_device_data { union { struct { uint16_t reserved_1 : 1; uint16_t retired_3 : 1; uint16_t response_incomplete : 1; uint16_t retired_2 : 3; uint16_t fixed_device : 1; uint16_t removable_media : 1; uint16_t retired_1 : 7; uint16_t device_type : 1; } general_config; struct { /* uint16_t is_atapi : 2; uint16_t reserved_1: 1; uint16_t command_set : 5; uint16_t obsolete_1: 1; uint16_t packet_drq_time : 2; uint16_t reserved_2 : 2; uint16_t identify_incomplete : 1; uint16_t packet_size : 2; */ uint16_t packet_size : 2; uint16_t identify_incomplete : 1; uint16_t reserved_2 : 2; uint16_t packet_drq_time : 2; uint16_t obsolete_1: 1; uint16_t command_set : 5; uint16_t reserved_1: 1; uint16_t is_atapi : 2; } atapi_general_config; }; uint16_t num_cylinders; uint16_t specific_configuration; uint16_t num_heads; uint16_t retired_1[2]; uint16_t num_sectors_per_track; uint16_t vendor_unique_1[3]; uint8_t serial_number[20]; uint16_t retired_2[2]; uint16_t obsolete_1; uint8_t firmware_revision[8]; uint8_t model_number[40]; uint8_t maximum_block_transfer; uint8_t vendor_unique_2; struct { uint16_t feature_supported : 1; uint16_t reserved : 15; } trusted_computing; struct { uint16_t reserved_word_50; uint8_t current_long_physical_sector_alignment : 2; uint8_t reserved_byte_49 : 6; uint8_t dma_supported : 1; uint8_t lba_supported : 1; uint8_t iordy_disable : 1; uint8_t iordy_supported : 1; uint8_t reserved_1 : 1; uint8_t standyby_timer_support : 1; uint8_t reserved_2 : 2; } capabilities; uint16_t obsolete_words_51[2]; uint16_t translation_fields_valid : 3; uint16_t reserved_3 : 5; uint16_t free_fall_control_sensitivity : 8; uint16_t number_of_current_cylinders; uint16_t number_of_current_heads; uint16_t current_sectors_per_track; uint32_t current_sector_capacity; uint8_t current_multi_sector_setting; uint8_t multi_sector_setting_valid : 1; uint8_t reserved_byte_59 : 3; uint8_t sanitize_feature_supported : 1; uint8_t crypto_scramble_ext_command_supported : 1; uint8_t overwrite_ext_command_supported : 1; uint8_t block_erase_ext_command_supported : 1; uint32_t user_addressable_sectors; uint16_t obsolete_word_62; uint16_t multi_word_dma_support : 8; uint16_t multi_word_dma_active : 8; uint16_t advanced_pio_modes : 8; uint16_t reserved_byte_64 : 8; uint16_t minimum_mw_xfer_cycle_time; uint16_t recommended_mw_xfer_cycle_time; uint16_t minimum_pio_cycle_time; uint16_t minimum_pio_cycle_time_iordy; struct { uint16_t zoned_capabilities : 2; uint16_t non_volatile_write_cache : 1; uint16_t extended_user_addressable_sectors_supported : 1; uint16_t device_encrypts_all_user_data : 1; uint16_t read_zero_after_trim_supported : 1; uint16_t optional_28_bit_commands_supported : 1; uint16_t ieee_1667 : 1; uint16_t download_microcode_dma_supported : 1; uint16_t set_max_set_password_unlock_dma_supported : 1; uint16_t write_buffer_dma_supported : 1; uint16_t read_buffer_dma_supported : 1; uint16_t device_config_identify_set_dma_supported : 1; uint16_t lpsaerc_supported : 1; uint16_t deterministic_read_after_trim_supported : 1; uint16_t c_fast_spec_supported : 1; } additional_supported; uint16_t reserved_words_70[5]; uint16_t queue_depth : 2; uint16_t reserved_word_75[11]; struct { uint16_t reserved_0 : 1; uint16_t sata_gen_1 : 1; uint16_t sata_gen_2 : 1; uint16_t sata_gen_3 : 1; uint16_t reserved_1 : 1; uint16_t ncq : 1; uint16_t hipm : 1; uint16_t phy_events : 1; uint16_t ncq_unload : 1; uint16_t ncq_priority : 1; uint16_t host_auto_ps : 1; uint16_t device_auto_ps : 1; uint16_t read_log_dma : 1; uint16_t reserved_2 : 1; uint16_t current_speed : 3; uint16_t ncq_streaming : 1; uint16_t ncq_queue_mgmt : 1; uint16_t ncq_receive_send : 1; uint16_t devsl_pto_reduced_pwr_state : 1; uint16_t reserved_3 : 8; } serial_ata_capabilities; struct { uint16_t reserved_0 : 1; uint16_t non_zero_offsets : 1; uint16_t dma_setup_auto_activate : 1; uint16_t dipm : 1; uint16_t in_order_data : 1; uint16_t hardware_feature_control : 1; uint16_t software_settings_preservation : 1; uint16_t ncq_autosense : 1; uint16_t devslp : 1; uint16_t hybrid_information : 1; uint16_t reserved_1 : 6; } serial_ata_features_supported; struct { uint16_t reserved_0 : 1; uint16_t non_zero_offsets : 1; uint16_t dma_setup_auto_activate : 1; uint16_t dipm : 1; uint16_t in_order_data : 1; uint16_t hardware_feature_control : 1; uint16_t software_settings_preservation : 1; uint16_t device_auto_ps : 1; uint16_t devslp : 1; uint16_t hybrid_information : 1; uint16_t reserved_1 : 6; } serial_ata_features_enabled; uint16_t major_revision; uint16_t minor_revision; struct { uint16_t smart_commands : 1; uint16_t security_mode : 1; uint16_t removable_media_feature : 1; uint16_t power_management : 1; uint16_t reserved_1 : 1; uint16_t write_cache : 1; uint16_t look_ahead : 1; uint16_t release_interrupt : 1; uint16_t service_interrupt : 1; uint16_t device_reset : 1; uint16_t host_protected_area : 1; uint16_t obsolete_1 : 1; uint16_t write_buffer : 1; uint16_t read_buffer : 1; uint16_t nop : 1; uint16_t obsolete_2 : 1; uint16_t download_microcode : 1; uint16_t dma_queued : 1; uint16_t cfa : 1; uint16_t advanced_pm : 1; uint16_t msn : 1; uint16_t power_up_in_standby : 1; uint16_t manual_power_up : 1; uint16_t reserved_2 : 1; uint16_t set_max : 1; uint16_t acoustics : 1; uint16_t big_lba : 1; uint16_t device_config_overlay : 1; uint16_t flush_cache : 1; uint16_t flush_cache_ext : 1; uint16_t word_valid_83 : 2; uint16_t smart_error_log : 1; uint16_t smart_self_test : 1; uint16_t media_serial_number : 1; uint16_t media_card_pass_through : 1; uint16_t streaming_feature : 1; uint16_t gp_logging : 1; uint16_t write_fua : 1; uint16_t write_queued_fua : 1; uint16_t wwn_64_bit : 1; uint16_t urg_read_stream : 1; uint16_t urg_write_stream : 1; uint16_t reserved_for_tech_report : 2; uint16_t idle_with_unload_feature : 1; uint16_t word_valid : 2; } command_set_support; struct { uint16_t smart_commands : 1; uint16_t security_mode : 1; uint16_t removable_media_feature : 1; uint16_t power_management : 1; uint16_t reserved_1 : 1; uint16_t write_cache : 1; uint16_t look_ahead : 1; uint16_t release_interrupt : 1; uint16_t service_interrupt : 1; uint16_t device_reset : 1; uint16_t host_protected_area : 1; uint16_t obsolete_1 : 1; uint16_t write_buffer : 1; uint16_t read_buffer : 1; uint16_t nop : 1; uint16_t obsolete_2 : 1; uint16_t download_microcode : 1; uint16_t dma_queued : 1; uint16_t cfa : 1; uint16_t advanced_pm : 1; uint16_t msn : 1; uint16_t power_up_in_standby : 1; uint16_t manual_power_up : 1; uint16_t reserved_2 : 1; uint16_t set_max : 1; uint16_t acoustics : 1; uint16_t big_lba : 1; uint16_t device_config_overlay : 1; uint16_t flush_cache : 1; uint16_t flush_cache_ext : 1; uint16_t resrved_3 : 1; uint16_t words_119_120_valid : 1; uint16_t smart_error_log : 1; uint16_t smart_self_test : 1; uint16_t media_serial_number : 1; uint16_t media_card_pass_through : 1; uint16_t streaming_feature : 1; uint16_t gp_logging : 1; uint16_t write_fua : 1; uint16_t write_queued_fua : 1; uint16_t wwn_64_bit : 1; uint16_t urg_read_stream : 1; uint16_t urg_write_stream : 1; uint16_t reserved_for_tech_report : 2; uint16_t idle_with_unload_feature : 1; uint16_t reserved_4 : 2; } command_set_active; uint16_t ultra_dma_support : 8; uint16_t ultra_dma_active : 8; struct { uint16_t time_required : 15; uint16_t extended_time_reported : 1; } normal_security_erase_unit; struct { uint16_t time_required : 15; uint16_t extended_time_reported : 1; } enhanced_security_erase_unit; uint16_t current_apm_level : 8; uint16_t reserved_word_91 : 8; uint16_t master_password_id; uint16_t hardware_reset_result; uint16_t current_acoustic_value : 8; uint16_t recommended_acoustic_value : 8; uint16_t stream_min_request_size; uint16_t streaming_transfer_time_dma; uint16_t streaming_access_latency_dmapio; uint32_t streaming_perf_granularity; uint32_t max_48_bit_lba_2; uint16_t streaming_transfer_time; uint16_t dsm_cap; struct { uint16_t logical_sectors_per_physical_sector : 4; uint16_t reserved_0 : 8; uint16_t logical_sector_longer_than_256_words : 1; uint16_t multiple_logical_sectors_per_physical_sector : 1; uint16_t reserved_1 : 2; } physical_logical_sector_size; uint16_t inter_seek_delay; uint16_t world_wide_name[4]; uint16_t reserved_for_world_wide_name_128[4]; uint16_t reserved_for_tlc_technical_report; uint16_t words_per_logical_sector[2]; struct { uint16_t reserved_for_drq_technical_report : 1; uint16_t write_read_verify : 1; uint16_t write_uncorrectable_ext : 1; uint16_t read_write_log_dma_ext : 1; uint16_t download_microcode_mode_3 : 1; uint16_t freefall_control : 1; uint16_t sense_data_reporting : 1; uint16_t extended_power_conditions : 1; uint16_t reserved_0 : 6; uint16_t word_valid : 2; } command_set_support_ext; struct { uint16_t reserved_for_drq_technical_report : 1; uint16_t write_read_verify : 1; uint16_t write_uncorrectable_ext : 1; uint16_t read_write_log_dma_ext : 1; uint16_t download_microcode_mode_3 : 1; uint16_t freefall_control : 1; uint16_t sense_data_reporting : 1; uint16_t extended_power_conditions : 1; uint16_t reserved_0 : 6; uint16_t reserved_1 : 2; } command_set_active_ext; uint16_t reserved_for_expanded_supportand_active[6]; uint16_t msn_support : 2; uint16_t reserved_word_127 : 14; struct { uint16_t security_supported : 1; uint16_t security_enabled : 1; uint16_t security_locked : 1; uint16_t security_frozen : 1; uint16_t security_count_expired : 1; uint16_t enhanced_security_erase_supported : 1; uint16_t reserved_0 : 2; uint16_t security_level : 1; uint16_t reserved_1 : 7; } security_status; uint16_t reserved_word_129[31]; struct { uint16_t maximum_current_in_ma : 12; uint16_t cfa_power_mode_1_disabled : 1; uint16_t cfa_power_mode_1_required : 1; uint16_t reserved_0 : 1; uint16_t word_160_supported : 1; } cfa_power_mode_1; uint16_t reserved_for_cfa_word_161[7]; uint16_t nominal_form_factor : 4; uint16_t reserved_word_168 : 12; struct { uint16_t supports_trim : 1; uint16_t reserved_0 : 15; } data_set_management_feature; uint16_t additional_product_id[4]; uint16_t reserved_for_cfa_word_174[2]; uint16_t current_media_serial_number[30]; struct { uint16_t supported : 1; uint16_t reserved_0 : 1; uint16_t write_same_suported : 1; uint16_t error_recovery_control_supported : 1; uint16_t feature_control_suported : 1; uint16_t data_tables_suported : 1; uint16_t reserved_1 : 6; uint16_t vendor_specific : 4; } sct_command_transport; uint16_t reserved_word_207[2]; struct { uint16_t alignment_of_logical_within_physical : 14; uint16_t word_209_supported : 1; uint16_t reserved_0 : 1; } block_alignment; uint16_t write_read_verify_sector_count_mode_3_only[2]; uint16_t write_read_verify_sector_count_mode_2_only[2]; struct { uint16_t nv_cache_power_mode_enabled : 1; uint16_t reserved_0 : 3; uint16_t nv_cache_feature_set_enabled : 1; uint16_t reserved_1 : 3; uint16_t nv_cache_power_mode_version : 4; uint16_t nv_cache_feature_set_version : 4; } nv_cache_capabilities; uint16_t nv_cache_size_lsw; uint16_t nv_cache_size_msw; uint16_t nominal_media_rotation_rate; uint16_t reserved_word_218; struct { uint8_t nv_cache_estimated_time_to_spin_up_in_seconds; uint8_t reserved; } nv_cache_options; uint16_t write_read_verify_sector_count_mode : 8; uint16_t reserved_word_220 : 8; uint16_t reserved_word_221; struct { uint16_t major_version : 12; uint16_t transport_type : 4; } transport_major_version; uint16_t transport_minor_version; uint16_t reserved_word_224[6]; uint32_t extended_number_of_user_addressable_sectors[2]; uint16_t min_blocks_per_download_microcode_mode_03; uint16_t max_blocks_per_download_microcode_mode_03; uint16_t reserved_word_236[19]; uint16_t signature : 8; uint16_t check_sum : 8; } __packed; struct scsi_read_capacity_data { uint32_t last_sector; uint32_t block_size; } __packed; struct rfis_data { struct fis_dma_setup dma_setup; char pad0[4]; struct fis_pio_setup pio_setup; char pad1[12]; struct fis_reg_d2h reg_d2h; } __packed; struct ahci_device { int port_no; int type; volatile struct hba_port *port; struct hba_cmd_header cmd_header[32]; union { char rfis_data[256]; struct rfis_data rfis; }; }; /* struct scsi_command /must/ be exactly 16 bytes long, so that it fits properly within struct hba_cmd_table */ _Static_assert(sizeof(struct scsi_command) == 16, "ahci: scsi_command must be 16 bytes"); _Static_assert(sizeof(struct fis_dma_setup) == 28, "ahci: fis_dma_setup must be 28 bytes"); _Static_assert(sizeof(struct fis_pio_setup) == 20, "ahci: fis_pio_set must be 20 bytes"); _Static_assert(sizeof(struct fis_reg_d2h) == 20, "ahci: fis_reg_d2h must be 20 bytes"); _Static_assert(offsetof(struct rfis_data, dma_setup) == 0, "offsetof dma_setup in rfis_data must be 0x00"); _Static_assert(offsetof(struct rfis_data, pio_setup) == 0x20, "offsetof dma_setup in rfis_data must be 0x20"); _Static_assert(offsetof(struct rfis_data, reg_d2h) == 0x40, "offsetof dma_setup in rfis_data must be 0x40"); extern struct driver *ahci_driver(void); extern kern_status_t identify_ata_device(struct ahci_device *dev, struct identify_device_data *out); extern kern_status_t identify_atapi_device(struct ahci_device *dev, struct identify_device_data *out); extern kern_status_t send_ata_command(struct ahci_device *dev, unsigned int cmd, struct iovec *vec, size_t nvec); extern kern_status_t send_ata_command_ex(struct ahci_device *dev, struct fis_reg_h2d *fis, unsigned int cmd, struct iovec *vec, size_t nvec); extern kern_status_t send_atapi_command(struct ahci_device *dev, struct scsi_command *cmd, struct iovec *vec, size_t nvec); extern kern_status_t send_atapi_command_ex(struct ahci_device *dev, struct fis_reg_h2d *fis, struct scsi_command *cmd, struct iovec *vec, size_t nvec); extern void rebase_ahci_port(struct ahci_device *dev); extern void probe_ahci_ports(struct driver *driver, struct device *controller, volatile struct hba_config *abar, void(*callback)(int, struct device *, volatile struct hba_port *, int)); extern void create_device_from_ahci_port(int port_no, struct device *controller, volatile struct hba_port *port, int type); #endif