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
95struct 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
120struct 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
153struct 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
162struct 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
169struct 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
179struct 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
202struct 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
221struct ahci_disk {
222 struct ahci_device *device;
223 uint32_t port;
224 uint16_t sector_size; // Sector size in bytes
225};
226
227struct 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
243struct ahci_cmd_header {
244 // DW0
245 uint8_t cfl : 5; // Command FIS length in DWORDS, 2 ~ 16
246 uint8_t a : 1; // ATAPI
247 uint8_t w : 1; // Write, 1: H2D, 0: D2H
248 uint8_t p : 1; // Prefetchable
249
250 uint8_t r : 1; // Reset
251 uint8_t b : 1; // BIST
252 uint8_t c : 1; // Clear busy upon R_OK
253 uint8_t rsv0 : 1; // Reserved
254 uint8_t pmp : 4; // Port multiplier port
255
256 uint16_t prdtl; // Physical region descriptor table length in entries
257
258 // DW1
259 uint32_t prdbc; // Physical region descriptor byte count transferred
260
261 // DW2, 3
262 uint32_t ctba; // Command table descriptor base address
263 uint32_t ctbau; // Command table descriptor base address upper 32 bits
264
265 // DW4 - 7
266 uint32_t rsv1[4]; // Reserved
267} __packed;
268static_assert_struct_size_eq(ahci_cmd_header, 32);
269
270LOG_HANDLE_EXTERN(ahci);
271LOG_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
276struct 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
293void ahci_discover(struct ahci_controller *ctrl);
294uint32_t ahci_find_slot(struct ahci_full_port *port);
295struct ahci_disk *ahci_setup_controller(struct ahci_controller *ctrl,
296 uint32_t *d_cnt);
297void ahci_identify(struct ahci_disk *disk);
298void ahci_prepare_command(struct ahci_full_port *port, uint32_t slot,
299 bool write, uint8_t *buf, uint64_t size);
300void ahci_setup_fis(struct ahci_cmd_table *cmd_tbl, uint8_t command,
301 bool is_atapi);
302
303void ahci_send_command(struct ahci_disk *disk, struct ahci_full_port *port,
304 struct ahci_request *req);
305
306struct ahci_disk *ahci_discover_device(uint8_t bus, uint8_t device,
307 uint8_t function,
308 uint32_t *out_disk_count);
309
310bool ahci_write_sector(struct block_device *disk, uint64_t lba,
311 const uint8_t *in_buf, uint16_t cnt);
312bool 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
316bool ahci_read_sector(struct block_device *disk, uint64_t lba, uint8_t *out_buf,
317 uint16_t cnt);
318bool ahci_read_sector_async(struct block_device *disk, uint64_t lba,
319 uint8_t *buf, uint16_t count,
320 struct ahci_request *req);
321
322bool ahci_read_sector_wrapper(struct block_device *disk, uint64_t lba,
323 uint8_t *buf, uint64_t cnt);
324
325bool 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
329bool ahci_write_sector_wrapper(struct block_device *disk, uint64_t lba,
330 const uint8_t *buf, uint64_t cnt);
331
332bool 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
336bool ahci_submit_bio_request(struct block_device *disk,
337 struct bio_request *bio);
338
339void ahci_do_coalesce(struct block_device *disk, struct bio_request *into,
340 struct bio_request *from);
341
342bool ahci_should_coalesce(struct block_device *disk,
343 const struct bio_request *a,
344 const struct bio_request *b);
345
346void ahci_reorder(struct block_device *disk);
347
348struct block_device *ahci_create_generic(struct ahci_disk *disk);
349enum 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
354static 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