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