1#include <asm.h>
2#include <block/block.h>
3#include <console/printf.h>
4#include <drivers/ata.h>
5#include <mem/alloc.h>
6#include <stdbool.h>
7#include <stdint.h>
8#include <structures/sll.h>
9#include <sync/lock_chk_assert.h>
10#include <thread/thread.h>
11#include <time/spin_sleep.h>
12#include <tsa.h>
13
14static void ide_start_next_locked(struct ide_channel *chan)
15 TSA_CALLER_HOLDS(&chan->lock);
16typedef bool (*sync_fn)(struct ata_drive *, uint64_t, uint8_t *, uint8_t);
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(struct irq_context *ictx, void *ctx) {
38 cc_ignore(ictx);
39
40 struct ide_channel *chan = ctx;
41 enum irql irql = spin_lock_high(&chan->lock);
42
43 struct ide_request *req = chan->head;
44
45 if (!req)
46 goto out;
47
48 struct ata_drive *d = chan->current_drive;
49
50 for (int i = 0; i < 1000; i++) {
51 uint8_t status = inb(REG_STATUS(d->io_base));
52 if ((status & STATUS_BSY) == 0 &&
53 (status & STATUS_DRQ || status & STATUS_ERR))
54 break;
55 sleep_spin_us(us: 1);
56 }
57
58 uint8_t status = inb(REG_STATUS(d->io_base));
59 uint8_t error = inb(REG_ERROR(d->io_base));
60
61 if (status & STATUS_ERR) {
62 req->status = translate_status(status, error);
63 req->done = true;
64 goto next_request;
65 }
66
67 uint8_t *buf = req->buffer + req->current_sector * 512;
68 if (req->write) {
69 outsw(REG_DATA(d->io_base), addr: buf, count: 256);
70 } else {
71 insw(REG_DATA(d->io_base), addr: buf, count: 256);
72 }
73
74 req->current_sector++;
75
76 if (req->current_sector < req->sector_count) {
77 goto out;
78 }
79
80 req->status = translate_status(status, error);
81 req->done = true;
82
83next_request:
84 chan->head = req->next;
85 if (chan->head) {
86 ide_start_next_locked(chan);
87 } else {
88 chan->busy = false;
89 }
90
91 if (req->has_waiter) {
92 event_set_full(e: &req->evt, reason: THREAD_WAKE_REASON_BLOCKING_IO, NULL);
93 } else if (req->trigger_completion && req->on_complete) {
94 req->on_complete(req);
95 }
96
97out:
98 spin_unlock(&chan->lock, irql);
99 return IRQ_HANDLED;
100}
101
102static void ide_on_complete(struct ide_request *req) {
103 struct bio_request *bio = req->user_data;
104 bio->done = true;
105 bio->status = req->status;
106
107 if (bio->on_complete)
108 bio->on_complete(bio);
109
110 vfree(req);
111}
112
113static void ide_wait_ready(struct ata_drive *d) {
114 while (inb(REG_STATUS(d->io_base)) & STATUS_BSY)
115 ;
116}
117
118static void ide_start_next_locked(struct ide_channel *chan) {
119 struct ide_request *req = chan->head;
120 struct ata_drive *d = chan->current_drive;
121
122 chan->busy = true;
123
124 ide_wait_ready(d);
125 outb(REG_DRIVE_HEAD(d->io_base),
126 value: 0xE0U | (d->slave << 4) | ((req->lba >> 24) & 0x0F));
127 outb(REG_SECTOR_COUNT(d->io_base), value: req->sector_count);
128 outb(REG_LBA_LOW(d->io_base), value: req->lba & 0xFF);
129 outb(REG_LBA_MID(d->io_base), value: (req->lba >> 8) & 0xFF);
130 outb(REG_LBA_HIGH(d->io_base), value: (req->lba >> 16) & 0xFF);
131 outb(REG_COMMAND(d->io_base), value: req->write ? COMMAND_WRITE : COMMAND_READ);
132
133 if (req->write) {
134 uint8_t status;
135 do {
136 status = inb(REG_STATUS(d->io_base));
137 } while ((status & STATUS_DRQ) == 0);
138
139 uint8_t *buf = req->buffer;
140 outsw(REG_DATA(d->io_base), addr: buf, count: 256);
141 req->current_sector = 1;
142 }
143}
144
145static void enqueue_request_locked(struct ide_channel *chan,
146 struct ide_request *req)
147 TSA_CALLER_HOLDS(&chan->lock) {
148 sll_add(chan, req);
149}
150
151static void submit_async(struct ata_drive *d, struct ide_request *req) {
152 enum irql irql = spin_lock(&d->channel.lock);
153 enqueue_request_locked(chan: &d->channel, req);
154 if (!d->channel.busy) {
155 ide_start_next_locked(chan: &d->channel);
156 }
157 spin_unlock(&d->channel.lock, irql);
158}
159
160static inline void submit_and_wait(struct ata_drive *d,
161 struct ide_request *req) {
162 enum irql irql = spin_lock(&req->lock);
163
164 event_init(e: &req->evt, type: EVENT_NOTIFICATION, false);
165 req->has_waiter = true;
166 submit_async(d, req);
167 spin_unlock(&req->lock, irql);
168
169 event_wait(e: &req->evt, type: THREAD_WAIT_UNINTERRUPTIBLE, reason: THREAD_BLOCK_REASON_IO);
170}
171
172static struct ide_request *request_init(uint64_t lba, uint8_t *buffer,
173 uint8_t count, bool write) {
174 struct ide_request *req = vnew(req, ALLOC_ZERO);
175 if (!req)
176 return NULL;
177
178 req->lba = lba;
179 req->buffer = buffer;
180 req->sector_count = (count == 0) ? 256 : count;
181 req->status = BIO_STATUS_INFLIGHT;
182 req->write = write;
183 req->next = NULL;
184 req->user_data = NULL;
185 req->trigger_completion = true;
186
187 return req;
188}
189
190bool ide_submit_bio_async(struct block_device *disk, struct bio_request *bio) {
191 struct ata_drive *ide = disk->driver_data;
192 uint64_t lba = bio->lba;
193 uint8_t *buf = bio->buffer;
194 uint64_t cnt = bio->sector_count;
195
196 bio->status = BIO_STATUS_INFLIGHT;
197 while (cnt > 0) {
198 uint8_t chunk = (cnt >= 256) ? 0 : (uint8_t) cnt;
199 uint64_t sectors = (chunk == 0) ? 256 : chunk;
200
201 struct ide_request *req = request_init(lba, buffer: buf, count: chunk, write: bio->write);
202 req->size = sectors * 512;
203 req->user_data = bio;
204 req->on_complete = ide_on_complete;
205
206 req->trigger_completion = (cnt == sectors);
207
208 submit_async(d: ide, req);
209
210 lba += sectors;
211 buf += sectors * 512;
212 cnt -= sectors;
213 }
214
215 return true;
216}
217
218static bool rw_sync(struct ata_drive *d, uint64_t lba, uint8_t *b, uint8_t cnt,
219 bool write) {
220 struct ide_request *req = request_init(lba, buffer: b, count: cnt, write);
221
222 submit_and_wait(d, req);
223
224 bool ret = !req->status;
225
226 vfree(req);
227 return ret;
228}
229
230static bool rw_sync_wrapper(struct block_device *d, uint64_t lba, uint8_t *buf,
231 uint64_t cnt, sync_fn function) {
232 struct ata_drive *ide = d->driver_data;
233
234 while (cnt > 0) {
235 uint8_t chunk = (cnt >= 256) ? 0 : (uint8_t) cnt;
236 function(ide, lba, buf, chunk);
237
238 uint64_t sectors = (chunk == 0) ? 256 : chunk;
239 lba += sectors;
240 buf += sectors * 512;
241 cnt -= sectors;
242 }
243
244 return true;
245}
246
247static bool ide_read_sector(struct ata_drive *d, uint64_t lba, uint8_t *b,
248 uint8_t count) {
249 return rw_sync(d, lba, b, cnt: count, false);
250}
251
252static bool ide_write_sector(struct ata_drive *d, uint64_t lba, uint8_t *b,
253 uint8_t count) {
254 return rw_sync(d, lba, b, cnt: count, true);
255}
256
257bool ide_read_sector_wrapper(struct block_device *d, uint64_t lba, uint8_t *buf,
258 uint64_t cnt) {
259 return rw_sync_wrapper(d, lba, buf, cnt, function: ide_read_sector);
260}
261
262bool ide_write_sector_wrapper(struct block_device *d, uint64_t lba,
263 const uint8_t *buf, uint64_t cnt) {
264 return rw_sync_wrapper(d, lba, buf: (uint8_t *) buf, cnt, function: ide_write_sector);
265}
266