Make it so that volumes can be mounted anywhere.
[monolithium.git] / kernel / src / device.c
1 /*
2  * device.c
3  *
4  * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <device.h>
21 #include <filesystem.h>
22 #include <memory.h>
23 #include <heap.h>
24 #include <syscalls.h>
25 #include <exception.h>
26
27 static DECLARE_LIST(block_dev_drivers);
28 static DECLARE_LIST(char_dev_drivers);
29 static DECLARE_LIST(block_devices);
30 static DECLARE_LIST(char_devices);
31 static lock_t block_dev_driver_list_lock = 0;
32 static lock_t char_dev_driver_list_lock = 0;
33 static resource_t block_device_list_res = 0;
34 static resource_t char_device_list_res = 0;
35
36 dword_t device_mount(const char *device, const char *mountpoint, dword_t flags);
37 dword_t device_unmount(mounted_volume_t *volume);
38 dword_t device_load_file(file_t **file);
39 dword_t device_unload_file(file_t *file);
40 dword_t device_open_file(file_instance_t **instance);
41 dword_t device_close_file(file_instance_t *instance);
42 dword_t device_delete_file(mounted_volume_t *volume, const char *path, bool_t purge);
43 dword_t device_read_file(file_instance_t *file, void *buffer, qword_t offset, size_t length, size_t *bytes_read);
44 dword_t device_write_file(file_instance_t *file, const void *buffer, qword_t offset, size_t length, size_t *bytes_written);
45 dword_t device_list_dir(file_instance_t *directory, char *filename, bool_t continue_scan);
46 dword_t device_set_file(file_t *file, dword_t info_type, const void *buffer, size_t size);
47
48 static fs_driver_t device_fs_driver =
49 {
50     .name        = "DeviceFS",
51     .mount       = device_mount,
52     .unmount     = device_unmount,
53     .load_file   = device_load_file,
54     .unload_file = device_unload_file,
55     .open_file   = device_open_file,
56     .close_file  = device_close_file,
57     .delete_file = device_delete_file,
58     .read_file   = device_read_file,
59     .write_file  = device_write_file,
60     .list_dir    = device_list_dir,
61     .set_file    = device_set_file
62 };
63
64
65 static mounted_volume_t block_device_volume =
66 {
67     .mountpoint = "BlockDevices",
68     .flags      = 0,
69     .resource   = 0,
70     .device     = NULL,
71     .open_files = 0ULL,
72     .driver     = &device_fs_driver
73 };
74
75 static mounted_volume_t char_device_volume =
76 {
77     .mountpoint = "CharDevices",
78     .flags      = 0,
79     .resource   = 0,
80     .device     = NULL,
81     .open_files = 0ULL,
82     .driver     = &device_fs_driver
83 };
84
85 device_t *get_block_device(const char *name)
86 {
87     list_entry_t *i;
88     device_t *ptr = NULL;
89     acquire_resource_shared(&block_device_list_res);
90
91     for (i = block_devices.next; i != &block_devices; i = i->next)
92     {
93         ptr = CONTAINER_OF(i, device_t, list);
94         if (strcmp(ptr->name, name) == 0) break;
95     }
96
97     if (i == &block_devices) ptr = NULL;
98
99     release_resource(&block_device_list_res);
100     return ptr;
101 }
102
103 dword_t register_block_dev_driver(block_dev_driver_t *driver)
104 {
105     dword_t ret = driver->init_proc();
106
107     if (ret == ERR_SUCCESS)
108     {
109         driver->mounted_devices = 0;
110         acquire_lock(&block_dev_driver_list_lock);
111         list_append(&block_dev_drivers, &driver->list);
112         release_lock(&block_dev_driver_list_lock);
113     }
114
115     return ret;
116 }
117
118 dword_t unregister_block_dev_driver(block_dev_driver_t *driver)
119 {
120     if (driver->mounted_devices) return ERR_BUSY;
121
122     driver->cleanup_proc();
123     acquire_lock(&block_dev_driver_list_lock);
124     list_remove(&driver->list);
125     release_lock(&block_dev_driver_list_lock);
126
127     return ERR_SUCCESS;
128 }
129
130 dword_t register_block_device(device_t *device)
131 {
132     dword_t ret = ERR_SUCCESS;
133     list_entry_t *i;
134
135     device->type = BLOCK_DEVICE;
136
137     acquire_resource_exclusive(&block_device_list_res);
138
139     for (i = block_devices.next; i != &block_devices; i = i->next)
140     {
141         device_t *ptr = CONTAINER_OF(i, device_t, list);
142
143         if (strcmp(ptr->name, device->name) == 0)
144         {
145             ret = ERR_EXISTS;
146             goto cleanup;
147         }
148     }
149
150     list_append(&block_devices, &device->list);
151
152 cleanup:
153     release_resource(&block_device_list_res);
154     return ret;
155 }
156
157 dword_t unregister_block_device(device_t *device)
158 {
159     acquire_resource_exclusive(&block_device_list_res);
160     list_remove(&device->list);
161     release_resource(&block_device_list_res);
162
163     return ERR_SUCCESS;
164 }
165
166 device_t *get_char_device(const char *name)
167 {
168     list_entry_t *i;
169     device_t *device;
170
171     acquire_resource_shared(&block_device_list_res);
172     for (i = char_devices.next; i != &char_devices; i = i->next)
173     {
174         device = CONTAINER_OF(i, device_t, list);
175         if (strcmp(device->name, name) == 0) break;
176     }
177     release_resource(&block_device_list_res);
178
179     return (i != &char_devices) ? device : NULL;
180 }
181
182 dword_t register_char_dev_driver(char_dev_driver_t *driver)
183 {
184     dword_t ret = driver->init_proc();
185
186     if (ret == ERR_SUCCESS)
187     {
188         acquire_lock(&char_dev_driver_list_lock);
189         list_append(&char_dev_drivers, &driver->list);
190         release_lock(&char_dev_driver_list_lock);
191     }
192
193     return ret;
194 }
195
196 dword_t unregister_char_dev_driver(char_dev_driver_t *driver)
197 {
198     driver->cleanup_proc();
199     acquire_lock(&char_dev_driver_list_lock);
200     list_remove(&driver->list);
201     release_lock(&char_dev_driver_list_lock);
202
203     return ERR_SUCCESS;
204 }
205
206 dword_t register_char_device(device_t *device)
207 {
208     dword_t ret = ERR_SUCCESS;
209     list_entry_t *i;
210
211     device->type = CHAR_DEVICE;
212
213     acquire_resource_exclusive(&char_device_list_res);
214
215     for (i = char_devices.next; i != &char_devices; i = i->next)
216     {
217         device_t *ptr = CONTAINER_OF(i, device_t, list);
218
219         if (strcmp(ptr->name, device->name) == 0)
220         {
221             ret = ERR_EXISTS;
222             goto cleanup;
223         }
224     }
225
226     list_append(&char_devices, &device->list);
227
228 cleanup:
229     release_resource(&char_device_list_res);
230     return ret;
231 }
232
233 dword_t unregister_char_device(device_t *device)
234 {
235     acquire_resource_exclusive(&char_device_list_res);
236     list_remove(&device->list);
237     release_resource(&char_device_list_res);
238
239     return ERR_SUCCESS;
240 }
241
242 dword_t device_mount(const char *device, const char *mountpoint, dword_t flags)
243 {
244     return ERR_INVALID;
245 }
246
247 dword_t device_unmount(mounted_volume_t *volume)
248 {
249     return ERR_INVALID;
250 }
251
252 dword_t device_load_file(file_t **_file)
253 {
254     device_file_t *file = (device_file_t*)realloc(*_file, sizeof(device_file_t));
255     if (file == NULL) return ERR_NOMEMORY;
256     *_file = &file->header;
257
258     if (strlen(file->header.path) > 0)
259     {
260         if (file->header.volume == &block_device_volume)
261         {
262             file->device = get_block_device(file->header.path);
263             return file->device ? ERR_SUCCESS : ERR_NOTFOUND;
264         }
265         else if (file->header.volume == &char_device_volume)
266         {
267             file->device = get_char_device(file->header.path);
268             return file->device ? ERR_SUCCESS : ERR_NOTFOUND;
269         }
270         else
271         {
272             return ERR_INVALID;
273         }
274     }
275     else
276     {
277         file->device = NULL;
278         return ERR_SUCCESS;
279     }
280 }
281
282 dword_t device_unload_file(file_t *file)
283 {
284     return ERR_SUCCESS;
285 }
286
287 dword_t device_open_file(file_instance_t **instance)
288 {
289     return ERR_SUCCESS;
290 }
291
292 dword_t device_close_file(file_instance_t *instance)
293 {
294     return ERR_SUCCESS;
295 }
296
297 dword_t device_delete_file(mounted_volume_t *volume, const char *path, bool_t purge)
298 {
299     return ERR_INVALID;
300 }
301
302 dword_t device_read_file(file_instance_t *_file, void *buffer, qword_t offset, size_t length, size_t *bytes_read)
303 {
304     dword_t ret = ERR_SUCCESS;
305     void *safe_buffer = NULL;
306     dword_t safe_bytes_read;
307     device_file_t *file = CONTAINER_OF(_file->global, device_file_t, header);
308
309     if (get_previous_mode() == USER_MODE)
310     {
311         if (!check_usermode(buffer, length)) return ERR_BADPTR;
312         if (!check_usermode(bytes_read, sizeof(dword_t))) return ERR_BADPTR;
313
314         ret = pin_memory(buffer, &safe_buffer, length, FALSE);
315         if (ret != ERR_SUCCESS) return ret;
316     }
317     else
318     {
319         safe_buffer = buffer;
320     }
321
322     ret = device_read(file->device, safe_buffer, offset, length, &safe_bytes_read);
323
324     if (get_previous_mode() == USER_MODE)
325     {
326         EH_TRY *bytes_read = safe_bytes_read;
327         EH_CATCH ret = ERR_BADPTR;
328         EH_DONE;
329
330         if (safe_buffer) unmap_memory(safe_buffer);
331     }
332
333     return ret;
334 }
335
336 dword_t device_write_file(file_instance_t *_file, const void *buffer, qword_t offset, size_t length, size_t *bytes_written)
337 {
338     dword_t ret = ERR_SUCCESS;
339     void *safe_buffer = NULL;
340     dword_t safe_bytes_written;
341     device_file_t *file = CONTAINER_OF(_file->global, device_file_t, header);
342
343     if (get_previous_mode() == USER_MODE)
344     {
345         if (!check_usermode(buffer, length)) return ERR_BADPTR;
346         if (!check_usermode(bytes_written, sizeof(dword_t))) return ERR_BADPTR;
347
348         ret = pin_memory(buffer, &safe_buffer, length, FALSE);
349         if (ret != ERR_SUCCESS) return ret;
350     }
351     else
352     {
353         safe_buffer = (void*)buffer;
354     }
355
356     ret = device_write(file->device, safe_buffer, offset, length, &safe_bytes_written);
357
358     if (get_previous_mode() == USER_MODE)
359     {
360         EH_TRY *bytes_written = safe_bytes_written;
361         EH_CATCH ret = ERR_BADPTR;
362         EH_DONE;
363
364         unmap_memory(safe_buffer);
365     }
366
367     return ret;
368 }
369
370 dword_t device_list_dir(file_instance_t *directory, char *filename, bool_t continue_scan)
371 {
372     device_file_t *file = CONTAINER_OF(directory->global, device_file_t, header);
373     if (!file->device && !file->device) return ERR_ISNOTDIR;
374
375     device_dir_inst_t *instance = CONTAINER_OF(directory, device_dir_inst_t, header);
376
377     if (!continue_scan)
378     {
379         instance->next_device = CONTAINER_OF(file->header.volume == &block_device_volume
380                                              ? block_devices.next : char_devices.next,
381                                              device_t,
382                                              list);
383     }
384
385     if (&instance->next_device->list == &block_devices
386         || &instance->next_device->list == &char_devices)
387     {
388         return ERR_NOMORE;
389     }
390
391     strcpy(filename, instance->next_device->name);
392     instance->next_device = CONTAINER_OF(instance->next_device->list.next, device_t, list);
393
394     return ERR_SUCCESS;
395 }
396
397 dword_t device_set_file(file_t *file, dword_t info_type, const void *buffer, size_t size)
398 {
399     return ERR_INVALID;
400 }
401
402 dword_t device_read(device_t *device, void *buffer, qword_t offset, size_t length, size_t *bytes_read)
403 {
404     dword_t ret;
405     acquire_resource_shared(&device->resource);
406
407     if (device->type == BLOCK_DEVICE)
408     {
409         ret = device->block_driver->read_proc(device, buffer, offset, length, bytes_read);
410     }
411     else if (device->type == CHAR_DEVICE)
412     {
413         ret = device->char_driver->read_proc(device, buffer, length, bytes_read);
414     }
415     else
416     {
417         ret = ERR_INVALID;
418     }
419
420     release_resource(&device->resource);
421     return ret;
422 }
423
424 dword_t device_write(device_t *device, const void *buffer, qword_t offset, size_t length, size_t *bytes_written)
425 {
426     dword_t ret;
427     acquire_resource_exclusive(&device->resource);
428
429     if (device->type == BLOCK_DEVICE)
430     {
431         ret = device->block_driver->write_proc(device, buffer, offset, length, bytes_written);
432     }
433     else if (device->type == CHAR_DEVICE)
434     {
435         ret = device->char_driver->write_proc(device, buffer, length, bytes_written);
436     }
437     else
438     {
439         ret = ERR_INVALID;
440     }
441
442     release_resource(&device->resource);
443     return ret;
444 }
445
446 dword_t device_ioctl_internal(device_t *device, dword_t control_code, const void *in_buffer, size_t in_length, void *out_buffer, size_t out_length)
447 {
448     dword_t ret;
449     acquire_resource_exclusive(&device->resource);
450
451     if (device->type == BLOCK_DEVICE)
452     {
453         ret = device->block_driver->ioctl_proc(device, control_code, in_buffer, in_length, out_buffer, out_length);
454     }
455     else if (device->type == CHAR_DEVICE)
456     {
457         ret = device->char_driver->ioctl_proc(device, control_code, in_buffer, in_length, out_buffer, out_length);
458     }
459     else
460     {
461         ret = ERR_INVALID;
462     }
463
464     release_resource(&device->resource);
465     return ret;
466 }
467
468 dword_t device_ioctl(handle_t device, dword_t control_code, const void *in_buffer, size_t in_length, void *out_buffer, size_t out_length)
469 {
470     dword_t ret = ERR_SUCCESS;
471     byte_t *safe_in_buffer = NULL;
472     byte_t *safe_out_buffer = NULL;
473
474     if (get_previous_mode() == USER_MODE)
475     {
476         if (!check_usermode(in_buffer, in_length)) return ERR_BADPTR;
477         if (!check_usermode(out_buffer, out_length)) return ERR_BADPTR;
478
479         safe_in_buffer = (byte_t*)malloc(in_length);
480         safe_out_buffer = (byte_t*)malloc(out_length);
481         if ((safe_in_buffer == NULL) || (safe_out_buffer == NULL))
482         {
483             ret = ERR_NOMEMORY;
484             goto cleanup;
485         }
486
487         EH_TRY
488         {
489             memcpy(safe_in_buffer, in_buffer, in_length);
490         }
491         EH_CATCH ret = ERR_BADPTR;
492         EH_DONE;
493
494         if (ret != ERR_SUCCESS) goto cleanup;
495     }
496     else
497     {
498         safe_in_buffer = (byte_t*)in_buffer;
499         safe_out_buffer = out_buffer;
500     }
501
502     file_instance_t *instance;
503
504     if (!reference_by_handle(device, OBJECT_FILE_INSTANCE, (object_t**)&instance))
505     {
506         ret = ERR_INVALID;
507         goto cleanup;
508     }
509
510     device_file_t *file = CONTAINER_OF(instance->global, device_file_t, header);
511     ret = device_ioctl_internal(file->device, control_code, safe_in_buffer, in_length, safe_out_buffer, out_length);
512
513 cleanup:
514     if (get_previous_mode() == USER_MODE)
515     {
516         EH_TRY
517         {
518             if (safe_out_buffer) memcpy(out_buffer, safe_out_buffer, out_length);
519         }
520         EH_CATCH ret = ERR_BADPTR;
521         EH_DONE;
522
523         if (safe_in_buffer) free(safe_in_buffer);
524         if (safe_out_buffer) free(safe_out_buffer);
525     }
526
527     return ret;
528 }
529
530 void device_init(void)
531 {
532     register_filesystem_driver(&device_fs_driver);
533     register_mounted_volume(&block_device_volume);
534     register_mounted_volume(&char_device_volume);
535 }