123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- /*
- * syscalls.c
- *
- * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <syscalls.h>
- #include <thread.h>
- #include <heap.h>
- #include <memory.h>
- #include <device.h>
- #include <process.h>
- #include <thread.h>
- #include <timer.h>
- #include <power.h>
- #include <exception.h>
- #include <pipe.h>
- extern sysret_t syscall_function(const void*, dword_t*, dword_t);
- #include "syscall_table.inl"
- static void system_service_handler(registers_t *regs, byte_t int_num)
- {
- dword_t parameters[MAX_PARAMETERS];
- thread_t *thread = get_current_thread();
- if (regs->eax >= SERVICE_COUNT)
- {
- regs->eax = ERR_NOSYSCALL;
- return;
- }
- acquire_lock(&thread->syscall_lock);
- thread->syscall_regs = regs;
- if (get_previous_mode() == USER_MODE)
- {
- if (!check_usermode((dword_t*)regs->edx, sizeof(parameters)))
- {
- regs->eax = ERR_BADPTR;
- goto cleanup;
- }
- EH_TRY
- {
- memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
- }
- EH_CATCH
- {
- regs->eax = ERR_BADPTR;
- EH_ESCAPE(goto cleanup);
- }
- EH_DONE;
- }
- else
- {
- memcpy(parameters, (dword_t*)regs->edx, sizeof(parameters));
- }
- sysret_t result = syscall_function(service_table[regs->eax], parameters, sizeof(parameters));
- regs->eax = (dword_t)result;
- regs->edx = (dword_t)(result >> 32);
- cleanup:
- thread->syscall_regs = NULL;
- release_lock(&thread->syscall_lock);
- if (thread->cancel_io) while (TRUE) syscall_yield_quantum();
- else if (thread->frozen) syscall_yield_quantum();
- }
- processor_mode_t get_previous_mode()
- {
- thread_t *thread = get_current_thread();
- return thread ? thread->previous_mode : KERNEL_MODE;
- }
- char *copy_user_string(const char *string)
- {
- int length = 0;
- ASSERT(get_previous_mode() == USER_MODE);
- EH_TRY length = strlen(string);
- EH_CATCH length = -1;
- EH_DONE;
- if (length == -1) return NULL;
- if (!check_usermode(string, length + 1)) return NULL;
- char *result = (char*)malloc(length + 1);
- if (result == NULL) return NULL;
- EH_TRY
- {
- strcpy(result, string);
- }
- EH_CATCH
- {
- free(result);
- result = NULL;
- }
- EH_DONE;
- return result;
- }
- sysret_t syscall(syscall_number_t num, ...)
- {
- int i;
- qword_t ret = 0ULL;
- dword_t parameters[MAX_PARAMETERS];
- thread_t *thread = get_current_thread();
- if (num >= SERVICE_COUNT) return ERR_NOSYSCALL;
- va_list params;
- va_start(params, num);
- for (i = 0; i < MAX_PARAMETERS; i++) parameters[i] = va_arg(params, dword_t);
- va_end(params);
- processor_mode_t old_mode = get_previous_mode();
- if (thread) thread->previous_mode = KERNEL_MODE;
- ret = syscall_function(service_table[num], parameters, sizeof(parameters));
- if (thread) thread->previous_mode = old_mode;
- return ret;
- }
- bool_t check_usermode(const void *pointer, dword_t size)
- {
- dword_t first_addr = PAGE_ALIGN((dword_t)pointer);
- dword_t last_addr = PAGE_ALIGN((dword_t)pointer + size - 1);
- if (first_addr >= USER_AREA_START && last_addr <= USER_AREA_END) return TRUE;
- else return FALSE;
- }
- void syscalls_init()
- {
- set_int_handler(SYSCALL_INTERRUPT, system_service_handler, TRUE, TRUE);
- }
|