fix: (blkio) enforce disk io buffer size alignment (to block size)
[lunaix-os.git] / lunaix-os / kernel / block / block.c
1 #include <hal/ahci/hba.h>
2 #include <klibc/stdio.h>
3 #include <klibc/string.h>
4 #include <lib/crc.h>
5 #include <lunaix/block.h>
6 #include <lunaix/fs/twifs.h>
7 #include <lunaix/mm/cake.h>
8 #include <lunaix/mm/valloc.h>
9 #include <lunaix/syslog.h>
10
11 #include <lunaix/blkpart_gpt.h>
12
13 #include <lunaix/spike.h>
14
15 #define BLOCK_EREAD 1
16 #define BLOCK_ESIG 2
17 #define BLOCK_ECRC 3
18 #define BLOCK_EFULL 3
19
20 LOG_MODULE("BLOCK")
21
22 #define MAX_DEV 32
23
24 static struct cake_pile* lbd_pile;
25 static struct block_dev** dev_registry;
26 static struct twifs_node* blk_sysroot;
27
28 int free_slot = 0;
29
30 int
31 __block_mount_partitions(struct hba_device* hd_dev);
32
33 int
34 __block_register(struct block_dev* dev);
35
36 void
37 block_init()
38 {
39     blkio_init();
40     lbd_pile = cake_new_pile("block_dev", sizeof(struct block_dev), 1, 0);
41     dev_registry = vcalloc(sizeof(struct block_dev*), MAX_DEV);
42     free_slot = 0;
43     blk_sysroot = twifs_dir_node(NULL, "block");
44 }
45
46 int
47 __block_read(struct device* dev, void* buf, size_t offset, size_t len)
48 {
49     int errno;
50     struct block_dev* bdev = (struct block_dev*)dev->underlay;
51     size_t bsize = bdev->blk_size, rd_block = offset / bsize + bdev->start_lba,
52            r = offset % bsize, rd_size = 0;
53
54     if (!(len = MIN(len, ((size_t)bdev->end_lba - rd_block + 1) * bsize))) {
55         return 0;
56     }
57
58     struct vecbuf* vbuf = NULL;
59     struct blkio_req* req;
60     void *head_buf = NULL, *tail_buf = NULL;
61
62     // align the boundary
63     if (r || len < bsize) {
64         head_buf = valloc(bsize);
65         rd_size = MIN(len, bsize - r);
66         vbuf_alloc(&vbuf, head_buf, bsize);
67     }
68
69     // align the length
70     if ((len - rd_size)) {
71         size_t llen = len - rd_size;
72         assert_msg(!(llen % bsize), "misalign block read");
73         vbuf_alloc(&vbuf, buf + rd_size, llen);
74     }
75
76     req = blkio_vrd(vbuf, rd_block, NULL, NULL, 0);
77     blkio_commit(bdev->blkio, req);
78
79     pwait(&req->wait);
80
81     if (!(errno = req->errcode)) {
82         memcpy(buf, head_buf + r, rd_size);
83         errno = len;
84     } else {
85         errno = -errno;
86     }
87
88     if (head_buf) {
89         vfree(head_buf);
90     }
91
92     blkio_free_req(req);
93     vbuf_free(vbuf);
94     return errno;
95 }
96
97 int
98 __block_write(struct device* dev, void* buf, size_t offset, size_t len)
99 {
100     struct block_dev* bdev = (struct block_dev*)dev->underlay;
101     size_t bsize = bdev->blk_size, rd_block = offset / bsize + bdev->start_lba,
102            r = offset % bsize, wr_size = 0;
103
104     if (!(len = MIN(len, ((size_t)bdev->end_lba - rd_block + 1) * bsize))) {
105         return 0;
106     }
107
108     struct vecbuf* vbuf = NULL;
109     struct blkio_req* req;
110     void* tmp_buf = NULL;
111
112     if (r) {
113         size_t wr_size = MIN(len, bsize - r);
114         tmp_buf = vzalloc(bsize);
115         vbuf_alloc(&vbuf, tmp_buf, bsize);
116
117         memcpy(tmp_buf + r, buf, wr_size);
118     }
119
120     if ((len - wr_size)) {
121         size_t llen = len - wr_size;
122         assert_msg(!(llen % bsize), "misalign block write");
123         vbuf_alloc(&vbuf, buf + wr_size, llen);
124     }
125
126     req = blkio_vwr(vbuf, rd_block, NULL, NULL, 0);
127     blkio_commit(bdev->blkio, req);
128
129     pwait(&req->wait);
130
131     int errno = req->errcode;
132     if (!errno) {
133         errno = len;
134     } else {
135         errno = -errno;
136     }
137
138     if (tmp_buf) {
139         vfree(tmp_buf);
140     }
141
142     blkio_free_req(req);
143     vbuf_free(vbuf);
144     return errno;
145 }
146
147 struct block_dev*
148 block_alloc_dev(const char* blk_id, void* driver, req_handler ioreq_handler)
149 {
150     struct block_dev* bdev = cake_grab(lbd_pile);
151     memset(bdev, 0, sizeof(struct block_dev));
152     llist_init_head(&bdev->parts);
153     strncpy(bdev->name, blk_id, PARTITION_NAME_SIZE);
154
155     bdev->blkio = blkio_newctx(ioreq_handler);
156     bdev->driver = driver;
157     bdev->blkio->driver = driver;
158
159     return bdev;
160 }
161
162 int
163 block_mount(struct block_dev* bdev, devfs_exporter fs_export)
164 {
165     int errno = 0;
166
167     if (!__block_register(bdev)) {
168         errno = BLOCK_EFULL;
169         goto error;
170     }
171
172     errno = blkpart_probegpt(bdev->dev);
173     if (errno < 0) {
174         kprintf(KERROR "Fail to parse partition table (%d)\n", errno);
175     } else if (!errno) {
176         // TODO try other PT parser...
177     }
178
179     struct twifs_node* dev_root = twifs_dir_node(blk_sysroot, bdev->bdev_id);
180     blk_set_blkmapping(bdev, dev_root);
181     fs_export(bdev, dev_root);
182
183     return errno;
184
185 error:
186     kprintf(KERROR "Fail to mount block device: %s (%x)\n", bdev->name, -errno);
187     return errno;
188 }
189
190 int
191 __block_register(struct block_dev* bdev)
192 {
193     if (free_slot >= MAX_DEV) {
194         return 0;
195     }
196
197     struct device* dev = device_addvol(NULL, bdev, "sd%c", 'a' + free_slot);
198     dev->write = __block_write;
199     dev->read = __block_read;
200
201     bdev->dev = dev;
202     strcpy(bdev->bdev_id, dev->name_val);
203     dev_registry[free_slot++] = bdev;
204     return 1;
205 }
206
207 struct block_dev*
208 blk_mount_part(struct block_dev* bdev,
209                const char* name,
210                size_t index,
211                u64_t start_lba,
212                u64_t end_lba)
213 {
214     struct block_dev* pbdev = cake_grab(lbd_pile);
215     memcpy(pbdev, bdev, sizeof(*bdev));
216
217     struct device* dev =
218       device_addvol(NULL, pbdev, "%sp%d", bdev->bdev_id, index + 1);
219     dev->write = __block_write;
220     dev->read = __block_read;
221
222     pbdev->start_lba = start_lba;
223     pbdev->end_lba = end_lba;
224     pbdev->dev = dev;
225
226     strcpy(pbdev->bdev_id, dev->name_val);
227     if (name) {
228         strncpy(pbdev->name, name, PARTITION_NAME_SIZE);
229     }
230
231     llist_append(&bdev->parts, &pbdev->parts);
232
233     return pbdev;
234 }