kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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 }