feat: block partition support
[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 = vbuf_alloc(NULL, buf, len);
59     struct blkio_req* req;
60     void* tmp_buf = NULL;
61
62     if (r) {
63         tmp_buf = vzalloc(bsize);
64         rd_size = MIN(len, bsize - r);
65         vbuf->buf.size = bsize;
66         vbuf->buf.buffer = tmp_buf;
67
68         if ((len - rd_size)) {
69             vbuf_alloc(vbuf, buf + rd_size, len - rd_size);
70         }
71     }
72
73     req = blkio_vrd(vbuf, rd_block, NULL, NULL, 0);
74     blkio_commit(bdev->blkio, req);
75
76     pwait(&req->wait);
77
78     // XXX temporary work-around
79     //      in case pwait used in proc0. Need a dummy process!
80     wait_if((req->flags & BLKIO_PENDING));
81
82     if (!(errno = req->errcode)) {
83         memcpy(buf, tmp_buf + r, rd_size);
84         errno = len;
85     } else {
86         errno = -errno;
87     }
88
89     if (tmp_buf) {
90         vfree(tmp_buf);
91     }
92
93     blkio_free_req(req);
94     vbuf_free(vbuf);
95     return errno;
96 }
97
98 int
99 __block_write(struct device* dev, void* buf, size_t offset, size_t len)
100 {
101     struct block_dev* bdev = (struct block_dev*)dev->underlay;
102     size_t bsize = bdev->blk_size, rd_block = offset / bsize + bdev->start_lba,
103            r = offset % bsize;
104
105     if (!(len = MIN(len, ((size_t)bdev->end_lba - rd_block + 1) * bsize))) {
106         return 0;
107     }
108
109     struct vecbuf* vbuf = vbuf_alloc(NULL, buf, len);
110     struct blkio_req* req;
111     void* tmp_buf = NULL;
112
113     if (r) {
114         size_t rd_size = MIN(len, bsize - r);
115         tmp_buf = vzalloc(bsize);
116         vbuf->buf.size = bsize;
117         vbuf->buf.buffer = tmp_buf;
118
119         memcpy(tmp_buf + r, buf, rd_size);
120         vbuf_alloc(vbuf, buf + rd_size, len - rd_size);
121     }
122
123     req = blkio_vwr(vbuf, rd_block, NULL, NULL, 0);
124     blkio_commit(bdev->blkio, req);
125
126     pwait(&req->wait);
127
128     // XXX temporary work-around
129     //      in case pwait used in proc0. Need a dummy process!
130     wait_if((req->flags & BLKIO_PENDING));
131
132     int errno = req->errcode;
133     if (!errno) {
134         errno = len;
135     } else {
136         errno = -errno;
137     }
138
139     if (tmp_buf) {
140         vfree(tmp_buf);
141     }
142
143     blkio_free_req(req);
144     vbuf_free(vbuf);
145     return errno;
146 }
147
148 struct block_dev*
149 block_alloc_dev(const char* blk_id, void* driver, req_handler ioreq_handler)
150 {
151     struct block_dev* bdev = cake_grab(lbd_pile);
152     memset(bdev, 0, sizeof(struct block_dev));
153     llist_init_head(&bdev->parts);
154     strncpy(bdev->name, blk_id, PARTITION_NAME_SIZE);
155
156     bdev->blkio = blkio_newctx(ioreq_handler);
157     bdev->driver = driver;
158     bdev->blkio->driver = driver;
159
160     return bdev;
161 }
162
163 int
164 block_mount(struct block_dev* bdev, devfs_exporter fs_export)
165 {
166     int errno = 0;
167
168     if (!__block_register(bdev)) {
169         errno = BLOCK_EFULL;
170         goto error;
171     }
172
173     errno = blkpart_probegpt(bdev->dev);
174     if (errno < 0) {
175         kprintf(KERROR "Corrupted partition table. (%d)", errno);
176     } else if (!errno) {
177         // TODO try other PT parser...
178     }
179
180     struct twifs_node* dev_root = twifs_dir_node(blk_sysroot, bdev->bdev_id);
181     blk_set_blkmapping(bdev, dev_root);
182     fs_export(bdev, dev_root);
183
184     return errno;
185
186 error:
187     kprintf(KERROR "Fail to mount block device: %s (%x)\n", bdev->name, -errno);
188     return errno;
189 }
190
191 int
192 __block_register(struct block_dev* bdev)
193 {
194     if (free_slot >= MAX_DEV) {
195         return 0;
196     }
197
198     struct device* dev = device_addvol(NULL, bdev, "sd%c", 'a' + free_slot);
199     dev->write = __block_write;
200     dev->read = __block_read;
201
202     bdev->dev = dev;
203     strcpy(bdev->bdev_id, dev->name_val);
204     dev_registry[free_slot++] = bdev;
205     return 1;
206 }
207
208 struct block_dev*
209 blk_mount_part(struct block_dev* bdev,
210                const char* name,
211                size_t index,
212                u64_t start_lba,
213                u64_t end_lba)
214 {
215     struct block_dev* pbdev = cake_grab(lbd_pile);
216     memcpy(pbdev, bdev, sizeof(*bdev));
217
218     struct device* dev =
219       device_addvol(NULL, pbdev, "%sp%d", bdev->bdev_id, index + 1);
220     dev->write = __block_write;
221     dev->read = __block_read;
222
223     pbdev->start_lba = start_lba;
224     pbdev->end_lba = end_lba;
225     pbdev->dev = dev;
226
227     strcpy(pbdev->bdev_id, dev->name_val);
228     if (name) {
229         strncpy(pbdev->name, name, PARTITION_NAME_SIZE);
230     }
231
232     llist_append(&bdev->parts, &pbdev->parts);
233
234     return pbdev;
235 }