chunk.c (6053B)
1 #define _XOPEN_SOURCE 500 2 #define _GNU_SOURCE // for MAP_32BIT 3 4 #include <stdio.h> 5 #include <unistd.h> 6 #include <fcntl.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/mman.h> 10 #include <errno.h> 11 #include <assert.h> 12 13 #include "vx32.h" 14 #include "vx32impl.h" 15 16 extern int vx_elfbigmem; 17 18 typedef struct vxmem_chunk vxmem_chunk; 19 20 struct vxmem_chunk { 21 vxmem mem; 22 int fd; 23 off_t size; 24 uint8_t *perm; 25 }; 26 27 static void chunk_free(vxmem *mem) 28 { 29 vxmem_chunk *chunk = (vxmem_chunk*)mem; 30 31 if(mem->mapped){ 32 vxmem_unmap(mem, mem->mapped); 33 mem->mapped = NULL; 34 } 35 36 free(chunk->perm); 37 close(chunk->fd); 38 free(chunk); 39 } 40 41 static int chunk_resize(vxmem *mem, size_t size) 42 { 43 vxmem_chunk *chunk = (vxmem_chunk*)mem; 44 uint8_t *perm; 45 uint32_t onpage, npage; 46 47 if(size == chunk->size) 48 return 0; 49 50 if(mem->mapped){ 51 assert(!vx_elfbigmem); 52 vxmem_unmap(mem, mem->mapped); 53 mem->mapped = NULL; 54 } 55 56 if(ftruncate(chunk->fd, size) < 0) 57 return -1; 58 onpage = VXPAGEROUND(chunk->size) / VXPAGESIZE; 59 npage = VXPAGEROUND(size) / VXPAGESIZE; 60 perm = realloc(chunk->perm, npage); 61 if (perm == NULL) 62 return -1; 63 if(npage > onpage) 64 memset(perm + onpage, 0, npage-onpage); 65 chunk->perm = perm; 66 chunk->size = size; 67 return 0; 68 } 69 70 vxmem *vxmem_chunk_copy(vxmem *mem) 71 { 72 vxmem_chunk *chunk = (vxmem_chunk*)mem; 73 uint8_t *perm; 74 uint32_t i, npage; 75 76 assert(mem->free == chunk_free); 77 vxmem *nmem = vxmem_chunk_new(chunk->size); 78 if (nmem == NULL) 79 return NULL; 80 npage = VXPAGEROUND(chunk->size) / VXPAGESIZE; 81 int n = 0; 82 vxmem_chunk *nchunk = (vxmem_chunk*)nmem; 83 char *buf = malloc(4096); 84 memmove(nchunk->perm, chunk->perm, npage); 85 for (i=0; i<npage; i++) { 86 if (nchunk->perm[i]) { 87 vxmem_read(mem, buf, i*VXPAGESIZE, VXPAGESIZE); 88 vxmem_write(nmem, buf, i*VXPAGESIZE, VXPAGESIZE); 89 n++; 90 } 91 } 92 free(buf); 93 return nmem; 94 } 95 96 static ssize_t chunk_read(vxmem *mem, void *data, uint32_t addr, uint32_t len) 97 { 98 vxmem_chunk *chunk = (vxmem_chunk*)mem; 99 100 if (addr >= chunk->size) 101 return -1; 102 if (len > chunk->size - addr) 103 len = chunk->size - addr; 104 return pread(chunk->fd, data, len, addr); 105 } 106 107 static ssize_t chunk_write(vxmem *mem, const void *data, uint32_t addr, uint32_t len) 108 { 109 vxmem_chunk *chunk = (vxmem_chunk*)mem; 110 111 if (addr >= chunk->size) 112 return -1; 113 if (len > chunk->size - addr) 114 len = chunk->size - addr; 115 return pwrite(chunk->fd, data, len, addr); 116 } 117 118 static int chunk_checkperm(vxmem *mem, uint32_t addr, uint32_t len, uint32_t perm, uint32_t *out_faultva) 119 { 120 uint32_t pn, pe, cpe, va; 121 vxmem_chunk *chunk = (vxmem_chunk*)mem; 122 123 if (addr + len < addr) 124 return 0; 125 if (len == 0) 126 return 1; 127 pn = addr / VXPAGESIZE; 128 pe = VXPAGEROUND(addr + len-1) / VXPAGESIZE; 129 cpe = VXPAGEROUND(chunk->size) / VXPAGESIZE; 130 for (; pn < pe; pn++) { 131 if (pn >= cpe) { 132 if (out_faultva) 133 *out_faultva = chunk->size; 134 return 0; 135 } 136 if ((chunk->perm[pn] & perm) != perm) { 137 if (out_faultva) { 138 va = pn * VXPAGESIZE; 139 if (va < addr) 140 va = addr; 141 *out_faultva = va; 142 } 143 return 0; 144 } 145 } 146 return 1; 147 } 148 149 static int mmperm(int perm) 150 { 151 int m; 152 153 if (perm == 0) 154 m = PROT_NONE; 155 else { 156 m = 0; 157 if (perm & VXPERM_READ) 158 m |= PROT_READ; 159 if (perm & VXPERM_WRITE) 160 m |= PROT_WRITE; 161 if (perm & VXPERM_EXEC) 162 m |= PROT_EXEC; 163 } 164 return m; 165 } 166 167 static int chunk_setperm(vxmem *mem, uint32_t addr, uint32_t len, uint32_t perm) 168 { 169 vxmem_chunk *chunk = (vxmem_chunk*)mem; 170 uint32_t a; 171 172 if (addr + len < addr || addr + len > chunk->size) { 173 errno = EINVAL; 174 return -1; 175 } 176 177 if (mem->mapped) { 178 if (mprotect(mem->mapped->base + addr, len, mmperm(perm)) < 0) 179 return -1; 180 } 181 182 for (a = addr; a < addr + len; a += VXPAGESIZE) 183 chunk->perm[a/VXPAGESIZE] = perm; 184 return 0; 185 } 186 187 static vxmmap *chunk_map(vxmem *mem, uint32_t flags) 188 { 189 vxmmap *mm; 190 void *v; 191 vxmem_chunk *chunk = (vxmem_chunk*)mem; 192 193 if (mem->mapped) { 194 mem->mapped->ref++; // XXX get rid of ref? 195 return mem->mapped; 196 } 197 198 mm = malloc(sizeof *mm); 199 if (mm == NULL) 200 return NULL; 201 if ((v = mmap(0, chunk->size, PROT_NONE, MAP_32BIT | (vx_elfbigmem ? MAP_PRIVATE : MAP_SHARED), chunk->fd, 0)) == (void*)-1) { 202 free(mm); 203 return NULL; 204 } 205 // vxprint("chunk_map %p size %08x\n", v, chunk->size); 206 207 // Now set the permissions on all pages that should be accessible. 208 unsigned npages = chunk->size / VXPAGESIZE; 209 for (unsigned i = 0; i < npages; ) { 210 uint8_t pageperm = chunk->perm[i]; 211 212 // For efficiency, merge mprotect calls on page ranges. 213 unsigned j; 214 for (j = i+1; j < npages; j++) 215 if (chunk->perm[j] != pageperm) 216 break; 217 218 if (pageperm == 0) { 219 i = j; // nothing to do for this range 220 continue; 221 } 222 223 // Calculate the effective permission to map with in the host. 224 int prot = mmperm(pageperm); 225 assert(prot >= 0); 226 227 // Set the permissions on this range. 228 if (mprotect((char*)v + i*VXPAGESIZE, (j-i)*VXPAGESIZE, prot) < 0) { 229 munmap(v, chunk->size); 230 free(mm); 231 return NULL; 232 } 233 234 i = j; 235 } 236 237 mm->base = v; 238 mm->size = chunk->size; 239 mm->ref = 2; // XXX get rid of ref? 240 mem->mapped = mm; 241 return mm; 242 } 243 244 static void chunk_unmap(vxmem *mem, vxmmap *mm) 245 { 246 vxmem_chunk *chunk = (vxmem_chunk*)mem; 247 248 if(mm == NULL) 249 return; 250 // if(--mm->ref > 0) // XXX get rid of ref? 251 // return; 252 if(mm == mem->mapped) 253 mem->mapped = NULL; 254 // vxprint("chunk_unmap %p size %08x\n", mm->base, mm->size); 255 munmap(mm->base, mm->size); 256 free(mm); 257 } 258 259 static vxmem chunk_proto = 260 { 261 chunk_read, 262 chunk_write, 263 chunk_map, 264 chunk_unmap, 265 chunk_checkperm, 266 chunk_setperm, 267 chunk_resize, 268 chunk_free, 269 }; 270 271 static vxmem *vxmem_chunk_fromfd(int fd, off_t size) 272 { 273 vxmem_chunk *chunk; 274 275 chunk = calloc(sizeof *chunk, 1); 276 if (chunk == NULL) 277 return NULL; 278 chunk->mem = chunk_proto; 279 chunk->fd = fd; 280 if (chunk_resize((vxmem*)chunk, size) < 0) { 281 free(chunk); 282 return NULL; 283 } 284 return (vxmem*)chunk; 285 } 286 287 vxmem *vxmem_chunk_new(int size) 288 { 289 int fd; 290 char tmpfn[] = "/var/tmp/vxXXXXXX"; 291 vxmem *mem; 292 293 if ((fd = mkstemp(tmpfn)) < 0) 294 return NULL; 295 unlink(tmpfn); 296 if ((mem = vxmem_chunk_fromfd(fd, size)) == NULL) { 297 close(fd); 298 return NULL; 299 } 300 return mem; 301 } 302