refactor: separate syscall interfaces from kernel space, into posix compliant structure.
[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/valloc.h>
4 #include <lunaix/mm/vmm.h>
5 #include <lunaix/spike.h>
6
7 #include <lunaix/syscall.h>
8
9 void*
10 mem_map(ptr_t mnt,
11         vm_regions_t* regions,
12         void* addr,
13         struct v_file* file,
14         off_t offset,
15         size_t length,
16         u32_t proct,
17         u32_t options)
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 =
48       region_create(addr, end, proct | (options & 0x1f));
49     region->mfile = file;
50     region->offset = offset;
51
52     region_add(regions, region);
53
54     u32_t attr = PG_ALLOW_USER;
55     if ((proct & REGION_WRITE)) {
56         attr |= PG_WRITE;
57     }
58
59     for (u32_t i = 0; i < length; i += PG_SIZE) {
60         vmm_set_mapping(mnt, addr + i, 0, attr, 0);
61     }
62
63     return addr;
64 }
65
66 void*
67 mem_unmap(ptr_t mnt, vm_regions_t* regions, void* addr, size_t length)
68 {
69     length = ROUNDUP(length, PG_SIZE);
70     ptr_t cur_addr = ROUNDDOWN((ptr_t)addr, PG_SIZE);
71     struct mm_region *pos, *n;
72
73     llist_for_each(pos, n, regions, head)
74     {
75         if (pos->start <= cur_addr) {
76             break;
77         }
78     }
79
80     while (&pos->head != regions && cur_addr > pos->start) {
81         u32_t l = pos->end - cur_addr;
82         pos->end = cur_addr;
83
84         if (l > length) {
85             // unmap cause discontinunity in a memory region -  do split
86             struct mm_region* region = valloc(sizeof(struct mm_region));
87             *region = *pos;
88             region->start = cur_addr + length;
89             llist_insert_after(&pos->head, &region->head);
90             l = length;
91         }
92
93         // TODO for shared mappings, sync page content if modified. (also
94         // implement msync)
95
96         for (size_t i = 0; i < l; i += PG_SIZE) {
97             ptr_t pa = vmm_del_mapping(mnt, cur_addr + i);
98             if (pa) {
99                 pmm_free_page(__current->pid, pa);
100             }
101         }
102
103         n = container_of(pos->head.next, typeof(*pos), head);
104         if (pos->end == pos->start) {
105             llist_delete(&pos->head);
106             vfree(pos);
107         }
108
109         pos = n;
110         length -= l;
111         cur_addr += length;
112     }
113 }
114
115 __DEFINE_LXSYSCALL3(void*, sys_mmap, void*, addr, size_t, length, va_list, lst)
116 {
117     int proct = va_arg(lst, int);
118     int fd = va_arg(lst, u32_t);
119     off_t offset = va_arg(lst, off_t);
120     int options = va_arg(lst, int);
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     length = ROUNDUP(length, PG_SIZE);
130
131     return mem_map(PD_REFERENCED,
132                    &__current->mm.regions,
133                    addr,
134                    vfd->file,
135                    offset,
136                    length,
137                    proct,
138                    options);
139 }
140
141 __DEFINE_LXSYSCALL2(void, munmap, void*, addr, size_t, length)
142 {
143     return mem_unmap(PD_REFERENCED, &__current->mm.regions, addr, length);
144 }