syscalls.c 3.9 KB

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