Move system calls to the SDK and normalize their names
[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 <thread.h>
24 #include <syscalls.h>
25 #include <heap.h>
26
27 sysret_t syscall_create_pipe(const char *name, handle_t *handle)
28 {
29     dword_t ret;
30     handle_t safe_handle;
31     char *safe_name = NULL;
32
33     if (get_previous_mode() == USER_MODE)
34     {
35         dword_t name_length = 0;
36
37         EH_TRY name_length = strlen(name);
38         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
39         EH_DONE;
40
41         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
42         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
43
44         safe_name = copy_user_string(name);
45         if (safe_name == NULL) return ERR_BADPTR;
46     }
47     else
48     {
49         safe_name = (char*)name;
50     }
51
52     pipe_t *pipe = (pipe_t*)malloc(sizeof(pipe_t));
53     if (pipe == NULL)
54     {
55         ret = ERR_NOMEMORY;
56         goto cleanup;
57     }
58
59     pipe->header.name = strdup(name);
60     pipe->header.type = OBJECT_PIPE;
61     pipe->lock = 0;
62     list_init(&pipe->fifo);
63
64     ret = create_object(&pipe->header);
65     if (ret != ERR_SUCCESS)
66     {
67         free(pipe->header.name);
68         free(pipe);
69         goto cleanup;
70     }
71
72     ret = open_object(&pipe->header, 0, &safe_handle);
73     if (ret != ERR_SUCCESS)
74     {
75         dereference(&pipe->header);
76         goto cleanup;
77     }
78
79     EH_TRY
80     {
81         *handle = safe_handle;
82     }
83     EH_CATCH
84     {
85         ret = ERR_BADPTR;
86     }
87     EH_DONE;
88
89 cleanup:
90     if (get_previous_mode() == USER_MODE) free(safe_name);
91     return ret;
92 }
93
94 sysret_t syscall_open_pipe(const char *name, handle_t *handle)
95 {
96     handle_t safe_handle;
97     char *safe_name = NULL;
98
99     if (get_previous_mode() == USER_MODE)
100     {
101         dword_t name_length = 0;
102
103         EH_TRY name_length = strlen(name);
104         EH_CATCH EH_ESCAPE(return ERR_BADPTR);
105         EH_DONE;
106
107         if (!check_usermode(name, name_length + 1)) return ERR_BADPTR;
108         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
109
110         safe_name = copy_user_string(name);
111         if (safe_name == NULL) return ERR_BADPTR;
112     }
113     else
114     {
115         safe_name = (char*)name;
116     }
117
118     dword_t ret = open_object_by_name(safe_name, OBJECT_PIPE, 0, &safe_handle);
119     if (ret != ERR_SUCCESS) goto cleanup;
120
121     EH_TRY
122     {
123         *handle = safe_handle;
124     }
125     EH_CATCH
126     {
127         ret = ERR_BADPTR;
128     }
129     EH_DONE;
130
131 cleanup:
132     if (get_previous_mode() == USER_MODE) free(safe_name);
133     return ret;
134 }
135
136 sysret_t syscall_read_pipe(handle_t handle, void *buffer, dword_t size, dword_t timeout)
137 {
138     pipe_t *pipe;
139     byte_t *safe_buffer = NULL;
140
141     if (get_previous_mode() == USER_MODE)
142     {
143         if (!check_usermode(buffer, size)) return ERR_BADPTR;
144
145         safe_buffer = (byte_t*)malloc(size);
146         if (safe_buffer == NULL) return ERR_NOMEMORY;
147     }
148     else safe_buffer = (byte_t*)buffer;
149
150     if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
151
152     dword_t ret = ERR_SUCCESS;
153     dword_t count = 0;
154     acquire_lock(&pipe->lock);
155
156     while (count < size)
157     {
158         if (pipe->fifo.next == NULL)
159         {
160             release_lock(&pipe->lock);
161
162             if (scheduler_wait(WAIT_UNTIL_NOT_EQUAL, timeout, (dword_t*)&pipe->fifo.next, 0) == WAIT_TIMED_OUT)
163             {
164                 ret = ERR_TIMEOUT;
165                 break;
166             }
167
168             acquire_lock(&pipe->lock);
169         }
170
171         pipe_fifo_entry_t *entry = (pipe_fifo_entry_t*)pipe->fifo.next;
172
173         while ((count < size) && ((entry->start != entry->end) || entry->full))
174         {
175             safe_buffer[count++] = entry->data[entry->start++];
176             entry->start %= PIPE_BLOCK_SIZE;
177             entry->full = FALSE;
178         }
179
180         if ((entry->start == entry->end) && !entry->full)
181         {
182             list_remove(&entry->list);
183             free(entry);
184         }
185     }
186
187     release_lock(&pipe->lock);
188     dereference(&pipe->header);
189
190     if (get_previous_mode() == USER_MODE)
191     {
192         EH_TRY memcpy(buffer, safe_buffer, size);
193         EH_CATCH ret = ERR_BADPTR;
194         EH_DONE;
195
196         free(safe_buffer);
197     }
198
199     return ret;
200 }
201
202 sysret_t syscall_write_pipe(handle_t handle, void *buffer, dword_t size)
203 {
204     pipe_t *pipe;
205     byte_t *safe_buffer = NULL;
206
207     if (get_previous_mode() == USER_MODE)
208     {
209         if (!check_usermode(buffer, size)) return ERR_BADPTR;
210
211         safe_buffer = (byte_t*)malloc(size);
212         if (safe_buffer == NULL) return ERR_NOMEMORY;
213
214         EH_TRY memcpy(safe_buffer, buffer, size);
215         EH_CATCH
216         {
217             free(safe_buffer);
218             EH_ESCAPE(return ERR_BADPTR);
219         }
220         EH_DONE;
221     }
222     else safe_buffer = (byte_t*)buffer;
223
224     if (!reference_by_handle(handle, OBJECT_PIPE, (object_t**)&pipe)) return ERR_INVALID;
225
226     dword_t ret = ERR_SUCCESS;
227     dword_t count = 0;
228     acquire_lock(&pipe->lock);
229
230     pipe_fifo_entry_t *entry = (pipe_fifo_entry_t*)pipe->fifo.prev;
231     while (count < size)
232     {
233         if (entry->full)
234         {
235             entry = (pipe_fifo_entry_t*)heap_alloc(&evictable_heap, sizeof(pipe_fifo_entry_t));
236             if (entry == NULL)
237             {
238                 ret = ERR_NOMEMORY;
239                 goto done;
240             }
241
242             entry->start = entry->end = 0;
243             entry->full = FALSE;
244             list_append(&pipe->fifo, &entry->list);
245         }
246
247         while ((count < size) && (entry->start != entry->end))
248         {
249             entry->data[entry->end++] = safe_buffer[count++];
250             entry->end %= PIPE_BLOCK_SIZE;
251         }
252
253         if (entry->start == entry->end) entry->full = TRUE;
254     }
255
256 done:
257     release_lock(&pipe->lock);
258     dereference(&pipe->header);
259
260     if (get_previous_mode() == USER_MODE)
261     {
262         EH_TRY memcpy(buffer, safe_buffer, size);
263         EH_CATCH ret = ERR_BADPTR;
264         EH_DONE;
265
266         free(safe_buffer);
267     }
268
269     return ret;
270 }
271
272 void pipe_cleanup(pipe_t *pipe)
273 {
274     pipe_fifo_entry_t *entry = (pipe_fifo_entry_t*)pipe->fifo.next;
275
276     while (entry != NULL)
277     {
278         pipe_fifo_entry_t *next_entry = (pipe_fifo_entry_t*)entry->list.next;
279         free(entry);
280         entry = next_entry;
281     }
282 }