aebc1f585e782c41ee7136e8bcc9d7f3eed38cc5
[monolithium.git] / kernel / src / pipe.c
1 /*
2  * pipe.c
3  *
4  * Copyright (C) 2015 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 <pipe.h>
21 #include <exception.h>
22 #include <timer.h>
23 #include <process.h>
24 #include <thread.h>
25 #include <syscalls.h>
26 #include <heap.h>
27
28 static dword_t create_pipe_internal(pipe_t **_pipe, dword_t flags)
29 {
30     pipe_t *pipe = (pipe_t*)malloc(sizeof(pipe_t));
31     if (pipe == NULL) return ERR_NOMEMORY;
32
33     init_object(&pipe->header, NULL, OBJECT_PIPE);
34     init_pipe(pipe, flags);
35
36     dword_t ret = create_object(&pipe->header);
37     if (ret != ERR_SUCCESS)
38     {
39         free(pipe);
40         return ret;
41     }
42
43     *_pipe = pipe;
44     return ERR_SUCCESS;
45 }
46
47 dword_t read_pipe(pipe_t *pipe, void *buffer, size_t size, dword_t timeout)
48 {
49     dword_t ret = ERR_SUCCESS;
50     acquire_lock(&pipe->lock);
51
52     if (pipe->flags & PIPE_MESSAGE)
53     {
54         if (pipe->fifo.next == &pipe->fifo)
55         {
56             release_lock(&pipe->lock);
57             wait_result_t status = scheduler_wait(WAIT_UNTIL_NOT_EQUAL, timeout, (dword_t*)&pipe->fifo.next, 0);
58             if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
59             acquire_lock(&pipe->lock);
60         }
61
62         pipe_message_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_message_entry_t, link);
63         if (size < entry->size)
64         {
65             ret = ERR_SMALLBUF;
66             goto cleanup;
67         }
68
69         memcpy(buffer, entry->data, entry->size);
70
71         list_remove(&entry->link);
72         heap_free(&evictable_heap, entry);
73     }
74     else
75     {
76         byte_t *data = (byte_t*)buffer;
77         dword_t count = 0;
78
79         while (count < size)
80         {
81             if (pipe->fifo.next == &pipe->fifo)
82             {
83                 release_lock(&pipe->lock);
84                 wait_result_t status = scheduler_wait(WAIT_UNTIL_NOT_EQUAL, timeout, (dword_t*)&pipe->fifo.next, 0);
85                 if (status != ERR_SUCCESS) return (status == WAIT_TIMED_OUT) ? ERR_TIMEOUT : ERR_CANCELED;
86                 acquire_lock(&pipe->lock);
87             }
88
89             pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
90
91             while ((count < size) && ((entry->start != entry->end) || entry->full))
92             {
93                 data[count++] = entry->data[entry->start++];
94                 entry->start %= PIPE_BLOCK_SIZE;
95                 entry->full = FALSE;
96             }
97
98             if ((entry->start == entry->end) && !entry->full)
99             {
100                 list_remove(&entry->link);
101                 heap_free(&evictable_heap, entry);
102             }
103         }
104     }
105
106 cleanup:
107     release_lock(&pipe->lock);
108     return ret;
109 }
110
111 dword_t write_pipe(pipe_t *pipe, const void *buffer, size_t size)
112 {
113     dword_t ret = ERR_SUCCESS;
114     dword_t count = 0;
115
116     acquire_lock(&pipe->lock);
117
118     if (pipe->flags & PIPE_MESSAGE)
119     {
120         pipe_message_entry_t *entry = heap_alloc(&evictable_heap, sizeof(pipe_message_entry_t) + size);
121         if (!entry)
122         {
123             ret = ERR_NOMEMORY;
124             goto cleanup;
125         }
126
127         entry->size = size;
128         memcpy(entry->data, buffer, size);
129         list_append(&pipe->fifo, &entry->link);
130     }
131     else
132     {
133         const byte_t *data = (const byte_t*)buffer;
134         pipe_fifo_entry_t *entry = (pipe->fifo.prev != &pipe->fifo) ? (pipe_fifo_entry_t*)pipe->fifo.prev : NULL;
135
136         while (count < size)
137         {
138             if (!entry || entry->full)
139             {
140                 entry = (pipe_fifo_entry_t*)heap_alloc(&evictable_heap, sizeof(pipe_fifo_entry_t));
141                 if (!entry)
142                 {
143                     ret = ERR_NOMEMORY;
144                     break;
145                 }
146
147                 entry->start = entry->end = 0;
148                 entry->full = FALSE;
149                 list_append(&pipe->fifo, &entry->link);
150             }
151
152             while ((count < size) && (entry->start != entry->end))
153             {
154                 entry->data[entry->end++] = data[count++];
155                 entry->end %= PIPE_BLOCK_SIZE;
156             }
157
158             if (entry->start == entry->end) entry->full = TRUE;
159         }
160     }
161
162 cleanup:
163     release_lock(&pipe->lock);
164     return ret;
165 }
166
167 void pipe_cleanup(pipe_t *pipe)
168 {
169     while (pipe->fifo.next != &pipe->fifo)
170     {
171         pipe_fifo_entry_t *entry = CONTAINER_OF(pipe->fifo.next, pipe_fifo_entry_t, link);
172         list_remove(&entry->link);
173         free(entry);
174     }
175 }
176
177 sysret_t syscall_create_pipeline(const char *name, dword_t flags, access_flags_t access, handle_t *handle)
178 {
179     dword_t ret;
180     handle_t safe_handle;
181     const char *safe_name = NULL;
182
183     if (get_previous_mode() == USER_MODE)
184     {
185         dword_t name_length = 0;
186
187         EH_TRY name_length = strlen(name);
188         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
189         EH_DONE;
190
191         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
192         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
193
194         safe_name = copy_user_string(name);
195         if (safe_name == NULL) return ERR_BADPTR;
196     }
197     else
198     {
199         safe_name = (const char*)name;
200     }
201
202     pipeline_t *pipeline = (pipeline_t*)malloc(sizeof(pipeline_t));
203     if (pipeline == NULL)
204     {
205         ret = ERR_NOMEMORY;
206         goto cleanup;
207     }
208
209     init_object(&pipeline->header, safe_name, OBJECT_PIPELINE);
210     pipeline->flags = flags;
211     pipeline->lock = 0;
212     pipeline->status = PIPELINE_IDLE;
213
214     ret = create_object(&pipeline->header);
215     if (ret != ERR_SUCCESS)
216     {
217         free(pipeline->header.name);
218         free(pipeline);
219         goto cleanup;
220     }
221
222     ret = open_object(&pipeline->header, access, &safe_handle);
223     if (ret != ERR_SUCCESS) goto cleanup;
224
225     EH_TRY
226     {
227         *handle = safe_handle;
228     }
229     EH_CATCH
230     {
231         syscall_close_object(safe_handle);
232         ret = ERR_BADPTR;
233     }
234     EH_DONE;
235
236 cleanup:
237     dereference(&pipeline->header);
238     if (get_previous_mode() == USER_MODE) free((void*)safe_name);
239     return ret;
240 }
241
242 sysret_t syscall_open_pipeline(const char *name, access_flags_t access, handle_t *handle)
243 {
244     dword_t ret;
245     handle_t safe_handle;
246     const char *safe_name = NULL;
247
248     if (get_previous_mode() == USER_MODE)
249     {
250         dword_t name_length = 0;
251
252         EH_TRY name_length = strlen(name);
253         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
254         EH_DONE;
255
256         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
257         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
258
259         safe_name = copy_user_string(name);
260         if (safe_name == NULL) return ERR_BADPTR;
261     }
262     else
263     {
264         safe_name = (const char*)name;
265     }
266
267     ret = open_object_by_name(safe_name, OBJECT_PIPELINE, access, &safe_handle);
268     if (ret != ERR_SUCCESS) goto cleanup;
269
270     EH_TRY
271     {
272         *handle = safe_handle;
273     }
274     EH_CATCH
275     {
276         ret = ERR_BADPTR;
277     }
278     EH_DONE;
279
280 cleanup:
281     if (get_previous_mode() == USER_MODE) free((void*)safe_name);
282     return ret;
283 }
284
285 static dword_t pipeline_create_pipes(pipeline_t *pipeline)
286 {
287     dword_t request_flags = (pipeline->flags & PIPELINE_REQUEST_MESSAGE) ? PIPE_MESSAGE : 0;
288     dword_t response_flags = (pipeline->flags & PIPELINE_RESPONSE_MESSAGE) ? PIPE_MESSAGE : 0;
289     dword_t ret = create_pipe_internal(&pipeline->request_pipe, request_flags);
290     if (ret != ERR_SUCCESS) return ret;
291
292     if (!(pipeline->flags & PIPELINE_REQUEST_ONLY))
293     {
294         ret = create_pipe_internal(&pipeline->response_pipe, response_flags);
295         if (ret != ERR_SUCCESS)
296         {
297             dereference(&pipeline->request_pipe->header);
298             pipeline->request_pipe = NULL;
299         }
300     }
301
302     return ret;
303 }
304
305 static dword_t connect_to_pipeline(pipeline_t *pipeline, pipe_connection_t *conn)
306 {
307     ASSERT(pipeline->lock);
308
309     dword_t ret = open_object(&pipeline->request_pipe->header, 0, &conn->request_pipe);
310     if (ret != ERR_SUCCESS) return ret;
311
312     if (pipeline->response_pipe)
313     {
314         ret = open_object(&pipeline->response_pipe->header, 0, &conn->response_pipe);
315         if (ret != ERR_SUCCESS) syscall_close_object(conn->response_pipe);
316     }
317
318     return ret;
319 }
320
321 sysret_t syscall_listen_pipeline(handle_t handle, dword_t timeout, pipe_connection_t *connection)
322 {
323     dword_t ret;
324     process_t *proc;
325
326     if (get_previous_mode() == USER_MODE)
327     {
328         proc = get_current_process();
329     }
330     else
331     {
332         proc = kernel_process;
333     }
334
335     pipeline_t *pipeline;
336     if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline)) return ERR_INVALID;
337
338     acquire_lock(&pipeline->lock);
339
340     if (pipeline->status != PIPELINE_IDLE)
341     {
342         release_lock(&pipeline->lock);
343         dereference(&pipeline->header);
344         return ERR_BUSY;
345     }
346
347     pipeline->server_pid = proc->pid;
348     pipeline->client_pid = 0;
349     pipeline->request_pipe = pipeline->response_pipe = NULL;
350
351     pipeline->status = PIPELINE_ACCEPTING;
352     release_lock(&pipeline->lock);
353
354     wait_result_t result = scheduler_wait(WAIT_UNTIL_NOT_EQUAL, timeout, &pipeline->status, PIPELINE_ACCEPTING);
355     acquire_lock(&pipeline->lock);
356
357     if (pipeline->status != PIPELINE_CONNECTING)
358     {
359         switch (result)
360         {
361         case WAIT_CONDITION_HIT:
362             ret = pipeline->last_error;
363             break;
364
365         case WAIT_TIMED_OUT:
366             ret = ERR_TIMEOUT;
367             break;
368
369         case WAIT_CANCELED:
370             ret = ERR_CANCELED;
371         }
372
373         goto cleanup;
374     }
375
376     pipe_connection_t conn;
377     conn.server_pid = pipeline->server_pid;
378     conn.client_pid = pipeline->client_pid;
379     conn.request_pipe = conn.response_pipe = INVALID_HANDLE;
380
381     ret = connect_to_pipeline(pipeline, &conn);
382     if (ret == ERR_SUCCESS)
383     {
384         EH_TRY
385         {
386             *connection = conn;
387         }
388         EH_CATCH
389         {
390             ret = ERR_BADPTR;
391         }
392         EH_DONE;
393     }
394
395 cleanup:
396     pipeline->status = PIPELINE_IDLE;
397
398     if (pipeline->request_pipe) dereference(&pipeline->request_pipe->header);
399     if (pipeline->response_pipe) dereference(&pipeline->response_pipe->header);
400     pipeline->request_pipe = pipeline->response_pipe = NULL;
401
402     release_lock(&pipeline->lock);
403     dereference(&pipeline->header);
404     return ret;
405 }
406
407 sysret_t syscall_connect_pipeline(handle_t handle, access_flags_t access, pipe_connection_t *connection)
408 {
409     dword_t ret;
410     process_t *proc;
411
412     if (get_previous_mode() == USER_MODE)
413     {
414         proc = get_current_process();
415         if (!check_usermode(connection, sizeof(pipe_connection_t))) return ERR_BADPTR;
416     }
417     else
418     {
419         proc = kernel_process;
420     }
421
422     pipeline_t *pipeline = NULL;
423     if (!reference_by_handle(handle, OBJECT_PIPELINE, (object_t**)&pipeline))
424     {
425         ret = ERR_NOTFOUND;
426         goto cleanup;
427     }
428
429     acquire_lock(&pipeline->lock);
430
431     if (pipeline->status != PIPELINE_ACCEPTING)
432     {
433         ret = ERR_BUSY;
434         goto cleanup;
435     }
436
437     pipeline->client_pid = proc->pid;
438
439     ret = pipeline_create_pipes(pipeline);
440     if (ret != ERR_SUCCESS)
441     {
442         pipeline->last_error = ret;
443         goto cleanup;
444     }
445
446     pipe_connection_t conn;
447     conn.server_pid = pipeline->server_pid;
448     conn.client_pid = pipeline->client_pid;
449     conn.request_pipe = INVALID_HANDLE;
450     conn.response_pipe = INVALID_HANDLE;
451
452     ret = connect_to_pipeline(pipeline, &conn);
453     if (ret == ERR_SUCCESS)
454     {
455         pipeline->status = PIPELINE_CONNECTING;
456
457         EH_TRY
458         {
459             *connection = conn;
460         }
461         EH_CATCH
462         {
463             ret = ERR_BADPTR;
464         }
465         EH_DONE;
466     }
467     else
468     {
469         pipeline->last_error = ret;
470         pipeline->status = PIPELINE_FAILED;
471     }
472
473 cleanup:
474     release_lock(&pipeline->lock);
475     if (pipeline) dereference(&pipeline->header);
476     return ret;
477 }
478
479 sysret_t syscall_read_pipe(handle_t handle, void *buffer, size_t size, dword_t timeout)
480 {
481     pipe_t *pipe;
482     void *safe_buffer = NULL;
483
484     if (get_previous_mode() == USER_MODE)
485     {
486         if (!check_usermode(buffer, size)) return ERR_BADPTR;
487
488         safe_buffer = malloc(size);
489         if (safe_buffer == NULL) return ERR_NOMEMORY;
490     }
491     else
492     {
493         safe_buffer = buffer;
494     }
495
496     if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
497     dword_t ret = read_pipe(pipe, (byte_t*)safe_buffer, size, timeout);
498     dereference(&pipe->header);
499
500     if (get_previous_mode() == USER_MODE)
501     {
502         EH_TRY memcpy(buffer, safe_buffer, size);
503         EH_CATCH ret = ERR_BADPTR;
504         EH_DONE;
505
506         free(safe_buffer);
507     }
508
509     return ret;
510 }
511
512 sysret_t syscall_write_pipe(handle_t handle, void *buffer, size_t size)
513 {
514     pipe_t *pipe;
515     byte_t *safe_buffer = NULL;
516
517     if (get_previous_mode() == USER_MODE)
518     {
519         if (!check_usermode(buffer, size)) return ERR_BADPTR;
520
521         safe_buffer = (byte_t*)malloc(size);
522         if (safe_buffer == NULL) return ERR_NOMEMORY;
523
524         EH_TRY memcpy(safe_buffer, buffer, size);
525         EH_CATCH
526         {
527             free(safe_buffer);
528             EH_ESCAPE(return ERR_BADPTR);
529         }
530         EH_DONE;
531     }
532     else safe_buffer = (byte_t*)buffer;
533
534     if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
535     dword_t ret = write_pipe(pipe, safe_buffer, size);
536     dereference(&pipe->header);
537
538     if (get_previous_mode() == USER_MODE)
539     {
540         EH_TRY memcpy(buffer, safe_buffer, size);
541         EH_CATCH ret = ERR_BADPTR;
542         EH_DONE;
543
544         free(safe_buffer);
545     }
546
547     return ret;
548 }