summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/syscall-invoke-mapping.c
blob: efecbdf66b9b6ed6631bdc0f420edb5fc1954383 (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "syscall/handles.h"
#include "arch/x86_64/syscall.h"
#include "arch/x86_64/page.h"
#include <stddef.h>
#include "error.h"
#include "lock.h"

static int
s_handle_invoke_mapping_release(
        objdir_t *root_dir, 
        objdir_entry_t *target_entry, 
        uint8_t *payload,
        size_t payload_at
        )
{
    target_entry->data = 0;
    target_entry->type = KO_NONE;
    return 0;
}

static int
s_handle_invoke_mapping_get(
        objdir_t *root_dir, 
        objdir_entry_t *target_entry, 
        uint8_t *payload, 
        size_t payload_at
        )
{
    pmli_t pmli;
    SYSCALL_PAYLOAD_TAKEL(payload, payload_at, pmli, pmli_t);

    if(pmli > 511) return -KE_OOB;

    uint8_t level = target_entry->data & 3;
    pmle_t *target_pml = (pmle_t*)vmem_phys_tovirt(target_entry->data & ~3ULL);
    if(level == 3 && pmli > 255) return -KE_OOB;
    if(level == 0) return -KE_BADOBJ;
    
    size_t dest_pathw;
    objdir_entry_t *dest_entry;
    SYSCALL_PAYLOAD_TAKEOBJ(payload, payload_at, dest_pathw, dest_entry);
    if(dest_entry->type != KO_NONE) return -KE_FULL;

    pmle_t pmle = target_pml[pmli];
    if(!pmle.p) return -KE_DNE;

    *dest_entry = (objdir_entry_t) {
        .type = KO_MEMORY_MAPPING,
        .data = (((uintptr_t)target_pml[pmli].paddr) << 12ULL) | (level - 1)
    };
    return 0;
}

static int
s_handle_invoke_mapping_map(
        objdir_t *root_dir, 
        objdir_entry_t *target_entry, 
        uint8_t *payload, 
        size_t payload_at
        )
{
    uint8_t level = target_entry->data & 3;
    pmle_t *target_pml = (pmle_t*)vmem_phys_tovirt(target_entry->data & ~3ULL);

    pmli_t pmli;
    SYSCALL_PAYLOAD_TAKEL(payload, payload_at, pmli, pmli_t);

    if(pmli > 511) return -KE_OOB;
    if(level == 3 && pmli > 255) return -KE_OOB;

    size_t untyped_pathw;
    objdir_entry_t *untyped_entry;
    SYSCALL_PAYLOAD_TAKEOBJ(payload, payload_at, untyped_pathw, untyped_entry);
    
    if(untyped_entry->type != KO_MEMORY_UNTYPED) return -KE_BADOBJ;
    mtx_acquire(&untyped_entry->lock);

    if((untyped_entry->data & 0xFFF) != 0) {
        mtx_release(&untyped_entry->lock);
        return -KE_ALIGN;
    }
    size_t *untyped_data = (size_t*)untyped_entry->data;
    size_t untyped_size = *untyped_data;
    if(untyped_size != 0x1000) {
        mtx_release(&untyped_entry->lock);
        return -KE_BADSIZE;
    }

    pmle_t pmle = target_pml[pmli];
    if(pmle.p) {
        mtx_release(&untyped_entry->lock);
        return -KE_OCCUPIED;
    }
    
    target_pml[pmli].value = vmem_ident_tophys((void*)untyped_entry->data) | PAGE_RW | PAGE_US | PAGE_PRESENT;
    untyped_entry->type = KO_MEMORY_MAPPING;
    untyped_entry->data |= level - 1;

    mtx_release(&untyped_entry->lock);
    return 0;
}

static int (*s_invoke_handles[])(objdir_t*, objdir_entry_t*, uint8_t*, size_t) = {
    [INVOKE_MAPPING_RELEASE] = s_handle_invoke_mapping_release,
    [INVOKE_MAPPING_GET] = s_handle_invoke_mapping_get,
    [INVOKE_MAPPING_MAP] = s_handle_invoke_mapping_map,
};

static size_t s_invoke_handles_count = sizeof(s_invoke_handles) / sizeof(void*);

int
syscall_handle_invoke_mapping(
        objdir_t *root_dir, 
        objdir_entry_t *target, 
        uint8_t *payload, 
        size_t payload_at
        )
{
    uint8_t funcid;
    SYSCALL_PAYLOAD_TAKEL(payload, payload_at, funcid, uint8_t);

    if(funcid >= s_invoke_handles_count) return -KE_BADFUNC;
    return s_invoke_handles[funcid](root_dir, target, payload, payload_at);
}