Make sure the IOCTL buffers are optional.
[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 DECLARE_LOCK(block_dev_driver_list_lock);
32 static DECLARE_LOCK(char_dev_driver_list_lock);
33 static DECLARE_LOCK(block_device_list_lock);
34 static DECLARE_LOCK(char_device_list_lock);
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     .lock       = {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     .lock       = {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     lock_acquire_shared(&block_device_list_lock);
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     lock_release(&block_device_list_lock);
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         lock_acquire(&block_dev_driver_list_lock);
111         list_append(&block_dev_drivers, &driver->list);
112         lock_release(&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     lock_acquire(&block_dev_driver_list_lock);
124     list_remove(&driver->list);
125     lock_release(&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     lock_acquire(&block_device_list_lock);
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     lock_release(&block_device_list_lock);
154     return ret;
155 }
156
157 dword_t unregister_block_device(device_t *device)
158 {
159     lock_acquire(&block_device_list_lock);
160     list_remove(&device->list);
161     lock_release(&block_device_list_lock);
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     lock_acquire_shared(&block_device_list_lock);
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     lock_release(&block_device_list_lock);
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         lock_acquire(&char_dev_driver_list_lock);
189         list_append(&char_dev_drivers, &driver->list);
190         lock_release(&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     lock_acquire(&char_dev_driver_list_lock);
200     list_remove(&driver->list);
201     lock_release(&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     lock_acquire(&char_device_list_lock);
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     lock_release(&char_device_list_lock);
230     return ret;
231 }
232
233 dword_t unregister_char_device(device_t *device)
234 {
235     lock_acquire(&char_device_list_lock);
236     list_remove(&device->list);
237     lock_release(&char_device_list_lock);
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     device_file_t *file = (device_file_t*)(*_instance)->global;
290
291     if (!file->device)
292     {
293         device_dir_inst_t *instance = (device_dir_inst_t*)realloc(*_instance, sizeof(device_dir_inst_t));
294         if (instance == NULL) return ERR_NOMEMORY;
295         *_instance = &instance->header;
296     }
297
298     return ERR_SUCCESS;
299 }
300
301 dword_t device_close_file(file_instance_t *instance)
302 {
303     return ERR_SUCCESS;
304 }
305
306 dword_t device_delete_file(mounted_volume_t *volume, const char *path, bool_t purge)
307 {
308     return ERR_INVALID;
309 }
310
311 dword_t device_read_file(file_instance_t *_file, void *buffer, qword_t offset, size_t length, size_t *bytes_read)
312 {
313     device_file_t *file = CONTAINER_OF(_file->global, device_file_t, header);
314     return device_read(file->device, buffer, offset, length, bytes_read);
315 }
316
317 dword_t device_write_file(file_instance_t *_file, const void *buffer, qword_t offset, size_t length, size_t *bytes_written)
318 {
319     device_file_t *file = CONTAINER_OF(_file->global, device_file_t, header);
320     return device_write(file->device, buffer, offset, length, bytes_written);
321 }
322
323 dword_t device_list_dir(file_instance_t *directory, char *filename, bool_t continue_scan)
324 {
325     device_file_t *file = CONTAINER_OF(directory->global, device_file_t, header);
326     if (file->device) return ERR_ISNOTDIR;
327
328     device_dir_inst_t *instance = CONTAINER_OF(directory, device_dir_inst_t, header);
329
330     if (!continue_scan)
331     {
332         instance->next_device = CONTAINER_OF(file->header.volume == &block_device_volume
333                                              ? block_devices.next : char_devices.next,
334                                              device_t,
335                                              list);
336     }
337
338     if (&instance->next_device->list == &block_devices
339         || &instance->next_device->list == &char_devices)
340     {
341         return ERR_NOMORE;
342     }
343
344     strcpy(filename, instance->next_device->name);
345     instance->next_device = CONTAINER_OF(instance->next_device->list.next, device_t, list);
346
347     return ERR_SUCCESS;
348 }
349
350 dword_t device_set_file(file_t *file, dword_t info_type, const void *buffer, size_t size)
351 {
352     return ERR_INVALID;
353 }
354
355 dword_t device_read(device_t *device, void *buffer, qword_t offset, size_t length, size_t *bytes_read)
356 {
357     dword_t ret;
358     lock_acquire_shared(&device->lock);
359
360     if (device->type == BLOCK_DEVICE)
361     {
362         ret = device->block_driver->read_proc(device, buffer, offset, length, bytes_read);
363     }
364     else if (device->type == CHAR_DEVICE)
365     {
366         ret = device->char_driver->read_proc(device, buffer, length, bytes_read);
367     }
368     else
369     {
370         ret = ERR_INVALID;
371     }
372
373     lock_release(&device->lock);
374     return ret;
375 }
376
377 dword_t device_write(device_t *device, const void *buffer, qword_t offset, size_t length, size_t *bytes_written)
378 {
379     dword_t ret;
380     lock_acquire(&device->lock);
381
382     if (device->type == BLOCK_DEVICE)
383     {
384         ret = device->block_driver->write_proc(device, buffer, offset, length, bytes_written);
385     }
386     else if (device->type == CHAR_DEVICE)
387     {
388         ret = device->char_driver->write_proc(device, buffer, length, bytes_written);
389     }
390     else
391     {
392         ret = ERR_INVALID;
393     }
394
395     lock_release(&device->lock);
396     return ret;
397 }
398
399 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)
400 {
401     dword_t ret;
402     lock_acquire(&device->lock);
403
404     if (device->type == BLOCK_DEVICE)
405     {
406         ret = device->block_driver->ioctl_proc(device, control_code, in_buffer, in_length, out_buffer, out_length);
407     }
408     else if (device->type == CHAR_DEVICE)
409     {
410         ret = device->char_driver->ioctl_proc(device, control_code, in_buffer, in_length, out_buffer, out_length);
411     }
412     else
413     {
414         ret = ERR_INVALID;
415     }
416
417     lock_release(&device->lock);
418     return ret;
419 }
420
421 sysret_t syscall_device_ioctl(handle_t device, dword_t control_code, const void *in_buffer, size_t in_length, void *out_buffer, size_t out_length)
422 {
423     dword_t ret = ERR_SUCCESS;
424     byte_t *safe_in_buffer = NULL;
425     byte_t *safe_out_buffer = NULL;
426
427     if (get_previous_mode() == USER_MODE)
428     {
429         if (in_buffer && !check_usermode(in_buffer, in_length)) return ERR_BADPTR;
430         if (out_buffer && !check_usermode(out_buffer, out_length)) return ERR_BADPTR;
431
432         if (in_buffer)
433         {
434             if ((safe_in_buffer = (byte_t*)malloc(in_length)) == NULL)
435             {
436                 ret = ERR_NOMEMORY;
437                 goto cleanup;
438             }
439
440             EH_TRY
441             {
442                 memcpy(safe_in_buffer, in_buffer, in_length);
443             }
444             EH_CATCH
445             {
446                 ret = ERR_BADPTR;
447                 EH_ESCAPE(goto cleanup);
448             }
449             EH_DONE;
450         }
451
452         if (out_buffer)
453         {
454             if ((safe_out_buffer = (byte_t*)malloc(out_length)) == NULL)
455             {
456                 ret = ERR_NOMEMORY;
457                 goto cleanup;
458             }
459         }
460     }
461     else
462     {
463         safe_in_buffer = (byte_t*)in_buffer;
464         safe_out_buffer = out_buffer;
465     }
466
467     file_instance_t *instance;
468
469     if (!reference_by_handle(device, OBJECT_FILE_INSTANCE, (object_t**)&instance))
470     {
471         ret = ERR_INVALID;
472         goto cleanup;
473     }
474
475     device_file_t *file = CONTAINER_OF(instance->global, device_file_t, header);
476     ret = device_ioctl_internal(file->device, control_code, safe_in_buffer, in_length, safe_out_buffer, out_length);
477
478 cleanup:
479     if (get_previous_mode() == USER_MODE)
480     {
481         EH_TRY
482         {
483             if (safe_out_buffer) memcpy(out_buffer, safe_out_buffer, out_length);
484         }
485         EH_CATCH ret = ERR_BADPTR;
486         EH_DONE;
487
488         if (safe_in_buffer) free(safe_in_buffer);
489         if (safe_out_buffer) free(safe_out_buffer);
490     }
491
492     return ret;
493 }
494
495 void device_init(void)
496 {
497     register_filesystem_driver(&device_fs_driver);
498     register_mounted_volume(&block_device_volume);
499     register_mounted_volume(&char_device_volume);
500 }