/[dynamips]/trunk/ppc32.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /trunk/ppc32.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7 - (hide annotations)
Sat Oct 6 16:23:47 2007 UTC (16 years, 6 months ago) by dpavlin
Original Path: upstream/dynamips-0.2.7-RC1/ppc32.c
File MIME type: text/plain
File size: 15112 byte(s)
dynamips-0.2.7-RC1

1 dpavlin 7 /*
2     * Cisco router simulation platform.
3     * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr)
4     *
5     * PowerPC (32-bit) generic routines.
6     */
7    
8     #include <stdio.h>
9     #include <stdlib.h>
10     #include <unistd.h>
11     #include <string.h>
12     #include <sys/types.h>
13     #include <sys/stat.h>
14     #include <sys/mman.h>
15     #include <fcntl.h>
16     #include <assert.h>
17    
18     #include "rbtree.h"
19     #include "cpu.h"
20     #include "dynamips.h"
21     #include "memory.h"
22     #include "device.h"
23     #include "ppc32_mem.h"
24     #include "ppc32_exec.h"
25     #include "ppc32_jit.h"
26    
27     /* Reset a PowerPC CPU */
28     int ppc32_reset(cpu_ppc_t *cpu)
29     {
30     cpu->ia = PPC32_ROM_START;
31     cpu->gpr[1] = PPC32_ROM_SP;
32     cpu->msr = PPC32_MSR_IP;
33    
34     /* Restart the MTS subsystem */
35     ppc32_mem_restart(cpu);
36    
37     /* Flush JIT structures */
38     ppc32_jit_flush(cpu,0);
39     return(0);
40     }
41    
42     /* Initialize a PowerPC processor */
43     int ppc32_init(cpu_ppc_t *cpu)
44     {
45     /* Initialize idle timer */
46     cpu->gen->idle_max = 1500;
47     cpu->gen->idle_sleep_time = 30000;
48    
49     /* Timer IRQ parameters (default frequency: 250 Hz <=> 4ms period) */
50     cpu->timer_irq_check_itv = 1000;
51     cpu->timer_irq_freq = 250;
52    
53     /* Idle loop mutex and condition */
54     pthread_mutex_init(&cpu->gen->idle_mutex,NULL);
55     pthread_cond_init(&cpu->gen->idle_cond,NULL);
56    
57     /* Set the CPU methods */
58     cpu->gen->reg_set = (void *)ppc32_reg_set;
59     cpu->gen->reg_dump = (void *)ppc32_dump_regs;
60     cpu->gen->mmu_dump = (void *)ppc32_dump_mmu;
61     cpu->gen->mmu_raw_dump = (void *)ppc32_dump_mmu;
62     cpu->gen->add_breakpoint = (void *)ppc32_add_breakpoint;
63     cpu->gen->remove_breakpoint = (void *)ppc32_remove_breakpoint;
64     cpu->gen->set_idle_pc = (void *)ppc32_set_idle_pc;
65     cpu->gen->get_idling_pc = (void *)ppc32_get_idling_pc;
66    
67     /* Set the startup parameters */
68     ppc32_reset(cpu);
69     return(0);
70     }
71    
72     /* Delete a PowerPC processor */
73     void ppc32_delete(cpu_ppc_t *cpu)
74     {
75     if (cpu) {
76     ppc32_mem_shutdown(cpu);
77     ppc32_jit_shutdown(cpu);
78     }
79     }
80    
81     /* Set the processor version register (PVR) */
82     void ppc32_set_pvr(cpu_ppc_t *cpu,m_uint32_t pvr)
83     {
84     cpu->pvr = pvr;
85     ppc32_mem_restart(cpu);
86     }
87    
88     /* Set idle PC value */
89     void ppc32_set_idle_pc(cpu_gen_t *cpu,m_uint64_t addr)
90     {
91     CPU_PPC32(cpu)->idle_pc = (m_uint32_t)addr;
92     }
93    
94     /* Timer IRQ */
95     void *ppc32_timer_irq_run(cpu_ppc_t *cpu)
96     {
97     pthread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER;
98     pthread_cond_t ucond = PTHREAD_COND_INITIALIZER;
99     struct timespec t_spc;
100     m_tmcnt_t expire;
101     u_int interval;
102     u_int threshold;
103    
104     #if 0
105     while(!cpu->timer_irq_armed)
106     sleep(1);
107     #endif
108    
109     interval = 1000000 / cpu->timer_irq_freq;
110     threshold = cpu->timer_irq_freq * 10;
111     expire = m_gettime_usec() + interval;
112    
113     while(cpu->gen->state != CPU_STATE_HALTED) {
114     pthread_mutex_lock(&umutex);
115     t_spc.tv_sec = expire / 1000000;
116     t_spc.tv_nsec = (expire % 1000000) * 1000;
117     pthread_cond_timedwait(&ucond,&umutex,&t_spc);
118     pthread_mutex_unlock(&umutex);
119    
120     if (likely(!cpu->irq_disable) &&
121     likely(cpu->gen->state == CPU_STATE_RUNNING) &&
122     likely(cpu->msr & PPC32_MSR_EE))
123     {
124     cpu->timer_irq_pending++;
125    
126     if (unlikely(cpu->timer_irq_pending > threshold)) {
127     cpu->timer_irq_pending = 0;
128     cpu->timer_drift++;
129     #if 0
130     printf("Timer IRQ not accurate (%u pending IRQ): "
131     "reduce the \"--timer-irq-check-itv\" parameter "
132     "(current value: %u)\n",
133     cpu->timer_irq_pending,cpu->timer_irq_check_itv);
134     #endif
135     }
136     }
137    
138     expire += interval;
139     }
140    
141     return NULL;
142     }
143    
144     #define IDLE_HASH_SIZE 8192
145    
146     /* Idle PC hash item */
147     struct ppc32_idle_pc_hash {
148     m_uint32_t ia;
149     u_int count;
150     struct ppc32_idle_pc_hash *next;
151     };
152    
153     /* Determine an "idling" PC */
154     int ppc32_get_idling_pc(cpu_gen_t *cpu)
155     {
156     cpu_ppc_t *pcpu = CPU_PPC32(cpu);
157     struct ppc32_idle_pc_hash **pc_hash,*p;
158     struct cpu_idle_pc *res;
159     u_int h_index,res_count;
160     m_uint32_t cur_ia;
161     int i;
162    
163     cpu->idle_pc_prop_count = 0;
164    
165     if (pcpu->idle_pc != 0) {
166     printf("\nYou already use an idle PC, using the calibration would give "
167     "incorrect results.\n");
168     return(-1);
169     }
170    
171     printf("\nPlease wait while gathering statistics...\n");
172    
173     pc_hash = calloc(IDLE_HASH_SIZE,sizeof(struct ppc32_idle_pc_hash *));
174    
175     /* Disable IRQ */
176     pcpu->irq_disable = TRUE;
177    
178     /* Take 1000 measures, each mesure every 10ms */
179     for(i=0;i<1000;i++) {
180     cur_ia = pcpu->ia;
181     h_index = (cur_ia >> 2) & (IDLE_HASH_SIZE-1);
182    
183     for(p=pc_hash[h_index];p;p=p->next)
184     if (p->ia == cur_ia) {
185     p->count++;
186     break;
187     }
188    
189     if (!p) {
190     if ((p = malloc(sizeof(*p)))) {
191     p->ia = cur_ia;
192     p->count = 1;
193     p->next = pc_hash[h_index];
194     pc_hash[h_index] = p;
195     }
196     }
197    
198     usleep(10000);
199     }
200    
201     /* Select PCs */
202     for(i=0,res_count=0;i<IDLE_HASH_SIZE;i++) {
203     for(p=pc_hash[i];p;p=p->next)
204     if ((p->count >= 20) && (p->count <= 80)) {
205     res = &cpu->idle_pc_prop[cpu->idle_pc_prop_count++];
206    
207     res->pc = p->ia;
208     res->count = p->count;
209    
210     if (cpu->idle_pc_prop_count >= CPU_IDLE_PC_MAX_RES)
211     goto done;
212     }
213     }
214    
215     done:
216     /* Set idle PC */
217     if (cpu->idle_pc_prop_count) {
218     printf("Done. Suggested idling PC:\n");
219    
220     for(i=0;i<cpu->idle_pc_prop_count;i++) {
221     printf(" 0x%llx (count=%u)\n",
222     cpu->idle_pc_prop[i].pc,
223     cpu->idle_pc_prop[i].count);
224     }
225    
226     printf("Restart the emulator with \"--idle-pc=0x%llx\" (for example)\n",
227     cpu->idle_pc_prop[0].pc);
228     } else {
229     printf("Done. No suggestion for idling PC\n");
230     }
231    
232     /* Re-enable IRQ */
233     pcpu->irq_disable = FALSE;
234     return(0);
235     }
236    
237     #if 0
238     /* Set an IRQ (VM IRQ standard routing) */
239     void ppc32_vm_set_irq(vm_instance_t *vm,u_int irq)
240     {
241     cpu_ppc_t *boot_cpu;
242    
243     boot_cpu = CPU_PPC32(vm->boot_cpu);
244    
245     if (boot_cpu->irq_disable) {
246     boot_cpu->irq_pending = 0;
247     return;
248     }
249    
250     ppc32_set_irq(boot_cpu,irq);
251    
252     if (boot_cpu->irq_idle_preempt[irq])
253     cpu_idle_break_wait(vm->boot_cpu);
254     }
255    
256     /* Clear an IRQ (VM IRQ standard routing) */
257     void ppc32_vm_clear_irq(vm_instance_t *vm,u_int irq)
258     {
259     cpu_ppc_t *boot_cpu;
260    
261     boot_cpu = CPU_PPC32(vm->boot_cpu);
262     ppc32_clear_irq(boot_cpu,irq);
263     }
264     #endif
265    
266     /* Generate an exception */
267     void ppc32_trigger_exception(cpu_ppc_t *cpu,u_int exc_vector)
268     {
269     //printf("TRIGGER_EXCEPTION: saving cpu->ia=0x%8.8x, msr=0x%8.8x\n",
270     // cpu->ia,cpu->msr);
271    
272     /* Save the return instruction address */
273     cpu->srr0 = cpu->ia;
274    
275     if (exc_vector == PPC32_EXC_SYSCALL)
276     cpu->srr0 += sizeof(ppc_insn_t);
277    
278     //printf("SRR0 = 0x%8.8x\n",cpu->srr0);
279    
280     /* Save Machine State Register (MSR) */
281     cpu->srr1 = cpu->msr & PPC32_EXC_SRR1_MASK;
282    
283     //printf("SRR1 = 0x%8.8x\n",cpu->srr1);
284    
285     /* Set the new SRR value */
286     cpu->msr &= ~PPC32_EXC_MSR_MASK;
287     cpu->irq_check = FALSE;
288    
289     //printf("MSR = 0x%8.8x\n",cpu->msr);
290    
291     /* Use bootstrap vectors ? */
292     if (cpu->msr & PPC32_MSR_IP)
293     cpu->ia = 0xFFF00000 + exc_vector;
294     else
295     cpu->ia = exc_vector;
296     }
297    
298     /* Trigger IRQs */
299     fastcall void ppc32_trigger_irq(cpu_ppc_t *cpu)
300     {
301     if (unlikely(cpu->irq_disable)) {
302     cpu->irq_pending = FALSE;
303     cpu->irq_check = FALSE;
304     return;
305     }
306    
307     /* Clear the IRQ check flag */
308     cpu->irq_check = FALSE;
309    
310     if (cpu->irq_pending && (cpu->msr & PPC32_MSR_EE)) {
311     cpu->irq_count++;
312     cpu->irq_pending = FALSE;
313     ppc32_trigger_exception(cpu,PPC32_EXC_EXT);
314     }
315     }
316    
317     /* Trigger the decrementer exception */
318     void ppc32_trigger_timer_irq(cpu_ppc_t *cpu)
319     {
320     cpu->timer_irq_count++;
321    
322     if (cpu->msr & PPC32_MSR_EE)
323     ppc32_trigger_exception(cpu,PPC32_EXC_DEC);
324     }
325    
326     /* Virtual breakpoint */
327     fastcall void ppc32_run_breakpoint(cpu_ppc_t *cpu)
328     {
329     cpu_log(cpu->gen,"BREAKPOINT",
330     "Virtual breakpoint reached at IA=0x%8.8x\n",cpu->ia);
331    
332     printf("[[[ Virtual Breakpoint reached at IA=0x%8.8x LR=0x%8.8x]]]\n",
333     cpu->ia,cpu->lr);
334    
335     ppc32_dump_regs(cpu->gen);
336     }
337    
338     /* Add a virtual breakpoint */
339     int ppc32_add_breakpoint(cpu_gen_t *cpu,m_uint64_t ia)
340     {
341     cpu_ppc_t *pcpu = CPU_PPC32(cpu);
342     int i;
343    
344     for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
345     if (!pcpu->breakpoints[i])
346     break;
347    
348     if (i == PPC32_MAX_BREAKPOINTS)
349     return(-1);
350    
351     pcpu->breakpoints[i] = ia;
352     pcpu->breakpoints_enabled = TRUE;
353     return(0);
354     }
355    
356     /* Remove a virtual breakpoint */
357     void ppc32_remove_breakpoint(cpu_gen_t *cpu,m_uint64_t ia)
358     {
359     cpu_ppc_t *pcpu = CPU_PPC32(cpu);
360     int i,j;
361    
362     for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
363     if (pcpu->breakpoints[i] == ia)
364     {
365     for(j=i;j<PPC32_MAX_BREAKPOINTS-1;j++)
366     pcpu->breakpoints[j] = pcpu->breakpoints[j+1];
367    
368     pcpu->breakpoints[PPC32_MAX_BREAKPOINTS-1] = 0;
369     }
370    
371     for(i=0;i<PPC32_MAX_BREAKPOINTS;i++)
372     if (pcpu->breakpoints[i] != 0)
373     return;
374    
375     pcpu->breakpoints_enabled = FALSE;
376     }
377    
378     /* Set a register */
379     void ppc32_reg_set(cpu_gen_t *cpu,u_int reg,m_uint64_t val)
380     {
381     if (reg < PPC32_GPR_NR)
382     CPU_PPC32(cpu)->gpr[reg] = (m_uint32_t)val;
383     }
384    
385     /* Dump registers of a PowerPC processor */
386     void ppc32_dump_regs(cpu_gen_t *cpu)
387     {
388     cpu_ppc_t *pcpu = CPU_PPC32(cpu);
389     int i;
390    
391     printf("PowerPC Registers:\n");
392    
393     for(i=0;i<PPC32_GPR_NR/4;i++) {
394     printf(" $%2d = 0x%8.8x $%2d = 0x%8.8x"
395     " $%2d = 0x%8.8x $%2d = 0x%8.8x\n",
396     i*4, pcpu->gpr[i*4], (i*4)+1, pcpu->gpr[(i*4)+1],
397     (i*4)+2, pcpu->gpr[(i*4)+2], (i*4)+3, pcpu->gpr[(i*4)+3]);
398     }
399    
400     printf("\n");
401     printf(" ia = 0x%8.8x, lr = 0x%8.8x\n", pcpu->ia, pcpu->lr);
402     printf(" cr = 0x%8.8x, msr = 0x%8.8x, xer = 0x%8.8x, dec = 0x%8.8x\n",
403     pcpu->cr, pcpu->msr,
404     pcpu->xer | (pcpu->xer_ca << PPC32_XER_CA_BIT),
405     pcpu->dec);
406    
407     printf(" sprg[0] = 0x%8.8x, sprg[1] = 0x%8.8x\n",
408     pcpu->sprg[0],pcpu->sprg[1]);
409    
410     printf(" sprg[2] = 0x%8.8x, sprg[3] = 0x%8.8x\n",
411     pcpu->sprg[2],pcpu->sprg[3]);
412    
413     printf("\n IRQ count: %llu, IRQ false positives: %llu, "
414     "IRQ Pending: %u, IRQ Check: %s\n",
415     pcpu->irq_count,pcpu->irq_fp_count,pcpu->irq_pending,
416     pcpu->irq_check ? "yes" : "no");
417    
418     printf(" Timer IRQ count: %llu, pending: %u, timer drift: %u\n\n",
419     pcpu->timer_irq_count,pcpu->timer_irq_pending,pcpu->timer_drift);
420    
421     printf("\n");
422     }
423    
424     /* Dump BAT registers */
425     static void ppc32_dump_bat(cpu_ppc_t *cpu,int index)
426     {
427     int i;
428    
429     for(i=0;i<PPC32_BAT_NR;i++)
430     printf(" BAT[%d] = 0x%8.8x 0x%8.8x\n",
431     i,cpu->bat[index][i].reg[0],cpu->bat[index][i].reg[1]);
432     }
433    
434     /* Dump MMU registers */
435     void ppc32_dump_mmu(cpu_gen_t *cpu)
436     {
437     cpu_ppc_t *pcpu = CPU_PPC32(cpu);
438     int i;
439    
440     printf("PowerPC MMU Registers:\n");
441    
442     printf(" - IBAT Registers:\n");
443     ppc32_dump_bat(pcpu,PPC32_IBAT_IDX);
444    
445     printf(" - DBAT Registers:\n");
446     ppc32_dump_bat(pcpu,PPC32_DBAT_IDX);
447    
448     printf(" - Segment Registers:\n");
449     for(i=0;i<PPC32_SR_NR;i++)
450     printf(" SR[%d] = 0x%8.8x\n",i,pcpu->sr[i]);
451    
452     printf(" - SDR1: 0x%8.8x\n",pcpu->sdr1);
453     }
454    
455     /* Load a raw image into the simulated memory */
456     int ppc32_load_raw_image(cpu_ppc_t *cpu,char *filename,m_uint32_t vaddr)
457     {
458     struct stat file_info;
459     size_t len,clen;
460     m_uint32_t remain;
461     void *haddr;
462     FILE *bfd;
463    
464     if (!(bfd = fopen(filename,"r"))) {
465     perror("fopen");
466     return(-1);
467     }
468    
469     if (fstat(fileno(bfd),&file_info) == -1) {
470     perror("stat");
471     return(-1);
472     }
473    
474     len = file_info.st_size;
475    
476     printf("Loading RAW file '%s' at virtual address 0x%8.8x (size=%lu)\n",
477     filename,vaddr,(u_long)len);
478    
479     while(len > 0)
480     {
481     haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE);
482    
483     if (!haddr) {
484     fprintf(stderr,"load_raw_image: invalid load address 0x%8.8x\n",
485     vaddr);
486     return(-1);
487     }
488    
489     if (len > PPC32_MIN_PAGE_SIZE)
490     clen = PPC32_MIN_PAGE_SIZE;
491     else
492     clen = len;
493    
494     remain = MIPS_MIN_PAGE_SIZE;
495     remain -= (vaddr - (vaddr & MIPS_MIN_PAGE_MASK));
496    
497     clen = m_min(clen,remain);
498    
499     if (fread((u_char *)haddr,clen,1,bfd) != 1)
500     break;
501    
502     vaddr += clen;
503     len -= clen;
504     }
505    
506     fclose(bfd);
507     return(0);
508     }
509    
510     /* Load an ELF image into the simulated memory */
511     int ppc32_load_elf_image(cpu_ppc_t *cpu,char *filename,int skip_load,
512     m_uint32_t *entry_point)
513     {
514     m_uint32_t vaddr,remain;
515     void *haddr;
516     Elf32_Ehdr *ehdr;
517     Elf32_Shdr *shdr;
518     Elf_Scn *scn;
519     Elf *img_elf;
520     size_t len,clen;
521     char *name;
522     int i,fd;
523     FILE *bfd;
524    
525     if (!filename)
526     return(-1);
527    
528     #ifdef __CYGWIN__
529     fd = open(filename,O_RDONLY|O_BINARY);
530     #else
531     fd = open(filename,O_RDONLY);
532     #endif
533    
534     if (fd == -1) {
535     perror("load_elf_image: open");
536     return(-1);
537     }
538    
539     if (elf_version(EV_CURRENT) == EV_NONE) {
540     fprintf(stderr,"load_elf_image: library out of date\n");
541     return(-1);
542     }
543    
544     if (!(img_elf = elf_begin(fd,ELF_C_READ,NULL))) {
545     fprintf(stderr,"load_elf_image: elf_begin: %s\n",
546     elf_errmsg(elf_errno()));
547     return(-1);
548     }
549    
550     if (!(ehdr = elf32_getehdr(img_elf))) {
551     fprintf(stderr,"load_elf_image: invalid ELF file\n");
552     return(-1);
553     }
554    
555     printf("Loading ELF file '%s'...\n",filename);
556     bfd = fdopen(fd,"rb");
557    
558     if (!bfd) {
559     perror("load_elf_image: fdopen");
560     return(-1);
561     }
562    
563     if (!skip_load) {
564     for(i=0;i<ehdr->e_shnum;i++) {
565     scn = elf_getscn(img_elf,i);
566    
567     shdr = elf32_getshdr(scn);
568     name = elf_strptr(img_elf, ehdr->e_shstrndx, (size_t)shdr->sh_name);
569     len = shdr->sh_size;
570    
571     if (!(shdr->sh_flags & SHF_ALLOC) || !len)
572     continue;
573    
574     fseek(bfd,shdr->sh_offset,SEEK_SET);
575     vaddr = shdr->sh_addr;
576    
577     if (cpu->vm->debug_level > 0) {
578     printf(" * Adding section at virtual address 0x%8.8x "
579     "(len=0x%8.8lx)\n",vaddr,(u_long)len);
580     }
581    
582     while(len > 0)
583     {
584     haddr = cpu->mem_op_lookup(cpu,vaddr,PPC32_MTS_DCACHE);
585    
586     if (!haddr) {
587     fprintf(stderr,"load_elf_image: invalid load address 0x%x\n",
588     vaddr);
589     return(-1);
590     }
591    
592     if (len > PPC32_MIN_PAGE_SIZE)
593     clen = PPC32_MIN_PAGE_SIZE;
594     else
595     clen = len;
596    
597     remain = PPC32_MIN_PAGE_SIZE;
598     remain -= (vaddr - (vaddr & PPC32_MIN_PAGE_MASK));
599    
600     clen = m_min(clen,remain);
601    
602     if (fread((u_char *)haddr,clen,1,bfd) < 1)
603     break;
604    
605     vaddr += clen;
606     len -= clen;
607     }
608     }
609     } else {
610     printf("ELF loading skipped, using a ghost RAM file.\n");
611     }
612    
613     printf("ELF entry point: 0x%x\n",ehdr->e_entry);
614    
615     if (entry_point)
616     *entry_point = ehdr->e_entry;
617    
618     elf_end(img_elf);
619     fclose(bfd);
620     return(0);
621     }

  ViewVC Help
Powered by ViewVC 1.1.26