| 1 | /* @title: AHCI */ |
| 2 | #pragma once |
| 3 | #include <block/block.h> |
| 4 | #include <block/sched.h> |
| 5 | #include <compiler.h> |
| 6 | #include <log.h> |
| 7 | #include <stdbool.h> |
| 8 | #include <stdint.h> |
| 9 | #include <thread/thread.h> |
| 10 | |
| 11 | #define AHCI_CMD_TIMEOUT_MS 5000 // Data commands (read/write) |
| 12 | #define AHCI_IDENT_TIMEOUT_MS 10000 // Identify, flush cache, etc. |
| 13 | #define AHCI_RESET_TIMEOUT_MS 30000 // Full controller reset or COMRESET |
| 14 | |
| 15 | #define AHCI_CAP 0x00 // Host Capabilities |
| 16 | #define AHCI_GHC 0x04 // Global Host Control |
| 17 | #define AHCI_IS 0x08 // Interrupt Status |
| 18 | #define AHCI_PI 0x0C // Ports Implemented |
| 19 | #define AHCI_VS 0x10 // AHCI Version |
| 20 | #define AHCI_CCC_CTL 0x14 // Command Completion Coalescing Control |
| 21 | #define AHCI_CCC_PORTS 0x18 // CCC Ports |
| 22 | #define AHCI_EM_LOC 0x1C // Enclosure Management Location |
| 23 | #define AHCI_EM_CTL 0x20 // Enclosure Management Control |
| 24 | #define AHCI_CAP2 0x24 // Host Capabilities Extended |
| 25 | #define AHCI_BOHC 0x28 // BIOS/OS Handoff Control and Status |
| 26 | |
| 27 | #define AHCI_PORT_BASE 0x100 // Base offset for port registers |
| 28 | #define AHCI_PORT_SIZE 0x80 // Size of each port register set |
| 29 | |
| 30 | #define AHCI_MAX_PORTS 32 |
| 31 | #define AHCI_MAX_SLOTS 32 |
| 32 | |
| 33 | #define AHCI_PORT_CLB 0x00 // Command List Base Address |
| 34 | #define AHCI_PORT_CLBU 0x04 // Command List Base Address Upper |
| 35 | #define AHCI_PORT_FB 0x08 // FIS Base Address |
| 36 | #define AHCI_PORT_FBU 0x0C // FIS Base Address Upper |
| 37 | #define AHCI_PORT_IS 0x10 // Interrupt Status |
| 38 | #define AHCI_PORT_IE 0x14 // Interrupt Enable |
| 39 | #define AHCI_PORT_CMD 0x18 // Command and Status |
| 40 | #define AHCI_PORT_TFD 0x20 // Task File Data |
| 41 | #define AHCI_PORT_SIG 0x24 // Signature |
| 42 | #define AHCI_PORT_SSTS 0x28 // SATA Status |
| 43 | #define AHCI_PORT_SCTL 0x2C // SATA Control |
| 44 | #define AHCI_PORT_SERR 0x30 // SATA Error |
| 45 | #define AHCI_PORT_SACT 0x34 // SATA Active |
| 46 | #define AHCI_PORT_CI 0x38 // Command Issue |
| 47 | #define AHCI_PORT_SNTF 0x3C // SATA Notification |
| 48 | |
| 49 | #define AHCI_DEV_NULL 0 |
| 50 | #define AHCI_DEV_SATA 1 |
| 51 | #define AHCI_DEV_BUSY (1 << 30) |
| 52 | #define AHCI_DEV_DRDY 0x40 |
| 53 | #define AHCI_DEV_SATAPI 2 |
| 54 | #define AHCI_DEV_SEMB 3 |
| 55 | #define AHCI_DEV_PM 4 |
| 56 | |
| 57 | #define AHCI_CMD_TABLE_FIS_SIZE 64 |
| 58 | #define AHCI_CMD_TABLE_ATAPI_SIZE 16 |
| 59 | #define AHCI_MAX_PRDT_ENTRIES 65535 |
| 60 | |
| 61 | #define AHCI_GHC_HR (1U << 0) |
| 62 | #define AHCI_GHC_AE (1U << 31) |
| 63 | |
| 64 | #define AHCI_DET_NO_DEVICE 0x0 |
| 65 | #define AHCI_DET_PRESENT 0x3 |
| 66 | |
| 67 | #define AHCI_IPM_NO_INTERFACE 0x0 |
| 68 | #define AHCI_IPM_ACTIVE 0x1 |
| 69 | |
| 70 | #define AHCI_CMD_READ_DMA_EXT 0x25 |
| 71 | #define AHCI_CMD_WRITE_DMA_EXT 0x35 |
| 72 | |
| 73 | #define AHCI_CMD_IDENTIFY 0xEC |
| 74 | #define AHCI_CMD_ST (1U << 0) // Start |
| 75 | #define AHCI_CMD_SUD (1U << 1) // Spin-Up Device |
| 76 | #define AHCI_CMD_FRE (1U << 4) // FIS Receive Enable |
| 77 | #define AHCI_CMD_FR (1U << 14) // FIS Receive Running |
| 78 | #define AHCI_CMD_CR (1U << 15) // Command List Running |
| 79 | #define AHCI_CMD_CLO (1U << 3) // Command List Override |
| 80 | |
| 81 | #define AHCI_PORT_IPM_ACTIVE 1 |
| 82 | #define AHCI_PORT_DET_PRESENT 3 |
| 83 | |
| 84 | #define AHCI_CMD_FLAGS_WRITE (1 << 6) |
| 85 | #define AHCI_CMD_FLAGS_PRDTL 1 |
| 86 | |
| 87 | #define FIS_TYPE_REG_H2D 0x27 |
| 88 | #define FIS_REG_CMD 0x80 |
| 89 | #define LBA_MODE 0x40 |
| 90 | #define CONTROL_BIT 0x80 |
| 91 | |
| 92 | #define AHCI_CMD_FLAGS_CFL_MASK 0x1F |
| 93 | #define AHCI_CMD_FLAGS_W_BIT 0x40 |
| 94 | |
| 95 | struct ahci_fis_reg_h2d { |
| 96 | uint8_t fis_type; // 0x27 |
| 97 | uint8_t pmport : 4; |
| 98 | uint8_t reserved1 : 3; |
| 99 | uint8_t c : 1; |
| 100 | uint8_t command; |
| 101 | uint8_t featurel; |
| 102 | |
| 103 | uint8_t lba0; |
| 104 | uint8_t lba1; |
| 105 | uint8_t lba2; |
| 106 | uint8_t device; |
| 107 | |
| 108 | uint8_t lba3; |
| 109 | uint8_t lba4; |
| 110 | uint8_t lba5; |
| 111 | uint8_t featureh; |
| 112 | |
| 113 | uint8_t countl; |
| 114 | uint8_t counth; |
| 115 | uint8_t icc; |
| 116 | uint8_t control; |
| 117 | uint8_t reserved2[4]; |
| 118 | }; |
| 119 | |
| 120 | struct ahci_fis_reg_d2h { |
| 121 | // DWORD 0 |
| 122 | uint8_t fis_type; // FIS_TYPE_REG_D2H |
| 123 | |
| 124 | uint8_t pmport : 4; // Port multiplier |
| 125 | uint8_t rsv0 : 2; // Reserved |
| 126 | uint8_t i : 1; // Interrupt bit |
| 127 | uint8_t rsv1 : 1; // Reserved |
| 128 | |
| 129 | uint8_t status; // Status register |
| 130 | uint8_t error; // Error register |
| 131 | |
| 132 | // DWORD 1 |
| 133 | uint8_t lba0; // LBA low register, 7:0 |
| 134 | uint8_t lba1; // LBA mid register, 15:8 |
| 135 | uint8_t lba2; // LBA high register, 23:16 |
| 136 | uint8_t device; // Device register |
| 137 | |
| 138 | // DWORD 2 |
| 139 | uint8_t lba3; // LBA register, 31:24 |
| 140 | uint8_t lba4; // LBA register, 39:32 |
| 141 | uint8_t lba5; // LBA register, 47:40 |
| 142 | uint8_t rsv2; // Reserved |
| 143 | |
| 144 | // DWORD 3 |
| 145 | uint8_t countl; // Count register, 7:0 |
| 146 | uint8_t counth; // Count register, 15:8 |
| 147 | uint8_t rsv3[2]; // Reserved |
| 148 | |
| 149 | // DWORD 4 |
| 150 | uint8_t rsv4[4]; // Reserved |
| 151 | }; |
| 152 | |
| 153 | struct ahci_prdt_entry { |
| 154 | uint32_t dba; // Data base address |
| 155 | uint32_t dbau; // Upper 32-bits of DBA |
| 156 | uint32_t reserved; |
| 157 | uint32_t dbc : 22; // Byte count (0-based) |
| 158 | uint32_t reserved2 : 9; |
| 159 | uint32_t i : 1; // Interrupt on completion |
| 160 | } __packed; |
| 161 | |
| 162 | struct ahci_cmd_table { |
| 163 | uint8_t cfis[AHCI_CMD_TABLE_FIS_SIZE]; // Command FIS (host to device) |
| 164 | uint8_t acmd[AHCI_CMD_TABLE_ATAPI_SIZE]; // ATAPI command |
| 165 | uint8_t reserved[48]; |
| 166 | struct ahci_prdt_entry prdt_entry[]; // up to 65535 |
| 167 | } __packed; |
| 168 | |
| 169 | struct ahci_full_port { |
| 170 | struct ahci_port *port; |
| 171 | void *cmd_list_base; |
| 172 | void *fis; |
| 173 | struct ahci_cmd_table **cmd_tables; |
| 174 | struct ahci_cmd_header **cmd_hdrs; |
| 175 | |
| 176 | volatile _Atomic uint32_t slot_bitmap; |
| 177 | }; |
| 178 | |
| 179 | struct ahci_port { |
| 180 | uint32_t clb; // Command List Base (lower 32 bits) |
| 181 | uint32_t clbu; // Command List Base (upper 32 bits) |
| 182 | uint32_t fb; // FIS Base (lower 32 bits) |
| 183 | uint32_t fbu; // FIS Base (upper 32 bits) |
| 184 | uint32_t is; // Interrupt Status |
| 185 | uint32_t ie; // Interrupt Enable |
| 186 | uint32_t cmd; // Command and Status |
| 187 | uint32_t rsv0; // Reserved |
| 188 | uint32_t tfd; // Task File Data |
| 189 | uint32_t sig; // Signature |
| 190 | uint32_t ssts; // SATA Status |
| 191 | uint32_t sctl; // SATA Control |
| 192 | uint32_t serr; // SATA Error |
| 193 | uint32_t sact; // SATA Active |
| 194 | uint32_t ci; // Command Issue |
| 195 | uint32_t sntf; // SATA Notification |
| 196 | uint32_t fbs; // FIS-based Switching |
| 197 | uint32_t rsv1[11]; // 0x44 ~ 0x6F, Reserved |
| 198 | uint32_t vendor[4]; // 0x70 ~ 0x7F, vendor specific |
| 199 | }; |
| 200 | |
| 201 | // one controller |
| 202 | struct ahci_device { |
| 203 | uint8_t type; // Device type |
| 204 | uint32_t signature; // Device signature |
| 205 | uint32_t sectors; // Total sectors (for disks) |
| 206 | uint16_t sector_size; // Sector size in bytes |
| 207 | struct ahci_controller *ctrl; |
| 208 | uint64_t port_count; |
| 209 | |
| 210 | struct thread *io_waiters[AHCI_MAX_PORTS][32]; |
| 211 | uint16_t io_statuses[AHCI_MAX_PORTS][32]; |
| 212 | |
| 213 | struct ahci_request *io_requests[AHCI_MAX_PORTS][32]; |
| 214 | |
| 215 | irq_t irq_num; |
| 216 | struct spinlock lock; |
| 217 | struct ahci_full_port regs[32]; // Pointer to port registers |
| 218 | }; |
| 219 | |
| 220 | // one disk |
| 221 | struct ahci_disk { |
| 222 | struct ahci_device *device; |
| 223 | uint32_t port; |
| 224 | uint16_t sector_size; // Sector size in bytes |
| 225 | }; |
| 226 | |
| 227 | struct ahci_controller { |
| 228 | uint32_t cap; // Host Capabilities |
| 229 | uint32_t ghc; // Global Host Control |
| 230 | uint32_t is; // Interrupt Status |
| 231 | uint32_t pi; // Ports Implemented |
| 232 | uint32_t vs; // Version |
| 233 | uint32_t ccc_ctl; // Command Completion Coalescing Control |
| 234 | uint32_t ccc_ports; // CCC Ports |
| 235 | uint32_t em_loc; // Enclosure Management Location |
| 236 | uint32_t em_ctl; // Enclosure Management Control |
| 237 | uint32_t cap2; // Extended Host Capabilities |
| 238 | uint32_t bohc; // BIOS/OS Handoff Control |
| 239 | uint8_t rsv[0xA0 - 0x2C]; |
| 240 | uint8_t vendor[0x100 - 0xA0]; |
| 241 | }; |
| 242 | |
| 243 | struct { |
| 244 | // DW0 |
| 245 | uint8_t : 5; // Command FIS length in DWORDS, 2 ~ 16 |
| 246 | uint8_t : 1; // ATAPI |
| 247 | uint8_t : 1; // Write, 1: H2D, 0: D2H |
| 248 | uint8_t : 1; // Prefetchable |
| 249 | |
| 250 | uint8_t : 1; // Reset |
| 251 | uint8_t : 1; // BIST |
| 252 | uint8_t : 1; // Clear busy upon R_OK |
| 253 | uint8_t : 1; // Reserved |
| 254 | uint8_t : 4; // Port multiplier port |
| 255 | |
| 256 | uint16_t ; // Physical region descriptor table length in entries |
| 257 | |
| 258 | // DW1 |
| 259 | uint32_t ; // Physical region descriptor byte count transferred |
| 260 | |
| 261 | // DW2, 3 |
| 262 | uint32_t ; // Command table descriptor base address |
| 263 | uint32_t ; // Command table descriptor base address upper 32 bits |
| 264 | |
| 265 | // DW4 - 7 |
| 266 | uint32_t [4]; // Reserved |
| 267 | } __packed; |
| 268 | static_assert_struct_size_eq(ahci_cmd_header, 32); |
| 269 | |
| 270 | LOG_HANDLE_EXTERN(ahci); |
| 271 | LOG_SITE_EXTERN(ahci); |
| 272 | |
| 273 | #define ahci_log(log_level, fmt, ...) \ |
| 274 | log(LOG_SITE(ahci), LOG_HANDLE(ahci), log_level, fmt, ##__VA_ARGS__) |
| 275 | |
| 276 | struct ahci_request { |
| 277 | uint32_t port; |
| 278 | uint32_t slot; |
| 279 | uint64_t lba; |
| 280 | void *buffer; |
| 281 | uint64_t size; |
| 282 | uint64_t sector_count; |
| 283 | bool write; |
| 284 | bool trigger_completion; |
| 285 | |
| 286 | volatile bool done; |
| 287 | int status; |
| 288 | |
| 289 | void (*on_complete)(struct ahci_request *); |
| 290 | void *user_data; |
| 291 | }; |
| 292 | |
| 293 | void ahci_discover(struct ahci_controller *ctrl); |
| 294 | uint32_t ahci_find_slot(struct ahci_full_port *port); |
| 295 | struct ahci_disk *ahci_setup_controller(struct ahci_controller *ctrl, |
| 296 | uint32_t *d_cnt); |
| 297 | void ahci_identify(struct ahci_disk *disk); |
| 298 | void ahci_prepare_command(struct ahci_full_port *port, uint32_t slot, |
| 299 | bool write, uint8_t *buf, uint64_t size); |
| 300 | void ahci_setup_fis(struct ahci_cmd_table *cmd_tbl, uint8_t command, |
| 301 | bool is_atapi); |
| 302 | |
| 303 | void ahci_send_command(struct ahci_disk *disk, struct ahci_full_port *port, |
| 304 | struct ahci_request *req); |
| 305 | |
| 306 | struct ahci_disk *ahci_discover_device(uint8_t bus, uint8_t device, |
| 307 | uint8_t function, |
| 308 | uint32_t *out_disk_count); |
| 309 | |
| 310 | bool ahci_write_sector(struct block_device *disk, uint64_t lba, |
| 311 | const uint8_t *in_buf, uint16_t cnt); |
| 312 | bool ahci_write_sector_async(struct block_device *disk, uint64_t lba, |
| 313 | uint8_t *in_buf, uint16_t count, |
| 314 | struct ahci_request *req); |
| 315 | |
| 316 | bool ahci_read_sector(struct block_device *disk, uint64_t lba, uint8_t *out_buf, |
| 317 | uint16_t cnt); |
| 318 | bool ahci_read_sector_async(struct block_device *disk, uint64_t lba, |
| 319 | uint8_t *buf, uint16_t count, |
| 320 | struct ahci_request *req); |
| 321 | |
| 322 | bool ahci_read_sector_wrapper(struct block_device *disk, uint64_t lba, |
| 323 | uint8_t *buf, uint64_t cnt); |
| 324 | |
| 325 | bool ahci_read_sector_async_wrapper(struct block_device *disk, uint64_t lba, |
| 326 | uint8_t *buf, uint64_t cnt, |
| 327 | struct ahci_request *req); |
| 328 | |
| 329 | bool ahci_write_sector_wrapper(struct block_device *disk, uint64_t lba, |
| 330 | const uint8_t *buf, uint64_t cnt); |
| 331 | |
| 332 | bool ahci_write_sector_async_wrapper(struct block_device *disk, uint64_t lba, |
| 333 | const uint8_t *buf, uint64_t cnt, |
| 334 | struct ahci_request *req); |
| 335 | |
| 336 | bool ahci_submit_bio_request(struct block_device *disk, |
| 337 | struct bio_request *bio); |
| 338 | |
| 339 | void ahci_do_coalesce(struct block_device *disk, struct bio_request *into, |
| 340 | struct bio_request *from); |
| 341 | |
| 342 | bool ahci_should_coalesce(struct block_device *disk, |
| 343 | const struct bio_request *a, |
| 344 | const struct bio_request *b); |
| 345 | |
| 346 | void ahci_reorder(struct block_device *disk); |
| 347 | |
| 348 | struct block_device *ahci_create_generic(struct ahci_disk *disk); |
| 349 | enum irq_result ahci_isr_handler(void *ctx, uint8_t vector, |
| 350 | struct irq_context *); |
| 351 | |
| 352 | #define AHCI_PORT_OFFSET(n) (0x100 + (n) * 0x80) |
| 353 | |
| 354 | static inline struct ahci_port *ahci_get_port(struct ahci_device *dev, int n) { |
| 355 | return (struct ahci_port *) ((uintptr_t) dev->ctrl + AHCI_PORT_OFFSET(n)); |
| 356 | } |
| 357 | |