1 /* Copyright (C) 2020-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <sys/types.h>
19 #include <sys/mman.h>
20 #include <errno.h>
21 #include <stdarg.h>
22 #include <hurd.h>
23
24 #include <stdio.h>
25
26 /* Remap pages mapped by the range [ADDR,ADDR+OLD_LEN) to new length
27 NEW_LEN. If MREMAP_MAYMOVE is set in FLAGS the returned address
28 may differ from ADDR. If MREMAP_FIXED is set in FLAGS the function
29 takes another parameter which is a fixed address at which the block
30 resides after a successful call. */
31
32 void *
__mremap(void * addr,size_t old_len,size_t new_len,int flags,...)33 __mremap (void *addr, size_t old_len, size_t new_len, int flags, ...)
34 {
35 error_t err;
36 vm_address_t vm_addr = (vm_address_t) addr;
37 vm_offset_t new_vm_addr = 0;
38
39 vm_address_t begin = vm_addr;
40 vm_address_t end;
41 vm_size_t len;
42 vm_prot_t prot;
43 vm_prot_t max_prot;
44 vm_inherit_t inherit;
45 boolean_t shared;
46 memory_object_name_t obj;
47 vm_offset_t offset;
48
49 if ((flags & ~(MREMAP_MAYMOVE | MREMAP_FIXED)) ||
50 ((flags & MREMAP_FIXED) && !(flags & MREMAP_MAYMOVE)) ||
51 (old_len == 0 && !(flags & MREMAP_MAYMOVE)))
52 return (void *) (long int) __hurd_fail (EINVAL);
53
54 if (flags & MREMAP_FIXED)
55 {
56 va_list arg;
57 va_start (arg, flags);
58 new_vm_addr = (vm_offset_t) va_arg (arg, void *);
59 va_end (arg);
60 }
61
62 err = __vm_region (__mach_task_self (),
63 &begin, &len, &prot, &max_prot, &inherit,
64 &shared, &obj, &offset);
65 if (err)
66 return (void *) (uintptr_t) __hurd_fail (err);
67
68 if (begin > vm_addr)
69 {
70 err = EFAULT;
71 goto out;
72 }
73
74 if (begin < vm_addr || (old_len != 0 && old_len != len))
75 {
76 err = EINVAL;
77 goto out;
78 }
79
80 end = begin + len;
81
82 if ((flags & MREMAP_FIXED) &&
83 ((new_vm_addr + new_len > vm_addr && new_vm_addr < end)))
84 {
85 /* Overlapping is not supported, like in Linux. */
86 err = EINVAL;
87 goto out;
88 }
89
90 /* FIXME: locked memory. */
91
92 if (old_len != 0 && !(flags & MREMAP_FIXED))
93 {
94 /* A mere change of the existing map. */
95
96 if (new_len == len)
97 {
98 new_vm_addr = vm_addr;
99 goto out;
100 }
101
102 if (new_len < len)
103 {
104 /* Shrink. */
105 __mach_port_deallocate (__mach_task_self (), obj);
106 err = __vm_deallocate (__mach_task_self (),
107 begin + new_len, len - new_len);
108 new_vm_addr = vm_addr;
109 goto out;
110 }
111
112 /* Try to expand. */
113 err = __vm_map (__mach_task_self (),
114 &end, new_len - len, 0, 0,
115 obj, offset + len, 0, prot, max_prot, inherit);
116 if (!err)
117 {
118 /* Ok, that worked. Now coalesce them. */
119 new_vm_addr = vm_addr;
120
121 /* XXX this is not atomic as it is in unix! */
122 err = __vm_deallocate (__mach_task_self (), begin, new_len);
123 if (err)
124 {
125 __vm_deallocate (__mach_task_self (), end, new_len - len);
126 goto out;
127 }
128
129 err = __vm_map (__mach_task_self (),
130 &begin, new_len, 0, 0,
131 obj, offset, 0, prot, max_prot, inherit);
132 if (err)
133 {
134 /* Oops, try to remap before reporting. */
135 __vm_map (__mach_task_self (),
136 &begin, len, 0, 0,
137 obj, offset, 0, prot, max_prot, inherit);
138 }
139
140 goto out;
141 }
142 }
143
144 if (!(flags & MREMAP_MAYMOVE))
145 {
146 /* Can not map here */
147 err = ENOMEM;
148 goto out;
149 }
150
151 err = __vm_map (__mach_task_self (),
152 &new_vm_addr, new_len, 0,
153 new_vm_addr == 0, obj, offset,
154 old_len == 0, prot, max_prot, inherit);
155
156 if (err == KERN_NO_SPACE && (flags & MREMAP_FIXED))
157 {
158 /* XXX this is not atomic as it is in unix! */
159 /* The region is already allocated; deallocate it first. */
160 err = __vm_deallocate (__mach_task_self (), new_vm_addr, new_len);
161 if (! err)
162 err = __vm_map (__mach_task_self (),
163 &new_vm_addr, new_len, 0,
164 0, obj, offset,
165 old_len == 0, prot, max_prot, inherit);
166 }
167
168 if (!err)
169 /* Alright, can remove old mapping. */
170 __vm_deallocate (__mach_task_self (), begin, len);
171
172 out:
173 __mach_port_deallocate (__mach_task_self (), obj);
174 if (err)
175 return (void *) (uintptr_t) __hurd_fail (err);
176 return (void *) new_vm_addr;
177 }
178
179 libc_hidden_def (__mremap)
180 weak_alias (__mremap, mremap)
181