1#include <block/block.h>
2#include <drivers/ahci.h>
3#include <math/bit.h>
4#include <sch/sched.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <test/export.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 = BIT(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
32static bool rw_async(struct block_device *disk, uint64_t lba, uint8_t *buf,
33 uint16_t count, struct ahci_request *req, bool write) {
34 struct ahci_disk *ahci_disk = (struct ahci_disk *) disk->driver_data;
35 struct ahci_full_port *port = &ahci_disk->device->regs[ahci_disk->port];
36
37 uint32_t slot = req->slot;
38 ahci_prepare_command(port, slot, write, buf, size: count * disk->sector_size);
39
40 struct ahci_cmd_table *tbl = port->cmd_tables[slot];
41 uint8_t cmd = write ? AHCI_CMD_WRITE_DMA_EXT : AHCI_CMD_READ_DMA_EXT;
42 bool is_atapi = false;
43
44 ahci_setup_fis(cmd_tbl: tbl, command: cmd, is_atapi);
45
46 ahci_set_lba_cmd(fis: (struct ahci_fis_reg_h2d *) tbl->cfis, lba, sector_count: count);
47
48 req->port = ahci_disk->port;
49 req->lba = lba;
50 req->buffer = buf;
51 req->sector_count = count;
52 req->write = write;
53 req->done = false;
54 req->status = -1;
55
56 ahci_send_command(disk: ahci_disk, port, req);
57 return true;
58}
59
60static bool rw_sync(struct block_device *disk, uint64_t lba, uint8_t *buf,
61 uint16_t count, async_fn function) {
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(&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 event_init(e: &req.evt, type: EVENT_NOTIFICATION, false);
74 req.has_waiter = true;
75
76 if (!function(disk, lba, buf, count, &req)) {
77 spin_unlock(&dev->lock, irql);
78 return false;
79 }
80
81 spin_unlock(&dev->lock, irql);
82 event_wait(e: &req.evt, type: THREAD_WAIT_UNINTERRUPTIBLE, reason: THREAD_BLOCK_REASON_IO);
83
84 return req.status == 0;
85}
86
87static bool rw_sync_wrapper(struct block_device *disk, uint64_t lba,
88 uint8_t *buf, uint64_t cnt, sync_fn function) {
89 while (cnt > 0) {
90 uint16_t chunk = (cnt > 65535) ? 0 : (uint16_t) cnt;
91 uint64_t sectors = (chunk == 0) ? 65536 : chunk;
92
93 if (!function(disk, lba, buf, chunk))
94 return false;
95
96 lba += sectors;
97 buf += sectors * disk->sector_size;
98 cnt -= sectors;
99 }
100
101 return true;
102}
103
104static bool rw_async_wrapper(struct block_device *disk, uint64_t lba,
105 uint8_t *buf, uint64_t cnt,
106 struct ahci_request *req, async_fn function) {
107 while (cnt > 0) {
108 uint16_t chunk = (cnt > 65535) ? 0 : (uint16_t) cnt;
109 uint64_t sectors = (chunk == 0) ? 65536 : chunk;
110
111 req->trigger_completion = (cnt == sectors);
112
113 if (!function(disk, lba, buf, chunk, req))
114 return false;
115
116 lba += sectors;
117 buf += sectors * disk->sector_size;
118 cnt -= sectors;
119 }
120 return true;
121}
122
123bool ahci_read_sector_async(struct block_device *disk, uint64_t lba,
124 uint8_t *buf, uint16_t count,
125 struct ahci_request *req) {
126 return rw_async(disk, lba, buf, count, req, false);
127}
128
129bool ahci_write_sector_async(struct block_device *disk, uint64_t lba,
130 uint8_t *in_buf, uint16_t count,
131 struct ahci_request *req) {
132 return rw_async(disk, lba, buf: in_buf, count, req, true);
133}
134
135static bool ahci_read_sector_blocking(struct block_device *disk, uint64_t lba,
136 uint8_t *buf, uint16_t count) {
137 return rw_sync(disk, lba, buf, count, function: ahci_read_sector_async);
138}
139
140static bool ahci_write_sector_blocking(struct block_device *disk, uint64_t lba,
141 uint8_t *buf, uint16_t count) {
142 return rw_sync(disk, lba, buf, count, function: ahci_write_sector_async);
143}
144
145bool ahci_read_sector_wrapper(struct block_device *disk, uint64_t lba,
146 uint8_t *buf, uint64_t cnt) {
147 return rw_sync_wrapper(disk, lba, buf, cnt, function: ahci_read_sector_blocking);
148}
149
150bool ahci_write_sector_wrapper(struct block_device *disk, uint64_t lba,
151 const uint8_t *buf, uint64_t cnt) {
152 return rw_sync_wrapper(disk, lba, buf: (uint8_t *) buf, cnt,
153 function: ahci_write_sector_blocking);
154}
155
156bool ahci_write_sector_async_wrapper(struct block_device *disk, uint64_t lba,
157 const uint8_t *buf, uint64_t cnt,
158 struct ahci_request *req) {
159 return rw_async_wrapper(disk, lba, buf: (uint8_t *) buf, cnt, req,
160 function: ahci_write_sector_async);
161}
162
163bool ahci_read_sector_async_wrapper(struct block_device *disk, uint64_t lba,
164 uint8_t *buf, uint64_t cnt,
165 struct ahci_request *req) {
166 return rw_async_wrapper(disk, lba, buf, cnt, req, function: ahci_read_sector_async);
167}
168