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