1#include <block/block.h>
2#include <drivers/ahci.h>
3#include <sch/sched.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7#include <thread/io_wait.h>
8#include <thread/thread.h>
9
10static void ahci_set_lba_cmd(struct ahci_fis_reg_h2d *fis, uint64_t lba,
11 uint16_t sector_count) {
12 fis->device = 1 << 6;
13
14 fis->lba0 = (uint8_t) (lba & 0xFF);
15 fis->lba1 = (uint8_t) ((lba >> 8) & 0xFF);
16 fis->lba2 = (uint8_t) ((lba >> 16) & 0xFF);
17 fis->lba3 = (uint8_t) ((lba >> 24) & 0xFF);
18 fis->lba4 = (uint8_t) ((lba >> 32) & 0xFF);
19 fis->lba5 = (uint8_t) ((lba >> 40) & 0xFF);
20
21 fis->countl = (uint8_t) (sector_count & 0xFF);
22 fis->counth = (uint8_t) ((sector_count >> 8) & 0xFF);
23}
24
25typedef bool (*async_fn)(struct block_device *, uint64_t, uint8_t *, uint16_t,
26 struct ahci_request *);
27
28typedef bool (*sync_fn)(struct block_device *, uint64_t, uint8_t *, uint16_t,
29 struct io_wait_token *tok);
30
31static bool rw_async(struct block_device *disk, uint64_t lba, uint8_t *buf,
32 uint16_t count, struct ahci_request *req, bool write) {
33 struct ahci_disk *ahci_disk = (struct ahci_disk *) disk->driver_data;
34 struct ahci_full_port *port = &ahci_disk->device->regs[ahci_disk->port];
35
36 uint32_t slot = req->slot;
37 ahci_prepare_command(port, slot, write, buf, size: count * disk->sector_size);
38
39 struct ahci_cmd_table *tbl = port->cmd_tables[slot];
40 uint8_t cmd = write ? AHCI_CMD_WRITE_DMA_EXT : AHCI_CMD_READ_DMA_EXT;
41 bool is_atapi = false;
42
43 ahci_setup_fis(cmd_tbl: tbl, command: cmd, is_atapi);
44
45 ahci_set_lba_cmd(fis: (struct ahci_fis_reg_h2d *) tbl->cfis, lba, sector_count: count);
46
47 req->port = ahci_disk->port;
48 req->lba = lba;
49 req->buffer = buf;
50 req->sector_count = count;
51 req->write = write;
52 req->done = false;
53 req->status = -1;
54
55 ahci_send_command(disk: ahci_disk, port, req);
56 return true;
57}
58
59static bool rw_sync(struct block_device *disk, uint64_t lba, uint8_t *buf,
60 uint16_t count, async_fn function,
61 struct io_wait_token *io_wait_tok) {
62 struct ahci_request req = {0};
63 struct ahci_disk *ahci_disk = (struct ahci_disk *) disk->driver_data;
64 struct ahci_device *dev = ahci_disk->device;
65 struct ahci_full_port *port = &dev->regs[ahci_disk->port];
66
67 enum irql irql = spin_lock(lock: &dev->lock);
68 req.slot = ahci_find_slot(port);
69
70 /* tells ISR handler to mark status properly */
71 req.trigger_completion = true;
72
73 struct thread *curr = thread_get_current();
74
75 if (io_wait_token_active(t: io_wait_tok))
76 io_wait_end(t: io_wait_tok, act: IO_WAIT_END_NO_OP);
77
78 io_wait_begin(out: io_wait_tok, io_object: dev);
79
80 dev->io_waiters[ahci_disk->port][req.slot] = curr;
81
82 if (!function(disk, lba, buf, count, &req)) {
83 spin_unlock(lock: &dev->lock, old: irql);
84 return false;
85 }
86
87 spin_unlock(lock: &dev->lock, old: irql);
88 thread_yield_until_wake_match();
89
90 dev->io_waiters[ahci_disk->port][req.slot] = NULL;
91
92 return req.status == 0;
93}
94
95static bool rw_sync_wrapper(struct block_device *disk, uint64_t lba,
96 uint8_t *buf, uint64_t cnt, sync_fn function) {
97 struct io_wait_token wt = IO_WAIT_TOKEN_EMPTY;
98 while (cnt > 0) {
99 uint16_t chunk = (cnt > 65535) ? 0 : (uint16_t) cnt;
100 uint64_t sectors = (chunk == 0) ? 65536 : chunk;
101
102 if (!function(disk, lba, buf, chunk, &wt)) {
103 io_wait_end(t: &wt, act: IO_WAIT_END_YIELD);
104 return false;
105 }
106
107 lba += sectors;
108 buf += sectors * disk->sector_size;
109 cnt -= sectors;
110 }
111
112 io_wait_end(t: &wt, act: IO_WAIT_END_YIELD);
113 return true;
114}
115
116static bool rw_async_wrapper(struct block_device *disk, uint64_t lba,
117 uint8_t *buf, uint64_t cnt,
118 struct ahci_request *req, async_fn function) {
119 while (cnt > 0) {
120 uint16_t chunk = (cnt > 65535) ? 0 : (uint16_t) cnt;
121 uint64_t sectors = (chunk == 0) ? 65536 : chunk;
122
123 req->trigger_completion = (cnt == sectors);
124
125 if (!function(disk, lba, buf, chunk, req))
126 return false;
127
128 lba += sectors;
129 buf += sectors * disk->sector_size;
130 cnt -= sectors;
131 }
132 return true;
133}
134
135bool ahci_read_sector_async(struct block_device *disk, uint64_t lba,
136 uint8_t *buf, uint16_t count,
137 struct ahci_request *req) {
138 return rw_async(disk, lba, buf, count, req, false);
139}
140
141bool ahci_write_sector_async(struct block_device *disk, uint64_t lba,
142 uint8_t *in_buf, uint16_t count,
143 struct ahci_request *req) {
144 return rw_async(disk, lba, buf: in_buf, count, req, true);
145}
146
147bool ahci_read_sector_blocking(struct block_device *disk, uint64_t lba,
148 uint8_t *buf, uint16_t count,
149 struct io_wait_token *tok) {
150 return rw_sync(disk, lba, buf, count, function: ahci_read_sector_async, io_wait_tok: tok);
151}
152
153bool ahci_write_sector_blocking(struct block_device *disk, uint64_t lba,
154 uint8_t *buf, uint16_t count,
155 struct io_wait_token *tok) {
156 return rw_sync(disk, lba, buf, count, function: ahci_write_sector_async, io_wait_tok: tok);
157}
158
159bool ahci_read_sector_wrapper(struct block_device *disk, uint64_t lba,
160 uint8_t *buf, uint64_t cnt) {
161 return rw_sync_wrapper(disk, lba, buf, cnt, function: ahci_read_sector_blocking);
162}
163
164bool ahci_write_sector_wrapper(struct block_device *disk, uint64_t lba,
165 const uint8_t *buf, uint64_t cnt) {
166 return rw_sync_wrapper(disk, lba, buf: (uint8_t *) buf, cnt,
167 function: ahci_write_sector_blocking);
168}
169
170bool ahci_write_sector_async_wrapper(struct block_device *disk, uint64_t lba,
171 const uint8_t *buf, uint64_t cnt,
172 struct ahci_request *req) {
173 return rw_async_wrapper(disk, lba, buf: (uint8_t *) buf, cnt, req,
174 function: ahci_write_sector_async);
175}
176
177bool ahci_read_sector_async_wrapper(struct block_device *disk, uint64_t lba,
178 uint8_t *buf, uint64_t cnt,
179 struct ahci_request *req) {
180 return rw_async_wrapper(disk, lba, buf, cnt, req, function: ahci_read_sector_async);
181}
182