1 /*
2  * Cisco router simulation platform.
3  * Copyright (c) 2008 Christophe Fillot (cf@utc.fr)
4  *
5  * Translation Sharing Groups.
6  */
7 #define _GNU_SOURCE
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <pthread.h>
15 #include <sys/types.h>
16 #include <assert.h>
17 
18 #include "device.h"
19 #include "pci_dev.h"
20 #include "pci_io.h"
21 #include "cpu.h"
22 #include "vm.h"
23 #include "tcb.h"
24 
25 #define DEBUG_JIT_FLUSH          0
26 #define DEBUG_JIT_BUFFER_ADJUST  0
27 #define DEBUG_JIT_PATCH          0
28 
29 /* Size of a JIT page */
30 #define TC_JIT_PAGE_SIZE  32768
31 
32 /* CPU provisionning */
33 #ifndef __CYGWIN__
34 #define TSG_EXEC_AREA_SINGLE_CPU  64
35 #else
36 #define TSG_EXEC_AREA_SINGLE_CPU  16
37 #endif
38 #define TSG_EXEC_AREA_SHARED      64
39 
40 /* Minimal number of exec pages to have to satisfy an allocation request */
41 #define TCB_MIN_EXEC_PAGES  (2 * TCB_DESC_MAX_CHUNKS)
42 
43 /* Maximum number of translation sharing groups */
44 #define TSG_MAX_GROUPS  128
45 
46 /* Hash table to retrieve Translated Code descriptors from their checksums */
47 #define TC_HASH_BITS   16
48 #define TC_HASH_SIZE   (1 << TC_HASH_BITS)
49 #define TC_HASH_MASK   (TC_HASH_SIZE - 1)
50 
51 typedef struct tsg tsg_t;
52 struct tsg {
53    /* Lock to synchronize multiple CPU accesses */
54    pthread_mutex_t lock;
55    pthread_mutexattr_t lock_attr;
56 
57    /* Hash table to retrieve Translated Code */
58    cpu_tc_t **tc_hash;
59 
60    /* Free list of TC descriptors */
61    cpu_tc_t *tc_free_list;
62 
63    /* List of CPUs attached to this group */
64    cpu_gen_t *cpu_list;
65 
66    /* Exec page allocator */
67    void *exec_area;
68 
69    insn_exec_page_t *exec_page_array;
70    insn_exec_page_t *exec_page_free_list;
71 
72    size_t exec_area_alloc_size;
73    u_int exec_page_alloc,exec_page_total;
74    u_int exec_area_full;
75 };
76 
77 #define TSG_LOCK(g)   pthread_mutex_lock(&(g)->lock)
78 #define TSG_UNLOCK(g) pthread_mutex_unlock(&(g)->lock)
79 
80 /* TCB groups */
81 static tsg_t *tsg_array[TSG_MAX_GROUPS];
82 
83 /* forward prototype declarations */
84 int tsg_remove_single_desc(cpu_gen_t *cpu);
85 static int tc_free(tsg_t *tsg,cpu_tc_t *tc);
86 
87 /* Create a new exec area */
exec_page_create_area(tsg_t * tsg)88 static int exec_page_create_area(tsg_t *tsg)
89 {
90    size_t area_size,page_count;
91    insn_exec_page_t *cp;
92    u_char *cp_addr;
93    int i;
94 
95    /* Area already created */
96    if (tsg->exec_area != NULL)
97       return(0);
98 
99    /* Allocate an executable area through MMAP */
100    area_size = tsg->exec_area_alloc_size * 1048756;
101    tsg->exec_area = memzone_map_exec_area(area_size);
102 
103    if (!tsg->exec_area) {
104       perror("exec_page_create_area: mmap");
105       goto err_mmap;
106    }
107 
108    /* Create the page array */
109    page_count = area_size / TC_JIT_PAGE_SIZE;
110    tsg->exec_page_array = calloc(page_count,sizeof(insn_exec_page_t));
111 
112    if (!tsg->exec_page_array)
113       goto err_array;
114 
115    for(i=0,cp_addr=tsg->exec_area;i<page_count;i++) {
116       cp = &tsg->exec_page_array[i];
117 
118       cp->ptr = cp_addr;
119       cp_addr += TC_JIT_PAGE_SIZE;
120 
121       tsg->exec_page_total++;
122 
123       cp->next = tsg->exec_page_free_list;
124       tsg->exec_page_free_list = cp;
125    }
126 
127    return(0);
128 
129  err_array:
130    memzone_unmap(tsg->exec_area,area_size);
131  err_mmap:
132    return(-1);
133 }
134 
135 /* Allocate an exec page */
exec_page_alloc(cpu_gen_t * cpu)136 static insn_exec_page_t *exec_page_alloc(cpu_gen_t *cpu)
137 {
138    tsg_t *tsg = tsg_array[cpu->tsg];
139    insn_exec_page_t *p;
140    _maybe_used int count;
141 
142    TSG_LOCK(tsg);
143 
144    //tcb_desc_check_consistency(tcbg);
145 
146    /*
147     * If the free list is empty, try to increase exec area capacity, then
148     * flush JIT for the requesting CPU.
149     */
150    if (unlikely(!(p = tsg->exec_page_free_list))) {
151       count = tsg_remove_single_desc(cpu);
152 #if DEBUG_JIT_FLUSH
153       cpu_log(cpu,"JIT","flushed %d TCB\n",count);
154 #endif
155 
156       if (unlikely(!(p = tsg->exec_page_free_list)))
157          tsg->exec_area_full = TRUE;
158    }
159 
160    /* If the area is full, stop allocating pages and free TCB */
161    if (tsg->exec_area_full) {
162       cpu_jit_tcb_flush_all(cpu);
163 
164       /* if we get >= 25% of free pages, we can reallocate */
165       if (tsg->exec_page_total >= (tsg->exec_page_alloc * 4)) {
166          tsg->exec_area_full = FALSE;
167       }
168 
169       TSG_UNLOCK(tsg);
170       return NULL;
171    }
172 
173    tsg->exec_page_free_list = p->next;
174    tsg->exec_page_alloc++;
175    TSG_UNLOCK(tsg);
176    return p;
177 }
178 
179 /*
180  * Free an exec page and returns it to the pool.
181  * Note: the lock is already taken when exec_page_free() is called.
182  */
exec_page_free(tsg_t * tcbg,insn_exec_page_t * p)183 static inline void exec_page_free(tsg_t *tcbg,insn_exec_page_t *p)
184 {
185    if (p != NULL) {
186       p->next = tcbg->exec_page_free_list;
187       tcbg->exec_page_free_list = p;
188       tcbg->exec_page_alloc--;
189    }
190 }
191 
192 /* Allocate a free TCB group */
tsg_alloc(void)193 int tsg_alloc(void)
194 {
195    int i;
196 
197    for(i=0;i<TSG_MAX_GROUPS;i++)
198       if (tsg_array[i] == NULL)
199          return(i);
200 
201    return(-1);
202 }
203 
204 /* Create a translation sharing group */
tsg_create(int id,size_t alloc_size)205 int tsg_create(int id,size_t alloc_size)
206 {
207    tsg_t *tsg;
208 
209    /* If the group is already initialized, skip it */
210    if (tsg_array[id] != NULL)
211       return(0);
212 
213    /* Allocate the holding structure */
214    if (!(tsg = malloc(sizeof(*tsg))))
215       return(-1);
216 
217    memset(tsg,0,sizeof(*tsg));
218    tsg->exec_area_full = FALSE;
219    tsg->exec_area_alloc_size = alloc_size;
220 
221    /* Create the TC hash table */
222    if (!(tsg->tc_hash = calloc(sizeof(cpu_tc_t *),TC_HASH_SIZE)))
223       goto err_hash;
224 
225    /* Create the exec page area */
226    if (exec_page_create_area(tsg) == -1)
227       goto err_area;
228 
229    tsg_array[id] = tsg;
230 
231    pthread_mutexattr_init(&tsg->lock_attr);
232    pthread_mutexattr_settype(&tsg->lock_attr,PTHREAD_MUTEX_RECURSIVE);
233    pthread_mutex_init(&tsg->lock,&tsg->lock_attr);
234    return(0);
235 
236  err_area:
237    free(tsg->tc_hash);
238  err_hash:
239    free(tsg);
240    return(-1);
241 }
242 
243 /* Bind a CPU to a TSG - If the group isn't specified, create one */
tsg_bind_cpu(cpu_gen_t * cpu)244 int tsg_bind_cpu(cpu_gen_t *cpu)
245 {
246    tsg_t *tsg;
247    ssize_t alloc_size;
248 
249    if (cpu->tsg == -1) {
250       cpu->tsg = tsg_alloc();
251 
252       if (cpu->tsg == -1)
253          return(-1);
254 
255       alloc_size = TSG_EXEC_AREA_SINGLE_CPU;
256    } else {
257       alloc_size = TSG_EXEC_AREA_SHARED;
258    }
259 
260    if (tsg_create(cpu->tsg,alloc_size) == -1)
261       return(-1);
262 
263    tsg = tsg_array[cpu->tsg];
264    M_LIST_ADD(cpu,tsg->cpu_list,tsg);
265    return(0);
266 }
267 
268 /* Unbind a CPU from a TSG - release all resources used */
tsg_unbind_cpu(cpu_gen_t * cpu)269 int tsg_unbind_cpu(cpu_gen_t *cpu)
270 {
271    tsg_t *tsg = tsg_array[cpu->tsg];
272    cpu_tb_t *tb,*next;
273 
274    if (cpu->tsg == -1)
275       return(-1);
276 
277    /* Free all descriptors in free list */
278    for(tb=cpu->tb_free_list;tb;tb=next) {
279       next = tb->tb_next;
280       free(tb);
281    }
282 
283    /* Free all descriptors currently in use */
284    for(tb=cpu->tb_list;tb;tb=next) {
285       next = tb->tb_next;
286       tb_free(cpu,tb);
287       free(tb);
288    }
289 
290    cpu->tb_list = NULL;
291    cpu->tb_free_list = NULL;
292 
293    TSG_LOCK(tsg);
294    M_LIST_REMOVE(cpu,tsg);
295    TSG_UNLOCK(tsg);
296    return(0);
297 }
298 
299 /* Create a JIT chunk */
tc_alloc_jit_chunk(cpu_gen_t * cpu,cpu_tc_t * tc)300 int tc_alloc_jit_chunk(cpu_gen_t *cpu,cpu_tc_t *tc)
301 {
302    insn_exec_page_t *chunk;
303 
304    if (tc->jit_chunk_pos >= TC_MAX_CHUNKS) {
305       cpu_log(cpu,"JIT","TC 0x%8.8llx: too many chunks.\n",tc->vaddr);
306       return(-1);
307    }
308 
309    if (!(chunk = exec_page_alloc(cpu)))
310       return(-1);
311 
312    tc->jit_chunks[tc->jit_chunk_pos++] = chunk;
313    tc->jit_buffer = chunk;
314    return(0);
315 }
316 
317 /* Free JIT chunks allocated for a TC descriptor */
tc_free_jit_chunks(tsg_t * tsg,cpu_tc_t * tc)318 static void tc_free_jit_chunks(tsg_t *tsg,cpu_tc_t *tc)
319 {
320    int i;
321 
322    for(i=0;i<tc->jit_chunk_pos;i++) {
323       exec_page_free(tsg,tc->jit_chunks[i]);
324       tc->jit_chunks[i] = NULL;
325    }
326 }
327 
328 /* Remove the TC descriptor from the TSG hash table */
tc_remove_from_hash(cpu_tc_t * tc)329 static inline void tc_remove_from_hash(cpu_tc_t *tc)
330 {
331    M_LIST_REMOVE(tc,hash);
332 }
333 
334 /*
335  * Add a TC descriptor as local to the specified CPU.
336  * This occurs when the descriptor has just been created and is not shared.
337  * It allows to free pages easily in case of contention.
338  */
tc_add_cpu_local(cpu_gen_t * cpu,cpu_tc_t * tc)339 static inline void tc_add_cpu_local(cpu_gen_t *cpu,cpu_tc_t *tc)
340 {
341    M_LIST_ADD(tc,cpu->tc_local_list,sc);
342 }
343 
344 /*
345  * Remove a TC descriptor from a local list of a CPU. It happens when the TC
346  * becomes shared between different virtual CPUs.
347  */
tc_remove_cpu_local(cpu_tc_t * tc)348 static inline void tc_remove_cpu_local(cpu_tc_t *tc)
349 {
350    M_LIST_REMOVE(tc,sc);
351 }
352 
353 /* Free a TC descriptor */
tc_free(tsg_t * tsg,cpu_tc_t * tc)354 static int tc_free(tsg_t *tsg,cpu_tc_t *tc)
355 {
356    TSG_LOCK(tsg);
357 
358    tc->ref_count--;
359    assert(tc->ref_count >= 0);
360 
361    if (tc->ref_count == 0) {
362       tc->flags &= ~TC_FLAG_VALID;
363 
364       tc_free_patches(tc);
365 
366       tc_remove_from_hash(tc);
367       tc_remove_cpu_local(tc);
368       tc_free_jit_chunks(tsg,tc);
369       free(tc->jit_insn_ptr);
370 
371       tc->sc_next = tsg->tc_free_list;
372       tsg->tc_free_list = tc;
373       TSG_UNLOCK(tsg);
374       return(TRUE);
375    }
376 
377    /* not yet deleted */
378    TSG_UNLOCK(tsg);
379    return(FALSE);
380 }
381 
382 /* Allocate a new TC descriptor */
tc_alloc(cpu_gen_t * cpu,m_uint64_t vaddr,m_uint32_t exec_state)383 cpu_tc_t *tc_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,m_uint32_t exec_state)
384 {
385    tsg_t *tsg = tsg_array[cpu->tsg];
386    cpu_tc_t *tc;
387    size_t len;
388 
389    TSG_LOCK(tsg);
390    if (tsg->tc_free_list) {
391       tc = tsg->tc_free_list;
392       tsg->tc_free_list = tc->sc_next;
393    } else {
394       if (!(tc = malloc(sizeof(*tc))))
395          return NULL;
396    }
397    TSG_UNLOCK(tsg);
398 
399    memset(tc,0,sizeof(*tc));
400    tc->vaddr = vaddr;
401    tc->exec_state = exec_state;
402    tc->ref_count = 1;
403 
404    /*
405     * Allocate the array used to convert target code ptr to native code ptr,
406     * and create the first JIT buffer.
407     */
408    len = VM_PAGE_SIZE / sizeof(m_uint32_t);
409 
410    if (!(tc->jit_insn_ptr = calloc(len,sizeof(u_char *))) ||
411        (tc_alloc_jit_chunk(cpu,tc) == -1))
412    {
413       tc_free(tsg,tc);
414       return NULL;
415    }
416 
417    tc->jit_ptr = tc->jit_buffer->ptr;
418    return tc;
419 }
420 
421 /* Compute a checksum on a page */
tsg_checksum_page(void * page,ssize_t size)422 tsg_checksum_t tsg_checksum_page(void *page,ssize_t size)
423 {
424    tsg_checksum_t cksum = 0;
425    m_uint64_t *ptr = page;
426 
427    while(size > 0) {
428       cksum ^= *ptr;
429       ptr++;
430       size -= sizeof(m_uint64_t);
431    }
432    return(cksum);
433 }
434 
435 /* Compute a hash on the specified checksum */
tsg_cksum_hash(tsg_checksum_t cksum)436 static inline u_int tsg_cksum_hash(tsg_checksum_t cksum)
437 {
438    tsg_checksum_t tmp;
439 
440    tmp = cksum ^ (cksum >> 17) ^ (cksum >> 23);
441    return((u_int)(tmp & TC_HASH_MASK));
442 }
443 
444 /* Compare physical pages */
tb_compare_page(cpu_tb_t * tb1,cpu_tb_t * tb2)445 static inline int tb_compare_page(cpu_tb_t *tb1,cpu_tb_t *tb2)
446 {
447    if (tb1->target_code == tb2->target_code)
448       return(0);
449 
450    return(memcmp(tb1->target_code,tb2->target_code,VM_PAGE_SIZE));
451 }
452 
453 /* Compare 2 TBs */
tb_compare(cpu_tb_t * tb1,cpu_tb_t * tb2)454 static forced_inline int tb_compare(cpu_tb_t *tb1,cpu_tb_t *tb2)
455 {
456    return((tb1->vaddr == tb2->vaddr) &&
457           (tb1->exec_state == tb2->exec_state));
458 }
459 
460 /* Try to find a TC descriptor to share generated code */
tc_find_shared(cpu_gen_t * cpu,cpu_tb_t * tb)461 int tc_find_shared(cpu_gen_t *cpu,cpu_tb_t *tb)
462 {
463    tsg_t *tsg = tsg_array[cpu->tsg];
464    cpu_tb_t *p;
465    cpu_tc_t *tc;
466    u_int hash_bucket;
467 
468    TSG_LOCK(tsg);
469 
470    assert(tb->target_code != NULL);
471 
472    hash_bucket = tsg_cksum_hash(tb->checksum);
473    for(tc=tsg->tc_hash[hash_bucket];tc;tc=tc->hash_next)
474    {
475       assert(tc->flags & TC_FLAG_VALID);
476 
477       if (tc->checksum == tb->checksum) {
478          for(p=tc->tb_list;p;p=p->tb_dl_next) {
479             //assert(p->flags & TCB_FLAG_VALID);
480 
481             if (!(p->flags & TB_FLAG_VALID)) {
482                tb_dump(tb);
483                abort();
484             }
485 
486             if (tb_compare(tb,p) && !tb_compare_page(tb,p))
487             {
488                /* matching page, we can share the code */
489                tc->ref_count++;
490                tb->tc = tc;
491                tc_remove_cpu_local(tc);
492                M_LIST_ADD(tb,tc->tb_list,tb_dl);
493                tb_enable(cpu,tb);
494 
495                TSG_UNLOCK(tsg);
496                return(TSG_LOOKUP_SHARED);
497             }
498          }
499       }
500    }
501 
502    /* A new TCB descriptor must be created */
503    TSG_UNLOCK(tsg);
504    return(TSG_LOOKUP_NEW);
505 }
506 
507 /* Register a newly compiled TCB descriptor */
tc_register(cpu_gen_t * cpu,cpu_tb_t * tb,cpu_tc_t * tc)508 void tc_register(cpu_gen_t *cpu,cpu_tb_t *tb,cpu_tc_t *tc)
509 {
510    tsg_t *tsg = tsg_array[cpu->tsg];
511    u_int hash_bucket = tsg_cksum_hash(tb->checksum);
512 
513    tb->tc = tc;
514    tc->checksum = tb->checksum;
515 
516    TSG_LOCK(tsg);
517    tc_add_cpu_local(cpu,tc);
518    M_LIST_ADD(tb,tc->tb_list,tb_dl);
519    M_LIST_ADD(tc,tsg->tc_hash[hash_bucket],hash);
520    tc->flags |= TC_FLAG_VALID;
521    TSG_UNLOCK(tsg);
522 }
523 
524 /* Remove all TC descriptors belonging to a single CPU (ie not shared) */
tsg_remove_single_desc(cpu_gen_t * cpu)525 int tsg_remove_single_desc(cpu_gen_t *cpu)
526 {
527    cpu_tc_t *tc,*next;
528    int count = 0;
529 
530    for(tc=cpu->tc_local_list;tc;tc=next) {
531       next = tc->sc_next;
532 
533       assert(tc->ref_count == 1);
534       assert(tc->tb_list->tb_dl_next == NULL);
535 
536       tb_free(cpu,tc->tb_list);
537       count++;
538    }
539 
540    cpu->tc_local_list = NULL;
541    return(count);
542 }
543 
544 /* Dump a TCB */
tb_dump(cpu_tb_t * tb)545 void tb_dump(cpu_tb_t *tb)
546 {
547    printf("TB 0x%8.8llx:\n",tb->vaddr);
548    printf("  - flags       : 0x%4.4x\n",tb->flags);
549    printf("  - checksum    : 0x%16.16llx\n",tb->checksum);
550    printf("  - target_code : %p\n",tb->target_code);
551    printf("  - virt_hash   : 0x%8.8x\n",tb->virt_hash);
552    printf("  - phys_hash   : 0x%8.8x\n",tb->phys_hash);
553    printf("  - phys_page   : 0x%8.8x\n",tb->phys_page);
554    printf("  - tc          : %p\n",tb->tc);
555    printf("  - tb_LIST     : (%p,%p)\n",tb->tb_pprev,tb->tb_next);
556    printf("  - tcb_dl_LIST : (%p,%p)\n",tb->tb_dl_pprev,tb->tb_dl_next);
557    printf("  - phys_LIST   : (%p,%p)\n",tb->phys_pprev,tb->phys_next);
558 }
559 
560 /* Dump a TCB descriptor */
tc_dump(cpu_tc_t * tc)561 void tc_dump(cpu_tc_t *tc)
562 {
563    printf("TC 0x%8.8llx:\n",tc->vaddr);
564    printf("  - flags      : 0x%4.4x\n",tc->flags);
565    printf("  - checksum   : 0x%8.8llx\n",tc->checksum);
566    printf("  - ref_count  : %u\n",tc->ref_count);
567    printf("  - tb_list    : %p\n",tc->tb_list);
568    printf("  - hash_LIST  : (%p,%p)\n",tc->hash_pprev,tc->hash_next);
569    printf("  - sc_LIST    : (%p,%p)\n",tc->sc_pprev,tc->sc_next);
570 }
571 
572 /* Consistency check */
tc_check_consistency(cpu_gen_t * cpu)573 int tc_check_consistency(cpu_gen_t *cpu)
574 {
575    tsg_t *tsg = tsg_array[cpu->tsg];
576    cpu_tb_t *tb;
577    cpu_tc_t *tc;
578    int i,err=0;
579 
580    TSG_LOCK(tsg);
581 
582    for(i=0;i<TC_HASH_SIZE;i++) {
583       for(tc=tsg->tc_hash[i];tc;tc=tc->hash_next) {
584          if (!(tc->flags & TC_FLAG_VALID)) {
585             cpu_log(cpu,"JIT",
586                     "consistency error: TC 0x%8.8llx (flags=0x%x)\n",
587                     tc->vaddr,tc->flags);
588             tc_dump(tc);
589             err++;
590          }
591 
592          for(tb=tc->tb_list;tb;tb=tb->tb_dl_next) {
593             if (!(tb->flags & TB_FLAG_VALID)) {
594                cpu_log(cpu,"JIT",
595                        "consistency error: TB 0x%8.8llx (flags=0x%x)\n",
596                        tb->vaddr,tb->flags);
597                err++;
598 
599                tb_dump(tb);
600             }
601          }
602       }
603    }
604 
605    TSG_UNLOCK(tsg);
606 
607    if (err > 0) {
608       printf("TSG %d: internal consistency error (%d pb detected)\n",
609              cpu->tsg,err);
610    }
611 
612    return(err);
613 }
614 
615 
616 /* Statistics: compute number of shared pages in a translation group */
tsg_get_stats(tsg_t * tsg,struct tsg_stats * s)617 static int tsg_get_stats(tsg_t *tsg,struct tsg_stats *s)
618 {
619    cpu_tc_t *tc;
620    int i;
621 
622    s->shared_tc = s->total_tc = 0;
623    s->shared_pages = 0;
624 
625    if (!tsg)
626       return(-1);
627 
628    for(i=0;i<TC_HASH_SIZE;i++) {
629       for(tc=tsg->tc_hash[i];tc;tc=tc->hash_next) {
630          if (tc->ref_count > 1) {
631             s->shared_pages += tc->jit_chunk_pos;
632             s->shared_tc++;
633          }
634 
635          s->total_tc++;
636       }
637    }
638 
639    return(0);
640 }
641 
642 /* Show statistics about all translation groups */
tsg_show_stats(void)643 void tsg_show_stats(void)
644 {
645    struct tsg_stats s;
646    int i;
647 
648    printf("\nTSG statistics:\n\n");
649 
650    printf("  ID   Shared TC     Total TC     Alloc.Pages  Shared Pages   Total Pages\n");
651 
652    for(i=0;i<TSG_MAX_GROUPS;i++) {
653       if (!tsg_get_stats(tsg_array[i],&s)) {
654          printf(" %3d     %8u     %8u       %8u      %8u      %8u\n",
655                 i,s.shared_tc,s.total_tc,
656                 tsg_array[i]->exec_page_alloc,
657                 s.shared_pages,
658                 tsg_array[i]->exec_page_total);
659       }
660    }
661 
662    printf("\n");
663 }
664 
665 /* Adjust the JIT buffer if its size is not sufficient */
tc_adjust_jit_buffer(cpu_gen_t * cpu,cpu_tc_t * tc,void (* set_jump)(u_char ** insn,u_char * dst))666 int tc_adjust_jit_buffer(cpu_gen_t *cpu,cpu_tc_t *tc,
667                          void (*set_jump)(u_char **insn,u_char *dst))
668 {
669    assert((tc->jit_ptr - tc->jit_buffer->ptr) < TC_JIT_PAGE_SIZE);
670 
671    if ((tc->jit_ptr - tc->jit_buffer->ptr) <= (TC_JIT_PAGE_SIZE - 512))
672       return(0);
673 
674 #if DEBUG_JIT_BUFFER_ADJUST
675    cpu_log(cpu,"JIT",
676            "TC 0x%8.8llx: adjusting JIT buffer (cur=%p,start=%p,delta=%u)\n",
677            tc->vaddr,tc->jit_ptr,tc->jit_buffer->ptr,
678            TC_JIT_PAGE_SIZE - (tc->jit_ptr-tc->jit_buffer->ptr));
679 #endif
680 
681    /*
682     * If we cannot allocate a new chunk, free the complete descriptor and
683     * return an error so that the caller uses non-JIT mode for this TCB.
684     */
685    if (tc_alloc_jit_chunk(cpu,tc) == -1) {
686       tc_free(tsg_array[cpu->tsg],tc);
687       return(-1);
688    }
689 
690    /* jump to the new exec page (link) */
691    set_jump(&tc->jit_ptr,tc->jit_buffer->ptr);
692    tc->jit_ptr = tc->jit_buffer->ptr;
693    return(0);
694 }
695 
696 /* Record a patch to apply in a compiled block */
tc_record_patch(cpu_gen_t * cpu,cpu_tc_t * tc,u_char * jit_ptr,m_uint64_t vaddr)697 struct insn_patch *tc_record_patch(cpu_gen_t *cpu,cpu_tc_t *tc,
698                                    u_char *jit_ptr,m_uint64_t vaddr)
699 {
700    struct insn_patch_table *ipt = tc->patch_table;
701    struct insn_patch *patch;
702 
703    /* vaddr must be 32-bit aligned */
704    if (vaddr & 0x03) {
705       cpu_log(cpu,"JIT",
706               "TC 0x%8.8llx: trying to record an invalid vaddr (0x%8.8llx)\n",
707               tc->vaddr,vaddr);
708       return NULL;
709    }
710 
711    if (!ipt || (ipt->cur_patch >= INSN_PATCH_TABLE_SIZE))
712    {
713       /* full table or no table, create a new one */
714       ipt = malloc(sizeof(*ipt));
715       if (!ipt) {
716          cpu_log(cpu,"JIT","TC 0x%8.8llx: unable to create patch table.\n",
717                  tc->vaddr);
718          return NULL;
719       }
720 
721       memset(ipt,0,sizeof(*ipt));
722       ipt->next = tc->patch_table;
723       tc->patch_table = ipt;
724    }
725 
726 #if DEBUG_JIT_PATCH
727    printf("TC 0x%8.8llx: recording patch [host %p -> target:0x%8.8llx], "
728           "TP=%d\n",tc->vaddr,jit_ptr,vaddr,tc->trans_pos);
729 #endif
730 
731    patch = &ipt->patches[ipt->cur_patch];
732    patch->jit_insn = jit_ptr;
733    patch->vaddr    = vaddr;
734    ipt->cur_patch++;
735 
736    return patch;
737 }
738 
739 /* Apply all patches */
tc_apply_patches(cpu_tc_t * tc,void (* set_patch)(u_char * insn,u_char * dst))740 int tc_apply_patches(cpu_tc_t *tc,void (*set_patch)(u_char *insn,u_char *dst))
741 {
742    struct insn_patch_table *ipt;
743    struct insn_patch *patch;
744    u_char *jit_dst;
745    int i;
746 
747    for(ipt=tc->patch_table;ipt;ipt=ipt->next)
748       for(i=0;i<ipt->cur_patch;i++)
749       {
750          patch = &ipt->patches[i];
751          jit_dst = tc_get_host_ptr(tc,patch->vaddr);
752 
753          if (jit_dst) {
754 #if DEBUG_JIT_PATCH
755             printf("TC 0x%8.8llx: applying patch "
756                    "[host %p -> target 0x%8.8llx=JIT:%p]\n",
757                    tc->vaddr,patch->jit_insn,patch->vaddr,jit_dst);
758 #endif
759             set_patch(patch->jit_insn,jit_dst);
760          }
761       }
762 
763    return(0);
764 }
765 
766 /* Free the patch table */
tc_free_patches(cpu_tc_t * tc)767 void tc_free_patches(cpu_tc_t *tc)
768 {
769    struct insn_patch_table *p,*next;
770 
771    for(p=tc->patch_table;p;p=next) {
772       next = p->next;
773       free(p);
774    }
775 
776    tc->patch_table = NULL;
777 }
778 
779 /* Initialize the JIT structures of a CPU */
cpu_jit_init(cpu_gen_t * cpu,size_t virt_hash_size,size_t phys_hash_size)780 int cpu_jit_init(cpu_gen_t *cpu,size_t virt_hash_size,size_t phys_hash_size)
781 {
782    size_t len;
783 
784    /* Virtual address mapping for TCB */
785    len = virt_hash_size * sizeof(void *);
786    cpu->tb_virt_hash = m_memalign(4096,len);
787    memset(cpu->tb_virt_hash,0,len);
788 
789    /* Physical address mapping for TCB */
790    len = phys_hash_size * sizeof(void *);
791    cpu->tb_phys_hash = m_memalign(4096,len);
792    memset(cpu->tb_phys_hash,0,len);
793 
794    return(0);
795 }
796 
797 /* Shutdown the JIT structures of a CPU */
cpu_jit_shutdown(cpu_gen_t * cpu)798 void cpu_jit_shutdown(cpu_gen_t *cpu)
799 {
800    tsg_unbind_cpu(cpu);
801 
802    /* Free virtual and physical hash tables */
803    free(cpu->tb_virt_hash);
804    free(cpu->tb_phys_hash);
805 
806    cpu->tb_virt_hash = NULL;
807    cpu->tb_phys_hash = NULL;
808 }
809 
810 /* Allocate a new TB */
tb_alloc(cpu_gen_t * cpu,m_uint64_t vaddr,u_int exec_state)811 cpu_tb_t *tb_alloc(cpu_gen_t *cpu,m_uint64_t vaddr,u_int exec_state)
812 {
813    cpu_tb_t *tb;
814 
815    if (cpu->tb_free_list) {
816       tb = cpu->tb_free_list;
817       cpu->tb_free_list = tb->tb_next;
818    } else {
819       if (!(tb = malloc(sizeof(*tb))))
820          return NULL;
821    }
822 
823    memset(tb,0,sizeof(*tb));
824    tb->vaddr = vaddr;
825    tb->exec_state = exec_state;
826    return tb;
827 }
828 
829 /* Free a Translation Block */
tb_free(cpu_gen_t * cpu,cpu_tb_t * tb)830 void tb_free(cpu_gen_t *cpu,cpu_tb_t *tb)
831 {
832    tsg_t *tsg = tsg_array[cpu->tsg];
833 
834    /* Remove this TB from the TB list bound to a TC descriptor */
835    TSG_LOCK(tsg);
836    M_LIST_REMOVE(tb,tb_dl);
837    TSG_UNLOCK(tsg);
838 
839    /* Release the TC descriptor first */
840    if (tb->tc != NULL)
841       tc_free(tsg,tb->tc);
842 
843    /* Remove the block from the CPU TCB list */
844    M_LIST_REMOVE(tb,tb);
845 
846    /* Remove the block from the CPU physical mapping hash table */
847    M_LIST_REMOVE(tb,phys);
848 
849    /* Invalidate the entry in hash table for virtual addresses */
850    if (cpu->tb_virt_hash[tb->virt_hash] == tb)
851       cpu->tb_virt_hash[tb->virt_hash] = NULL;
852 
853    /* Make the block return to the free list */
854    memset(tb,0,sizeof(*tb));
855    tb->tb_next = cpu->tb_free_list;
856    cpu->tb_free_list = tb;
857 }
858 
859 /* Enable a Tranlsation Block  */
tb_enable(cpu_gen_t * cpu,cpu_tb_t * tb)860 void tb_enable(cpu_gen_t *cpu,cpu_tb_t *tb)
861 {
862    M_LIST_ADD(tb,cpu->tb_list,tb);
863    M_LIST_ADD(tb,cpu->tb_phys_hash[tb->phys_hash],phys);
864    tb->flags |= TB_FLAG_VALID;
865 }
866 
867 /* Flush all TCB of a virtual CPU */
cpu_jit_tcb_flush_all(cpu_gen_t * cpu)868 void cpu_jit_tcb_flush_all(cpu_gen_t *cpu)
869 {
870    cpu_tb_t *tb,*next;
871 
872    for(tb=cpu->tb_list;tb;tb=next) {
873       next = tb->tb_next;
874       tb_free(cpu,tb);
875    }
876 
877    assert(cpu->tb_list == NULL);
878 }
879 
880 /* Mark a TB as containing self-modifying code */
tb_mark_smc(cpu_gen_t * cpu,cpu_tb_t * tb)881 void tb_mark_smc(cpu_gen_t *cpu,cpu_tb_t *tb)
882 {
883    if (tb->flags & TB_FLAG_SMC)
884       return; /* already done */
885 
886    tb->flags |= TB_FLAG_SMC;
887 
888    if (tb->tc != NULL) {
889       tc_free(tsg_array[cpu->tsg],tb->tc);
890       tb->tc = NULL;
891    }
892 }
893 
894 /* Handle write access on an executable page */
cpu_jit_write_on_exec_page(cpu_gen_t * cpu,m_uint32_t wr_phys_page,m_uint32_t wr_hp,m_uint32_t ip_phys_page)895 void cpu_jit_write_on_exec_page(cpu_gen_t *cpu,
896                                 m_uint32_t wr_phys_page,
897                                 m_uint32_t wr_hp,
898                                 m_uint32_t ip_phys_page)
899 {
900    cpu_tb_t *tb,**tbp,*tb_next;
901 
902    if (wr_phys_page != ip_phys_page) {
903       /* Clear all TCB matching the physical page being modified */
904       for(tbp=&cpu->tb_phys_hash[wr_hp];*tbp;)
905          if ((*tbp)->phys_page == wr_phys_page) {
906             tb_next = (*tbp)->phys_next;
907             tb_free(cpu,(*tbp));
908             *tbp = tb_next;
909          } else {
910             tbp = &(*tbp)->phys_next;
911          }
912    } else {
913       /* Self-modifying page */
914       for(tb=cpu->tb_phys_hash[wr_hp];tb;tb=tb->phys_next)
915          if (tb->phys_page == wr_phys_page)
916             tb_mark_smc(cpu,tb);
917 
918       cpu_exec_loop_enter(cpu);
919    }
920 }
921