xref: /minix3/minix/lib/libsys/safecopies.c (revision 903ca1e525d31068eb447066c1f496b51cdcba31)
1 
2 /* Library functions to maintain internal data copying tables.
3  *
4  * April 21 2006: Initial version (Ben Gras)
5  *
6  */
7 
8 #include <lib.h>
9 #include <errno.h>
10 #include <minix/sysutil.h>
11 #include <assert.h>
12 #include <stdlib.h>
13 #include <minix/syslib.h>
14 #include <minix/safecopies.h>
15 #include <minix/com.h>
16 #include <string.h>
17 
18 #define ACCESS_CHECK(a) { 			\
19 	if((a) & ~(CPF_READ|CPF_WRITE|CPF_TRY)) {	\
20 		errno = EINVAL;			\
21 		return -1;			\
22 	}					\
23    }
24 
25 #define GID_CHECK(gid) {					\
26 	if(!GRANT_VALID(gid) || GRANT_IDX(gid) >= ngrants ||	\
27 	    GRANT_SEQ(gid) != grants[GRANT_IDX(gid)].cp_seq) {	\
28 		errno = EINVAL;					\
29 		return -1;					\
30 	}							\
31    }
32 
33 #define GID_CHECK_USED(gid) {					\
34 	GID_CHECK(gid);						\
35 	if(!(grants[GRANT_IDX(gid)].cp_flags & CPF_USED)) {	\
36 		errno = EINVAL;					\
37 		return -1;					\
38 	}							\
39    }
40 
41 #define NR_STATIC_GRANTS 3
42 static cp_grant_t static_grants[NR_STATIC_GRANTS];
43 static cp_grant_t *grants = NULL;
44 static int ngrants = 0;
45 static int freelist = -1;
46 
47 static void
48 cpf_grow(void)
49 {
50 /* Grow the grants table if possible. */
51 	cp_grant_t *new_grants;
52 	int g, new_size;
53 
54 	if(!ngrants) {
55 		/* Use statically allocated grants the first time. */
56 		new_size = NR_STATIC_GRANTS;
57 		new_grants = static_grants;
58 	}
59 	else {
60 		/* Double(ish) the size, up to the maximum number of slots. */
61 		if (ngrants >= GRANT_MAX_IDX)
62 			return;
63 		new_size = (1+ngrants)*2;
64 		if (new_size >= GRANT_MAX_IDX)
65 			new_size = GRANT_MAX_IDX;
66 		assert(new_size > ngrants);
67 
68 		/* Allocate a block of new size. */
69 		if(!(new_grants=malloc(new_size * sizeof(grants[0])))) {
70 			return;
71 		}
72 	}
73 
74 	/* Copy old block to new block. */
75 	if(grants && ngrants > 0)
76 		memcpy(new_grants, grants, ngrants * sizeof(grants[0]));
77 
78 	/*
79 	 * Make sure new slots are marked unused (CPF_USED is clear).
80 	 * Also start with a zero sequence number, for consistency; since the
81 	 * grant table is never shrunk, this introduces no issues by itself.
82 	 * Finally, form a new free list, in ascending order so that the lowest
83 	 * IDs get allocated first.  Both the zeroed sequence number and the
84 	 * ascending order are necessary so that the first grant to be
85 	 * allocated has a zero ID (see the live update comment below).
86 	 */
87 	for(g = ngrants; g < new_size; g++) {
88 		new_grants[g].cp_flags = 0;
89 		new_grants[g].cp_seq = 0;
90 		new_grants[g].cp_u.cp_free.cp_next =
91 		    (g < new_size - 1) ? (g + 1) : freelist;
92 	}
93 
94 	/* Inform kernel about new size (and possibly new location). */
95 	if((sys_setgrant(new_grants, new_size))) {
96                 if(new_grants != static_grants) free(new_grants);
97 		return;	/* Failed - don't grow then. */
98 	}
99 
100 	/* Update internal data. */
101 	if(grants && ngrants > 0 && grants != static_grants) free(grants);
102 	freelist = ngrants;
103 	grants = new_grants;
104 	ngrants = new_size;
105 }
106 
107 static int
108 cpf_new_grantslot(void)
109 {
110 /* Find a new, free grant slot in the grant table, grow it if
111  * necessary. If no free slot is found and the grow failed,
112  * return -1. Otherwise, return grant slot number.
113  */
114 	int g;
115 
116 	/* Obtain a free slot. */
117 	if ((g = freelist) == -1) {
118 		/* Table full - try to make the table larger. */
119 		cpf_grow();
120 		if ((g = freelist) == -1) {
121 			/* ngrants hasn't increased. */
122 			errno = ENOSPC;
123 			return -1;
124 		}
125 	}
126 
127 	/* Basic sanity checks - if we get this far, g must be a valid,
128 	 * free slot.
129 	 */
130 	assert(g >= 0);
131 	assert(g < ngrants);
132 	assert(!(grants[g].cp_flags & CPF_USED));
133 
134 	/* Take the slot off the free list, and return its slot number. */
135 	freelist = grants[g].cp_u.cp_free.cp_next;
136 
137 	return g;
138 }
139 
140 cp_grant_id_t
141 cpf_grant_direct(endpoint_t who_to, vir_bytes addr, size_t bytes, int access)
142 {
143 	int g;
144 
145 	ACCESS_CHECK(access);
146 
147 	/* Get new slot to put new grant in. */
148 	if((g = cpf_new_grantslot()) < 0)
149 		return -1;
150 
151 	/* Fill in new slot data. */
152 	grants[g].cp_u.cp_direct.cp_who_to = who_to;
153 	grants[g].cp_u.cp_direct.cp_start = addr;
154 	grants[g].cp_u.cp_direct.cp_len = bytes;
155 	grants[g].cp_faulted = GRANT_INVALID;
156 	__insn_barrier();
157 	grants[g].cp_flags = access | CPF_DIRECT | CPF_USED | CPF_VALID;
158 
159 	return GRANT_ID(g, grants[g].cp_seq);
160 }
161 
162 cp_grant_id_t
163 cpf_grant_indirect(endpoint_t who_to, endpoint_t who_from, cp_grant_id_t gr)
164 {
165 /* Grant process A access into process B. B has granted us access as grant
166  * id 'gr'.
167  */
168 	int g;
169 
170 	/* Obtain new slot. */
171 	if((g = cpf_new_grantslot()) < 0)
172 		return -1;
173 
174 	/* Fill in new slot data. */
175 	grants[g].cp_u.cp_indirect.cp_who_to = who_to;
176 	grants[g].cp_u.cp_indirect.cp_who_from = who_from;
177 	grants[g].cp_u.cp_indirect.cp_grant = gr;
178 	grants[g].cp_faulted = GRANT_INVALID;
179 	__insn_barrier();
180 	grants[g].cp_flags = CPF_USED | CPF_INDIRECT | CPF_VALID;
181 
182 	return GRANT_ID(g, grants[g].cp_seq);
183 }
184 
185 cp_grant_id_t
186 cpf_grant_magic(endpoint_t who_to, endpoint_t who_from,
187 	vir_bytes addr, size_t bytes, int access)
188 {
189 /* Grant process A access into process B. Not everyone can do this. */
190 	int g;
191 
192 	ACCESS_CHECK(access);
193 
194 	/* Obtain new slot. */
195 	if((g = cpf_new_grantslot()) < 0)
196 		return -1;
197 
198 	/* Fill in new slot data. */
199 	grants[g].cp_u.cp_magic.cp_who_to = who_to;
200 	grants[g].cp_u.cp_magic.cp_who_from = who_from;
201 	grants[g].cp_u.cp_magic.cp_start = addr;
202 	grants[g].cp_u.cp_magic.cp_len = bytes;
203 	grants[g].cp_faulted = GRANT_INVALID;
204 	__insn_barrier();
205 	grants[g].cp_flags = CPF_USED | CPF_MAGIC | CPF_VALID | access;
206 
207 	return GRANT_ID(g, grants[g].cp_seq);
208 }
209 
210 /*
211  * Revoke previously granted access, identified by grant ID.  Return -1 on
212  * error, with errno set as appropriate.  Return 0 on success, with one
213  * exception: return GRANT_FAULTED (1) if a grant was created with CPF_TRY and
214  * during its lifetime, a copy from or to the grant experienced a soft fault.
215  */
216 int
217 cpf_revoke(cp_grant_id_t grant)
218 {
219 	int r, g;
220 
221 	GID_CHECK_USED(grant);
222 
223 	g = GRANT_IDX(grant);
224 
225 	/*
226 	 * If a safecopy action on a (direct or magic) grant with the CPF_TRY
227 	 * flag failed on a soft fault, the kernel will have set the cp_faulted
228 	 * field to the grant identifier.  Here, we test this and return
229 	 * GRANT_FAULTED (1) on a match.
230 	 */
231 	r = ((grants[g].cp_flags & CPF_TRY) &&
232 	    grants[g].cp_faulted == grant) ? GRANT_FAULTED : 0;
233 
234 	/*
235 	 * Make grant invalid by setting flags to 0, clearing CPF_USED.
236 	 * This invalidates the grant.
237 	 */
238 	grants[g].cp_flags = 0;
239 	__insn_barrier();
240 
241 	/*
242 	 * Increase the grant slot's sequence number now, rather than on
243 	 * allocation, because live update relies on the first allocated grant
244 	 * having a zero ID (SEF_STATE_TRANSFER_GID) and thus a zero sequence
245 	 * number.
246 	 */
247 	if (grants[g].cp_seq < GRANT_MAX_SEQ - 1)
248 		grants[g].cp_seq++;
249 	else
250 		grants[g].cp_seq = 0;
251 
252 	/*
253 	 * Put the grant back on the free list.  The list is single-headed, so
254 	 * the last freed grant will be the first to be reused.  Especially
255 	 * given the presence of sequence numbers, this is not a problem.
256 	 */
257 	grants[g].cp_u.cp_free.cp_next = freelist;
258 	freelist = g;
259 
260 	return r;
261 }
262 
263 /*
264  * START OF DEPRECATED API
265  *
266  * The grant preallocation and (re)assignment API below imposes that grant IDs
267  * stay the same across reuse, thus disallowing that the grants' sequence
268  * numbers be updated as a part of reassignment.  As a result, this API does
269  * not offer the same protection against accidental reuse of an old grant by a
270  * remote party as the regular API does, and is therefore deprecated.
271  */
272 int
273 cpf_getgrants(cp_grant_id_t *grant_ids, int n)
274 {
275 	int i;
276 
277 	for(i = 0; i < n; i++) {
278 	  if((grant_ids[i] = cpf_new_grantslot()) < 0)
279 		break;
280 	  grants[grant_ids[i]].cp_flags = CPF_USED;
281 	  grants[grant_ids[i]].cp_seq = 0;
282 	}
283 
284 	/* return however many grants were assigned. */
285 	return i;
286 }
287 
288 int
289 cpf_setgrant_direct(gid, who, addr, bytes, access)
290 cp_grant_id_t gid;
291 endpoint_t who;
292 vir_bytes addr;
293 size_t bytes;
294 int access;
295 {
296 	GID_CHECK(gid);
297 	ACCESS_CHECK(access);
298 
299 	/* Fill in new slot data. */
300 	grants[gid].cp_flags = access | CPF_DIRECT | CPF_USED | CPF_VALID;
301 	grants[gid].cp_u.cp_direct.cp_who_to = who;
302 	grants[gid].cp_u.cp_direct.cp_start = addr;
303 	grants[gid].cp_u.cp_direct.cp_len = bytes;
304 
305 	return 0;
306 }
307 
308 int
309 cpf_setgrant_indirect(gid, who_to, who_from, his_gid)
310 cp_grant_id_t gid;
311 endpoint_t who_to, who_from;
312 cp_grant_id_t his_gid;
313 {
314 	GID_CHECK(gid);
315 
316 	/* Fill in new slot data. */
317 	grants[gid].cp_flags = CPF_USED | CPF_INDIRECT | CPF_VALID;
318 	grants[gid].cp_u.cp_indirect.cp_who_to = who_to;
319 	grants[gid].cp_u.cp_indirect.cp_who_from = who_from;
320 	grants[gid].cp_u.cp_indirect.cp_grant = his_gid;
321 
322 	return 0;
323 }
324 
325 int
326 cpf_setgrant_magic(gid, who_to, who_from, addr, bytes, access)
327 cp_grant_id_t gid;
328 endpoint_t who_to, who_from;
329 vir_bytes addr;
330 size_t bytes;
331 int access;
332 {
333 	GID_CHECK(gid);
334 	ACCESS_CHECK(access);
335 
336 	/* Fill in new slot data. */
337 	grants[gid].cp_flags = CPF_USED | CPF_MAGIC | CPF_VALID | access;
338 	grants[gid].cp_u.cp_magic.cp_who_to = who_to;
339 	grants[gid].cp_u.cp_magic.cp_who_from = who_from;
340 	grants[gid].cp_u.cp_magic.cp_start = addr;
341 	grants[gid].cp_u.cp_magic.cp_len = bytes;
342 
343 	return 0;
344 }
345 
346 int
347 cpf_setgrant_disable(gid)
348 cp_grant_id_t gid;
349 {
350 	GID_CHECK(gid);
351 
352 	/* Grant is now no longer valid, but still in use. */
353 	grants[gid].cp_flags = CPF_USED;
354 
355 	return 0;
356 }
357 /*
358  * END OF DEPRECATED API
359  */
360 
361 void
362 cpf_reload(void)
363 {
364 /* Inform the kernel about the location of the grant table. This is needed
365  * after a fork.
366  */
367 	if (grants)
368 		sys_setgrant(grants, ngrants);	/* Do we need error checking? */
369 }
370