1#include <acpi/lapic.h>
2#include <asm.h>
3#include <block/block.h>
4#include <console/printf.h>
5#include <drivers/ata.h>
6#include <mem/alloc.h>
7#include <stdbool.h>
8#include <stdint.h>
9#include <structures/sll.h>
10#include <thread/io_wait.h>
11#include <thread/thread.h>
12#include <time/spin_sleep.h>
13
14static void ide_start_next(struct ide_channel *chan, bool locked);
15typedef bool (*sync_fn)(struct ata_drive *, uint64_t, uint8_t *, uint8_t,
16 struct io_wait_token *);
17
18static enum bio_request_status translate_status(uint8_t status, uint8_t error) {
19 if ((status & STATUS_ERR) == 0) {
20 return BIO_STATUS_OK;
21 }
22
23 if (error & 0x04)
24 return BIO_STATUS_ABRT;
25 if (error & 0x40)
26 return BIO_STATUS_UNCORRECTABLE;
27 if (error & 0x10 || error & 0x01 || error & 0x02)
28 return BIO_STATUS_ID_NOT_FOUND;
29 if (error & 0x08 || error & 0x20)
30 return BIO_STATUS_MEDIA_CHANGE;
31 if (error & 0x80)
32 return BIO_STATUS_BAD_SECTOR;
33
34 return BIO_STATUS_UNKNOWN_ERR;
35}
36
37enum irq_result ide_irq_handler(void *ctx, irq_t irq_num,
38 struct irq_context *rsp) {
39 (void) irq_num, (void) rsp;
40
41 struct ide_channel *chan = ctx;
42 enum irql irql = spin_lock_irq_disable(&chan->lock);
43
44 struct ide_request *req = chan->head;
45
46 if (!req)
47 goto out;
48
49 struct ata_drive *d = chan->current_drive;
50
51 for (int i = 0; i < 1000; i++) {
52 uint8_t status = inb(REG_STATUS(d->io_base));
53 if ((status & STATUS_BSY) == 0 &&
54 (status & STATUS_DRQ || status & STATUS_ERR))
55 break;
56 sleep_spin_us(us: 1);
57 }
58
59 uint8_t status = inb(REG_STATUS(d->io_base));
60 uint8_t error = inb(REG_ERROR(d->io_base));
61
62 if (status & STATUS_ERR) {
63 req->status = translate_status(status, error);
64 req->done = true;
65 goto next_request;
66 }
67
68 uint8_t *buf = req->buffer + req->current_sector * 512;
69 if (req->write) {
70 outsw(REG_DATA(d->io_base), addr: buf, count: 256);
71 } else {
72 insw(REG_DATA(d->io_base), addr: buf, count: 256);
73 }
74
75 req->current_sector++;
76
77 if (req->current_sector < req->sector_count) {
78 goto out;
79 }
80
81 if (req->trigger_completion) {
82 req->status = translate_status(status, error);
83 req->done = true;
84
85 if (req->on_complete)
86 req->on_complete(req);
87 }
88
89 if (req->waiter)
90 thread_wake_from_io_block(t: req->waiter, wake_src: d);
91
92next_request:
93
94 chan->head = chan->head->next;
95
96 if (chan->head) {
97 ide_start_next(chan, true);
98 } else {
99 chan->busy = false;
100 }
101
102out:
103 spin_unlock(&chan->lock, irql);
104 return IRQ_HANDLED;
105}
106
107static void ide_on_complete(struct ide_request *req) {
108 struct bio_request *bio = req->user_data;
109 bio->done = true;
110 bio->status = req->status;
111
112 if (bio->on_complete)
113 bio->on_complete(bio);
114
115 kfree(req);
116}
117
118static void ide_wait_ready(struct ata_drive *d) {
119 while (inb(REG_STATUS(d->io_base)) & STATUS_BSY)
120 ;
121}
122
123static void ide_start_next(struct ide_channel *chan, bool locked) {
124 struct ide_request *req = chan->head;
125 struct ata_drive *d = chan->current_drive;
126
127 enum irql i;
128 if (!locked)
129 i = spin_lock(&chan->lock);
130
131 chan->busy = true;
132
133 ide_wait_ready(d);
134 outb(REG_DRIVE_HEAD(d->io_base),
135 value: 0xE0U | (d->slave << 4) | ((req->lba >> 24) & 0x0F));
136 outb(REG_SECTOR_COUNT(d->io_base), value: req->sector_count);
137 outb(REG_LBA_LOW(d->io_base), value: req->lba & 0xFF);
138 outb(REG_LBA_MID(d->io_base), value: (req->lba >> 8) & 0xFF);
139 outb(REG_LBA_HIGH(d->io_base), value: (req->lba >> 16) & 0xFF);
140 outb(REG_COMMAND(d->io_base), value: req->write ? COMMAND_WRITE : COMMAND_READ);
141
142 if (req->write) {
143 uint8_t status;
144 do {
145 status = inb(REG_STATUS(d->io_base));
146 } while ((status & STATUS_DRQ) == 0);
147
148 uint8_t *buf = req->buffer;
149 outsw(REG_DATA(d->io_base), addr: buf, count: 256);
150 req->current_sector = 1;
151 }
152
153 if (!locked)
154 spin_unlock(&chan->lock, i);
155}
156
157static void enqueue_request(struct ide_channel *chan, struct ide_request *req,
158 bool locked) {
159
160 enum irql i;
161 if (!locked)
162 i = spin_lock(&chan->lock);
163
164 sll_add(chan, req);
165
166 if (!locked)
167 spin_unlock(&chan->lock, i);
168}
169
170static void submit_async(struct ata_drive *d, struct ide_request *req) {
171 enum irql irql = spin_lock(&d->channel.lock);
172 enqueue_request(chan: &d->channel, req, true);
173 if (!d->channel.busy) {
174 ide_start_next(chan: &d->channel, true);
175 }
176 spin_unlock(&d->channel.lock, irql);
177}
178
179static inline void submit_and_wait(struct ata_drive *d, struct ide_request *req,
180 struct io_wait_token *token) {
181 enum irql irql = spin_lock(&req->lock);
182
183 if (io_wait_token_active(t: token))
184 io_wait_end(t: token, act: IO_WAIT_END_NO_OP);
185
186 io_wait_begin(out: token, io_object: d);
187
188 req->waiter = thread_get_current();
189 submit_async(d, req);
190 spin_unlock(&req->lock, irql);
191}
192
193static struct ide_request *request_init(uint64_t lba, uint8_t *buffer,
194 uint8_t count, bool write) {
195 struct ide_request *req =
196 kmalloc(sizeof(struct ide_request), ALLOC_FLAGS_ZERO);
197 if (!req)
198 return NULL;
199
200 req->lba = lba;
201 req->buffer = buffer;
202 req->sector_count = (count == 0) ? 256 : count;
203 req->status = BIO_STATUS_INFLIGHT;
204 req->write = write;
205 req->next = NULL;
206 req->user_data = NULL;
207 req->trigger_completion = true;
208
209 return req;
210}
211
212bool ide_submit_bio_async(struct block_device *disk, struct bio_request *bio) {
213 struct ata_drive *ide = disk->driver_data;
214 uint64_t lba = bio->lba;
215 uint8_t *buf = bio->buffer;
216 uint64_t cnt = bio->sector_count;
217
218 bio->status = BIO_STATUS_INFLIGHT;
219 while (cnt > 0) {
220 uint8_t chunk = (cnt >= 256) ? 0 : (uint8_t) cnt;
221 uint64_t sectors = (chunk == 0) ? 256 : chunk;
222
223 struct ide_request *req = request_init(lba, buffer: buf, count: chunk, write: bio->write);
224 req->size = sectors * 512;
225 req->user_data = bio;
226 req->on_complete = ide_on_complete;
227
228 req->trigger_completion = (cnt == sectors);
229
230 submit_async(d: ide, req);
231
232 lba += sectors;
233 buf += sectors * 512;
234 cnt -= sectors;
235 }
236
237 return true;
238}
239
240static bool rw_sync(struct ata_drive *d, uint64_t lba, uint8_t *b, uint8_t cnt,
241 bool write, struct io_wait_token *tk) {
242 struct ide_request *req = request_init(lba, buffer: b, count: cnt, write);
243
244 submit_and_wait(d, req, token: tk);
245 thread_yield_until_wake_match();
246
247 bool ret = !req->status;
248
249 kfree(req);
250 return ret;
251}
252
253static bool rw_sync_wrapper(struct block_device *d, uint64_t lba, uint8_t *buf,
254 uint64_t cnt, sync_fn function) {
255 struct ata_drive *ide = d->driver_data;
256
257 struct io_wait_token tok = IO_WAIT_TOKEN_EMPTY;
258
259 while (cnt > 0) {
260 uint8_t chunk = (cnt >= 256) ? 0 : (uint8_t) cnt;
261 function(ide, lba, buf, chunk, &tok);
262
263 uint64_t sectors = (chunk == 0) ? 256 : chunk;
264 lba += sectors;
265 buf += sectors * 512;
266 cnt -= sectors;
267 }
268
269 io_wait_end(t: &tok, act: IO_WAIT_END_YIELD);
270 return true;
271}
272
273static bool ide_read_sector(struct ata_drive *d, uint64_t lba, uint8_t *b,
274 uint8_t count, struct io_wait_token *tok) {
275 return rw_sync(d, lba, b, cnt: count, false, tk: tok);
276}
277
278static bool ide_write_sector(struct ata_drive *d, uint64_t lba, uint8_t *b,
279 uint8_t count, struct io_wait_token *tok) {
280 return rw_sync(d, lba, b, cnt: count, true, tk: tok);
281}
282
283bool ide_read_sector_wrapper(struct block_device *d, uint64_t lba, uint8_t *buf,
284 uint64_t cnt) {
285 return rw_sync_wrapper(d, lba, buf, cnt, function: ide_read_sector);
286}
287
288bool ide_write_sector_wrapper(struct block_device *d, uint64_t lba,
289 const uint8_t *buf, uint64_t cnt) {
290 return rw_sync_wrapper(d, lba, buf: (uint8_t *) buf, cnt, function: ide_write_sector);
291}
292