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