1#include <acpi/lapic.h>
2#include <asm.h>
3#include <block/bio.h>
4#include <block/block.h>
5#include <drivers/ahci.h>
6#include <drivers/ata.h>
7#include <irq/idt.h>
8#include <mem/alloc.h>
9#include <mem/vmm.h>
10#include <sch/sched.h>
11#include <stdbool.h>
12#include <stdint.h>
13#include <string.h>
14#include <thread/thread.h>
15#include <time/spin_sleep.h>
16
17#define MAX_PRDT_ENTRY_SIZE (4 * 1024 * 1024) // 4MB
18
19/* TODO: horrible code - bit-op spam */
20void ahci_process_completions(struct ahci_device *dev, uint32_t port) {
21 struct ahci_full_port *fp = &dev->regs[port];
22 struct ahci_port *p = fp->port;
23
24 uint32_t ci = mmio_read_32(address: &p->ci);
25 uint32_t sact = mmio_read_32(address: &p->sact);
26 uint32_t completed = ~(ci | sact);
27
28 for (uint32_t slot = 0; slot < AHCI_MAX_SLOTS; slot++) {
29 uint32_t mask = 1U << slot;
30 if (!(completed & mask))
31 continue;
32
33 struct ahci_request *req = dev->io_requests[port][slot];
34
35 if (req && req->trigger_completion) {
36 req->done = true;
37 req->status = 0;
38
39 if (req->on_complete)
40 req->on_complete(req);
41 atomic_store(&fp->slot_bitmap,
42 atomic_load(&fp->slot_bitmap) & ~mask);
43 }
44
45 struct thread *t = dev->io_waiters[port][slot];
46 if (t)
47 thread_wake_from_io_block(t, wake_src: dev);
48
49 dev->io_requests[port][slot] = NULL;
50 }
51
52 mmio_write_32(address: &p->is, value: p->is);
53}
54
55enum irq_result ahci_isr_handler(void *ctx, uint8_t vector,
56 struct irq_context *rsp) {
57 (void) vector, (void) rsp;
58
59 struct ahci_device *dev = ctx;
60 for (uint32_t port = 0; port < AHCI_MAX_PORTS; port++) {
61 if (!dev->regs[port].port)
62 continue;
63
64 ahci_process_completions(dev, port);
65 }
66
67 return IRQ_HANDLED;
68}
69
70void ahci_send_command(struct ahci_disk *disk, struct ahci_full_port *port,
71 struct ahci_request *req) {
72 uint32_t slot = req->slot;
73
74 mmio_write_32(address: &port->port->is, value: 0xFFFFFFFF);
75 disk->device->io_requests[disk->port][slot] = req;
76
77 uint32_t command_issue = mmio_read_32(address: &port->port->ci);
78 command_issue |= (1U << slot);
79 mmio_write_32(address: &port->port->ci, value: command_issue);
80}
81
82static uint32_t try_find_slot(struct ahci_full_port *p) {
83 while (true) {
84 uint32_t old = atomic_load(&p->slot_bitmap);
85 for (int slot = 0; slot < AHCI_MAX_SLOTS; slot++) {
86 uint32_t mask = 1U << slot;
87 if (!(old & mask)) {
88 uint32_t new_bitmap = old | mask;
89 if (atomic_compare_exchange_weak(&p->slot_bitmap, &old,
90 new_bitmap)) {
91 return slot;
92 }
93 break;
94 }
95 }
96 return (uint32_t) -1; // no free slot found
97 }
98}
99
100uint32_t ahci_find_slot(struct ahci_full_port *p) {
101
102 uint32_t slot = try_find_slot(p);
103
104 while (slot == (uint32_t) -1)
105 slot = try_find_slot(p);
106
107 return slot;
108}
109
110/* TODO: BUG here when there are too many concurrent requests
111 * eating up all the available slots */
112void ahci_prepare_command(struct ahci_full_port *port, uint32_t slot,
113 bool write, uint8_t *buf, uint64_t size) {
114
115 struct ahci_cmd_header *hdr = port->cmd_hdrs[slot];
116 struct ahci_cmd_table *cmd_tbl = port->cmd_tables[slot];
117
118 if (!hdr || !cmd_tbl || size == 0)
119 return;
120
121 uint64_t prdt_count =
122 (size + MAX_PRDT_ENTRY_SIZE - 1) / MAX_PRDT_ENTRY_SIZE;
123
124 if (prdt_count > 65535)
125 return;
126
127 hdr->cfl = sizeof(struct ahci_fis_reg_h2d) / sizeof(uint32_t);
128 hdr->w = write ? 1 : 0;
129 hdr->p = 0;
130 hdr->a = 0;
131 hdr->c = 1;
132 hdr->prdtl = prdt_count;
133 hdr->prdbc = 0;
134
135 uint64_t remaining = size;
136 uint64_t offset = 0;
137 uint64_t phys_base =
138 vmm_get_phys((uint64_t) buf, VMM_FLAG_NONE) & ~(PAGE_SIZE - 1);
139
140 for (uint32_t i = 0; i < prdt_count; i++) {
141 uint64_t chunk =
142 (remaining > MAX_PRDT_ENTRY_SIZE) ? MAX_PRDT_ENTRY_SIZE : remaining;
143
144 uint64_t phys_addr = phys_base + offset;
145
146 cmd_tbl->prdt_entry[i].dba = (uint32_t) (phys_addr & 0xFFFFFFFF);
147 cmd_tbl->prdt_entry[i].dbau = (uint32_t) (phys_addr >> 32);
148 cmd_tbl->prdt_entry[i].dbc = (uint32_t) (chunk - 1); // size - 1
149 cmd_tbl->prdt_entry[i].i = (i == prdt_count - 1) ? 1 : 0;
150
151 offset += chunk;
152 remaining -= chunk;
153 }
154}
155
156void ahci_setup_fis(struct ahci_cmd_table *cmd_tbl, uint8_t command,
157 bool is_atapi) {
158 struct ahci_fis_reg_h2d *fis = (struct ahci_fis_reg_h2d *) cmd_tbl->cfis;
159 memset(fis, 0, sizeof(struct ahci_fis_reg_h2d));
160
161 fis->fis_type = FIS_TYPE_REG_H2D;
162 fis->c = 1;
163 fis->command = command;
164
165 if (is_atapi) {
166 fis->device = 1 << 6; // LBA bit
167 }
168}
169
170void ahci_identify(struct ahci_disk *disk) {
171 struct ahci_full_port *port = &disk->device->regs[disk->port];
172 uint32_t slot = ahci_find_slot(p: port);
173
174 uint8_t *buffer = kmalloc_aligned(PAGE_SIZE, PAGE_SIZE);
175 if (!buffer)
176 return;
177
178 ahci_prepare_command(port, slot, false, buf: buffer, PAGE_SIZE);
179
180 if (!buffer)
181 return;
182
183 ahci_setup_fis(cmd_tbl: port->cmd_tables[slot], AHCI_CMD_IDENTIFY, false);
184
185 struct ahci_request req = {
186 .slot = slot, .port = disk->port, .buffer = buffer};
187 ahci_send_command(disk, port, req: &req);
188
189 uint32_t logical_sector_size = 512;
190
191 /*if (buffer[106] & (1 << 14)) {
192 uint32_t low = buffer[117];
193 uint32_t high = buffer[118];
194 logical_sector_size = ((uint32_t) high << 16) | low;
195 }*/
196
197 disk->sector_size = logical_sector_size;
198
199 ahci_log(LOG_INFO, "Sector size is %u bytes", disk->sector_size);
200
201 kfree_aligned(buffer);
202}
203
204static void ahci_on_bio_complete(struct ahci_request *req) {
205 struct bio_request *bio = (struct bio_request *) req->user_data;
206
207 bio->done = true;
208 bio->status = req->status;
209
210 if (bio->on_complete)
211 bio->on_complete(bio);
212
213 kfree(req);
214}
215
216bool ahci_submit_bio_request(struct block_device *disk,
217 struct bio_request *bio) {
218 struct ahci_disk *ahci_disk = (struct ahci_disk *) disk->driver_data;
219 struct ahci_request *ahci_req =
220 kmalloc(sizeof(struct ahci_request), ALLOC_FLAGS_ZERO);
221 if (!ahci_req)
222 return false;
223
224 struct ahci_full_port *p = &ahci_disk->device->regs[ahci_disk->port];
225
226 ahci_req->port = ahci_disk->port;
227 ahci_req->slot = ahci_find_slot(p);
228 ahci_req->lba = bio->lba;
229 ahci_req->buffer = bio->buffer;
230 ahci_req->sector_count = bio->sector_count;
231 ahci_req->size = bio->size;
232 ahci_req->write = bio->write;
233 ahci_req->done = false;
234
235 ahci_req->on_complete = ahci_on_bio_complete;
236 ahci_req->user_data = bio;
237
238 if (bio->write) {
239 return ahci_write_sector_async_wrapper(disk, lba: bio->lba, buf: bio->buffer,
240 cnt: bio->sector_count, req: ahci_req);
241 } else {
242 return ahci_read_sector_async_wrapper(disk, lba: bio->lba, buf: bio->buffer,
243 cnt: bio->sector_count, req: ahci_req);
244 }
245}
246