feat: (vm) memory mapping support: mmap/munmap
[lunaix-os.git] / lunaix-os / kernel / mm / mmap.c
1 #include <lunaix/mm/mmap.h>
2 #include <lunaix/mm/pmm.h>
3 #include <lunaix/mm/region.h>
4 #include <lunaix/mm/valloc.h>
5 #include <lunaix/mm/vmm.h>
6 #include <lunaix/spike.h>
7
8 #include <lunaix/syscall.h>
9
10 void*
11 mem_map(ptr_t mnt,
12         struct llist_header* regions,
13         void* addr,
14         struct v_file* file,
15         u32_t offset,
16         size_t length,
17         u32_t proct)
18 {
19     if (!length || (length & (PG_SIZE - 1)) || (offset & (PG_SIZE - 1))) {
20         __current->k_status = EINVAL;
21         return (void*)-1;
22     }
23
24     // read_page is not supported
25     if (!file->ops->read_page) {
26         __current->k_status = ENODEV;
27         return (void*)-1;
28     }
29
30     ptr_t last_end = USER_START;
31     struct mm_region *pos, *n;
32     llist_for_each(pos, n, regions, head)
33     {
34         if (pos->start - last_end >= length && last_end >= addr) {
35             goto found;
36         }
37         last_end = pos->end;
38     }
39
40     __current->k_status = ENOMEM;
41     return (void*)-1;
42
43 found:
44     addr = last_end;
45     ptr_t end = addr + length;
46
47     struct mm_region* region = region_add(regions, addr, end, proct);
48     region->mfile = file;
49     region->offset = offset;
50
51     u32_t attr = PG_ALLOW_USER;
52     if ((proct & REGION_WRITE)) {
53         attr |= PG_WRITE;
54     }
55
56     for (u32_t i = 0; i < length; i += PG_SIZE) {
57         vmm_set_mapping(mnt, addr + i, 0, attr, 0);
58     }
59
60     return addr;
61 }
62
63 void*
64 mem_unmap(ptr_t mnt, struct llist_header* regions, void* addr, size_t length)
65 {
66     length = ROUNDUP(length, PG_SIZE);
67     ptr_t cur_addr = ROUNDDOWN((ptr_t)addr, PG_SIZE);
68     struct mm_region *pos, *n;
69
70     llist_for_each(pos, n, regions, head)
71     {
72         if (pos->start >= cur_addr) {
73             break;
74         }
75     }
76
77     while (&pos->head != regions && cur_addr > pos->start) {
78         u32_t l = pos->end - cur_addr;
79         pos->end = cur_addr;
80
81         if (l > length) {
82             // unmap cause discontinunity in a memory region -  do split
83             struct mm_region* region = valloc(sizeof(struct mm_region));
84             *region = *pos;
85             region->start = cur_addr + length;
86             llist_insert_after(&pos->head, &region->head);
87             l = length;
88         }
89
90         for (size_t i = 0; i < l; i += PG_SIZE) {
91             ptr_t pa = vmm_del_mapping(mnt, cur_addr + i);
92             if (pa) {
93                 pmm_free_page(__current->pid, pa);
94             }
95         }
96
97         n = container_of(pos->head.next, typeof(*pos), head);
98         if (pos->end == pos->start) {
99             llist_delete(&pos->head);
100             vfree(pos);
101         }
102
103         pos = n;
104         length -= l;
105         cur_addr += length;
106     }
107 }
108
109 __DEFINE_LXSYSCALL5(void*,
110                     mmap,
111                     void*,
112                     addr,
113                     size_t,
114                     length,
115                     int,
116                     proct,
117                     int,
118                     fd,
119                     size_t,
120                     offset)
121 {
122     int errno = 0;
123     struct v_fd* vfd;
124     if ((errno = vfs_getfd(fd, &vfd))) {
125         __current->k_status = errno;
126         return (void*)-1;
127     }
128
129     return mem_map(PD_REFERENCED,
130                    &__current->mm.regions,
131                    addr,
132                    vfd->file,
133                    offset,
134                    length,
135                    proct);
136 }
137
138 __DEFINE_LXSYSCALL2(void, munmap, void*, addr, size_t, length)
139 {
140     return mem_unmap(PD_REFERENCED, &__current->mm.regions, addr, length);
141 }