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