kexts: add AHCI block driver
This commit is contained in:
631
kexts/drivers/block/ahci/ahci.h
Normal file
631
kexts/drivers/block/ahci/ahci.h
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
#ifndef _AHCI_H_
|
||||||
|
#define _AHCI_H_
|
||||||
|
|
||||||
|
#include <socks/compiler.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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 ATA_CMD_READ_DMA_EX 0x25u
|
||||||
|
#define ATA_CMD_IDENTIFY_DEVICE 0xECu
|
||||||
|
|
||||||
|
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 hba_cmd_table {
|
||||||
|
uint8_t cfis[64];
|
||||||
|
|
||||||
|
uint8_t acmd[16];
|
||||||
|
|
||||||
|
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 ata_identify_device_data {
|
||||||
|
uint16_t g_config;
|
||||||
|
uint8_t rsv0[2];
|
||||||
|
uint16_t s_config;
|
||||||
|
uint8_t rsv1[14];
|
||||||
|
uint8_t serial_number[20];
|
||||||
|
uint8_t rsv2[6];
|
||||||
|
uint32_t firmware;
|
||||||
|
char model[40];
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct identify_device_data {
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
#endif
|
||||||
8
kexts/drivers/block/ahci/extension.yaml
Normal file
8
kexts/drivers/block/ahci/extension.yaml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
name: ahci
|
||||||
|
description: |
|
||||||
|
AHCI (SATA) disk driver
|
||||||
|
id: net.doorstuck.socks.ahci
|
||||||
|
license: BSD-3-Clause
|
||||||
|
copyright: Copyright © Max Wash 2023
|
||||||
|
sources:
|
||||||
|
- main.c
|
||||||
551
kexts/drivers/block/ahci/main.c
Normal file
551
kexts/drivers/block/ahci/main.c
Normal file
@@ -0,0 +1,551 @@
|
|||||||
|
#include <socks/printk.h>
|
||||||
|
#include <socks/pci.h>
|
||||||
|
#include <socks/device.h>
|
||||||
|
#include <socks/kext.h>
|
||||||
|
#include <socks/pci.h>
|
||||||
|
#include <socks/machine/init.h>
|
||||||
|
#include <socks/libc/stdio.h>
|
||||||
|
#include <socks/libc/ctype.h>
|
||||||
|
#include <arch/irq.h>
|
||||||
|
#include "ahci.h"
|
||||||
|
|
||||||
|
struct ahci_device {
|
||||||
|
int port_no;
|
||||||
|
int type;
|
||||||
|
volatile struct hba_port *port;
|
||||||
|
|
||||||
|
struct hba_cmd_header cmd_header[32];
|
||||||
|
char fis_data[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
/************************** STOLEN **********************/
|
||||||
|
|
||||||
|
#define ATA_DEV_BUSY 0x80
|
||||||
|
#define ATA_DEV_DRQ 0x08
|
||||||
|
|
||||||
|
// Find a free command list slot
|
||||||
|
int find_cmdslot(volatile struct hba_port *port)
|
||||||
|
{
|
||||||
|
// If not set in SACT and CI, the slot is free
|
||||||
|
uint32_t slots = (port->sact | port->ci);
|
||||||
|
for (int i=0; i<32; i++)
|
||||||
|
{
|
||||||
|
if ((slots&1) == 0)
|
||||||
|
return i;
|
||||||
|
slots >>= 1;
|
||||||
|
}
|
||||||
|
printk("Cannot find free command list entry");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hba_cmd_table *create_cmd_table(struct iovec *vec, size_t nvec)
|
||||||
|
{
|
||||||
|
size_t sz = sizeof(struct hba_cmd_table) + (sizeof(struct hba_prdt_entry) * nvec);
|
||||||
|
struct hba_cmd_table *out = kzalloc(sz, VM_NORMAL);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nvec; i++) {
|
||||||
|
phys_addr_t vec_phys = vm_virt_to_phys(vec->io_buf);
|
||||||
|
out->prdt_entry[i].dba = vec_phys & 0xFFFFFFFF;
|
||||||
|
out->prdt_entry[i].dbau = (vec_phys >> 32) & 0xFFFFFFFF;
|
||||||
|
out->prdt_entry[i].dbc = vec->io_len - 1;
|
||||||
|
out->prdt_entry[i].i = (i == nvec - 1) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_cmd_table(struct hba_cmd_table *table)
|
||||||
|
{
|
||||||
|
kfree(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_status_t send_command(struct ahci_device *dev, unsigned int cmd, struct iovec *vec, size_t nvec)
|
||||||
|
{
|
||||||
|
if (nvec == 0) {
|
||||||
|
return KERN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile struct hba_port *port = dev->port;
|
||||||
|
port->is = (uint32_t) -1;
|
||||||
|
int spin = 0;
|
||||||
|
int slot = find_cmdslot(port);
|
||||||
|
if (slot == -1) {
|
||||||
|
return KERN_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hba_cmd_header *cmdheader = &dev->cmd_header[slot];
|
||||||
|
cmdheader->cfl = sizeof(struct fis_reg_h2d) / sizeof(uint32_t);
|
||||||
|
cmdheader->w = 0;
|
||||||
|
cmdheader->prdtl = nvec;
|
||||||
|
|
||||||
|
struct hba_cmd_table *cmdtbl = create_cmd_table(vec, nvec);
|
||||||
|
phys_addr_t cmdtbl_phys = vm_virt_to_phys(cmdtbl);
|
||||||
|
cmdheader->ctba = cmdtbl_phys & 0xFFFFFFFF;
|
||||||
|
cmdheader->ctbau = (cmdtbl_phys >> 32) & 0xFFFFFFFF;
|
||||||
|
|
||||||
|
struct fis_reg_h2d *cmdfis = (struct fis_reg_h2d*)(&cmdtbl->cfis);
|
||||||
|
|
||||||
|
cmdfis->fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
cmdfis->c = 1;
|
||||||
|
cmdfis->command = cmd;
|
||||||
|
|
||||||
|
while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000) {
|
||||||
|
spin++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spin == 1000000) {
|
||||||
|
printk("Port is hung");
|
||||||
|
free_cmd_table(cmdtbl);
|
||||||
|
return KERN_DEVICE_STUCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->ci = 1 << slot;
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if ((port->ci & (1 << slot)) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port->is & HBA_PxIS_TFES) {
|
||||||
|
printk("Read disk error 1");
|
||||||
|
free_cmd_table(cmdtbl);
|
||||||
|
return KERN_IO_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_cmd_table(cmdtbl);
|
||||||
|
|
||||||
|
if (port->is & HBA_PxIS_TFES) {
|
||||||
|
printk("Read disk error 2");
|
||||||
|
return KERN_IO_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_status_t identify_device(struct ahci_device *dev, struct identify_device_data *out)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
port->is = (uint32_t) -1; // Clear pending interrupt bits
|
||||||
|
int spin = 0; // Spin lock timeout counter
|
||||||
|
int slot = find_cmdslot(port);
|
||||||
|
if (slot == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct hba_cmd_header *cmdheader = vm_phys_to_virt(port->clb);
|
||||||
|
cmdheader += slot;
|
||||||
|
cmdheader->cfl = sizeof(struct fis_reg_h2d)/sizeof(uint32_t); // Command FIS size
|
||||||
|
cmdheader->w = 0; // Read from device
|
||||||
|
cmdheader->prdtl = 1;
|
||||||
|
|
||||||
|
struct hba_cmd_table *cmdtbl = vm_phys_to_virt(cmdheader->ctba);
|
||||||
|
memset(cmdtbl, 0, sizeof(struct hba_cmd_table) +
|
||||||
|
(cmdheader->prdtl-1)*sizeof(struct hba_prdt_entry));
|
||||||
|
|
||||||
|
cmdtbl->prdt_entry[0].dba = (uint32_t)vm_virt_to_phys(out);
|
||||||
|
cmdtbl->prdt_entry[0].dbc = sizeof *out - 1;
|
||||||
|
cmdtbl->prdt_entry[0].i = 1;
|
||||||
|
|
||||||
|
// Setup command
|
||||||
|
struct fis_reg_h2d *cmdfis = (struct fis_reg_h2d*)(&cmdtbl->cfis);
|
||||||
|
|
||||||
|
cmdfis->fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
cmdfis->c = 1; // Command
|
||||||
|
cmdfis->command = ATA_CMD_IDENTIFY_DEVICE;
|
||||||
|
|
||||||
|
// The below loop waits until the port is no longer busy before issuing a new command
|
||||||
|
while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000)
|
||||||
|
{
|
||||||
|
spin++;
|
||||||
|
}
|
||||||
|
if (spin == 1000000)
|
||||||
|
{
|
||||||
|
printk("Port is hung");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->ci = 1<<slot; // Issue command
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// In some longer duration reads, it may be helpful to spin on the DPS bit
|
||||||
|
// in the PxIS port field as well (1 << 5)
|
||||||
|
if ((port->ci & (1<<slot)) == 0)
|
||||||
|
break;
|
||||||
|
if (port->is & HBA_PxIS_TFES) // Task file error
|
||||||
|
{
|
||||||
|
printk("Read disk error 1");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check again
|
||||||
|
if (port->is & HBA_PxIS_TFES)
|
||||||
|
{
|
||||||
|
printk("Read disk error 2");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
*/
|
||||||
|
struct iovec vec = { .io_buf = out, .io_len = sizeof *out };
|
||||||
|
kern_status_t status = send_command(dev, ATA_CMD_IDENTIFY_DEVICE, &vec, 1);
|
||||||
|
if (status != KERN_OK) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->model_number[39] = 0;
|
||||||
|
for (int i = 0; i < 40; i += 2) {
|
||||||
|
unsigned char c = out->model_number[i];
|
||||||
|
out->model_number[i] = out->model_number[i + 1];
|
||||||
|
out->model_number[i + 1] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = sizeof out->model_number - 1; i >= 0; i--) {
|
||||||
|
if (isspace(out->model_number[i]) || out->model_number[i] == 0) {
|
||||||
|
out->model_number[i] = 0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return KERN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start command engine
|
||||||
|
void start_cmd(volatile struct hba_port *port)
|
||||||
|
{
|
||||||
|
// Wait until CR (bit15) is cleared
|
||||||
|
while (port->cmd & HBA_PxCMD_CR)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Set FRE (bit4) and ST (bit0)
|
||||||
|
port->cmd |= HBA_PxCMD_FRE;
|
||||||
|
port->cmd |= HBA_PxCMD_ST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop command engine
|
||||||
|
void stop_cmd(volatile struct hba_port *port)
|
||||||
|
{
|
||||||
|
// Clear ST (bit0)
|
||||||
|
port->cmd &= ~HBA_PxCMD_ST;
|
||||||
|
|
||||||
|
// Clear FRE (bit4)
|
||||||
|
port->cmd &= ~HBA_PxCMD_FRE;
|
||||||
|
|
||||||
|
// Wait until FR (bit14), CR (bit15) are cleared
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if (port->cmd & HBA_PxCMD_FR)
|
||||||
|
continue;
|
||||||
|
if (port->cmd & HBA_PxCMD_CR)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void port_rebase(struct ahci_device *dev)
|
||||||
|
{
|
||||||
|
volatile struct hba_port *port = dev->port;
|
||||||
|
|
||||||
|
stop_cmd(port); // Stop command engine
|
||||||
|
|
||||||
|
struct hba_cmd_header *cmdheader = dev->cmd_header;
|
||||||
|
|
||||||
|
// Command list offset: 1K*portno
|
||||||
|
// Command list entry size = 32
|
||||||
|
// Command list entry maxim count = 32
|
||||||
|
// Command list maxim size = 32*32 = 1K per port
|
||||||
|
phys_addr_t cmdheader_phys = vm_virt_to_phys(cmdheader);
|
||||||
|
port->clb = cmdheader_phys & 0xFFFFFFFF;
|
||||||
|
port->clbu = (cmdheader_phys >> 32) & 0xFFFFFFFF;
|
||||||
|
memset(cmdheader, 0, sizeof *cmdheader);
|
||||||
|
|
||||||
|
// FIS offset: 32K+256*portno
|
||||||
|
// FIS entry size = 256 bytes per port
|
||||||
|
phys_addr_t fis_phys = vm_virt_to_phys(dev->fis_data);
|
||||||
|
port->fb = fis_phys & 0xFFFFFFFF;
|
||||||
|
port->fbu = (fis_phys >> 32) & 0xFFFFFFFF;
|
||||||
|
memset(dev->fis_data, 0, 256);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Command table offset: 40K + 8K*portno
|
||||||
|
// Command table size = 256*32 = 8K per port
|
||||||
|
for (int i=0; i<32; i++)
|
||||||
|
{
|
||||||
|
cmdheader[i].prdtl = 8; // 8 prdt entries per command table
|
||||||
|
// 256 bytes per command table, 64+16+48+16*8
|
||||||
|
// Command table offset: 40K + 8K*portno + cmdheader_index*256
|
||||||
|
cmdheader[i].ctba = phys + offset;
|
||||||
|
cmdheader[i].ctbau = 0;
|
||||||
|
memset(virt + offset, 0, 256);
|
||||||
|
|
||||||
|
offset += 256;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
start_cmd(port); // Start command engine
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static int read(struct hba_port *port, uint32_t startl, uint32_t starth, uint32_t count, uint16_t *buf)
|
||||||
|
{
|
||||||
|
port->is = (uint32_t) -1; // Clear pending interrupt bits
|
||||||
|
int spin = 0; // Spin lock timeout counter
|
||||||
|
int slot = find_cmdslot(port);
|
||||||
|
if (slot == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct hba_cmd_header *cmdheader = (struct hba_cmd_header*)(uintptr_t)port->clb;
|
||||||
|
cmdheader += slot;
|
||||||
|
cmdheader->cfl = sizeof(struct fis_reg_h2d)/sizeof(uint32_t); // Command FIS size
|
||||||
|
cmdheader->w = 0; // Read from device
|
||||||
|
cmdheader->prdtl = (uint16_t)((count-1)>>4) + 1; // PRDT entries count
|
||||||
|
|
||||||
|
struct hba_cmd_table *cmdtbl = (struct hba_cmd_table*)((uintptr_t)cmdheader->ctba);
|
||||||
|
memset(cmdtbl, 0, sizeof(struct hba_cmd_table) +
|
||||||
|
(cmdheader->prdtl-1)*sizeof(struct hba_prdt_entry));
|
||||||
|
|
||||||
|
// 8K bytes (16 sectors) per PRDT
|
||||||
|
int i;
|
||||||
|
for (i=0; i<cmdheader->prdtl-1; i++)
|
||||||
|
{
|
||||||
|
cmdtbl->prdt_entry[i].dba = (uint32_t)(uintptr_t)buf;
|
||||||
|
cmdtbl->prdt_entry[i].dbc = 8*1024-1; // 8K bytes (this value should always be set to 1 less than the actual value)
|
||||||
|
cmdtbl->prdt_entry[i].i = 1;
|
||||||
|
buf += 4*1024; // 4K words
|
||||||
|
count -= 16; // 16 sectors
|
||||||
|
}
|
||||||
|
// Last entry
|
||||||
|
cmdtbl->prdt_entry[i].dba = (uint32_t)(uintptr_t)buf;
|
||||||
|
cmdtbl->prdt_entry[i].dbc = (count<<9)-1; // 512 bytes per sector
|
||||||
|
cmdtbl->prdt_entry[i].i = 1;
|
||||||
|
|
||||||
|
// Setup command
|
||||||
|
struct fis_reg_h2d *cmdfis = (struct fis_reg_h2d*)(&cmdtbl->cfis);
|
||||||
|
|
||||||
|
cmdfis->fis_type = FIS_TYPE_REG_H2D;
|
||||||
|
cmdfis->c = 1; // Command
|
||||||
|
cmdfis->command = ATA_CMD_READ_DMA_EX;
|
||||||
|
|
||||||
|
cmdfis->lba0 = (uint8_t)startl;
|
||||||
|
cmdfis->lba1 = (uint8_t)(startl>>8);
|
||||||
|
cmdfis->lba2 = (uint8_t)(startl>>16);
|
||||||
|
cmdfis->device = 1<<6; // LBA mode
|
||||||
|
|
||||||
|
cmdfis->lba3 = (uint8_t)(startl>>24);
|
||||||
|
cmdfis->lba4 = (uint8_t)starth;
|
||||||
|
cmdfis->lba5 = (uint8_t)(starth>>8);
|
||||||
|
|
||||||
|
cmdfis->count_l = count & 0xFF;
|
||||||
|
cmdfis->count_h = (count >> 8) & 0xFF;
|
||||||
|
|
||||||
|
// The below loop waits until the port is no longer busy before issuing a new command
|
||||||
|
while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000)
|
||||||
|
{
|
||||||
|
spin++;
|
||||||
|
}
|
||||||
|
if (spin == 1000000)
|
||||||
|
{
|
||||||
|
printk("Port is hung");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->ci = 1<<slot; // Issue command
|
||||||
|
|
||||||
|
// Wait for completion
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// In some longer duration reads, it may be helpful to spin on the DPS bit
|
||||||
|
// in the PxIS port field as well (1 << 5)
|
||||||
|
if ((port->ci & (1<<slot)) == 0)
|
||||||
|
break;
|
||||||
|
if (port->is & HBA_PxIS_TFES) // Task file error
|
||||||
|
{
|
||||||
|
printk("Read disk error 1");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check again
|
||||||
|
if (port->is & HBA_PxIS_TFES)
|
||||||
|
{
|
||||||
|
printk("Read disk error 2");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int check_type(volatile struct hba_port *port)
|
||||||
|
{
|
||||||
|
uint32_t ssts = port->ssts;
|
||||||
|
|
||||||
|
uint8_t ipm = (ssts >> 8) & 0x0F;
|
||||||
|
uint8_t det = ssts & 0x0F;
|
||||||
|
|
||||||
|
if (det != HBA_PORT_DET_PRESENT) {
|
||||||
|
return AHCI_DEV_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipm != HBA_PORT_IPM_ACTIVE) {
|
||||||
|
return AHCI_DEV_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (port->sig) {
|
||||||
|
case SATA_SIG_ATAPI:
|
||||||
|
return AHCI_DEV_SATAPI;
|
||||||
|
case SATA_SIG_SEMB:
|
||||||
|
return AHCI_DEV_SEMB;
|
||||||
|
case SATA_SIG_PM:
|
||||||
|
return AHCI_DEV_PM;
|
||||||
|
default:
|
||||||
|
return AHCI_DEV_SATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void probe_ports(struct driver *driver, struct device *controller, volatile struct hba_config *abar)
|
||||||
|
{
|
||||||
|
uint32_t pi = abar->pi;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (!(pi & 1)) {
|
||||||
|
pi >>= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dt = check_type(&abar->ports[i]);
|
||||||
|
|
||||||
|
switch (dt) {
|
||||||
|
case AHCI_DEV_SATA:
|
||||||
|
case AHCI_DEV_SATAPI:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pi >>= 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct block_device *bdev = block_device_create();
|
||||||
|
if (!bdev) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct device *bdev_base = block_device_base(bdev);
|
||||||
|
|
||||||
|
struct ahci_device *ahci_dev = kmalloc(sizeof *ahci_dev, VM_NORMAL);
|
||||||
|
if (!ahci_dev) {
|
||||||
|
device_deref(bdev_base);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ahci_dev->port = &abar->ports[i];
|
||||||
|
ahci_dev->port_no = i;
|
||||||
|
ahci_dev->type = dt;
|
||||||
|
bdev_base->dev_priv = ahci_dev;
|
||||||
|
port_rebase(ahci_dev);
|
||||||
|
|
||||||
|
snprintf(bdev_base->dev_name, sizeof bdev_base->dev_name, "ahci%d", i);
|
||||||
|
|
||||||
|
if (dt == AHCI_DEV_SATA) {
|
||||||
|
struct identify_device_data identity_data = {0};
|
||||||
|
kern_status_t status = identify_device(ahci_dev, &identity_data);
|
||||||
|
if (status != KERN_OK) {
|
||||||
|
kfree(ahci_dev);
|
||||||
|
device_deref(bdev_base);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
printk("ahci: found device '%s'", identity_data.model_number);
|
||||||
|
printk("ahci: version: %u.%u", identity_data.major_revision, identity_data.major_revision);
|
||||||
|
printk("ahci: C:%u, H:%u, S/T:%u", identity_data.num_cylinders, identity_data.num_heads, identity_data.num_sectors_per_track);
|
||||||
|
printk("ahci: nr sectors=%u", identity_data.user_addressable_sectors);
|
||||||
|
printk("ahci: offset: %u", offsetof(struct identify_device_data, current_sector_capacity));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* TODO read IDENTIFY DEVICE log to support non-512 sector sizes */
|
||||||
|
bdev->sector_size = 512;
|
||||||
|
bdev->capacity = identity_data.user_addressable_sectors;
|
||||||
|
|
||||||
|
snprintf(bdev_base->dev_model_name, sizeof bdev_base->dev_model_name, "%s", identity_data.model_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
device_register(bdev_base, driver, controller);
|
||||||
|
|
||||||
|
pi >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************************** STOLEN **********************/
|
||||||
|
|
||||||
|
static struct pci_driver *ahci_driver = NULL;
|
||||||
|
static volatile struct hba_config *hba_config = NULL;
|
||||||
|
|
||||||
|
static int irq_callback(void);
|
||||||
|
|
||||||
|
static struct irq_hook irq_hook = {
|
||||||
|
.irq_callback = irq_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pci_device_id ahci_device_ids[] = {
|
||||||
|
PCI_CLASS_ID(0x01, 0x06),
|
||||||
|
PCI_DEVICE_ID_INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static kern_status_t ahci_scan(struct device *ahci_bus)
|
||||||
|
{
|
||||||
|
printk("ahci: scanning connected SATA devices");
|
||||||
|
uint32_t abar = pci_device_read_field(ahci_bus, PCI_REG_BAR5, 4);
|
||||||
|
hba_config = vm_phys_to_virt(abar);
|
||||||
|
probe_ports(pci_driver_base(ahci_driver), ahci_bus, hba_config);
|
||||||
|
|
||||||
|
uint8_t irq = pci_device_read_field(ahci_bus, PCI_REG_INTERRUPT_LINE, 1);
|
||||||
|
printk("ahci: IRQ line: %u", irq);
|
||||||
|
return KERN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bus_device_ops ahci_bus_ops = {
|
||||||
|
.scan = ahci_scan,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int irq_callback(void)
|
||||||
|
{
|
||||||
|
printk("ahci: received IRQ");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_status_t ahci_probe(struct pci_driver *driver, struct device *dev)
|
||||||
|
{
|
||||||
|
printk("ahci: found AHCI controller");
|
||||||
|
struct bus_device *ahci_bus = bus_device_from_generic(dev);
|
||||||
|
ahci_bus->b_ops = &ahci_bus_ops;
|
||||||
|
snprintf(dev->dev_name, sizeof dev->dev_name, "ahci");
|
||||||
|
|
||||||
|
uint8_t irq_vec = pci_device_read_field(dev, PCI_REG_INTERRUPT_LINE, 1);
|
||||||
|
hook_irq(IRQ0 + irq_vec, &irq_hook);
|
||||||
|
|
||||||
|
return device_register(dev, pci_driver_base(ahci_driver), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static kern_status_t online(struct kext *self)
|
||||||
|
{
|
||||||
|
printk("ahci: registering AHCI driver");
|
||||||
|
ahci_driver = pci_driver_create(self, "ahci", ahci_device_ids);
|
||||||
|
if (!ahci_driver) {
|
||||||
|
return KERN_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
ahci_driver->probe = ahci_probe;
|
||||||
|
|
||||||
|
pci_driver_register(ahci_driver);
|
||||||
|
return KERN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_KEXT("net.doorstuck.socks.ahci",
|
||||||
|
online, NULL,
|
||||||
|
PCI_SUBSYSTEM_KEXT_ID);
|
||||||
Reference in New Issue
Block a user