summaryrefslogtreecommitdiffstats
path: root/usr/syscall.c
blob: fbc5fe7a0d4bf5c77c2dd554da5cc9024ec8f038 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include "syscall.h"
#include "sys/errno.h"
#include "sys/permission.h"
#include "usr/tasking.h"
#include "mem/memory.h"
#include "io/log.h"

#define ENSURE_ADDR(ptr) \
    if(!mem_check_ptr(ptr)) { klogf("User passed bad address %#016X\n", ptr); return -EFAULT; }

#define ENSURE_PERM(p) \
    if(!(task_current->perm & p)) return -ENOPERM

#define PD_FOR_LINEAR_ADDRESS(addr) current_page_directory; \
    if(addr.tid == -1) addr.tid = task_current->id; \
    if(addr.tid != task_current->id) { \
        ENSURE_PERM(PERM_MEM_VIRT_PD); \
        struct Task *task = task_get(addr.tid); \
        if(task == NULL) return -EFAULT; \
        pd = task->pd; \
    }

int _syscall_handler_log(struct syscall_log *req)
{
    ENSURE_ADDR(req->message);
    klogf("%s", req->message);
    return 0;
}

intmax_t _syscall_handler_tid(syscall_t *req)
{
    return task_current->id;
}

int _syscall_handler_mem_phys_resv(struct syscall_mem_phys_range_op *req)
{
    ENSURE_PERM(PERM_MEM_PHYS_RESV);
    mem_phys_reserve(req->base, req->limit);
    return 0;
}

int _syscall_handler_mem_phys_free(struct syscall_mem_phys_range_op *req)
{
    ENSURE_PERM(PERM_MEM_PHYS_FREE);
    mem_phys_release(req->base, req->limit);
    return 0;
}

int _syscall_handler_mem_phys_alloc(struct syscall_mem_phys_alloc *req)
{
    ENSURE_ADDR(req->result);
    ENSURE_PERM(PERM_MEM_PHYS_ALLOC);
    *req->result = mem_phys_alloc(req->npages);
    return 0;
}

int _syscall_handler_mem_virt_mapping(struct syscall_mem_virt_mapping *req)
{
    ENSURE_ADDR(req->result);
    ENSURE_PERM(PERM_MEM_VIRT_MAP);
    page_directory_t *pd = PD_FOR_LINEAR_ADDRESS(req->addr);
    *req->result = mem_get_mapping_as(pd, req->addr.addr);
    return 0;
}

int _syscall_handler_mem_virt_map(struct syscall_mem_virt_map *req)
{
    ENSURE_PERM(PERM_MEM_VIRT_MAP);
    page_directory_t *pd = PD_FOR_LINEAR_ADDRESS(req->addr);
    mem_set_mapping_as(pd, req->map, req->addr.addr);
    return 0;
}

int _syscall_handler_mem_virt_alloc(struct syscall_mem_virt_alloc *req)
{
    ENSURE_PERM(PERM_MEM_VIRT_MAP);
    ENSURE_PERM(PERM_MEM_PHYS_ALLOC);
    page_directory_t *pd = PD_FOR_LINEAR_ADDRESS(req->from);
    mem_ensure_range_as(pd, req->from.addr, req->to, req->flg);
    return 0;
}

void *_syscall_handlers[SYSCALL_COUNT] = {
    _syscall_handler_log,
    _syscall_handler_tid,

    _syscall_handler_mem_phys_resv,
    _syscall_handler_mem_phys_free,
    _syscall_handler_mem_phys_alloc,

    _syscall_handler_mem_virt_mapping,
    _syscall_handler_mem_virt_map,
    _syscall_handler_mem_virt_alloc,
};

int
syscall_handler(syscall_t *req)
{
    ENSURE_ADDR(req);
    if(req->id >= SYSCALL_COUNT) return -ENOSYS;

    ENSURE_ADDR(_syscall_handlers[req->id]);
    return ((syscall_handler_t)(_syscall_handlers[req->id]))(req);
}