]>
Commit | Line | Data |
---|---|---|
2b778ceb | 1 | /* Copyright (C) 2020-2021 Free Software Foundation, Inc. |
c013d5d3 ST |
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 * | |
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) |