syscalls.c 3.6 KB

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