Automatically generate syscalls.h and syscalls.c as well
[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 #include <pipe.h>
31
32 extern sysret_t syscall_function(const void*, dword_t*, dword_t);
33
34 #include "syscall_table.inl"
35
36 static void system_service_handler(registers_t *regs, byte_t int_num)
37 {
38     dword_t parameters[MAX_PARAMETERS];
39     thread_t *thread = get_current_thread();
40
41     if (regs->eax >= SERVICE_COUNT)
42     {
43         regs->eax = ERR_NOSYSCALL;
44         return;
45     }
46
47     acquire_lock(&thread->syscall_lock);
48     thread->syscall_regs = regs;
49
50     if (get_previous_mode() == USER_MODE)
51     {
52         if (!check_usermode((dword_t*)regs->edx, sizeof(parameters)))
53         {
54             regs->eax = ERR_BADPTR;
55             goto cleanup;
56         }
57
58         EH_TRY
59         {
60             memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
61         }
62         EH_CATCH
63         {
64             regs->eax = ERR_BADPTR;
65             EH_ESCAPE(goto cleanup);
66         }
67         EH_DONE;
68     }
69     else
70     {
71         memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
72     }
73
74     sysret_t result = syscall_function(service_table[regs->eax], parameters, sizeof(parameters));
75     regs->eax = (dword_t)result;
76     regs->edx = (dword_t)(result >> 32);
77
78 cleanup:
79     thread->syscall_regs = NULL;
80     release_lock(&thread->syscall_lock);
81     if (thread->cancel_io) while (TRUE) syscall_yield_quantum();
82     else if (thread->frozen) syscall_yield_quantum();
83 }
84
85 processor_mode_t get_previous_mode()
86 {
87     thread_t *thread = get_current_thread();
88     return thread ? thread->previous_mode : KERNEL_MODE;
89 }
90
91 char *copy_user_string(const char *string)
92 {
93     int length = 0;
94     ASSERT(get_previous_mode() == USER_MODE);
95
96     EH_TRY length = strlen(string);
97     EH_CATCH length = -1;
98     EH_DONE;
99
100     if (length == -1) return NULL;
101     if (!check_usermode(string, length + 1)) return NULL;
102
103     char *result = (char*)malloc(length + 1);
104     if (result == NULL) return NULL;
105
106     EH_TRY
107     {
108         strcpy(result, string);
109     }
110     EH_CATCH
111     {
112         free(result);
113         result = NULL;
114     }
115     EH_DONE;
116
117     return result;
118 }
119
120 sysret_t syscall(syscall_number_t num, ...)
121 {
122     int i;
123     qword_t ret = 0ULL;
124     dword_t parameters[MAX_PARAMETERS];
125     thread_t *thread = get_current_thread();
126
127     if (num >= SERVICE_COUNT) return ERR_NOSYSCALL;
128
129     va_list params;
130     va_start(params, num);
131     for (i = 0; i < MAX_PARAMETERS; i++) parameters[i] = va_arg(params, dword_t);
132     va_end(params);
133
134     processor_mode_t old_mode = get_previous_mode();
135     if (thread) thread->previous_mode = KERNEL_MODE;
136     ret = syscall_function(service_table[num], parameters, sizeof(parameters));
137     if (thread) thread->previous_mode = old_mode;
138
139     return ret;
140 }
141
142 bool_t check_usermode(const void *pointer, dword_t size)
143 {
144     dword_t first_addr = PAGE_ALIGN((dword_t)pointer);
145     dword_t last_addr  = PAGE_ALIGN((dword_t)pointer + size - 1);
146
147     if (first_addr >= USER_AREA_START && last_addr <= USER_AREA_END) return TRUE;
148     else return FALSE;
149 }
150
151 void syscalls_init()
152 {
153     set_int_handler(SYSCALL_INTERRUPT, system_service_handler, TRUE, TRUE);
154 }