image.c (14342B)
1 #include <string.h> 2 3 #include "emu/emu.h" 4 5 static u64 emu_round_down(u64 v, u64 a) { return v & ~(a - 1u); } 6 static u64 emu_round_up(u64 v, u64 a) { return (v + a - 1u) & ~(a - 1u); } 7 8 static u64 map_pages(const EmuAddrSpace* as, u64 start, u64 end) { 9 return (end - start) / as->page_size; 10 } 11 12 static void fault_set(EmuAddrSpace* as, EmuFaultKind kind, u64 addr, 13 u8 access) { 14 if (!as) return; 15 as->last_fault.kind = kind; 16 as->last_fault.addr = addr; 17 as->last_fault.access = access; 18 } 19 20 static void map_free(EmuAddrSpace* as, EmuMap* m) { 21 u64 npages; 22 if (!as || !m) return; 23 npages = map_pages(as, m->start, m->end); 24 if (m->bytes) as->heap->free(as->heap, m->bytes, (size_t)(m->end - m->start)); 25 if (m->dirty_pages) as->heap->free(as->heap, m->dirty_pages, (size_t)npages); 26 if (m->translated_pages) 27 as->heap->free(as->heap, m->translated_pages, (size_t)npages); 28 memset(m, 0, sizeof(*m)); 29 } 30 31 static KitStatus map_alloc_storage(EmuAddrSpace* as, EmuMap* m) { 32 u64 nbytes, npages; 33 nbytes = m->end - m->start; 34 npages = map_pages(as, m->start, m->end); 35 m->bytes = (u8*)as->heap->alloc(as->heap, (size_t)nbytes, 16u); 36 m->dirty_pages = (u8*)as->heap->alloc(as->heap, (size_t)npages, 1u); 37 m->translated_pages = (u8*)as->heap->alloc(as->heap, (size_t)npages, 1u); 38 if (!m->bytes || !m->dirty_pages || !m->translated_pages) { 39 map_free(as, m); 40 return KIT_NOMEM; 41 } 42 memset(m->bytes, 0, (size_t)nbytes); 43 memset(m->dirty_pages, 0, (size_t)npages); 44 memset(m->translated_pages, 0, (size_t)npages); 45 return KIT_OK; 46 } 47 48 static int map_overlaps(const EmuMap* m, u64 start, u64 end) { 49 return start < m->end && end > m->start; 50 } 51 52 static EmuMap* find_map(EmuAddrSpace* as, u64 va) { 53 u32 i; 54 if (!as) return NULL; 55 for (i = 0; i < as->nmaps; ++i) { 56 if (va >= as->maps[i].start && va < as->maps[i].end) return &as->maps[i]; 57 if (va < as->maps[i].start) return NULL; 58 } 59 return NULL; 60 } 61 62 static int range_is_free(EmuAddrSpace* as, u64 start, u64 end) { 63 u32 i; 64 for (i = 0; i < as->nmaps; ++i) { 65 if (map_overlaps(&as->maps[i], start, end)) return 0; 66 if (end <= as->maps[i].start) return 1; 67 } 68 return 1; 69 } 70 71 static KitStatus ensure_map_cap(EmuAddrSpace* as, u32 need) { 72 EmuMap* grown; 73 u32 old_cap, new_cap; 74 if (need <= as->maps_cap) return KIT_OK; 75 old_cap = as->maps_cap; 76 new_cap = old_cap ? old_cap * 2u : 8u; 77 while (new_cap < need) new_cap *= 2u; 78 grown = (EmuMap*)as->heap->realloc( 79 as->heap, as->maps, sizeof(*as->maps) * old_cap, 80 sizeof(*as->maps) * new_cap, _Alignof(EmuMap)); 81 if (!grown) return KIT_NOMEM; 82 as->maps = grown; 83 as->maps_cap = new_cap; 84 return KIT_OK; 85 } 86 87 static KitStatus insert_map(EmuAddrSpace* as, EmuMap map) { 88 u32 idx; 89 KitStatus st; 90 st = ensure_map_cap(as, as->nmaps + 1u); 91 if (st != KIT_OK) return st; 92 idx = 0; 93 while (idx < as->nmaps && as->maps[idx].start < map.start) ++idx; 94 if (idx < as->nmaps) { 95 memmove(&as->maps[idx + 1u], &as->maps[idx], 96 sizeof(*as->maps) * (as->nmaps - idx)); 97 } 98 as->maps[idx] = map; 99 ++as->nmaps; 100 return KIT_OK; 101 } 102 103 static KitStatus append_piece(EmuAddrSpace* as, EmuMap* src, u64 start, u64 end, 104 u8 perms) { 105 EmuMap dst; 106 KitStatus st; 107 u64 src_off, npages, page_off; 108 memset(&dst, 0, sizeof(dst)); 109 dst.start = start; 110 dst.end = end; 111 dst.perms = perms; 112 dst.kind = src->kind; 113 dst.flags = src->flags; 114 dst.generation = ++as->generation; 115 st = map_alloc_storage(as, &dst); 116 if (st != KIT_OK) return st; 117 src_off = start - src->start; 118 memcpy(dst.bytes, src->bytes + src_off, (size_t)(end - start)); 119 npages = map_pages(as, start, end); 120 page_off = src_off / as->page_size; 121 memcpy(dst.dirty_pages, src->dirty_pages + page_off, (size_t)npages); 122 memcpy(dst.translated_pages, src->translated_pages + page_off, 123 (size_t)npages); 124 st = insert_map(as, dst); 125 if (st != KIT_OK) map_free(as, &dst); 126 return st; 127 } 128 129 static int range_is_mapped(EmuAddrSpace* as, u64 start, u64 end) { 130 u64 cur = start; 131 EmuMap* m; 132 while (cur < end) { 133 m = find_map(as, cur); 134 if (!m) return 0; 135 cur = m->end < end ? m->end : end; 136 } 137 return 1; 138 } 139 140 KitStatus emu_addr_space_init(EmuAddrSpace* as, Compiler* c, u64 page_size) { 141 if (!as || !c || !page_size || (page_size & (page_size - 1u)) != 0) 142 return KIT_INVALID; 143 memset(as, 0, sizeof(*as)); 144 as->compiler = c; 145 as->heap = c->ctx->heap; 146 as->page_size = page_size; 147 return KIT_OK; 148 } 149 150 void emu_addr_space_destroy(EmuAddrSpace* as) { 151 u32 i; 152 if (!as || !as->heap) return; 153 for (i = 0; i < as->nmaps; ++i) map_free(as, &as->maps[i]); 154 if (as->maps) 155 as->heap->free(as->heap, as->maps, sizeof(*as->maps) * as->maps_cap); 156 memset(as, 0, sizeof(*as)); 157 } 158 159 KitStatus emu_addr_space_map(EmuAddrSpace* as, u64 va, u64 nbytes, u8 perms, 160 EmuMapKind kind) { 161 EmuMap m; 162 KitStatus st; 163 if (!as || !as->heap || !nbytes) return KIT_INVALID; 164 if ((va & (as->page_size - 1u)) != 0 || (nbytes & (as->page_size - 1u)) != 0) 165 return KIT_INVALID; 166 if (va + nbytes < va) return KIT_INVALID; 167 if (!range_is_free(as, va, va + nbytes)) return KIT_INVALID; 168 memset(&m, 0, sizeof(m)); 169 m.start = va; 170 m.end = va + nbytes; 171 m.perms = perms; 172 m.kind = kind; 173 m.generation = ++as->generation; 174 st = map_alloc_storage(as, &m); 175 if (st != KIT_OK) return st; 176 st = insert_map(as, m); 177 if (st != KIT_OK) map_free(as, &m); 178 return st; 179 } 180 181 KitStatus emu_addr_space_unmap(EmuAddrSpace* as, u64 va, u64 nbytes) { 182 u64 start, end; 183 u32 i; 184 if (!as || !as->heap || !nbytes) return KIT_INVALID; 185 start = emu_round_down(va, as->page_size); 186 end = emu_round_up(va + nbytes, as->page_size); 187 if (end <= start) return KIT_INVALID; 188 i = 0; 189 while (i < as->nmaps) { 190 EmuMap old = as->maps[i]; 191 u64 old_end = old.end; 192 if (!map_overlaps(&old, start, end)) { 193 ++i; 194 continue; 195 } 196 memmove(&as->maps[i], &as->maps[i + 1u], 197 sizeof(*as->maps) * (as->nmaps - i - 1u)); 198 --as->nmaps; 199 if (old.start < start) { 200 KitStatus st = append_piece(as, &old, old.start, start, old.perms); 201 if (st != KIT_OK) { 202 map_free(as, &old); 203 return st; 204 } 205 } 206 if (end < old.end) { 207 KitStatus st = append_piece(as, &old, end, old.end, old.perms); 208 if (st != KIT_OK) { 209 map_free(as, &old); 210 return st; 211 } 212 } 213 map_free(as, &old); 214 while (i < as->nmaps && as->maps[i].start < old_end) ++i; 215 } 216 return KIT_OK; 217 } 218 219 KitStatus emu_addr_space_protect(EmuAddrSpace* as, u64 va, u64 nbytes, 220 u8 perms) { 221 u64 start, end; 222 u32 i; 223 if (!as || !as->heap || !nbytes) return KIT_INVALID; 224 start = emu_round_down(va, as->page_size); 225 end = emu_round_up(va + nbytes, as->page_size); 226 if (end <= start || !range_is_mapped(as, start, end)) return KIT_INVALID; 227 i = 0; 228 while (i < as->nmaps) { 229 EmuMap old = as->maps[i]; 230 u64 old_end = old.end; 231 if (!map_overlaps(&old, start, end)) { 232 ++i; 233 continue; 234 } 235 memmove(&as->maps[i], &as->maps[i + 1u], 236 sizeof(*as->maps) * (as->nmaps - i - 1u)); 237 --as->nmaps; 238 if (old.start < start) { 239 KitStatus st = append_piece(as, &old, old.start, start, old.perms); 240 if (st != KIT_OK) { 241 map_free(as, &old); 242 return st; 243 } 244 } 245 { 246 u64 mid_start = old.start > start ? old.start : start; 247 u64 mid_end = old.end < end ? old.end : end; 248 KitStatus st = append_piece(as, &old, mid_start, mid_end, perms); 249 if (st != KIT_OK) { 250 map_free(as, &old); 251 return st; 252 } 253 } 254 if (end < old.end) { 255 KitStatus st = append_piece(as, &old, end, old.end, old.perms); 256 if (st != KIT_OK) { 257 map_free(as, &old); 258 return st; 259 } 260 } 261 map_free(as, &old); 262 while (i < as->nmaps && as->maps[i].start < old_end) ++i; 263 } 264 return KIT_OK; 265 } 266 267 KitStatus emu_addr_space_find_gap(EmuAddrSpace* as, u64 nbytes, u64 align, 268 u64 min_va, u64 max_va, u64* out) { 269 u64 cur; 270 u32 i; 271 if (!as || !out || !nbytes) return KIT_INVALID; 272 if (!align) align = as->page_size; 273 nbytes = emu_round_up(nbytes, as->page_size); 274 cur = emu_round_up(min_va, align); 275 for (i = 0; i < as->nmaps; ++i) { 276 if (cur + nbytes < cur || cur + nbytes > max_va) return KIT_NOMEM; 277 if (cur + nbytes <= as->maps[i].start) { 278 *out = cur; 279 return KIT_OK; 280 } 281 if (cur < as->maps[i].end) cur = emu_round_up(as->maps[i].end, align); 282 } 283 if (cur + nbytes < cur || cur + nbytes > max_va) return KIT_NOMEM; 284 *out = cur; 285 return KIT_OK; 286 } 287 288 KitStatus emu_addr_space_set_brk(EmuAddrSpace* as, u64 requested, 289 u64* actual_out) { 290 u64 old_page, new_page; 291 KitStatus st; 292 if (!as) return KIT_INVALID; 293 if (requested == 0) { 294 if (actual_out) *actual_out = as->brk_cur; 295 return KIT_OK; 296 } 297 if (requested < as->brk_base || requested > as->brk_max) { 298 if (actual_out) *actual_out = as->brk_cur; 299 return KIT_OK; 300 } 301 old_page = emu_round_up(as->brk_cur, as->page_size); 302 new_page = emu_round_up(requested, as->page_size); 303 if (new_page > old_page) { 304 st = emu_addr_space_map(as, old_page, new_page - old_page, 305 EMU_MEM_READ | EMU_MEM_WRITE, EMU_MAP_ANON); 306 if (st != KIT_OK) { 307 if (actual_out) *actual_out = as->brk_cur; 308 return KIT_OK; 309 } 310 } else if (new_page < old_page) { 311 st = emu_addr_space_unmap(as, new_page, old_page - new_page); 312 if (st != KIT_OK) { 313 if (actual_out) *actual_out = as->brk_cur; 314 return KIT_OK; 315 } 316 } 317 as->brk_cur = requested; 318 if (actual_out) *actual_out = as->brk_cur; 319 return KIT_OK; 320 } 321 322 KitStatus emu_addr_space_copy_in(EmuAddrSpace* as, u64 va, const void* src, 323 u64 nbytes) { 324 u8* p; 325 if (!nbytes) return KIT_OK; 326 if (!src) return KIT_INVALID; 327 p = emu_addr_space_ptr(as, va, nbytes, 0); 328 if (!p) return KIT_INVALID; 329 memcpy(p, src, (size_t)nbytes); 330 return KIT_OK; 331 } 332 333 KitStatus emu_addr_space_set_perm(EmuAddrSpace* as, u64 va, u64 nbytes, 334 u8 perms) { 335 return emu_addr_space_protect(as, va, nbytes, perms); 336 } 337 338 u8* emu_addr_space_ptr(EmuAddrSpace* as, u64 va, u64 nbytes, u8 need_perms) { 339 EmuMap* m; 340 u64 off, start_page, end_page, i; 341 if (!as || !as->maps) return NULL; 342 m = find_map(as, va); 343 if (!m) { 344 fault_set(as, EMU_FAULT_UNMAPPED, va, need_perms); 345 return NULL; 346 } 347 if (va + nbytes < va || va + nbytes > m->end) { 348 fault_set(as, EMU_FAULT_UNMAPPED, va + nbytes, need_perms); 349 return NULL; 350 } 351 if (need_perms && (m->perms & need_perms) != need_perms) { 352 fault_set(as, EMU_FAULT_PROT, va, need_perms); 353 return NULL; 354 } 355 off = va - m->start; 356 if ((need_perms & EMU_MEM_WRITE) && nbytes) { 357 start_page = off / as->page_size; 358 end_page = (off + nbytes + as->page_size - 1u) / as->page_size; 359 for (i = start_page; i < end_page; ++i) { 360 m->dirty_pages[i] = 1u; 361 if (m->translated_pages[i]) { 362 m->translated_pages[i] = 0; 363 ++m->generation; 364 ++as->generation; 365 } 366 } 367 } 368 fault_set(as, EMU_FAULT_NONE, 0, 0); 369 return m->bytes + off; 370 } 371 372 u64 emu_addr_space_contig_len(EmuAddrSpace* as, u64 va, u8 need_perms) { 373 EmuMap* m; 374 if (!as) return 0; 375 m = find_map(as, va); 376 if (!m) return 0; 377 if (need_perms && (m->perms & need_perms) != need_perms) return 0; 378 return m->end - va; 379 } 380 381 const EmuMemFault* emu_addr_space_last_fault(const EmuAddrSpace* as) { 382 return as ? &as->last_fault : NULL; 383 } 384 385 void emu_addr_space_mark_translated(EmuAddrSpace* as, u64 va, u64 nbytes) { 386 EmuMap* m; 387 u64 end, off, start_page, end_page, i; 388 if (!as || !nbytes) return; 389 m = find_map(as, va); 390 if (!m) return; 391 end = va + nbytes; 392 if (end < va || end > m->end) end = m->end; 393 off = va - m->start; 394 start_page = off / as->page_size; 395 end_page = (off + (end - va) + as->page_size - 1u) / as->page_size; 396 for (i = start_page; i < end_page; ++i) m->translated_pages[i] = 1u; 397 } 398 399 void emu_addr_space_invalidate(EmuAddrSpace* as, u64 va, u64 nbytes) { 400 u64 start, end; 401 u32 i; 402 if (!as || !nbytes) return; 403 start = emu_round_down(va, as->page_size); 404 end = emu_round_up(va + nbytes, as->page_size); 405 for (i = 0; i < as->nmaps; ++i) { 406 EmuMap* m = &as->maps[i]; 407 u64 s, e, j; 408 if (!map_overlaps(m, start, end)) continue; 409 s = (start > m->start ? start : m->start) - m->start; 410 e = (end < m->end ? end : m->end) - m->start; 411 for (j = s / as->page_size; j < e / as->page_size; ++j) { 412 m->translated_pages[j] = 0; 413 } 414 ++m->generation; 415 } 416 ++as->generation; 417 } 418 419 int emu_loaded_image_attach_cpu(EmuCPUState* cpu, EmuLoadedImage* img) { 420 if (!cpu || !img || !img->addr_space.heap) return 1; 421 emu_cpu_attach_addr_space(cpu, &img->addr_space); 422 return 0; 423 } 424 425 void emu_unload_image(Compiler* c, EmuLoadedImage* img) { 426 Heap* heap; 427 u32 i; 428 if (!img) return; 429 heap = c ? c->ctx->heap : img->addr_space.heap; 430 if (heap && img->link_map.objects) { 431 for (i = 0; i < img->link_map.nobjects; ++i) { 432 EmuLoadedObject* obj = &img->link_map.objects[i]; 433 if (obj->format.data) { 434 heap->free(heap, obj->format.data, obj->format.size); 435 } 436 } 437 } 438 emu_addr_space_destroy(&img->addr_space); 439 if (heap && img->imports) { 440 heap->free(heap, img->imports, sizeof(*img->imports) * img->nimports); 441 } 442 if (heap && img->import_bindings) { 443 heap->free(heap, img->import_bindings, 444 sizeof(*img->import_bindings) * img->import_bindings_cap); 445 } 446 if (heap && img->link_map.objects) { 447 heap->free(heap, img->link_map.objects, 448 sizeof(*img->link_map.objects) * img->link_map.objects_cap); 449 } 450 memset(img, 0, sizeof(*img)); 451 } 452 453 KitStatus emu_object_format_data_alloc(Compiler* c, EmuLoadedObject* obj, 454 size_t size, size_t align, void** out) { 455 Heap* heap; 456 if (out) *out = NULL; 457 if (!c || !obj || !size || !out) return KIT_INVALID; 458 heap = c->ctx->heap; 459 obj->format.data = heap->alloc(heap, size, align ? align : _Alignof(u64)); 460 if (!obj->format.data) return KIT_NOMEM; 461 memset(obj->format.data, 0, size); 462 obj->format.size = size; 463 obj->format.align = align ? align : _Alignof(u64); 464 *out = obj->format.data; 465 return KIT_OK; 466 }