Reorganize the headers
[monolithium.git] / kernel / src / syscalls.c
1 /*
2  * syscalls.c
3  *
4  * Copyright (C) 2016 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 <syscalls.h>
21 #include <thread.h>
22 #include <heap.h>
23 #include <memory.h>
24 #include <device.h>
25 #include <process.h>
26 #include <thread.h>
27 #include <timer.h>
28 #include <power.h>
29 #include <exception.h>
30
31 extern qword_t syscall_function(const void*, dword_t*, dword_t);
32
33 const void *service_table[] =
34 {
35     &alloc_memory,
36     &clock_get_time,
37     &clock_set_time,
38     &close_object,
39     &commit_memory,
40     &create_memory_section,
41     &create_process,
42     &create_semaphore,
43     &create_thread,
44     &create_user,
45     &delete_file,
46     &delete_user,
47     &device_ioctl,
48     &duplicate_handle,
49     &enum_processes,
50     &flush_memory_section,
51     &free_memory,
52     &get_exception_info,
53     &get_milliseconds,
54     &get_nanoseconds,
55     &get_process_id,
56     &get_thread_id,
57     &get_user_id,
58     &list_directory,
59     &logon_user,
60     &map_memory_section,
61     &mount,
62     &open_file,
63     &open_memory_section,
64     &open_pipe,
65     &open_process,
66     &open_thread,
67     &power_control,
68     &query_file,
69     &query_handle,
70     &query_process,
71     &query_thread,
72     &query_user,
73     &raise_exception,
74     &read_file,
75     &read_memory,
76     &read_pipe,
77     &release_semaphore_by_handle,
78     &restore_exception_handler,
79     &revert_user,
80     &save_exception_handler,
81     &set_memory_flags,
82     &set_user_id,
83     &sleep,
84     &terminate,
85     &terminate_thread,
86     &uncommit_memory,
87     &unmount,
88     &wait_directory_event,
89     &wait_process,
90     &wait_semaphore_by_handle,
91     &wait_thread,
92     &write_file,
93     &write_memory,
94     &write_pipe,
95     &yield_quantum,
96 };
97
98 static void system_service_handler(registers_t *regs, byte_t int_num)
99 {
100     dword_t parameters[MAX_PARAMETERS];
101     thread_t *thread = get_current_thread();
102
103     if (regs->eax >= SERVICE_COUNT)
104     {
105         regs->eax = ERR_NOSYSCALL;
106         return;
107     }
108
109     acquire_lock(&thread->syscall_lock);
110     thread->syscall_regs = regs;
111
112     if (get_previous_mode() == USER_MODE)
113     {
114         if (!check_usermode((dword_t*)regs->edx, sizeof(parameters)))
115         {
116             regs->eax = ERR_BADPTR;
117             goto cleanup;
118         }
119
120         EH_TRY
121         {
122             memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
123         }
124         EH_CATCH
125         {
126             regs->eax = ERR_BADPTR;
127             EH_ESCAPE(goto cleanup);
128         }
129         EH_DONE;
130     }
131     else
132     {
133         memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
134     }
135
136     qword_t result = syscall_function(service_table[regs->eax], parameters, sizeof(parameters));
137     regs->eax = (dword_t)result;
138     regs->edx = (dword_t)(result >> 32);
139
140 cleanup:
141     thread->syscall_regs = NULL;
142     release_lock(&thread->syscall_lock);
143     if (thread->cancel_io) while (TRUE) yield_quantum();
144     else if (thread->frozen) yield_quantum();
145 }
146
147 processor_mode_t get_previous_mode()
148 {
149     thread_t *thread = get_current_thread();
150     return thread ? thread->previous_mode : KERNEL_MODE;
151 }
152
153 char *copy_user_string(const char *string)
154 {
155     int length = 0;
156     ASSERT(get_previous_mode() == USER_MODE);
157
158     EH_TRY length = strlen(string);
159     EH_CATCH length = -1;
160     EH_DONE;
161
162     if (length == -1) return NULL;
163     if (!check_usermode(string, length + 1)) return NULL;
164
165     char *result = (char*)malloc(length + 1);
166     if (result == NULL) return NULL;
167
168     EH_TRY
169     {
170         strcpy(result, string);
171     }
172     EH_CATCH
173     {
174         free(result);
175         result = NULL;
176     }
177     EH_DONE;
178
179     return result;
180 }
181
182 qword_t syscall(syscall_number_t num, ...)
183 {
184     int i;
185     qword_t ret = 0ULL;
186     dword_t parameters[MAX_PARAMETERS];
187     thread_t *thread = get_current_thread();
188
189     if (num >= SERVICE_COUNT) return ERR_NOSYSCALL;
190
191     va_list params;
192     va_start(params, num);
193     for (i = 0; i < MAX_PARAMETERS; i++) parameters[i] = va_arg(params, dword_t);
194     va_end(params);
195
196     processor_mode_t old_mode = get_previous_mode();
197     if (thread) thread->previous_mode = KERNEL_MODE;
198     ret = syscall_function(service_table[num], parameters, sizeof(parameters));
199     if (thread) thread->previous_mode = old_mode;
200
201     return ret;
202 }
203
204 bool_t check_usermode(const void *pointer, dword_t size)
205 {
206     dword_t first_addr = PAGE_ALIGN((dword_t)pointer);
207     dword_t last_addr  = PAGE_ALIGN((dword_t)pointer + size - 1);
208
209     if (first_addr >= USER_AREA_START && last_addr <= USER_AREA_END) return TRUE;
210     else return FALSE;
211 }
212
213 void syscalls_init()
214 {
215     set_int_handler(SYSCALL_INTERRUPT, system_service_handler, TRUE, TRUE);
216 }